diff --git a/README.md b/README.md index 755b648e6..bdac8ddf2 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ - [Install](#install) - [Roadmap](#roadmap) - [Getting help](#getting-help) -- [Contributing](#contributing) +- [Contribute](#contribute) # Introduction diff --git a/bin/doom b/bin/doom index 46a06bf8d..6ce7dc813 100755 --- a/bin/doom +++ b/bin/doom @@ -5,25 +5,28 @@ :; case "$VERSION" in *\ 2[0-5].[0-9]) echo "Detected Emacs $VERSION"; echo "Doom only supports Emacs 26.1 and newer"; echo; exit 2 ;; esac :; DOOMBASE="$(dirname "$0")/.." :; [ "$1" = -d ] || [ "$1" = --debug ] && { shift; export DEBUG=1; } -:; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l bin/doom "$@"; exit 0; } +:; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l init.el -f doom-run-all-startup-hooks-h "$@"; exit 0; } :; exec $EMACS --no-site-file --script "$0" -- "$@" :; exit 0 -(let* ((load-prefer-newer t) - (loaddir (file-name-directory (file-truename load-file-name))) +(let* ((loaddir (file-name-directory (file-truename load-file-name))) (emacsdir (getenv "EMACSDIR")) (user-emacs-directory - (abbreviate-file-name (or emacsdir (expand-file-name "../" loaddir))))) + (abbreviate-file-name + (if emacsdir + (file-name-as-directory emacsdir) + (expand-file-name "../" loaddir))))) + + ;; + (load (expand-file-name "core/core.el" user-emacs-directory) nil t) ;; HACK Load `cl' and site files manually so we can stop them from polluting ;; CLI logs with deprecation and file load messages. - (let ((inhibit-message t)) - (when (> emacs-major-version 26) - (require 'cl)) - (load "site-start" t t)) + (quiet! (when (> emacs-major-version 26) + (require 'cl)) + (load "site-start" t t)) - (push (expand-file-name "core" user-emacs-directory) load-path) - (require 'core) + (doom-log "Initializing Doom CLI") (require 'core-cli) (defcli! :main @@ -45,10 +48,10 @@ with a different private module." (setq user-emacs-directory (file-name-as-directory emacsdir)) (print! (info "EMACSDIR=%s") localdir)) (when doomdir - (setenv "DOOMDIR" doomdir) + (setenv "DOOMDIR" (file-name-as-directory doomdir)) (print! (info "DOOMDIR=%s") localdir)) (when localdir - (setenv "DOOMLOCALDIR" localdir) + (setenv "DOOMLOCALDIR" (file-name-as-directory localdir)) (print! (info "DOOMLOCALDIR=%s") localdir)) (when debug-p (setenv "DEBUG" "1") @@ -75,54 +78,50 @@ with a different private module." (when (or emacsdir doomdir localdir) (load! "core/core.el" user-emacs-directory)) - (cond ((not noninteractive) - (print! "Doom launched out of %s (test mode)" (path user-emacs-directory)) - (load! "init.el" user-emacs-directory) - (doom-run-all-startup-hooks-h)) - - ((null command) - (doom-cli-execute "help")) - - ((condition-case e - (let ((start-time (current-time))) - (and (doom-cli-execute command args) - (print! (success "Finished! (%.4fs)") - (float-time - (time-subtract (current-time) - start-time))))) - (user-error - (print! (error "%s\n") (error-message-string e)) - (print! (yellow "See 'doom help %s' for documentation on this command.") (car args)) - (error "")) ; Ensure non-zero exit code - ((debug error) - (print! (error "There was an unexpected error:")) - (print-group! - (print! "%s %s" (bold "Type:") (car e)) - (print! (bold "Message:")) - (print-group! - (print! "%s" (get (car e) 'error-message))) - (print! (bold "Data:")) - (print-group! - (if (cdr e) - (dolist (item (cdr e)) - (print! "%S" item)) - (print! "n/a"))) - (when (featurep 'straight) - (when (string-match-p (regexp-quote straight-process-buffer) - (error-message-string e)) - (print! (bold "Straight output:")) - (print-group! (print! "%s" (straight--process-get-output)))))) - (unless debug-on-error - (terpri) - (print! - (concat "Run the command again with the -d (or --debug) switch to enable debug\n" - "mode and (hopefully) generate a backtrace from this error:\n" - "\n %s\n\n" - "If you file a bug report, please include it!") - (string-join (append (list (file-name-nondirectory load-file-name) "-d" command) - args) - " ")) - (error ""))))))) ; Ensure non-zero exit code + (let (print-level print-gensym print-length) + (condition-case e + (if (null command) + (doom-cli-execute "help") + (let ((start-time (current-time))) + (and (doom-cli-execute command args) + (print! (success "Finished! (%.4fs)") + (float-time + (time-subtract (current-time) + start-time)))))) + (user-error + (print! (error "%s\n") (error-message-string e)) + (print! (yellow "See 'doom help %s' for documentation on this command.") (car args)) + (error "")) ; Ensure non-zero exit code + ((debug error) + (print! (error "There was an unexpected error:")) + (print-group! + (print! "%s %s" (bold "Type:") (car e)) + (print! (bold "Message:")) + (print-group! + (print! "%s" (get (car e) 'error-message))) + (print! (bold "Data:")) + (print-group! + (if (cdr e) + (dolist (item (cdr e)) + (print! "%S" item)) + (print! "n/a"))) + (when (and (bound-and-true-p straight-process-buffer) + (string-match-p (regexp-quote straight-process-buffer) + (error-message-string e))) + (print! (bold "Straight output:")) + (print-group! (print! "%s" (straight--process-get-output))))) + (unless debug-on-error + (terpri) + (print! + (concat "Run the command again with the -d (or --debug) switch to enable debug\n" + "mode and (hopefully) generate a backtrace from this error:\n" + "\n %s\n\n" + "If you file a bug report, please include it!") + (string-join (append (list (file-name-nondirectory load-file-name) "-d" command) + args) + " ")) + ;; Ensure non-zero exit code + (error "")))))) (doom-cli-execute :main (cdr (member "--" argv))) (setq argv nil)) diff --git a/core/autoload/config.el b/core/autoload/config.el index cacd0d32d..02d78d725 100644 --- a/core/autoload/config.el +++ b/core/autoload/config.el @@ -55,14 +55,19 @@ And jumps to your `doom!' block." (defmacro doom--if-compile (command on-success &optional on-failure) (declare (indent 2)) - `(with-current-buffer (compile ,command) - (add-hook - 'compilation-finish-functions - (lambda (_buf status) - (if (equal status "finished\n") - ,on-success - ,on-failure)) - nil 'local))) + (let ((windowsym (make-symbol "doom-sync-window"))) + `(with-current-buffer (compile ,command t) + (let ((,windowsym (get-buffer-window (current-buffer)))) + (select-window ,windowsym) + (add-hook + 'compilation-finish-functions + (lambda (_buf status) + (if (equal status "finished\n") + (progn + (delete-window ,windowsym) + ,on-success) + ,on-failure)) + nil 'local))))) ;;;###autoload (defun doom/reload () @@ -89,7 +94,7 @@ Runs `doom-reload-hook' afterwards." (doom-initialize-modules 'force) (general-auto-unbind-keys t))) (run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook) - (print! (success "Config successfully reloaded!"))) + (message "Config successfully reloaded!")) (user-error "Failed to reload your config"))) ;;;###autoload diff --git a/core/autoload/debug.el b/core/autoload/debug.el index c6d58e8f9..ed1683be7 100644 --- a/core/autoload/debug.el +++ b/core/autoload/debug.el @@ -1,5 +1,36 @@ ;;; core/autoload/debug.el -*- lexical-binding: t; -*- +;; +;;; Doom's debug mode + +;;;###autoload +(defvar doom-debug-variables + '(doom-debug-mode + init-file-debug + debug-on-error + garbage-collection-messages + use-package-verbose + jka-compr-verbose + lsp-log-io + gcmh-verbose + magit-refresh-verbose + url-debug) + "A list of variable to toggle on `doom/toggle-debug-mode'.") + +;;;###autoload +(defun doom/toggle-debug-mode (&optional arg) + "Toggle `debug-on-error' and `doom-debug-mode' for verbose logging." + (interactive (list (or current-prefix-arg 'toggle))) + (let ((value + (cond ((eq arg 'toggle) (not doom-debug-mode)) + ((> (prefix-numeric-value arg) 0))))) + (mapc (doom-rpartial #'set value) doom-debug-variables) + (message "Debug mode %s" (if value "on" "off")))) + + +;; +;;; Hooks + ;;;###autoload (defun doom-run-all-startup-hooks-h () "Run all startup Emacs hooks. Meant to be executed after starting Emacs with @@ -17,18 +48,12 @@ ;; ;;; Helpers -(defun doom-template-insert (template) - "TODO" - (let ((file (expand-file-name (format "templates/%s" template) doom-core-dir))) - (when (file-exists-p file) - (insert-file-contents file)))) - (defsubst doom--collect-forms-in (file form) (when (file-readable-p file) (let (forms) (with-temp-buffer (insert-file-contents file) - (delay-mode-hooks (emacs-lisp-mode)) + (let (emacs-lisp-mode) (emacs-lisp-mode)) (while (re-search-forward (format "(%s " (regexp-quote form)) nil t) (let ((ppss (syntax-ppss))) (unless (or (nth 4 ppss) @@ -45,7 +70,7 @@ ready to be pasted in a bug report on github." (require 'vc-git) (require 'core-packages) (let ((default-directory doom-emacs-dir) - (doom-modules (doom-modules))) + (doom-modules (doom-module-list))) (letf! (defun sh (&rest args) (cdr (apply #'doom-call-process args))) `((emacs (version . ,emacs-version) @@ -243,48 +268,62 @@ Some items are not supported by the `nsm.el' module." (file (make-temp-file "doom-sandbox-"))) (require 'package) (with-temp-file file - (insert - (prin1-to-string - (macroexp-progn - (append `((setq noninteractive nil - init-file-debug t - load-path ',load-path - package--init-file-ensured t - package-user-dir ,package-user-dir - package-archives ',package-archives - user-emacs-directory ,doom-emacs-dir) - (with-eval-after-load 'undo-tree - ;; undo-tree throws errors because `buffer-undo-tree' isn't - ;; corrrectly initialized - (setq-default buffer-undo-tree (make-undo-tree)))) - (pcase mode - (`vanilla-doom+ ; Doom core + modules - private config - `((load-file ,(expand-file-name "core.el" doom-core-dir)) - (doom-initialize) - (doom-initialize-core) - (add-hook 'window-setup-hook #'doom-display-benchmark-h) - (setq doom-modules ',doom-modules) - (maphash (lambda (key plist) - (let ((doom--current-module key) - (doom--current-flags (plist-get plist :flags))) - (load! "init" (doom-module-locate-path (car key) (cdr key)) t))) - doom-modules) - (maphash (lambda (key plist) - (let ((doom--current-module key) - (doom--current-flags (plist-get plist :flags))) - (load! "config" (doom-module-locate-path (car key) (cdr key)) t))) - doom-modules) - (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) - (doom-run-all-startup-hooks-h))) - (`vanilla-doom ; only Doom core - `((load-file ,(expand-file-name "core.el" doom-core-dir)) - (doom-initialize) - (doom-initialize-core) - (doom-run-all-startup-hooks-h))) - (`vanilla ; nothing loaded - `((package-initialize))))))) - "\n(unwind-protect (progn\n" contents "\n)\n" - (format "(delete-file %S))" file))) + (prin1 `(progn + (setq noninteractive nil + process-environment ',doom--initial-process-environment + exec-path ',doom--initial-exec-path + init-file-debug t + load-path ',load-path + package--init-file-ensured t + package-user-dir ,package-user-dir + package-archives ',package-archives + user-emacs-directory ,doom-emacs-dir) + (with-eval-after-load 'undo-tree + ;; undo-tree throws errors because `buffer-undo-tree' isn't + ;; correctly initialized + (setq-default buffer-undo-tree (make-undo-tree))) + (ignore-errors + (delete-directory ,(expand-file-name "auto-save-list" doom-emacs-dir) 'parents))) + (current-buffer)) + (prin1 `(unwind-protect + (defun --run-- () ,(read (concat "(progn\n" contents "\n)"))) + (delete-file ,file)) + (current-buffer)) + (prin1 (pcase mode + (`vanilla-doom+ ; Doom core + modules - private config + `(progn + (load-file ,(expand-file-name "core.el" doom-core-dir)) + (setq doom-modules-dirs (list doom-modules-dir)) + (let ((doom-init-modules-p t)) + (doom-initialize) + (doom-initialize-core-modules)) + (setq doom-modules ',doom-modules) + (--run--) + (maphash (lambda (key plist) + (let ((doom--current-module key) + (doom--current-flags (plist-get plist :flags))) + (load! "init" (doom-module-locate-path (car key) (cdr key)) t))) + doom-modules) + (maphash (lambda (key plist) + (let ((doom--current-module key) + (doom--current-flags (plist-get plist :flags))) + (load! "config" (doom-module-locate-path (car key) (cdr key)) t))) + doom-modules) + (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) + (doom-run-all-startup-hooks-h))) + (`vanilla-doom ; only Doom core + `(progn + (load-file ,(expand-file-name "core.el" doom-core-dir)) + (let ((doom-init-modules-p t)) + (doom-initialize) + (doom-initialize-core-modules)) + (--run--) + (doom-run-all-startup-hooks-h))) + (`vanilla ; nothing loaded + `(progn + (package-initialize) + (--run--)))) + (current-buffer))) (let ((args (if (eq mode 'doom) (list "-l" file) (list "-Q" "-l" file)))) @@ -343,7 +382,7 @@ to reproduce bugs and determine if Doom is to blame." (doom-sandbox-emacs-lisp-mode) (setq-local default-directory doom-emacs-dir) (unless (buffer-live-p exists) - (doom-template-insert "VANILLA_SANDBOX") + (insert-file-contents (doom-glob doom-core-dir "templates/VANILLA_SANDBOX")) (let ((contents (substitute-command-keys (buffer-string)))) (erase-buffer) (insert contents "\n"))) @@ -378,20 +417,3 @@ will be automatically appended to the result." (profiler-report) (profiler-stop)) (setq doom--profiler (not doom--profiler))) - -;;;###autoload -(defun doom/toggle-debug-mode (&optional arg) - "Toggle `debug-on-error' and `doom-debug-mode' for verbose logging." - (interactive (list (or current-prefix-arg 'toggle))) - (let ((value - (cond ((eq arg 'toggle) (not doom-debug-mode)) - ((> (prefix-numeric-value arg) 0))))) - (setq doom-debug-mode value - debug-on-error value - garbage-collection-messages value - use-package-verbose value - jka-compr-verbose value - lsp-log-io value - gcmh-verbose value - magit-refresh-verbose value) - (message "Debug mode %s" (if value "on" "off")))) diff --git a/core/autoload/help.el b/core/autoload/help.el index 060731b18..4bff9a079 100644 --- a/core/autoload/help.el +++ b/core/autoload/help.el @@ -115,7 +115,8 @@ selection of all minor-modes, active or not." (require 'org) (let* ((default-directory doom-docs-dir) (org-agenda-files (mapcar #'expand-file-name (doom-enlist files))) - (depth (if (integerp depth) depth))) + (depth (if (integerp depth) depth)) + (org-inhibit-startup t)) (message "Loading search results...") (unwind-protect (delq @@ -218,7 +219,7 @@ selection of all minor-modes, active or not." :prompt "Search documentation for: ")) ;;;###autoload -(defun doom/help-news-search (&optional initial-input) +(defun doom/help-search-news (&optional initial-input) "Search headlines in Doom's newsletters." (interactive) (doom-completing-read-org-headings @@ -427,8 +428,8 @@ If prefix arg is present, refresh the cache." (let ((guess (or (function-called-at-point) (symbol-at-point)))) (require 'finder-inf nil t) - (require 'core-packages) - (doom-initialize-packages) + (require 'package) + (require 'straight) (let ((packages (delete-dups (append (mapcar #'car package-alist) (mapcar #'car package--builtins) @@ -503,7 +504,7 @@ If prefix arg is present, refresh the cache." (modules (if (gethash (symbol-name package) straight--build-cache) (doom-package-get package :modules) - (plist-get (cdr (assq package (doom-packages-list 'all))) + (plist-get (cdr (assq package (doom-package-list 'all))) :modules))) (package--print-help-section "Modules") (insert "Declared by the following Doom modules:\n") diff --git a/core/autoload/packages.el b/core/autoload/packages.el index 349f34805..2c0825a9b 100644 --- a/core/autoload/packages.el +++ b/core/autoload/packages.el @@ -1,276 +1,5 @@ ;;; core/autoload/packages.el -*- lexical-binding: t; -*- -;; -;;; Package metadata - -;;;###autoload -(defun doom-package-get (package &optional prop nil-value) - "Returns PACKAGE's `package!' recipe from `doom-packages'." - (let ((plist (cdr (assq package doom-packages)))) - (if prop - (if (plist-member plist prop) - (plist-get plist prop) - nil-value) - plist))) - -;;;###autoload -(defun doom-package-set (package prop value) - "Set PROPERTY in PACKAGE's recipe to VALUE." - (setf (alist-get package doom-packages) - (plist-put (alist-get package doom-packages) - prop value))) - -;;;###autoload -(defun doom-package-recipe (package &optional prop nil-value) - "Returns the `straight' recipe PACKAGE was registered with." - (let ((plist (gethash (symbol-name package) straight--recipe-cache))) - (if prop - (if (plist-member plist prop) - (plist-get plist prop) - nil-value) - plist))) - -;;;###autoload -(defun doom-package-recipe-repo (package) - "Resolve and return PACKAGE's (symbol) local-repo property." - (if-let* ((recipe (cdr (straight-recipes-retrieve package))) - (repo (straight-vc-local-repo-name recipe))) - repo - (symbol-name package))) - -;;;###autoload -(defun doom-package-build-recipe (package &optional prop nil-value) - "Returns the `straight' recipe PACKAGE was installed with." - (let ((plist (nth 2 (gethash (symbol-name package) straight--build-cache)))) - (if prop - (if (plist-member plist prop) - (plist-get plist prop) - nil-value) - plist))) - -;;;###autoload -(defun doom-package-build-time (package) - "TODO" - (car (gethash (symbol-name package) straight--build-cache))) - -;;;###autoload -(defun doom-package-dependencies (package &optional recursive _noerror) - "Return a list of dependencies for a package." - (let ((deps (nth 1 (gethash (symbol-name package) straight--build-cache)))) - (if recursive - (nconc deps (mapcan (lambda (dep) (doom-package-dependencies dep t t)) - deps)) - deps))) - -;;;###autoload -(defun doom-package-depending-on (package &optional noerror) - "Return a list of packages that depend on the package named NAME." - (cl-check-type name symbol) - ;; can't get dependencies for built-in packages - (unless (or (doom-package-build-recipe name) - noerror) - (error "Couldn't find %s, is it installed?" name)) - (cl-loop for pkg in (hash-table-keys straight--build-cache) - for deps = (doom-package-dependencies pkg) - if (memq package deps) - collect pkg - and append (doom-package-depending-on pkg t))) - - -;; -;;; Predicate functions - -;;;###autoload -(defun doom-package-built-in-p (package) - "Return non-nil if PACKAGE (a symbol) is built-in." - (eq (doom-package-build-recipe package :type) - 'built-in)) - -;;;###autoload -(defun doom-package-installed-p (package) - "Return non-nil if PACKAGE (a symbol) is installed." - (file-directory-p (straight--build-dir (symbol-name package)))) - -;;;###autoload -(defun doom-package-registered-p (package) - "Return non-nil if PACKAGE (a symbol) has been registered with `package!'. - -Excludes packages that have a non-nil :built-in property." - (when-let (plist (doom-package-get package)) - (not (plist-get plist :ignore)))) - -;;;###autoload -(defun doom-package-private-p (package) - "Return non-nil if PACKAGE was installed by the user's private config." - (assq :private (doom-package-get package :modules))) - -;;;###autoload -(defun doom-package-protected-p (package) - "Return non-nil if PACKAGE is protected. - -A protected package cannot be deleted and will be auto-installed if missing." - (memq package doom-core-packages)) - -;;;###autoload -(defun doom-package-core-p (package) - "Return non-nil if PACKAGE is a core Doom package." - (or (doom-package-protected-p package) - (assq :core (doom-package-get package :modules)))) - -;;;###autoload -(defun doom-package-backend (package) - "Return 'straight, 'builtin, 'elpa or 'other, depending on how PACKAGE is -installed." - (cond ((gethash (symbol-name package) straight--build-cache) - 'straight) - ((or (doom-package-built-in-p package) - (assq package package--builtins)) - 'builtin) - ((assq package package-alist) - 'elpa) - ((locate-library (symbol-name package)) - 'other))) - -;;;###autoload -(defun doom-package-different-recipe-p (name) - "Return t if a package named NAME (a symbol) has a different recipe than it -was installed with." - (cl-check-type name symbol) - ;; TODO - ;; (when (doom-package-installed-p name) - ;; (when-let* ((doom-recipe (assq name doom-packages)) - ;; (install-recipe (doom-package-recipe))) - ;; (not (equal (cdr quelpa-recipe) - ;; (cdr (plist-get (cdr doom-recipe) :recipe)))))) - ) - - -;; -;;; Package list getters - -(defun doom--read-module-packages-file (file &optional noeval noerror) - (with-temp-buffer ; prevent buffer-local settings from propagating - (condition-case e - (if (not noeval) - (load file noerror t t) - (when (file-readable-p file) - (insert-file-contents file) - (delay-mode-hooks (emacs-lisp-mode)) - (while (search-forward "(package! " nil t) - (save-excursion - (goto-char (match-beginning 0)) - (unless (let ((ppss (syntax-ppss))) - (or (nth 3 ppss) - (nth 4 ppss))) - (cl-destructuring-bind (name . plist) - (cdr (sexp-at-point)) - (push (cons - name (plist-put - plist :modules - (list (doom-module-from-path file)))) - doom-packages))))))) - ((debug error) - (signal 'doom-package-error - (list (doom-module-from-path file) - file - e)))))) - -;;;###autoload -(defun doom-package-list (&optional all-p) - "Retrieve a list of explicitly declared packages from enabled modules. - -This excludes core packages listed in `doom-core-packages'. - -If ALL-P, gather packages unconditionally across all modules, including disabled -ones." - (let ((doom-interactive-mode t) - (doom-modules (doom-modules)) - doom-packages) - (doom--read-module-packages-file - (doom-path doom-core-dir "packages.el") all-p t) - (let ((private-packages (doom-path doom-private-dir "packages.el"))) - (unless all-p - ;; We load the private packages file twice to ensure disabled packages - ;; are seen ASAP, and a second time to ensure privately overridden - ;; packages are properly overwritten. - (doom--read-module-packages-file private-packages nil t)) - (if all-p - (mapc #'doom--read-module-packages-file - (doom-files-in doom-modules-dir - :depth 2 - :match "/packages\\.el$")) - (cl-loop for key being the hash-keys of doom-modules - for path = (doom-module-path (car key) (cdr key) "packages.el") - for doom--current-module = key - do (doom--read-module-packages-file path nil t))) - (doom--read-module-packages-file private-packages all-p t)) - (nreverse doom-packages))) - -;;;###autoload -(defun doom-package-pinned-list () - "Return an alist mapping package names (strings) to pinned commits (strings)." - (let (alist) - (dolist (package doom-packages alist) - (cl-destructuring-bind (name &key disable ignore pin unpin &allow-other-keys) - package - (when (and (not ignore) - (not disable) - (or pin unpin)) - (setf (alist-get (doom-package-recipe-repo name) alist - nil 'remove #'equal) - (unless unpin pin))))))) - -;;;###autoload -(defun doom-package-unpinned-list () - "Return an alist mapping package names (strings) to pinned commits (strings)." - (let (alist) - (dolist (package doom-packages alist) - (cl-destructuring-bind - (_ &key recipe disable ignore pin unpin &allow-other-keys) - package - (when (and (not ignore) - (not disable) - (or unpin - (and (plist-member recipe :pin) - (null pin)))) - (cl-pushnew (doom-package-recipe-repo (car package)) alist - :test #'equal)))))) - -;;;###autoload -(defun doom-package-recipe-list () - "Return straight recipes for non-builtin packages with a local-repo." - (let (recipes) - (dolist (recipe (hash-table-values straight--recipe-cache)) - (cl-destructuring-bind (&key local-repo type no-build &allow-other-keys) - recipe - (unless (or (null local-repo) - (eq type 'built-in) - no-build) - (push recipe recipes)))) - (nreverse recipes))) - -;;;###autoload -(defmacro doom-with-package-recipes (recipes binds &rest body) - "TODO" - (declare (indent 2)) - (let ((recipe-var (make-symbol "recipe")) - (recipes-var (make-symbol "recipes"))) - `(let* ((,recipes-var ,recipes) - (built ()) - (straight-use-package-pre-build-functions - (cons (lambda (pkg) (cl-pushnew pkg built :test #'equal)) - straight-use-package-pre-build-functions))) - (dolist (,recipe-var ,recipes-var) - (cl-block nil - (straight--with-plist (append (list :recipe ,recipe-var) ,recipe-var) - ,(doom-enlist binds) - ,@body))) - (nreverse built)))) - - -;; -;;; Main functions - ;;;###autoload (defun doom/reload-packages () "Reload `doom-packages', `package' and `quelpa'." @@ -280,47 +9,192 @@ ones." (doom-initialize-packages t) (message "Reloading packages...DONE")) -;;;###autoload -(defun doom/update-pinned-package-form (&optional select) - "Inserts or updates a `:pin' for the `package!' statement at point. +;; +;;; Bump commands + +(defun doom--package-full-recipe (package plist) + (doom-plist-merge + (plist-get plist :recipe) + (or (cdr (straight-recipes-retrieve package)) + (plist-get (cdr (assq package doom-packages)) + :recipe)))) + +(defun doom--package-to-bump-string (package plist) + "Return a PACKAGE and its PLIST in 'username/repo@commit' format." + (format "%s@%s" + (plist-get (doom--package-full-recipe package plist) :repo) + (substring-no-properties (plist-get plist :pin) 0 7))) + +(defun doom--package-at-point (&optional point) + "Return the package and plist from the (package! PACKAGE PLIST...) at point." + (save-match-data + (save-excursion + (and point (goto-char point)) + (if (eq (sexp-at-point) 'package!) + (progn + (backward-sexp) + (backward-char) + t) + (while (and (search-backward "(package!" nil t) + (doom-point-in-string-or-comment-p)))) + (unless (or (doom-point-in-string-or-comment-p) + (not (eq (car-safe (sexp-at-point)) 'package!))) + (cl-destructuring-bind (beg . end) + (bounds-of-thing-at-point 'sexp) + (let ((package (let (doom-packages) + (eval (sexp-at-point) t)))) + (list :beg beg + :end end + :package (car package) + :plist (cdr package)))))))) + +;;;###autoload +(defun doom/bumpify-package-at-point () + "Convert `package!' call at point to a bump string." + (interactive) + (cl-destructuring-bind (&key package plist beg end) + (doom--package-at-point) + (when-let (str (doom--package-to-bump-string package plist)) + (goto-char beg) + (delete-region beg end) + (insert str)))) + +;;;###autoload +(defun doom/bumpify-packages-in-buffer () + "Convert all `package!' calls in buffer into bump strings." + (interactive) + (save-excursion + (goto-char (point-min)) + (while (search-forward "(package!" nil t) + (unless (doom-point-in-string-or-comment-p) + (doom/bumpify-package-at-point))))) + +;;;###autoload +(defun doom/bump-package-at-point (&optional select) + "Inserts or updates a `:pin' for the `package!' statement at point. +Grabs the latest commit id of the package using 'git'." + (interactive "P") + (doom-initialize-packages) + (cl-destructuring-bind (&key package plist beg end) + (or (doom--package-at-point) + (user-error "Not on a `package!' call")) + (let* ((recipe (doom--package-full-recipe package plist)) + (branch (or (plist-get recipe :branch) + straight-vc-git-default-branch)) + (oldid (or (plist-get plist :pin) + (doom-package-get package :pin))) + id url) + (when (or (eq (plist-get plist :built-in) t) + (and (eq (plist-get plist :built-in) 'prefer) + (locate-library (symbol-name package)))) + (user-error "%s: skipping built-in package" package)) + (setq url (straight-vc-git--destructure recipe (upstream-repo upstream-host) + (straight-vc-git--encode-url upstream-repo upstream-host)) + id (when url + (cdr (doom-call-process + "git" "ls-remote" url + (unless select + (or branch straight-vc-git-default-branch)))))) + (unless id + (user-error "%s: no id from %s" package url)) + (let* ((id (car (split-string + (if select + (completing-read "Commit: " (split-string id "\n" t)) + id))))) + (when (and oldid (equal oldid id)) + (user-error "%s: no update necessary" package)) + (save-excursion + (if (re-search-forward ":pin +\"\\([^\"]+\\)\"" end t) + (replace-match id t t nil 1) + (thing-at-point--end-of-sexp) + (backward-char) + (insert " :pin " (prin1-to-string id)))) + (message "Updated %S: %s -> %s" + package + (substring oldid 0 10) + (substring id 0 10)))))) + +;;;###autoload +(defun doom/bump-packages-in-buffer (&optional select) + "Inserts or updates a `:pin' for the `package!' statement at point. Grabs the latest commit id of the package using 'git'." (interactive "P") - ;; REVIEW Better error handling - ;; TODO Insert a new `package!' if no `package!' at poin - (require 'straight) - (ignore-errors - (while (and (atom (sexp-at-point)) - (not (bolp))) - (forward-sexp -1))) (save-excursion - (if (not (eq (sexp-at-point) 'package!)) - (user-error "Not on a `package!' call") - (backward-char) - (let* ((recipe (cdr (sexp-at-point))) - (package (car recipe)) - (oldid (doom-package-get package :pin)) - (id - (cdr (doom-call-process - "git" "ls-remote" - (straight-vc-git--destructure - (doom-plist-merge - (plist-get (cdr recipe) :recipe) - (or (cdr (straight-recipes-retrieve package)) - (plist-get (cdr (assq package doom-packages)) :recipe))) - (upstream-repo upstream-host) - (straight-vc-git--encode-url upstream-repo upstream-host)))))) - (unless id - (user-error "No id for %S package" package)) - (let* ((id (if select - (car (split-string (completing-read "Commit: " (split-string id "\n" t)))) - (car (split-string id)))) - (id (substring id 0 10))) - (if (and oldid (string-match-p (concat "^" oldid) id)) - (user-error "No update necessary") - (if (re-search-forward ":pin +\"\\([^\"]+\\)\"" (cdr (bounds-of-thing-at-point 'sexp)) t) - (replace-match id t t nil 1) - (thing-at-point--end-of-sexp) - (backward-char) - (insert " :pin " (prin1-to-string id))) - (message "Updated %S: %s -> %s" package oldid id))))))) + (goto-char (point-min)) + (doom-initialize-packages) + (let (packages) + (while (search-forward "(package! " nil t) + (unless (let ((ppss (syntax-ppss))) + (or (nth 4 ppss) + (nth 3 ppss) + (save-excursion + (and (goto-char (match-beginning 0)) + (not (plist-member (sexp-at-point) :pin)))))) + (condition-case e + (push (doom/bump-package-at-point) packages) + (user-error (message "%s" (error-message-string e)))))) + (if packages + (message "Updated %d packages\n- %s" (length packages) (string-join packages "\n- ")) + (message "No packages to update"))))) + +;;;###autoload +(defun doom/bump-module (category &optional module select) + "Bump packages in CATEGORY MODULE. +If SELECT (prefix arg) is non-nil, prompt you to choose a specific commit for +each package." + (interactive + (let* ((module (completing-read + "Bump module: " + (let ((modules (cons (list :core) (doom-module-list 'all)))) + (mapcar (lambda (m) + (if (listp m) + (format "%s %s" (car m) (cdr m)) + (format "%s" m))) + (append (list ":private") + (delete-dups (mapcar #'car modules)) + modules))) + nil t nil nil)) + (module (split-string module " " t))) + (list (intern (car module)) + (ignore-errors (intern (cadr module))) + current-prefix-arg))) + (mapc (lambda (module) + (if-let (packages-file + (pcase category + (:private (doom-glob doom-private-dir "packages.el")) + (:core (doom-glob doom-core-dir "packages.el")) + (_ (doom-module-locate-path category module "packages.el")))) + (with-current-buffer + (or (get-file-buffer packages-file) + (find-file-noselect packages-file)) + (doom/bump-packages-in-buffer select) + (save-buffer)) + (message "Module %s has no packages.el file" (cons category module)))) + (if module + (list (cons category module)) + (cl-remove-if-not (lambda (m) (eq (car m) category)) + (cons (list :core) (doom-module-list 'all)))))) + +;;;###autoload +(defun doom/bump-package (package) + "Bump PACKAGE in all modules that install it." + (interactive + (list (completing-read "Bump package: " + (mapcar #'car (doom-package-list 'all))))) + + (let* ((packages (doom-package-list 'all)) + (modules (plist-get (alist-get package packages) :modules))) + (unless modules + (user-error "This package isn't installed by any Doom module")) + (dolist (module modules) + (when-let (packages-file (doom-module-locate-path (car module) (cdr module))) + (doom/bump-module (car module) (cdr module)))))) + + +;; +;;; Bump commits + +;;;###autoload +(defun doom/commit-bumps () + (interactive)) diff --git a/core/autoload/plist.el b/core/autoload/plist.el index 87f897598..fc291dee7 100644 --- a/core/autoload/plist.el +++ b/core/autoload/plist.el @@ -35,24 +35,6 @@ list, the pair is destructured into (CAR . CDR)." "Delete PROP from PLIST in-place." `(setq ,plist (doom-plist-delete ,plist ,prop))) -;;;###autoload -(defmacro with-plist! (plist props &rest body) - "With props bound from PLIST to PROPS, evaluate BODY. - -PROPS is a list of symbols. Each one is converted to a keyword and then its -value is looked up in the PLIST and bound to the symbol for the duration of -BODY." - (declare (indent 2)) - (let ((plist-sym (make-symbol "plist"))) - `(let* ((,plist-sym ,plist) - ,@(cl-loop for prop in props - collect - `(,prop - (plist-get - ,plist-sym - ,(doom-keyword-intern (symbol-name prop)))))) - ,@body))) - ;; ;;; Library diff --git a/core/autoload/cli.el b/core/autoload/process.el similarity index 88% rename from core/autoload/cli.el rename to core/autoload/process.el index f0582ef1c..66d5d6a8c 100644 --- a/core/autoload/cli.el +++ b/core/autoload/process.el @@ -1,7 +1,4 @@ -;;; core/autoload/cli.el -*- lexical-binding: t; -*- - -;; -;;; Library +;;; core/autoload/process.el -*- lexical-binding: t; -*- ;;;###autoload (defun doom-call-process (command &rest args) @@ -10,7 +7,7 @@ Returns (STATUS . OUTPUT) when it is done, where STATUS is the returned error code of the process and OUTPUT is its stdout output." (with-temp-buffer - (cons (or (apply #'call-process command nil t nil args) + (cons (or (apply #'call-process command nil t nil (remq nil args)) -1) (string-trim (buffer-string))))) @@ -28,7 +25,7 @@ Warning: freezes indefinitely on any stdin prompt." (cons (let ((process (make-process :name "doom-sh" :buffer (current-buffer) - :command (cons command args) + :command (cons command (remq nil args)) :connection-type 'pipe)) done-p) (set-process-filter diff --git a/core/autoload/sessions.el b/core/autoload/sessions.el index de08be93d..ece9fe2f9 100644 --- a/core/autoload/sessions.el +++ b/core/autoload/sessions.el @@ -71,6 +71,9 @@ "TODO" (add-hook 'window-setup-hook #'doom-load-session 'append)) +;;;###autoload +(add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler)) + ;; ;;; Commands diff --git a/core/cli/byte-compile.el b/core/cli/byte-compile.el index 49121e0ff..2f55f2b48 100644 --- a/core/cli/byte-compile.el +++ b/core/cli/byte-compile.el @@ -2,7 +2,9 @@ (defcli! (compile c) ((recompile-p ["-r" "--recompile"]) - &rest targets) + (core-p ["-c" "--core"]) + (private-p ["-p" "--private"]) + (verbose-p ["-v" "--verbose"])) "Byte-compiles your config or selected modules. compile [TARGETS...] @@ -11,8 +13,23 @@ Accepts :core and :private as special arguments, which target Doom's core files and your private config files, respectively. To recompile your packages, use -'doom rebuild' instead." - (doom-cli-byte-compile targets recompile-p)) +'doom build' instead." + (doom-cli-byte-compile + (if (or core-p private-p) + (append (when core-p + (list (doom-glob doom-emacs-dir "init.el") + doom-core-dir)) + (when private-p + (list doom-private-dir))) + (append (list (doom-glob doom-emacs-dir "init.el") + doom-core-dir) + (cl-remove-if-not + ;; Only compile Doom's modules + (lambda (path) (file-in-directory-p path doom-emacs-dir)) + ;; Omit `doom-private-dir', which is always first + (cdr (doom-module-load-path))))) + recompile-p + verbose-p)) (defcli! clean () "Delete all *.elc files." @@ -25,17 +42,20 @@ and your private config files, respectively. To recompile your packages, use (defun doom--byte-compile-ignore-file-p (path) (let ((filename (file-name-nondirectory path))) - (or (string-prefix-p "." filename) + (or (not (equal (file-name-extension path) "el")) + (member filename (list "packages.el" "doctor.el")) + (string-prefix-p "." filename) (string-prefix-p "test-" filename) - (string-suffix-p ".example.el" filename) - (not (equal (file-name-extension path) "el")) - (member filename (list "packages.el" "doctor.el"))))) + (string-prefix-p "flycheck_" filename) + (string-suffix-p ".example.el" filename)))) -(cl-defun doom-cli-byte-compile (&optional modules recompile-p) +(cl-defun doom-cli-byte-compile (&optional targets recompile-p verbose-p) "Byte compiles your emacs configuration. init.el is always byte-compiled by this. +If TARGETS is specified, as a list of direcotries + If MODULES is specified (a list of module strings, e.g. \"lang/php\"), those are byte-compiled. Otherwise, all enabled modules are byte-compiled, including Doom core. It always ignores unit tests and files with `no-byte-compile' enabled. @@ -48,138 +68,109 @@ Use `doom-clean-byte-compiled-files' or `make clean' to reverse byte-compilation. If RECOMPILE-P is non-nil, only recompile out-of-date files." - (let ((default-directory doom-emacs-dir) - (doom-modules (doom-modules)) - (byte-compile-verbose doom-debug-mode) - (byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local)) - - ;; In case it is changed during compile-time - (auto-mode-alist auto-mode-alist) - (noninteractive t) - - targets) - - (let (target-dirs) - (dolist (module (delete-dups modules)) - (pcase module - (":core" - (push (doom-glob doom-emacs-dir "init.el") targets) - (push doom-core-dir target-dirs)) - (":private" - (push doom-private-dir target-dirs)) - ((pred file-directory-p) - (push module target-dirs)) - ((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$")) - (push (doom-module-locate-path - (doom-keyword-intern (match-string 1 module)) - (intern (match-string 2 module))) - target-dirs)) - (_ (user-error "%S is not a valid target" module)))) - - (and (or (null modules) (member ":private" modules)) - (not recompile-p) - (not (or doom-auto-accept - (y-or-n-p - (concat "Warning: byte compiling is for advanced users. It will interfere with your\n" - "efforts to debug issues. It is not recommended you do it if you frequently\n" - "tinker with your Emacs config.\n\n" - "Alternatively, use `bin/doom compile :core` instead to byte-compile only the\n" - "Doom core files, as these don't change often.\n\n" - "If you have issues, please make sure byte-compilation isn't the cause by using\n" - "`bin/doom clean` to clear out your *.elc files.\n\n" - "Byte-compile anyway?")))) - (user-error "Aborting")) + (let* ((default-directory doom-emacs-dir) + (targets (nreverse (delete-dups targets))) + ;; In case it is changed during compile-time + (auto-mode-alist auto-mode-alist) + kill-emacs-hook kill-buffer-query-functions) + (let ((after-load-functions + (if (null targets) + after-load-functions + ;; Assemble el files we want to compile, and preserve in the order + ;; they are loaded in, so we don't run into any scary catch-22s + ;; while byte-compiling, like missing macros. + (cons (let ((target-dirs (cl-remove-if-not #'file-directory-p targets))) + (lambda (path) + (and (not (doom--byte-compile-ignore-file-p path)) + (cl-find-if (doom-partial #'file-in-directory-p path) + target-dirs) + (cl-pushnew path targets)))) + after-load-functions)))) + (doom-log "Reloading Doom in preparation for byte-compilation") ;; But first we must be sure that Doom and your private config have been ;; fully loaded. Which usually aren't so in an noninteractive session. - (let ((doom-interactive-mode 'byte-compile)) - (doom-initialize) - (doom-initialize-packages) - (doom-initialize-core)) + (let ((load-prefer-newer t) + (noninteractive t) + doom-interactive-mode) + (doom-initialize 'force) + (quiet! (doom-initialize-packages)))) - ;; - (unless target-dirs - (push (doom-glob doom-emacs-dir "init.el") targets) - ;; If no targets were supplied, then we use your module list. - (appendq! target-dirs - (list doom-core-dir) - (nreverse - (cl-remove-if-not - (lambda (path) (file-in-directory-p path doom-emacs-dir)) - ;; Omit `doom-private-dir', which is always first - (cdr (doom-module-load-path)))))) + (if (null targets) + (print! (info "No targets to %scompile" (if recompile-p "re" ""))) + (print! (start "%scompiling your config...") + (if recompile-p "Re" "Byte-")) - ;; Assemble el files we want to compile; taking into account that MODULES - ;; may be a list of MODULE/SUBMODULE strings from the command line. - (appendq! targets - (doom-files-in target-dirs - :match "\\.el$" - :filter #'doom--byte-compile-ignore-file-p))) + (dolist (dir + (cl-remove-if-not #'file-directory-p targets) + (setq targets (cl-remove-if #'file-directory-p targets))) + (prependq! targets + (doom-files-in + dir :match "\\.el" :filter #'doom--byte-compile-ignore-file-p))) - (unless targets - (print! - (if targets - (warn "Couldn't find any valid targets") - (info "No targets to %scompile" (if recompile-p "re" "")))) - (cl-return nil)) - - (print! - (start (if recompile-p - "Recompiling stale elc files..." - "Byte-compiling your config (may take a while)..."))) - (print-group! - (require 'use-package) - (condition-case e - (let ((total-ok 0) - (total-fail 0) - (total-noop 0) - (use-package-defaults use-package-defaults) - (use-package-expand-minimally t) - kill-emacs-hook kill-buffer-query-functions) - ;; Prevent packages from being loaded at compile time if they - ;; don't meet their own predicates. - (push (list :no-require t - (lambda (_name args) - (or (when-let (pred (or (plist-get args :if) - (plist-get args :when))) - (not (eval pred t))) - (when-let (pred (plist-get args :unless)) - (eval pred t))))) - use-package-defaults) - - (unless recompile-p - (doom-clean-byte-compiled-files)) - - (dolist (target (delete-dups (delq nil targets))) - (cl-incf - (if (not (or (not recompile-p) - (let ((elc-file (byte-compile-dest-file target))) - (and (file-exists-p elc-file) - (file-newer-than-file-p target elc-file))))) - total-noop - (pcase (if (doom-file-cookie-p target "if" t) - (byte-compile-file target) - 'no-byte-compile) - (`no-byte-compile - (print! (info "Ignored %s") (relpath target)) - total-noop) - (`nil - (print! (error "Failed to compile %s") (relpath target)) - total-fail) - (_ - (print! (success "Compiled %s") (relpath target)) - (load target t t) - total-ok))))) - (print! (class (if (= total-fail 0) 'success 'error) - "%s %d/%d file(s) (%d ignored)") - (if recompile-p "Recompiled" "Compiled") - total-ok (- (length targets) total-noop) - total-noop) - t) - ((debug error) - (print! (error "\nThere were breaking errors.\n\n%s") - "Reverting changes...") - (signal 'doom-error (list 'byte-compile e))))))) + (print-group! + (require 'use-package) + (condition-case-unless-debug e + (let* ((total-ok 0) + (total-fail 0) + (total-noop 0) + (byte-compile-verbose nil) + (byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local)) + (byte-compile-dynamic-docstrings t) + (use-package-compute-statistics nil) + (use-package-defaults use-package-defaults) + (use-package-expand-minimally t) + (targets (delete-dups targets)) + (modules (seq-group-by #'doom-module-from-path targets)) + (total-files (length targets)) + (total-modules (length modules)) + (i 0) + last-module) + ;; Prevent packages from being loaded at compile time if they + ;; don't meet their own predicates. + (push (list :no-require t + (lambda (_name args) + (or (when-let (pred (or (plist-get args :if) + (plist-get args :when))) + (not (eval pred t))) + (when-let (pred (plist-get args :unless)) + (eval pred t))))) + use-package-defaults) + (dolist (module-files modules) + (cl-incf i) + (dolist (target (cdr module-files)) + (let ((elc-file (byte-compile-dest-file target))) + (cl-incf + (if (and recompile-p (not (file-newer-than-file-p target elc-file))) + total-noop + (pcase (if (not (doom-file-cookie-p target "if" t)) + 'no-byte-compile + (unless (equal last-module (car module-files)) + (print! (success "(% 3d/%d) Compiling %s %s module...") + i total-modules (caar module-files) (cdar module-files)) + (setq last-module (car module-files))) + (if verbose-p + (byte-compile-file target) + (quiet! (byte-compile-file target)))) + (`no-byte-compile + (print! (debug "(% 3d/%d) Ignored %s") + i total-modules (relpath target)) + total-noop) + (`nil + (print! (error "(% 3d/%d) Failed to compile %s") + i total-modules (relpath target)) + total-fail) + (_ total-ok))))))) + (print! (class (if (= total-fail 0) 'success 'warn) + "%s %d/%d file(s) (%d ignored)") + (if recompile-p "Recompiled" "Byte-compiled") + total-ok total-files + total-noop) + (= total-fail 0)) + ((debug error) + (print! (error "There were breaking errors.\n\n%s") + "Reverting changes...") + (signal 'doom-error (list 'byte-compile e)))))))) (defun doom-clean-byte-compiled-files () "Delete all the compiled elc files in your Emacs configuration and private @@ -188,7 +179,8 @@ module. This does not include your byte-compiled, third party packages.'" (print! (start "Cleaning .elc files")) (print-group! (cl-loop with default-directory = doom-emacs-dir - with success = nil + with success = 0 + with esc = (if doom-debug-mode "" "\033[1A") for path in (append (doom-glob doom-emacs-dir "*.elc") (doom-files-in doom-private-dir :match "\\.elc$" :depth 1) @@ -196,10 +188,10 @@ module. This does not include your byte-compiled, third party packages.'" (doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4)) if (file-exists-p path) do (delete-file path) - and do (print! (success "Deleted %s") (relpath path)) - and do (setq success t) + and do (print! (success "\033[KDeleted %s%s") (relpath path) esc) + and do (cl-incf success) finally do - (print! (if success - (success "All elc files deleted") - (info "No elc files to clean")))) + (print! (if (> success 0) + (success "\033[K%d elc files deleted" success) + (info "\033[KNo elc files to clean")))) t)) diff --git a/core/cli/debug.el b/core/cli/debug.el index d36f73dc6..1faa6956e 100644 --- a/core/cli/debug.el +++ b/core/cli/debug.el @@ -1,10 +1,12 @@ ;;; core/cli/debug.el -*- lexical-binding: t; -*- +(load! "autoload/debug" doom-core-dir) + ;; ;;; Commands (defcli! info - ((format ["--json" "--md" "--lisp"] "What format to dump info into")) + ((format ["--json" "--md" "--lisp"] "What format to dump info into")) "Output system info in markdown for bug reports." (pcase format ("--json" @@ -24,6 +26,5 @@ (defcli! (version v) () "Show version information for Doom & Emacs." - :bare t (doom/version) nil) diff --git a/core/cli/doctor.el b/core/cli/doctor.el index 22e690e2e..5c4dc2f40 100644 --- a/core/cli/doctor.el +++ b/core/cli/doctor.el @@ -48,8 +48,8 @@ in." ;; REVIEW Refactor me (print! (start "Checking your Emacs version...")) - (when EMACS27+ - (warn! "Emacs %s detected. Emacs HEAD is unstable and may cause errors." + (when EMACS28+ + (warn! "Emacs %s detected. Doom doesn't support Emacs 28/HEAD. It is unstable and may cause errors." emacs-version)) (print! (start "Checking for Emacs config conflicts...")) @@ -81,7 +81,6 @@ in." (print-group! (let ((doom-interactive-mode 'doctor)) (doom-initialize 'force) - (doom-initialize-core) (doom-initialize-modules)) (print! (success "Initialized Doom Emacs %s") doom-version) @@ -133,14 +132,21 @@ in." (`darwin "~/Library/Fonts/")) (require 'all-the-icons nil t)) (with-temp-buffer - (insert (cdr (doom-call-process "fc-list" "" "file"))) - (dolist (font all-the-icons-font-names) - (if (save-excursion (re-search-backward font nil t)) - (success! "Found font %s" font) - (print! (warn "Warning: couldn't find %S font") font) - (explain! "You can install it by running `M-x all-the-icons-install-fonts' within Emacs.\n\n" - "This could also mean you've installed them in non-standard locations, in which " - "case feel free to ignore this warning."))))))) + (let ((errors 0)) + (cl-destructuring-bind (status . output) + (doom-call-process "fc-list" "" "file") + (if (not (zerop status)) + (print! (error "There was an error running `fc-list'. Is fontconfig installed correctly?")) + (insert (cdr (doom-call-process "fc-list" "" "file"))) + (dolist (font all-the-icons-font-names) + (if (save-excursion (re-search-backward font nil t)) + (success! "Found font %s" font) + (print! (warn "Warning: couldn't find %S font") font))) + (when (> errors 0) + (explain! "Some all-the-icons fonts were missing.\n\n" + "You can install them by running `M-x all-the-icons-install-fonts' within Emacs.\n" + "This could also mean you've installed them in non-standard locations, in which " + "case feel free to ignore this warning."))))))))) (print! (start "Checking for stale elc files in your DOOMDIR...")) (when (file-directory-p doom-private-dir) @@ -158,15 +164,17 @@ in." (condition-case-unless-debug ex (let ((doctor-file (doom-module-path (car key) (cdr key) "doctor.el")) (packages-file (doom-module-path (car key) (cdr key) "packages.el"))) - (cl-loop for name in (let (doom-packages + (cl-loop with doom-format-indent = 6 + for name in (let (doom-packages doom-disabled-packages) (load packages-file 'noerror 'nomessage) (mapcar #'car doom-packages)) unless (or (doom-package-get name :disable) (eval (doom-package-get name :ignore)) + (plist-member (doom-package-get name :recipe) :local-repo) (doom-package-built-in-p name) (doom-package-installed-p name)) - do (print! (error "%s is not installed") name)) + do (print! (error "Missing emacs package: %S") name)) (let ((inhibit-message t)) (load doctor-file 'noerror 'nomessage))) (file-missing (error! "%s" (error-message-string ex))) diff --git a/core/cli/env.el b/core/cli/env.el index f13a838d3..b9090617b 100644 --- a/core/cli/env.el +++ b/core/cli/env.el @@ -1,8 +1,10 @@ ;;; core/cli/env.el -*- lexical-binding: t; -*- (defcli! env - ((clear-p ["-c" "--clear"] "Clear and delete your envvar file") - (outputfile ["-o" PATH] + ((allow ["-a" "--allow" regexp] "An envvar whitelist regexp") + (reject ["-r" "--reject" regexp] "An envvar blacklist regexp") + (clear-p ["-c" "--clear"] "Clear and delete your envvar file") + (outputfile ["-o" PATH] "Generate the envvar file at PATH. Envvar files that aren't in `doom-env-file' won't be loaded automatically at startup. You will need to load them manually from your private config with the `doom-load-envvars-file' @@ -46,86 +48,86 @@ Why this over exec-path-from-shell? (print! (success "Successfully deleted %S") (path env-file))) - (args + ((and args (not (or allow reject))) (user-error "I don't understand 'doom env %s'" (string-join args " "))) - ((doom-cli-reload-env-file 'force env-file))))) + ((doom-cli-reload-env-file + 'force env-file (list allow) (list reject)))))) ;; ;; Helpers -(defvar doom-env-ignored-vars +(defvar doom-env-blacklist '("^DBUS_SESSION_BUS_ADDRESS$" - "^GPG_AGENT_INFO$" - "^\\(SSH\\|GPG\\)_TTY$" - "^HOME$" - "^PS1$" - "^PWD$" - "^R?PROMPT$" - "^SSH_AGENT_PID$" - "^SSH_AUTH_SOCK$" - "^TERM$" + "^GPG_AGENT_INFO$" "^\\(SSH\\|GPG\\)_TTY$" + "^SSH_\\(AUTH_SOCK\\|AGENT_PID\\)$" + "^HOME$" "^PWD$" "^PS1$" "^R?PROMPT$" "^TERM$" ;; Doom envvars - "^DEBUG$" - "^INSECURE$" - "^YES$" - "^__") + "^DEBUG$" "^INSECURE$" "^YES$" "^__") "Environment variables to not save in `doom-env-file'. Each string is a regexp, matched against variable names to omit from `doom-env-file'.") -(defun doom-cli-reload-env-file (&optional force-p env-file) +(defvar doom-env-whitelist '() + "A whitelist for envvars to save in `doom-env-file'. + +This overrules `doom-env-ignored-vars'. Each string is a regexp, matched against +variable names to omit from `doom-env-file'.") + +(defun doom-cli-reload-env-file (&optional force-p env-file whitelist blacklist) "Generates `doom-env-file', if it doesn't exist (or if FORCE-P). This scrapes the variables from your shell environment by running `doom-env-executable' through `shell-file-name' with `doom-env-switches'. By default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in `doom-env-ignored-vars' are removed." - (let ((env-file (if env-file - (expand-file-name env-file) - doom-env-file))) + (let ((env-file (if env-file (expand-file-name env-file) doom-env-file)) + (process-environment doom--initial-process-environment)) (when (or force-p (not (file-exists-p env-file))) (with-temp-file env-file + (setq-local coding-system-for-write 'utf-8) (print! (start "%s envvars file at %S") (if (file-exists-p env-file) "Regenerating" "Generating") (path env-file)) - (let ((process-environment doom--initial-process-environment)) - (print! (info "Scraping shell environment")) - (print-group! - (when doom-interactive-mode - (user-error "'doom env' must be run on the command line, not an interactive session")) - (goto-char (point-min)) - (insert - (concat - "# -*- mode: sh -*-\n" - "# ---------------------------------------------------------------------------\n" - "# This file was auto-generated by `doom env'. It contains a list of environment\n" - "# variables scraped from your default shell (excluding variables blacklisted\n" - "# in doom-env-ignored-vars).\n" - "#\n" - (if (file-equal-p env-file doom-env-file) - (concat "# It is NOT safe to edit this file. Changes will be overwritten next time you\n" - "# run 'doom sync'. To create a safe-to-edit envvar file use:\n#\n" - "# doom env -o ~/.doom.d/myenv\n#\n" - "# And load it with (doom-load-envvars-file \"~/.doom.d/myenv\").\n") - (concat "# This file is safe to edit by hand, but remember to preserve the null bytes at\n" - "# the end of each line! needs to be loaded manually with:\n#\n" - "# (doom-load-envvars-file \"path/to/this/file\")\n#\n" - "# Use 'doom env -o path/to/this/file' to regenerate it.")) - "# ---------------------------------------------------------------------------\n\0\n")) - ;; We assume that this noninteractive session was spawned from the - ;; user's interactive shell, therefore we just dump - ;; `process-environment' to a file. - (dolist (env process-environment) - (if (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "="))) - doom-env-ignored-vars) - (print! (info "Ignoring %s") env) - (insert env "\0\n"))) - (print! (success "Successfully generated %S") - (path env-file)) - t)))))) + (print-group! + (when doom-interactive-mode + (user-error "'doom env' must be run on the command line, not an interactive session")) + (goto-char (point-min)) + (insert + (concat + "# -*- mode: sh -*-\n" + "# ---------------------------------------------------------------------------\n" + "# This file was auto-generated by `doom env'. It contains a list of environment\n" + "# variables scraped from your default shell (excluding variables blacklisted\n" + "# in doom-env-ignored-vars).\n" + "#\n" + (if (file-equal-p env-file doom-env-file) + (concat "# It is NOT safe to edit this file. Changes will be overwritten next time you\n" + "# run 'doom sync'. To create a safe-to-edit envvar file use:\n#\n" + "# doom env -o ~/.doom.d/myenv\n#\n" + "# And load it with (doom-load-envvars-file \"~/.doom.d/myenv\").\n") + (concat "# This file is safe to edit by hand, but remember to preserve the null bytes at\n" + "# the end of each line! needs to be loaded manually with:\n#\n" + "# (doom-load-envvars-file \"path/to/this/file\")\n#\n" + "# Use 'doom env -o path/to/this/file' to regenerate it.")) + "# ---------------------------------------------------------------------------\n\0\n")) + ;; We assume that this noninteractive session was spawned from the + ;; user's interactive shell, therefore we just dump + ;; `process-environment' to a file. + (dolist (env process-environment) + (if (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "="))) + (remq nil (append blacklist doom-env-blacklist))) + (if (not (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "="))) + (remq nil (append whitelist doom-env-whitelist)))) + (print! (info "Ignoring %s") env) + (print! (info "Whitelisted %s") env) + (insert env "\0\n")) + (insert env "\0\n"))) + (print! (success "Successfully generated %S") + (path env-file)) + t))))) diff --git a/core/cli/install.el b/core/cli/install.el index 719ca07b0..f5082d698 100644 --- a/core/cli/install.el +++ b/core/cli/install.el @@ -24,7 +24,6 @@ DOOMDIR environment variable. e.g. doom -p ~/.config/doom install DOOMDIR=~/.config/doom doom install" - :bare t (print! (green "Installing Doom Emacs!\n")) (let ((default-directory (doom-path "~"))) ;; Create `doom-private-dir' @@ -59,8 +58,14 @@ DOOMDIR environment variable. e.g. ;; In case no init.el was present the first time `doom-initialize-modules' was ;; called in core.el (e.g. on first install) - (doom-initialize 'force 'noerror) - (doom-initialize-modules) + (ignore-errors + (load! "init" doom-private-dir t)) + (when doom-modules + (maphash (lambda (key plist) + (let ((doom--current-module key) + (doom--current-flags (plist-get plist :flags))) + (ignore-errors (load! "init" (plist-get plist :path) t)))) + doom-modules)) ;; Ask if user would like an envvar file generated (if noenv-p @@ -79,28 +84,28 @@ DOOMDIR environment variable. e.g. (print! "Regenerating autoloads files") (doom-cli-reload-autoloads) - + (cond (nofonts-p) - (IS-WINDOWS - (print! (warn "Doom cannot install all-the-icons' fonts on Windows!\n")) - (print-group! - (print! - (concat "You'll have to do so manually:\n\n" - " 1. Launch Doom Emacs\n" - " 2. Execute 'M-x all-the-icons-install-fonts' to download the fonts\n" - " 3. Open the download location in windows explorer\n" - " 4. Open each font file to install them")))) - ((or doom-auto-accept - (y-or-n-p "Download and install all-the-icon's fonts?")) - (require 'all-the-icons) - (let ((window-system (cond (IS-MAC 'ns) - (IS-LINUX 'x)))) - (all-the-icons-install-fonts 'yes)))) + (IS-WINDOWS + (print! (warn "Doom cannot install all-the-icons' fonts on Windows!\n")) + (print-group! + (print! + (concat "You'll have to do so manually:\n\n" + " 1. Launch Doom Emacs\n" + " 2. Execute 'M-x all-the-icons-install-fonts' to download the fonts\n" + " 3. Open the download location in windows explorer\n" + " 4. Open each font file to install them")))) + ((or doom-auto-accept + (y-or-n-p "Download and install all-the-icon's fonts?")) + (require 'all-the-icons) + (let ((window-system (cond (IS-MAC 'ns) + (IS-LINUX 'x)))) + (all-the-icons-install-fonts 'yes)))) (when (file-exists-p "~/.emacs") (print! (warn "A ~/.emacs file was detected. This conflicts with Doom and should be deleted!"))) (print! (success "\nFinished! Doom is ready to go!\n")) (with-temp-buffer - (doom-template-insert "QUICKSTART_INTRO") - (print! (buffer-string))))) + (insert-file-contents (doom-glob doom-core-dir "templates/QUICKSTART_INTRO")) + (print! "%s" (buffer-string))))) diff --git a/core/cli/packages.el b/core/cli/packages.el index 71b7b583e..a6c7ec4e5 100644 --- a/core/cli/packages.el +++ b/core/cli/packages.el @@ -2,7 +2,7 @@ ;;; core/cli/packages.el (defcli! (update u) - ((discard-p ["--discard"] "All local changes to packages are discarded")) + ((discard-p ["--discard"] "All local changes to packages are discarded")) "Updates packages. This works by fetching all installed package repos and checking the distance @@ -74,23 +74,79 @@ list remains lean." "-n" "25" end-ref (concat "^" (regexp-quote start-ref))) (straight--process-get-output))) +(defun doom--barf-if-incomplete-packages () + (let ((straight-safe-mode t)) + (condition-case _ (straight-check-all) + (error (user-error "Package state is incomplete. Run 'doom sync' first"))))) + +(defmacro doom--with-package-recipes (recipes binds &rest body) + (declare (indent 2)) + (let ((recipe-var (make-symbol "recipe")) + (recipes-var (make-symbol "recipes"))) + `(let* ((,recipes-var ,recipes) + (built ()) + (straight-use-package-pre-build-functions + (cons (lambda (pkg &rest _) (cl-pushnew pkg built :test #'equal)) + straight-use-package-pre-build-functions))) + (dolist (,recipe-var ,recipes-var (nreverse built)) + (cl-block nil + (straight--with-plist (append (list :recipe ,recipe-var) ,recipe-var) + ,(doom-enlist binds) + ,@body)))))) + +(defvar doom--cli-updated-recipes nil) +(defun doom--cli-recipes-update () + "Updates straight and recipe repos." + (unless doom--cli-updated-recipes + (straight--make-build-cache-available) + (print! (start "Updating recipe repos...")) + (print-group! + (doom--with-package-recipes + (delq + nil (mapcar (doom-rpartial #'gethash straight--repo-cache) + (mapcar #'symbol-name straight-recipe-repositories))) + (recipe package type local-repo) + (let ((esc (unless doom-debug-mode "\033[1A")) + (ref (straight-vc-get-commit type local-repo)) + newref output) + (print! (start "\033[KUpdating recipes for %s...%s") package esc) + (when (straight-vc-fetch-from-remote recipe) + (setq output (straight--process-get-output)) + (straight-merge-package package) + (unless (equal ref (setq newref (straight-vc-get-commit type local-repo))) + (print! (success "\033[K%s updated (%s -> %s)") + package + (doom--abbrev-commit ref) + (doom--abbrev-commit newref)) + (unless (string-empty-p output) + (print-group! (print! (info "%s" output))))))))) + (setq straight--recipe-lookup-cache (make-hash-table :test #'eq) + doom--cli-updated-recipes t))) + + (defun doom-cli-packages-install () "Installs missing packages. This function will install any primary package (i.e. a package with a `package!' declaration) or dependency thereof that hasn't already been." - (straight--transaction-finalize) + (doom-initialize-packages) (print! (start "Installing packages...")) (let ((pinned (doom-package-pinned-list))) (print-group! (if-let (built - (doom-with-package-recipes (doom-package-recipe-list) + (doom--with-package-recipes (doom-package-recipe-list) (recipe package type local-repo) + (unless (file-directory-p (straight--repos-dir local-repo)) + (doom--cli-recipes-update)) (condition-case-unless-debug e - (straight-use-package (intern package)) + (let ((straight-use-package-pre-build-functions + (cons (lambda (pkg &rest _) + (when-let (commit (cdr (assoc pkg pinned))) + (print! (info "Checked out %s") commit))) + straight-use-package-pre-build-functions))) + (straight-use-package (intern package))) (error - (signal 'doom-package-error - (list package e (straight--process-get-output))))))) + (signal 'doom-package-error (list package e)))))) (print! (success "Installed %d packages") (length built)) (print! (info "No packages need to be installed")) @@ -99,7 +155,7 @@ declaration) or dependency thereof that hasn't already been." (defun doom-cli-packages-build (&optional force-p) "(Re)build all packages." - (straight--transaction-finalize) + (doom-initialize-packages) (print! (start "(Re)building %spackages...") (if force-p "all " "")) (print-group! (let ((straight-check-for-modifications @@ -118,7 +174,7 @@ declaration) or dependency thereof that hasn't already been." (unless force-p (straight--make-build-cache-available)) (if-let (built - (doom-with-package-recipes recipes (package local-repo) + (doom--with-package-recipes recipes (package local-repo) (unless force-p ;; Ensure packages with outdated files/bytecode are rebuilt (let ((build-dir (straight--build-dir package)) @@ -143,18 +199,21 @@ declaration) or dependency thereof that hasn't already been." (defun doom-cli-packages-update () "Updates packages." - (straight--transaction-finalize) - (print! (start "Updating packages (this may take a while)...")) + (doom-initialize-packages) + (doom--barf-if-incomplete-packages) (let* ((repo-dir (straight--repos-dir)) (pinned (doom-package-pinned-list)) + (recipes (doom-package-recipe-list)) (packages-to-rebuild (make-hash-table :test 'equal)) (repos-to-rebuild (make-hash-table :test 'equal)) - (recipes (doom-package-recipe-list)) (total (length recipes)) (esc (unless doom-debug-mode "\033[1A")) (i 0) errors) - (doom-with-package-recipes recipes (recipe package type local-repo) + (when recipes + (doom--cli-recipes-update)) + (print! (start "Updating packages (this may take a while)...")) + (doom--with-package-recipes recipes (recipe package type local-repo) (cl-incf i) (print-group! (unless (straight--repository-is-available-p recipe) @@ -173,7 +232,9 @@ declaration) or dependency thereof that hasn't already been." (error "%S is not a valid repository" package))) (condition-case-unless-debug e (let ((ref (straight-vc-get-commit type local-repo)) - (target-ref (cdr (assoc local-repo pinned))) + (target-ref + (cdr (or (assoc local-repo pinned) + (assoc package pinned)))) output) (or (cond ((not (stringp target-ref)) @@ -201,9 +262,9 @@ declaration) or dependency thereof that hasn't already been." (doom--same-commit-p target-ref (straight-vc-get-commit type local-repo))) ((print! (start "\033[K(%d/%d) Re-cloning %s...") i total local-repo esc) - (let ((repo (straight--repos-dir local-repo))) - (ignore-errors - (delete-directory repo 'recursive)) + (let ((repo (straight--repos-dir local-repo)) + (straight-vc-git-default-clone-depth 'full)) + (delete-directory repo 'recursive) (print-group! (straight-use-package (intern package) nil 'no-build)) (prog1 (file-directory-p repo) @@ -219,7 +280,7 @@ declaration) or dependency thereof that hasn't already been." (puthash package t packages-to-rebuild) (unless (string-empty-p output) (print! (start "\033[K(%d/%d) Updating %s...") i total local-repo) - (print-group! (print! (indent 2 output)))) + (print-group! (print! "%s" (indent 2 output)))) (print! (success "\033[K(%d/%d) %s updated (%s -> %s)") i total local-repo (doom--abbrev-commit ref) @@ -227,24 +288,19 @@ declaration) or dependency thereof that hasn't already been." (user-error (signal 'user-error (error-message-string e))) (error - (print! (warn "\033[K(%d/%d) Encountered error with %s" i total package)) - (print-group! - (print! (error "%s") e) - (print-group! (print! (info "%s" (straight--process-get-output))))) - (push package errors)))))) - (princ "\033[K") - (when errors - (print! (error "Encountered %d error(s), the offending packages: %s") - (length errors) (string-join errors ", "))) - (if (hash-table-empty-p packages-to-rebuild) - (ignore (print! (success "All %d packages are up-to-date") total)) - (let ((default-directory (straight--build-dir))) - (mapc (doom-rpartial #'delete-directory 'recursive) - (hash-table-keys packages-to-rebuild))) - (print! (success "Updated %d package(s)") - (hash-table-count packages-to-rebuild)) - (doom-cli-packages-build) - t))) + (signal 'doom-package-error (list package e))))))) + (print-group! + (princ "\033[K") + (if (hash-table-empty-p packages-to-rebuild) + (ignore (print! (success "All %d packages are up-to-date") total)) + (straight--transaction-finalize) + (let ((default-directory (straight--build-dir))) + (mapc (doom-rpartial #'delete-directory 'recursive) + (hash-table-keys packages-to-rebuild))) + (print! (success "Updated %d package(s)") + (hash-table-count packages-to-rebuild)) + (doom-cli-packages-build) + t)))) ;;; PURGE (for the emperor) @@ -258,8 +314,8 @@ declaration) or dependency thereof that hasn't already been." (defun doom--cli-packages-purge-builds (builds) (if (not builds) - (progn (print! (info "No builds to purge")) - 0) + (prog1 0 + (print! (info "No builds to purge"))) (print! (start "Purging straight builds..." (length builds))) (print-group! (length @@ -287,8 +343,8 @@ declaration) or dependency thereof that hasn't already been." (defun doom--cli-packages-regraft-repos (repos) (if (not repos) - (progn (print! (info "No repos to regraft")) - 0) + (prog1 0 + (print! (info "No repos to regraft"))) (print! (start "Regrafting %d repos..." (length repos))) (let ((before-size (doom-directory-size (straight--repos-dir)))) (print-group! @@ -302,8 +358,7 @@ declaration) or dependency thereof that hasn't already been." (defun doom--cli-packages-purge-repo (repo) (let ((repo-dir (straight--repos-dir repo))) (delete-directory repo-dir 'recursive) - (ignore-errors - (delete-file (straight--modified-file repo))) + (delete-file (straight--modified-file repo)) (if (file-directory-p repo-dir) (ignore (print! (error "Failed to purge repos/%s" repo))) (print! (success "Purged repos/%s" repo)) @@ -311,8 +366,8 @@ declaration) or dependency thereof that hasn't already been." (defun doom--cli-packages-purge-repos (repos) (if (not repos) - (progn (print! (info "No repos to purge")) - 0) + (prog1 0 + (print! (info "No repos to purge"))) (print! (start "Purging straight repositories...")) (print-group! (length @@ -322,8 +377,8 @@ declaration) or dependency thereof that hasn't already been." (require 'core-packages) (let ((dirs (doom-files-in package-user-dir :type t :depth 0))) (if (not dirs) - (progn (print! (info "No ELPA packages to purge")) - 0) + (prog1 0 + (print! (info "No ELPA packages to purge"))) (print! (start "Purging ELPA packages...")) (dolist (path dirs (length dirs)) (condition-case e @@ -346,16 +401,18 @@ a `package!' declaration) or isn't depended on by another primary package. If BUILDS-P, include straight package builds. If REPOS-P, include straight repos. If ELPA-P, include packages installed with package.el (M-x package-install)." + (doom-initialize-packages) + (doom--barf-if-incomplete-packages) (print! (start "Purging orphaned packages (for the emperor)...")) (cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft) (let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort)) (bdirs (straight--directory-files (straight--build-dir) nil nil 'sort))) - (list (cl-remove-if (doom-rpartial #'gethash straight--profile-cache) - bdirs) - (cl-remove-if (doom-rpartial #'straight--checkhash straight--repo-cache) - rdirs) - (cl-remove-if-not (doom-rpartial #'straight--checkhash straight--repo-cache) - rdirs))) + (list (seq-remove (doom-rpartial #'gethash straight--profile-cache) + bdirs) + (seq-remove (doom-rpartial #'straight--checkhash straight--repo-cache) + rdirs) + (seq-filter (doom-rpartial #'straight--checkhash straight--repo-cache) + rdirs))) (let (success) (print-group! (if (not builds-p) diff --git a/core/cli/upgrade.el b/core/cli/upgrade.el index 5e327f525..d346baceb 100644 --- a/core/cli/upgrade.el +++ b/core/cli/upgrade.el @@ -1,8 +1,8 @@ ;;; core/cli/upgrade.el -*- lexical-binding: t; -*- (defcli! (upgrade up) - ((force-p ["-f" "--force"] "Discard local changes to Doom and packages, and upgrade anyway") - (packages-only-p ["-p" "--packages"] "Only upgrade packages, not Doom")) + ((force-p ["-f" "--force"] "Discard local changes to Doom and packages, and upgrade anyway") + (packages-only-p ["-p" "--packages"] "Only upgrade packages, not Doom")) "Updates Doom and packages. This requires that ~/.emacs.d is a git repo, and is the equivalent of the @@ -19,7 +19,7 @@ following shell commands: nil (list (unless packages-only-p (doom-cli-upgrade doom-auto-accept doom-auto-discard)) - (doom-cli-execute "refresh") + (doom-cli-execute "sync") (when (doom-cli-packages-update) (doom-cli-reload-package-autoloads) t))) @@ -110,6 +110,24 @@ following shell commands: (equal (vc-git--rev-parse "HEAD") new-rev)) (print! (info "%s") (cdr result)) (error "Failed to check out %s" (substring new-rev 0 10))) + + ;; Reload Doom's CLI & libraries, in case there were any + ;; upstream changes. Major changes will still break, however + (condition-case-unless-debug e + (progn + (mapc (doom-rpartial #'unload-feature t) + '(core core-lib + core-cli + core-modules + core-packages)) + (require 'core) + (setq doom-init-p nil + doom-init-modules-p nil) + (doom-initialize)) + (error + (signal 'doom-error (list "Could not upgrade Doom without issues" + e)))) + (print! (success "Finished upgrading Doom Emacs"))) t))))) (ignore-errors diff --git a/core/core-cli.el b/core/core-cli.el index 852f6fd43..c53207d2a 100644 --- a/core/core-cli.el +++ b/core/core-cli.el @@ -1,5 +1,24 @@ ;;; -*- lexical-binding: t; no-byte-compile: t; -*- +(require 'seq) + +(load! "autoload/process") +(load! "autoload/plist") +(load! "autoload/files") +(load! "autoload/format") + +;; Create all our core directories to quell file errors +(mapc (doom-rpartial #'make-directory 'parents) + (list doom-local-dir + doom-etc-dir + doom-cache-dir)) + +;; Ensure straight and the bare minimum is ready to go +(require 'core-modules) +(require 'core-packages) +(doom-initialize-core-packages) + + ;; ;;; Variables @@ -11,7 +30,6 @@ commands like `doom-cli-packages-install', `doom-cli-packages-update' and (defvar doom-auto-discard (getenv "FORCE") "If non-nil, discard all local changes while updating.") -(defvar doom--cli-p nil) (defvar doom--cli-commands (make-hash-table :test 'equal)) (defvar doom--cli-groups (make-hash-table :test 'equal)) (defvar doom--cli-group nil) @@ -54,9 +72,10 @@ commands like `doom-cli-packages-install', `doom-cli-packages-update' and (defun doom--cli-process (cli args) (let* ((args (copy-sequence args)) (arglist (copy-sequence (doom-cli-arglist cli))) - (expected (or (cl-position-if (doom-rpartial #'memq cl--lambda-list-keywords) - arglist) - (length arglist))) + (expected + (or (cl-position-if (doom-rpartial #'memq cl--lambda-list-keywords) + arglist) + (length arglist))) (got 0) restvar rest @@ -137,7 +156,7 @@ Executes a cli defined with `defcli!' with the name or alias specified by COMMAND, and passes ARGS to it." (if-let (cli (doom-cli-get command)) (funcall (doom-cli-fn cli) - (doom--cli-process cli args)) + (doom--cli-process cli (remq nil args))) (user-error "Couldn't find any %S command" command))) (defmacro defcli! (name speclist &optional docstring &rest body) @@ -186,10 +205,6 @@ BODY will be run when this dispatcher is called." for optsym = (if (listp opt) (car opt) opt) unless (memq optsym cl--lambda-list-keywords) collect (list optsym `(cdr (assq ',optsym --alist--)))) - ,@(unless (plist-get plist :bare) - '((unless doom-init-p - (doom-initialize 'force 'noerror) - (doom-initialize-modules)))) ,@body))) doom--cli-commands) (when aliases @@ -208,10 +223,25 @@ BODY will be run when this dispatcher is called." ;;; Straight hacks (defvar doom--cli-straight-discard-options - '("^Delete remote \"[^\"]+\", re-create it with correct " - "^Reset branch " - "^Abort merge$" - "^Discard changes$")) + '(("has diverged from" + . "^Reset [^ ]+ to branch") + ("but recipe specifies a URL of" + . "re-create it with correct URL") + ("has a merge conflict:" + . "^Abort merge$") + ("has a dirty worktree:" + . "^Discard changes$") + ("^In repository " + . "^Reset branch \\|^Delete remote [^,]+, re-create it with correct URL")) + "A list of regexps, mapped to regexps. + +Their CAR is tested against the prompt, and CDR is tested against the presented +option, and is used by `straight-vc-git--popup-raw' to select which option to +recommend. + +It may not be obvious to users what they should do for some straight prompts, +so Doom will recommend the one that reverts a package back to its (or target) +original state.") ;; HACK Remove dired & magit options from prompt, since they're inaccessible in ;; noninteractive sessions. @@ -222,63 +252,69 @@ BODY will be run when this dispatcher is called." (defadvice! doom--straight-fallback-to-y-or-n-prompt-a (orig-fn &optional prompt) :around #'straight-are-you-sure (or doom-auto-accept - (if noninteractive - (y-or-n-p (format! "%s" (or prompt ""))) - (funcall orig-fn prompt)))) + (if doom-interactive-mode + (funcall orig-fn prompt) + (y-or-n-p (format! "%s" (or prompt "")))))) + +(defun doom--straight-recommended-option-p (prompt option) + (cl-loop for (prompt-re . opt-re) in doom--cli-straight-discard-options + if (string-match-p prompt-re prompt) + return (string-match-p opt-re option))) (defadvice! doom--straight-fallback-to-tty-prompt-a (orig-fn prompt actions) "Modifies straight to prompt on the terminal when in noninteractive sessions." :around #'straight--popup-raw - (if (not noninteractive) + (if doom-interactive-mode (funcall orig-fn prompt actions) - ;; We can't intercept C-g, so no point displaying any options for this key - ;; when C-c is the proper way to abort batch Emacs. - (delq! "C-g" actions 'assoc) - ;; HACK These are associated with opening dired or magit, which isn't - ;; possible in tty Emacs, so... - (delq! "e" actions 'assoc) - (delq! "g" actions 'assoc) - (if doom-auto-discard - (cl-loop with doom-auto-accept = t - for (_key desc func) in actions - when desc - when (cl-find-if (doom-rpartial #'string-match-p desc) - doom--cli-straight-discard-options) - return (funcall func)) - (print! (start "%s") (red prompt)) - (print-group! - (terpri) - (let (options) - (print-group! - (print! " 1) Abort") - (cl-loop for (_key desc func) in actions + (let ((doom--cli-straight-discard-options doom--cli-straight-discard-options)) + ;; We can't intercept C-g, so no point displaying any options for this key + ;; when C-c is the proper way to abort batch Emacs. + (delq! "C-g" actions 'assoc) + ;; HACK These are associated with opening dired or magit, which isn't + ;; possible in tty Emacs, so... + (delq! "e" actions 'assoc) + (delq! "g" actions 'assoc) + (if doom-auto-discard + (cl-loop with doom-auto-accept = t + for (_key desc func) in actions when desc - do (push func options) - and do - (print! "%2s) %s" (1+ (length options)) - (if (cl-find-if (doom-rpartial #'string-match-p desc) - doom--cli-straight-discard-options) - (green (concat desc " (Recommended)")) - desc)))) + when (doom--straight-recommended-option-p prompt desc) + return (funcall func)) + (print! (start "%s") (red prompt)) + (print-group! (terpri) - (let* ((options - (cons (lambda () - (let ((doom-format-indent 0)) - (terpri) - (print! (warn "Aborted"))) - (kill-emacs 1)) - (nreverse options))) - (prompt - (format! "How to proceed? (%s) " - (mapconcat #'number-to-string - (number-sequence 1 (length options)) - ", "))) - answer fn) - (while (null (nth (setq answer (1- (read-number prompt))) - options)) - (print! (warn "%s is not a valid answer, try again.") - answer)) - (funcall (nth answer options)))))))) + (let (options) + (print-group! + (print! " 1) Abort") + (cl-loop for (_key desc func) in actions + when desc + do (push func options) + and do + (print! "%2s) %s" (1+ (length options)) + (if (doom--straight-recommended-option-p prompt desc) + (progn + (setq doom--cli-straight-discard-options nil) + (green (concat desc " (Recommended)"))) + desc)))) + (terpri) + (let* ((options + (cons (lambda () + (let ((doom-format-indent 0)) + (terpri) + (print! (warn "Aborted"))) + (kill-emacs 1)) + (nreverse options))) + (prompt + (format! "How to proceed? (%s) " + (mapconcat #'number-to-string + (number-sequence 1 (length options)) + ", "))) + answer fn) + (while (null (nth (setq answer (1- (read-number prompt))) + options)) + (print! (warn "%s is not a valid answer, try again.") + answer)) + (funcall (nth answer options))))))))) (defadvice! doom--straight-respect-print-indent-a (args) "Indent straight progress messages to respect `doom-format-indent', so we @@ -297,34 +333,18 @@ everywhere we use it (and internally)." interactive))) -;; -;;; Dependencies - -(require 'seq) - -;; Eagerly load these libraries because we may be in a session that hasn't been -;; fully initialized (e.g. where autoloads files haven't been generated or -;; `load-path' populated). -(load! "autoload/cli") -(load! "autoload/debug") -(load! "autoload/files") -(load! "autoload/format") -(load! "autoload/plist") - - ;; ;;; CLI Commands (load! "cli/help") (load! "cli/install") -(defcligroup! "Maintenance" - "For managing your config and packages" - (defcli! (sync s refresh re) - ((if-necessary-p ["-n" "--if-necessary"] "Only regenerate autoloads files if necessary") - (inhibit-envvar-p ["-e"] "Don't regenerate the envvar file") +(defcli! (sync s refresh re) + ((inhibit-envvar-p ["-e"] "Don't regenerate the envvar file") + (inhibit-elc-p ["-c"] "Don't recompile config") + (update-p ["-u"] "...") (prune-p ["-p" "--prune"] "Purge orphaned packages & regraft repos")) - "Synchronize your config with Doom Emacs. + "Synchronize your config with Doom Emacs. This is the equivalent of running autoremove, install, autoloads, then recompile. Run this whenever you: @@ -337,48 +357,44 @@ recompile. Run this whenever you: It will ensure that unneeded packages are removed, all needed packages are installed, autoloads files are up-to-date and no byte-compiled files have gone stale." - :bare t - (let (success) - ;; Ensures that no pre-existing state pollutes the generation of the new - ;; autoloads files. - (dolist (file (list doom-autoload-file doom-package-autoload-file)) - (delete-file file) - (delete-file (byte-compile-dest-file file))) + (print! (start "Synchronizing your config with Doom Emacs...")) + (print-group! + (and (not inhibit-envvar-p) + (file-exists-p doom-env-file) + (doom-cli-reload-env-file 'force)) + (doom-cli-reload-core-autoloads) + (doom-cli-packages-install) + (doom-cli-packages-build) + (when update-p + (doom-cli-packages-update)) + (doom-cli-packages-purge prune-p 'builds-p prune-p prune-p) + (doom-cli-reload-package-autoloads) + t)) - (doom-initialize 'force 'noerror) - (doom-initialize-modules) - - (print! (start "Synchronizing your config with Doom Emacs...")) - (print-group! - (when (and (not inhibit-envvar-p) - (file-exists-p doom-env-file)) - (doom-cli-reload-env-file 'force)) - - (doom-cli-reload-core-autoloads) - (doom-cli-packages-install) - (doom-cli-packages-build) - (doom-cli-packages-purge prune-p 'builds-p prune-p prune-p) - (doom-cli-reload-package-autoloads) - t))) - - (load! "cli/env") - (load! "cli/upgrade") - (load! "cli/packages") - (load! "cli/autoloads")) +(load! "cli/env") +(load! "cli/upgrade") +(load! "cli/packages") +(load! "cli/autoloads") (defcligroup! "Diagnostics" "For troubleshooting and diagnostics" (load! "cli/doctor") (load! "cli/debug") - (load! "cli/test")) + + ;; Our tests are broken at the moment. Working on fixing them, but for now we + ;; disable them: + ;; (load! "cli/test") + ) + (defcligroup! "Compilation" "For compiling Doom and your config" (load! "cli/byte-compile")) + (defcligroup! "Utilities" "Conveniences for interacting with Doom externally" - (defcli! run () + (defcli! run (&rest args) "Run Doom Emacs from bin/doom's parent directory. All arguments are passed on to Emacs. @@ -388,11 +404,16 @@ All arguments are passed on to Emacs. WARNING: this command exists for convenience and testing. Doom will suffer additional overhead by being started this way. For the best performance, it is -best to run Doom out of ~/.emacs.d and ~/.doom.d.") +best to run Doom out of ~/.emacs.d and ~/.doom.d.")) - ;; (load! "cli/batch") - ;; (load! "cli/org") - ) + +;; +;;; Load user config + +(condition-case-unless-debug e + (load! "init" doom-private-dir t) + (error + (signal 'doom-private-error (list "init.el" e)))) (provide 'core-cli) ;;; core-cli.el ends here diff --git a/core/core-editor.el b/core/core-editor.el index d4638d5c6..165dd5d48 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -79,8 +79,8 @@ possible." t)))))) ;; Don't autosave files or create lock/history/backup files. We don't want -;; copies of potentially sensitive material floating around, and we'll rely on -;; git and our own good fortune instead. Fingers crossed! +;; copies of potentially sensitive material floating around or polluting our +;; filesystem. We rely on git and our own good fortune instead. Fingers crossed! (setq auto-save-default nil create-lockfiles nil make-backup-files nil @@ -216,7 +216,7 @@ possible." (use-package! recentf ;; Keep track of recently opened files :defer-incrementally easymenu tree-widget timer - :after-call after-find-file + :hook (doom-first-file . recentf-mode) :commands recentf-open-files :config (defun doom--recent-file-truename (file) @@ -246,22 +246,20 @@ possible." "Add dired directory to recentf file list." (recentf-add-file default-directory))) - (when doom-interactive-mode - (add-hook 'kill-emacs-hook #'recentf-cleanup) - (quiet! (recentf-mode +1)))) + (add-hook 'kill-emacs-hook #'recentf-cleanup) + (advice-add #'recentf-load-list :around #'doom-shut-up-a)) (use-package! savehist ;; persist variables across sessions :defer-incrementally custom - :after-call post-command-hook + :hook (doom-first-input . savehist-mode) + :init + (setq savehist-file (concat doom-cache-dir "savehist")) :config - (setq savehist-file (concat doom-cache-dir "savehist") - savehist-save-minibuffer-history t + (setq savehist-save-minibuffer-history t savehist-autosave-interval nil ; save on kill only savehist-additional-variables '(kill-ring search-ring regexp-search-ring)) - (savehist-mode +1) - (add-hook! 'kill-emacs-hook (defun doom-unpropertize-kill-ring-h () "Remove text properties from `kill-ring' for a smaller savehist file." @@ -273,11 +271,11 @@ possible." (use-package! saveplace ;; persistent point location in buffers - :after-call after-find-file dired-initial-position-hook - :config + :hook (doom-first-file . save-place-mode) + :init (setq save-place-file (concat doom-cache-dir "saveplace") save-place-limit 100) - + :config (defadvice! doom--recenter-on-load-saveplace-a (&rest _) "Recenter on cursor when loading a saved place." :after-while #'save-place-find-file-hook @@ -312,7 +310,8 @@ files, so we replace calls to `pp' with the much faster `prin1'." ;;; Packages (use-package! better-jumper - :after-call pre-command-hook + :hook (doom-first-input . better-jumper-mode) + :hook (better-jumper-post-jump . recenter) :preface ;; REVIEW Suppress byte-compiler warning spawning a *Compile-Log* buffer at ;; startup. This can be removed once gilbertw1/better-jumper#2 is merged. @@ -322,17 +321,14 @@ files, so we replace calls to `pp' with the much faster `prin1'." (global-set-key [remap evil-jump-backward] #'better-jumper-jump-backward) (global-set-key [remap xref-pop-marker-stack] #'better-jumper-jump-backward) :config - (better-jumper-mode +1) - (add-hook 'better-jumper-post-jump-hook #'recenter) - - (defadvice! doom-set-jump-a (orig-fn &rest args) + (defun doom-set-jump-a (orig-fn &rest args) "Set a jump point and ensure ORIG-FN doesn't set any new jump points." (better-jumper-set-jump (if (markerp (car args)) (car args))) (let ((evil--jumps-jumping t) (better-jumper--jumping t)) (apply orig-fn args))) - (defadvice! doom-set-jump-maybe-a (orig-fn &rest args) + (defun doom-set-jump-maybe-a (orig-fn &rest args) "Set a jump point if ORIG-FN returns non-nil." (let ((origin (point-marker)) (result @@ -439,7 +435,7 @@ files, so we replace calls to `pp' with the much faster `prin1'." (use-package! smartparens ;; Auto-close delimiters and blocks as you type. It's more powerful than that, ;; but that is all Doom uses it for. - :after-call doom-switch-buffer-hook after-find-file + :hook (doom-first-buffer . smartparens-global-mode) :commands sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string :config ;; smartparens recognizes `slime-mrepl-mode', but not `sly-mrepl-mode', so... @@ -499,16 +495,12 @@ files, so we replace calls to `pp' with the much faster `prin1'." (defun doom-disable-smartparens-mode-maybe-h () (when smartparens-mode (setq-local doom-buffer-smartparens-mode t) - (turn-off-smartparens-mode)))) - - (smartparens-global-mode +1)) + (turn-off-smartparens-mode))))) (use-package! so-long - :after-call after-find-file + :hook (doom-first-file . global-so-long-mode) :config - (when doom-interactive-mode - (global-so-long-mode +1)) (setq so-long-threshold 400) ; reduce false positives w/ larger threshold ;; Don't disable syntax highlighting and line numbers, or make the buffer ;; read-only, in `so-long-minor-mode', so we can have a basic editing @@ -528,6 +520,7 @@ files, so we replace calls to `pp' with the much faster `prin1'." (appendq! so-long-minor-modes '(flycheck-mode flyspell-mode + spell-fu-mode eldoc-mode smartparens-mode highlight-numbers-mode @@ -558,8 +551,7 @@ files, so we replace calls to `pp' with the much faster `prin1'." (use-package! ws-butler ;; a less intrusive `delete-trailing-whitespaces' on save - :after-call after-find-file - :config (ws-butler-global-mode +1)) + :hook (doom-first-buffer . ws-butler-global-mode)) (provide 'core-editor) ;;; core-editor.el ends here diff --git a/core/core-keybinds.el b/core/core-keybinds.el index 0e89a270c..abe650a8f 100644 --- a/core/core-keybinds.el +++ b/core/core-keybinds.el @@ -26,9 +26,12 @@ and Emacs states, and for non-evil users.") ;; ;;; Keybind settings -(when IS-MAC - (setq mac-command-modifier 'super - mac-option-modifier 'meta)) +(cond (IS-MAC + (setq mac-command-modifier 'super + mac-option-modifier 'meta)) + (IS-WINDOWS + (setq w32-lwindow-modifier 'super + w32-rwindow-modifier 'super))) ;; @@ -176,8 +179,7 @@ localleader prefix." ;;; Packages (use-package! which-key - :defer 1 - :after-call pre-command-hook + :hook (doom-first-input . which-key-mode) :init (setq which-key-sort-order #'which-key-prefix-then-key-order which-key-sort-uppercase-first nil @@ -192,9 +194,7 @@ localleader prefix." (setq-hook! 'which-key-init-buffer-hook line-spacing 3) (which-key-add-key-based-replacements doom-leader-key "") - (which-key-add-key-based-replacements doom-localleader-key "") - - (which-key-mode +1)) + (which-key-add-key-based-replacements doom-localleader-key "")) ;; diff --git a/core/core-lib.el b/core/core-lib.el index 0dd055f72..a4680456e 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -118,6 +118,7 @@ unreadable. Returns the names of envvars that were changed." (env (with-temp-buffer (save-excursion + (setq-local coding-system-for-read 'utf-8) (insert "\0\n") ; to prevent off-by-one (insert-file-contents file)) (save-match-data @@ -157,7 +158,7 @@ at the values with which this function was called." A factory for quickly producing interaction commands, particularly for keybinds or aliases." (declare (doc-string 1) (pure t) (side-effect-free t)) - `(lambda () (interactive) ,@body)) + `(lambda (&rest _) (interactive) ,@body)) (defalias 'lambda! 'λ!) (defun λ!! (command &optional arg) @@ -165,7 +166,7 @@ or aliases." A factory for quickly producing interactive, prefixed commands for keybinds or aliases." (declare (doc-string 1) (pure t) (side-effect-free t)) - (lambda () (interactive) + (lambda (&rest _) (interactive) (let ((current-prefix-arg arg)) (call-interactively command)))) (defalias 'lambda!! 'λ!!) @@ -209,38 +210,41 @@ the same name, for use with `funcall' or `apply'. ARGLIST and BODY are as in (setq body (macroexp-progn body)) (when (memq (car bindings) '(defun defmacro)) (setq bindings (list bindings))) - (dolist (binding (nreverse bindings) body) + (dolist (binding (reverse bindings) (macroexpand body)) (let ((type (car binding)) (rest (cdr binding))) (setq body (pcase type (`defmacro `(cl-macrolet ((,(car rest) ,(cadr rest) ,@(cddr rest))) ,body)) (`defun `(cl-letf* ((,(car rest) (symbol-function #',(car rest))) - ((symbol-function #',(car rest)) - (lambda ,(cadr rest) ,@(cddr rest)))) - ,body)) + ((symbol-function #',(car rest)) + (lambda ,(cadr rest) ,@(cddr rest)))) + (ignore ,(car rest)) + ,body)) (_ (when (eq (car-safe type) 'function) - (setq type `(symbol-function ,type))) - `(cl-letf ((,type ,@rest)) ,body))))))) + (setq type (list 'symbol-function type))) + (list 'cl-letf (list (cons type rest)) body))))))) (defmacro quiet! (&rest forms) "Run FORMS without generating any output. -This silences calls to `message', `load-file', `write-region' and anything that +This silences calls to `message', `load', `write-region' and anything that writes to `standard-output'." - `(cond (doom-debug-mode ,@forms) - ((not doom-interactive-mode) - (letf! ((standard-output (lambda (&rest _))) - (defun load-file (file) (load-file nil t)) - (defun message (&rest _)) - (defun write-region (start end filename &optional append visit lockname mustbenew) - (unless visit (setq visit 'no-message)) - (funcall write-region start end filename append visit lockname mustbenew))) - ,@forms)) - ((let ((inhibit-message t) - (save-silently t)) - (prog1 ,@forms (message "")))))) + `(if doom-debug-mode + (progn ,@forms) + ,(if doom-interactive-mode + `(let ((inhibit-message t) + (save-silently t)) + (prog1 ,@forms (message ""))) + `(letf! ((standard-output (lambda (&rest _))) + (defun message (&rest _)) + (defun load (file &optional noerror nomessage nosuffix must-suffix) + (funcall load file noerror t nosuffix must-suffix)) + (defun write-region (start end filename &optional append visit lockname mustbenew) + (unless visit (setq visit 'no-message)) + (funcall write-region start end filename append visit lockname mustbenew))) + ,@forms)))) (defmacro if! (cond then &rest body) "Expands to THEN if COND is non-nil, to BODY otherwise. @@ -466,11 +470,19 @@ advised)." (put ',fn 'permanent-local-hook t) (add-hook sym #',fn ,append)))))) +(defmacro add-hook-trigger! (hook-var &rest targets) + "TODO" + `(let ((fn (intern (format "%s-h" ,hook-var)))) + (fset fn (lambda (&rest _) (run-hooks ,hook-var) (set ,hook-var nil))) + (put ,hook-var 'permanent-local t) + (dolist (on (list ,@targets)) + (if (functionp on) + (advice-add on :before fn) + (add-hook on fn))))) + (defmacro add-hook! (hooks &rest rest) "A convenience macro for adding N functions to M hooks. -If N and M = 1, there's no benefit to using this macro over `add-hook'. - This macro accepts, in order: 1. The mode(s) or hook(s) to add to. This is either an unquoted mode, an diff --git a/core/core-modules.el b/core/core-modules.el index c866e1289..db7104c25 100644 --- a/core/core-modules.el +++ b/core/core-modules.el @@ -71,6 +71,13 @@ before the user's private module.") ;; ;;; Bootstrap API +(defun doom-initialize-core-modules () + "Load Doom's core files for an interactive session." + (require 'core-keybinds) + (require 'core-ui) + (require 'core-projects) + (require 'core-editor)) + (defun doom-initialize-modules (&optional force-p) "Loads the init.el in `doom-private-dir' and sets up hooks for a healthy session of Dooming. Will noop if used more than once, unless FORCE-P is @@ -78,16 +85,18 @@ non-nil." (when (or force-p (not doom-init-modules-p)) (setq doom-init-modules-p t doom-modules nil) - (load custom-file 'noerror 'nomessage) - (when (load! "init" doom-private-dir t) - (when doom-modules - (maphash (lambda (key plist) - (let ((doom--current-module key) - (doom--current-flags (plist-get plist :flags))) - (load! "init" (plist-get plist :path) t))) - doom-modules)) - (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook) - (when doom-interactive-mode + (with-temp-buffer + (doom-log "Initializing core modules") + (doom-initialize-core-modules) + (when-let (init-p (load! "init" doom-private-dir t)) + (doom-log "Initializing user config") + (when doom-modules + (maphash (lambda (key plist) + (let ((doom--current-module key) + (doom--current-flags (plist-get plist :flags))) + (load! "init" (plist-get plist :path) t))) + doom-modules)) + (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook) (when doom-modules (maphash (lambda (key plist) (let ((doom--current-module key) @@ -95,7 +104,8 @@ non-nil." (load! "config" (plist-get plist :path) t))) doom-modules)) (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) - (load! "config" doom-private-dir t))))) + (load! "config" doom-private-dir t) + (load custom-file 'noerror (not doom-debug-mode)))))) ;; @@ -122,15 +132,17 @@ non-nil." of PROPERTY and VALUEs. \(fn CATEGORY MODULE PROPERTY VALUE &rest [PROPERTY VALUE [...]])" - (if-let (old-plist (doom-module-get category module)) - (progn - (when plist - (when (cl-oddp (length plist)) - (signal 'wrong-number-of-arguments (list (length plist)))) - (while plist - (plist-put old-plist (pop plist) (pop plist)))) - (puthash (cons category module) old-plist doom-modules)) - (puthash (cons category module) plist doom-modules))) + (puthash (cons category module) + (if-let (old-plist (doom-module-get category module)) + (if (null plist) + old-plist + (when (cl-oddp (length plist)) + (signal 'wrong-number-of-arguments (list (length plist)))) + (while plist + (plist-put old-plist (pop plist) (pop plist))) + old-plist) + plist) + doom-modules)) (defun doom-module-set (category module &rest plist) "Enables a module by adding it to `doom-modules'. @@ -223,18 +235,21 @@ those directories. The first returned path is always `doom-private-dir'." collect (plist-get plist :path))) nil)) -(defun doom-modules (&optional refresh-p) +(defun doom-module-list (&optional all-p) "Minimally initialize `doom-modules' (a hash table) and return it. This value is cached. If REFRESH-P, then don't use the cached value." - (or (unless refresh-p doom-modules) - (let (doom-interactive-mode - doom-modules - doom-init-modules-p) - (load! "init" doom-private-dir t) - (or doom-modules - (make-hash-table :test 'equal - :size 20 - :rehash-threshold 1.0))))) + (if all-p + (cl-loop for path in (cdr (doom-module-load-path 'all)) + collect (doom-module-from-path path)) + (or doom-modules + (let (doom-interactive-mode + doom-modules + doom-init-modules-p) + (load! "init" doom-private-dir t) + (or doom-modules + (make-hash-table :test 'equal + :size 20 + :rehash-threshold 1.0)))))) ;; @@ -254,6 +269,17 @@ This value is cached. If REFRESH-P, then don't use the cached value." ;; Macros are already fontified, no need for this (font-lock-remove-keywords 'emacs-lisp-mode use-package-font-lock-keywords) + ;; A common mistake for new users is that they inadvertantly try to install + ;; their packages with package.el, by copying over old `use-package' + ;; declarations with an :ensure t property. Doom doesn't use package.el, so + ;; this will throw an error that will confuse beginners. + (setq use-package-ensure-function #'ignore) + ;; ...On the other hand, if the user has loaded `package', then we should + ;; assume they know what they're doing and restore the old behavior: + (add-transient-hook! 'package-initialize + (when (eq use-package-ensure-function #'ignore) + (setq use-package-ensure-function #'use-package-ensure-elpa))) + ;; We define :minor and :magic-minor from the `auto-minor-mode' package here ;; so we don't have to load `auto-minor-mode' so early. (dolist (keyword '(:minor :magic-minor)) @@ -364,8 +390,8 @@ for a list of all recognized module trees. Order defines precedence (from most to least)." `(let ((modules ,@(if (keywordp (car modules)) - (list (list 'quote modules)) - modules))) + (list (list 'quote modules)) + modules))) (unless doom-modules (setq doom-modules (make-hash-table :test 'equal diff --git a/core/core-packages.el b/core/core-packages.el index 1ad951a37..83020e72c 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -30,43 +30,20 @@ ;; `doom-core-dir'. These contain `package!' declarations that tell DOOM what ;; plugins to install and where from. ;; -;; All that said, you can still use package.el's commands, but 'bin/doom -;; refresh' will purge ELPA packages. - -(defvar doom-init-packages-p nil - "If non-nil, Doom's package management system has been initialized.") +;; All that said, you can still use package.el's commands, but 'bin/doom sync' +;; will purge ELPA packages. (defvar doom-packages () "A list of enabled packages. Each element is a sublist, whose CAR is the package's name as a symbol, and whose CDR is the plist supplied to its `package!' declaration. Set by `doom-initialize-packages'.") -(defvar doom-core-packages '(straight use-package) - "A list of packages that must be installed (and will be auto-installed if -missing) and shouldn't be deleted.") - -(defvar doom-core-package-sources - '((org-elpa :local-repo nil) - (melpa - :type git :host github - :repo "melpa/melpa" - :no-build t) - (gnu-elpa-mirror - :type git :host github - :repo "emacs-straight/gnu-elpa-mirror" - :no-build t) - (emacsmirror-mirror - :type git :host github - :repo "emacs-straight/emacsmirror-mirror" - :no-build t)) - "A list of recipes for straight's recipe repos.") - (defvar doom-disabled-packages () "A list of packages that should be ignored by `use-package!' and `after!'.") ;; -;;; Package managers +;;; package.el ;; Ensure that, if we do need package.el, it is configured correctly. You really ;; shouldn't be using it, but it may be convenient for quick package testing. @@ -94,7 +71,7 @@ missing) and shouldn't be deleted.") ;;; Straight (setq straight-base-dir doom-local-dir - straight-repository-branch "master" + straight-repository-branch "develop" straight-cache-autoloads nil ; we already do this, and better. ;; Doom doesn't encourage you to modify packages in place. Disabling this ;; makes 'doom sync' instant (once everything set up), which is much nicer @@ -123,88 +100,327 @@ missing) and shouldn't be deleted.") ;; -;;; Bootstrapper +;;; Bootstrappers + +(defun doom--ensure-straight () + (cl-destructuring-bind (&key pin recipe &allow-other-keys) + (doom-package-get 'straight) + (let ((repo-dir (doom-path straight-base-dir "straight/repos/straight.el")) + (repo-url (concat "http" (if gnutls-verify-error "s") + "://github.com/" + (or (plist-get recipe :repo) "raxod502/straight.el"))) + (branch (or (plist-get recipe :branch) straight-repository-branch)) + (call (if doom-debug-mode #'doom-exec-process #'doom-call-process))) + (if (not (file-directory-p repo-dir)) + (message "Installing straight...") + ;; TODO Rethink this clumsy workaround + (let ((default-directory repo-dir)) + (unless (equal (cdr (doom-call-process "git" "rev-parse" "HEAD")) pin) + (delete-directory repo-dir 'recursive) + (message "Updating straight...")))) + (unless (file-directory-p repo-dir) + (cond + ((eq straight-vc-git-default-clone-depth 'full) + (funcall call "git" "clone" "--origin" "origin" repo-url repo-dir)) + ((null pin) + (funcall call "git" "clone" "--origin" "origin" repo-url repo-dir + "--depth" (number-to-string straight-vc-git-default-clone-depth) + "--branch" straight-repository-branch)) + ((integerp straight-vc-git-default-clone-depth) + (make-directory repo-dir t) + (let ((default-directory repo-dir)) + (funcall call "git" "init") + (funcall call "git" "checkout" "-b" straight-repository-branch) + (funcall call "git" "remote" "add" "origin" repo-url) + (funcall call "git" "fetch" "origin" pin + "--depth" (number-to-string straight-vc-git-default-clone-depth)) + (funcall call "git" "checkout" "--detach" pin))))) + (require 'straight (concat repo-dir "/straight.el")) + (doom-log "Initializing recipes") + (with-temp-buffer + (insert-file-contents (doom-path repo-dir "bootstrap.el")) + ;; Don't install straight for us -- we've already done that -- only set + ;; up its recipe repos for us. + (eval-region (search-forward "(require 'straight)") + (point-max)))))) + +(defun doom-initialize-core-packages (&optional force-p) + "Ensure `straight' is installed and was compiled with this version of Emacs." + (when (or force-p (null (bound-and-true-p straight-recipe-repositories))) + (doom-log "Initializing straight") + (let ((doom-disabled-packages nil) + (doom-packages (doom-package-list nil 'core-only))) + (doom--ensure-straight) + (doom-log "Installing core packages") + (dolist (package doom-packages) + (cl-destructuring-bind (name &key recipe &allow-other-keys) + package + (when recipe + (straight-override-recipe (cons name recipe))) + (straight-use-package name)))))) (defun doom-initialize-packages (&optional force-p) - "Ensures that Doom's package system and straight.el are initialized. + "Process all packages, essential and otherwise, if they haven't already been. If FORCE-P is non-nil, do it anyway. -This ensure `doom-packages' is populated, if isn't aren't already. Use this -before any of straight's or Doom's package management's API to ensure all the -necessary package metadata is initialized and available for them." - (unless doom-init-packages-p - (setq force-p t)) +This ensures `doom-packages' is populated and `straight' recipes are properly +processed." + (doom-initialize-core-packages force-p) (when (or force-p (not (bound-and-true-p package--initialized))) (doom-log "Initializing package.el") (require 'package) - (package-initialize)) - (when (or force-p (not doom-packages)) - (doom-log "Initializing straight") - (setq doom-init-packages-p t) - (doom-ensure-straight) - (mapc #'straight-use-package doom-core-packages) - (doom-log "Initializing doom-packages") - (setq doom-disabled-packages nil - doom-pinned-packages nil - doom-packages (doom-package-list)) + (package-initialize) + (unless package--initialized + (error "Failed to initialize package.el"))) + (when (or force-p (null doom-packages)) + (doom-log "Initializing straight.el") + (or (setq doom-disabled-packages nil + doom-packages (doom-package-list)) + (error "Failed to read any packages")) (dolist (package doom-packages) - (let ((name (car package))) - (with-plist! (cdr package) (recipe modules disable ignore pin) - (if ignore - (doom-log "Ignoring package %S" name) - (if (not disable) - (with-demoted-errors "Package error: %s" - (when recipe - (straight-override-recipe (cons name recipe))) - (straight-register-package name)) - (doom-log "Disabling package %S" name) + (cl-destructuring-bind + (name &key recipe disable ignore &allow-other-keys) package + (unless ignore + (if disable (cl-pushnew name doom-disabled-packages) - ;; Warn about disabled core packages - (when (cl-find :core modules :key #'car) - (print! (warn "%s\n%s") - (format "You've disabled %S" name) - (indent 2 (concat "This is a core package. Disabling it will cause errors, as Doom assumes\n" - "core packages are always available. Disable their minor-modes or hooks instead."))))))))))) + (when recipe + (straight-override-recipe (cons name recipe))) + (straight-register-package name))))))) -(defun doom-ensure-straight () - "Ensure `straight' is installed and was compiled with this version of Emacs." - (unless (fboundp 'straight--reset-caches) - (defvar bootstrap-version) - (let* (;; Force straight to install into ~/.emacs.d/.local/straight instead of - ;; ~/.emacs.d/straight by pretending `doom-local-dir' is our .emacs.d. - (user-emacs-directory straight-base-dir) - (bootstrap-file (doom-path straight-base-dir "straight/repos/straight.el/straight.el")) - (bootstrap-version 5)) - (make-directory (doom-path straight-base-dir "straight/build") 'parents) - (or (require 'straight nil t) - (file-readable-p bootstrap-file) - (with-current-buffer - (url-retrieve-synchronously - (format "https://raw.githubusercontent.com/raxod502/straight.el/%s/install.el" - straight-repository-branch) - 'silent 'inhibit-cookies) - (goto-char (point-max)) - (eval-print-last-sexp))) - (load bootstrap-file nil t)) - (require 'straight)) - (straight--reset-caches) - (setq straight-recipe-repositories nil - straight-recipe-overrides nil) - (mapc #'straight-use-recipes doom-core-package-sources) - (straight-register-package - `(straight :type git :host github - :repo ,(format "%s/straight.el" straight-repository-user) - :files ("straight*.el") - :branch ,straight-repository-branch - :no-byte-compile t))) + +;; +;;; Package management API + +(defun doom-package-get (package &optional prop nil-value) + "Returns PACKAGE's `package!' recipe from `doom-packages'." + (let ((plist (cdr (assq package doom-packages)))) + (if prop + (if (plist-member plist prop) + (plist-get plist prop) + nil-value) + plist))) + +(defun doom-package-set (package prop value) + "Set PROPERTY in PACKAGE's recipe to VALUE." + (setf (alist-get package doom-packages) + (plist-put (alist-get package doom-packages) + prop value))) + +(defun doom-package-recipe (package &optional prop nil-value) + "Returns the `straight' recipe PACKAGE was registered with." + (let ((plist (gethash (symbol-name package) straight--recipe-cache))) + (if prop + (if (plist-member plist prop) + (plist-get plist prop) + nil-value) + plist))) + +(defun doom-package-recipe-repo (package) + "Resolve and return PACKAGE's (symbol) local-repo property." + (if-let* ((recipe (cdr (straight-recipes-retrieve package))) + (repo (straight-vc-local-repo-name recipe))) + repo + (symbol-name package))) + +(defun doom-package-build-recipe (package &optional prop nil-value) + "Returns the `straight' recipe PACKAGE was installed with." + (let ((plist (nth 2 (gethash (symbol-name package) straight--build-cache)))) + (if prop + (if (plist-member plist prop) + (plist-get plist prop) + nil-value) + plist))) + +(defun doom-package-build-time (package) + "TODO" + (car (gethash (symbol-name package) straight--build-cache))) + +(defun doom-package-dependencies (package &optional recursive _noerror) + "Return a list of dependencies for a package." + (let ((deps (nth 1 (gethash (symbol-name package) straight--build-cache)))) + (if recursive + (append deps (mapcan (lambda (dep) (doom-package-dependencies dep t t)) + (copy-sequence deps))) + (copy-sequence deps)))) + +(defun doom-package-depending-on (package &optional noerror) + "Return a list of packages that depend on the package named NAME." + (cl-check-type name symbol) + ;; can't get dependencies for built-in packages + (unless (or (doom-package-build-recipe name) + noerror) + (error "Couldn't find %s, is it installed?" name)) + (cl-loop for pkg in (hash-table-keys straight--build-cache) + for deps = (doom-package-dependencies pkg) + if (memq package deps) + collect pkg + and append (doom-package-depending-on pkg t))) + + +;;; Predicate functions +(defun doom-package-built-in-p (package) + "Return non-nil if PACKAGE (a symbol) is built-in." + (eq (doom-package-build-recipe package :type) + 'built-in)) + +(defun doom-package-installed-p (package) + "Return non-nil if PACKAGE (a symbol) is installed." + (file-directory-p (straight--build-dir (symbol-name package)))) + +(defun doom-package-is-type-p (package type) + "TODO" + (memq type (doom-enlist (doom-package-get package :type)))) + +(defun doom-package-in-module-p (package category &optional module) + "Return non-nil if PACKAGE was installed by the user's private config." + (when-let (modules (doom-package-get package :modules)) + (or (and (not module) (assq :private modules)) + (member (cons category module) modules)))) + +(defun doom-package-backend (package) + "Return 'straight, 'builtin, 'elpa or 'other, depending on how PACKAGE is +installed." + (cond ((gethash (symbol-name package) straight--build-cache) + 'straight) + ((or (doom-package-built-in-p package) + (assq package package--builtins)) + 'builtin) + ((assq package package-alist) + 'elpa) + ((locate-library (symbol-name package)) + 'other))) + +(defun doom-package-changed-recipe-p (name) + "Return t if a package named NAME (a symbol) has a different recipe than it +was installed with." + (cl-check-type name symbol) + ;; TODO + ;; (when (doom-package-installed-p name) + ;; (when-let* ((doom-recipe (assq name doom-packages)) + ;; (install-recipe (doom-package-recipe))) + ;; (not (equal (cdr quelpa-recipe) + ;; (cdr (plist-get (cdr doom-recipe) :recipe)))))) + ) + + +;;; Package getters +(defun doom--read-packages (file &optional noeval noerror) + (condition-case-unless-debug e + (with-temp-buffer ; prevent buffer-local state from propagating + (if (not noeval) + (load file noerror 'nomessage 'nosuffix) + (when (file-exists-p file) + (insert-file-contents file) + (let (emacs-lisp-mode) (emacs-lisp-mode)) + ;; Scrape `package!' blocks from FILE for a comprehensive listing of + ;; packages used by this module. + (while (search-forward "(package!" nil t) + (let ((ppss (save-excursion (syntax-ppss)))) + ;; Don't collect packages in comments or strings + (unless (or (nth 3 ppss) + (nth 4 ppss)) + (goto-char (match-beginning 0)) + (cl-destructuring-bind (_ name . plist) + (read (current-buffer)) + (push (cons + name (plist-put + plist :modules + (list (doom-module-from-path file)))) + doom-packages)))))))) + (error + (signal 'doom-package-error + (list (doom-module-from-path file) + file e))))) + +(defun doom-package-list (&optional all-p core-only-p) + "Retrieve a list of explicitly declared packages from enabled modules. + +If ALL-P, gather packages unconditionally across all modules, including disabled +ones." + (let ((doom-interactive-mode t) + doom-packages) + (doom--read-packages + (doom-path doom-core-dir "packages.el") all-p 'noerror) + (unless core-only-p + (let ((private-packages (doom-path doom-private-dir "packages.el")) + (doom-modules (doom-module-list))) + (if all-p + (mapc #'doom--read-packages + (doom-files-in doom-modules-dir + :depth 2 + :match "/packages\\.el$")) + ;; We load the private packages file twice to ensure disabled packages + ;; are seen ASAP, and a second time to ensure privately overridden + ;; packages are properly overwritten. + (doom--read-packages private-packages nil 'noerror) + (cl-loop for key being the hash-keys of doom-modules + for path = (doom-module-path (car key) (cdr key) "packages.el") + for doom--current-module = key + do (doom--read-packages path nil 'noerror))) + (doom--read-packages private-packages all-p 'noerror))) + (cl-remove-if-not + (if core-only-p + (lambda (pkg) (eq (plist-get (cdr pkg) :type) 'core)) + #'identity) + (nreverse doom-packages)))) + +(defun doom-package-pinned-list () + "Return an alist mapping package names (strings) to pinned commits (strings)." + (let (alist) + (dolist (package doom-packages alist) + (cl-destructuring-bind (name &key disable ignore pin unpin &allow-other-keys) + package + (when (and (not ignore) + (not disable) + (or pin unpin)) + (setf (alist-get (doom-package-recipe-repo name) alist + nil 'remove #'equal) + (unless unpin pin))))))) + +(defun doom-package-unpinned-list () + "Return an alist mapping package names (strings) to pinned commits (strings)." + (let (alist) + (dolist (package doom-packages alist) + (cl-destructuring-bind + (_ &key recipe disable ignore pin unpin &allow-other-keys) + package + (when (and (not ignore) + (not disable) + (or unpin + (and (plist-member recipe :pin) + (null pin)))) + (cl-pushnew (doom-package-recipe-repo (car package)) alist + :test #'equal)))))) + +(defun doom-package-recipe-list () + "Return straight recipes for non-builtin packages with a local-repo." + (let (recipes) + (dolist (recipe (hash-table-values straight--recipe-cache)) + (cl-destructuring-bind (&key local-repo type no-build &allow-other-keys) + recipe + (unless (or (null local-repo) + (eq type 'built-in) + no-build) + (push recipe recipes)))) + (nreverse recipes))) + +(defun doom-package-state-list () + "TODO" + (let (alist) + (dolist (recipe (hash-table-values straight--repo-cache) alist) + (cl-destructuring-bind (&key local-repo type &allow-other-keys) + recipe + (when (and local-repo (not (eq type 'built-in))) + (setf (alist-get local-repo alist nil nil #'equal) + (straight-vc-get-commit type local-repo))))))) ;; ;;; Module package macros (cl-defmacro package! - (name &rest plist &key built-in recipe ignore _pin _disable) + (name &rest plist &key built-in recipe ignore _type _pin _disable) "Declares a package and how to install it (if applicable). This macro is declarative and does not load nor install packages. It is used to @@ -215,6 +431,15 @@ Only use this macro in a module's packages.el file. Accepts the following properties: + :type core|local|built-in|virtual + Specifies what kind of package this is. Can be a symbol or a list thereof. + `core' = this is a protected package and cannot be disabled! + `local' = this package is being modified in-place. This package's repo is + unshallowed and will be skipped when you update packages. + `built-in' = this package is already built-in (otherwise, will be + installed) + `virtual' = this package is not tracked by Doom's package manager. It won't + be installed or uninstalled. Use this to pin 2nd order dependencies. :recipe RECIPE Specifies a straight.el recipe to allow you to acquire packages from external sources. See https://github.com/raxod502/straight.el#the-recipe-format for @@ -254,6 +479,8 @@ elsewhere." (plist-put! plist :modules (append module-list (list module) + (when (file-in-directory-p ,(dir!) doom-private-dir) + '((:private . modules))) nil)))) ;; Merge given plist with pre-existing one (doplist! ((prop val) (list ,@plist) plist) @@ -282,8 +509,9 @@ elsewhere." (error-message-string e))))) ;; This is the only side-effect of this macro! (setf (alist-get name doom-packages) plist) - (with-no-warnings - (not (plist-get plist :disable))))) + (unless (plist-get plist :disable) + (with-no-warnings + (cons name plist))))) (defmacro disable-packages! (&rest packages) "A convenience macro for disabling packages in bulk. @@ -311,24 +539,26 @@ should use it!" (if (memq t targets) `(mapc (doom-rpartial #'doom-package-set :unpin t) (mapcar #'car doom-packages)) - (let (forms) - (dolist (target targets) - (cl-check-type target (or symbol keyword list)) - (cond - ((symbolp target) - (push `(doom-package-set ',target :unpin t) forms)) - ((or (keywordp target) - (listp target)) - (cl-destructuring-bind (category . modules) (doom-enlist target) - (dolist (pkg doom-packages) - (let ((pkg-modules (plist-get (cdr pkg) :modules))) - (and (assq category pkg-modules) - (or (null modules) - (cl-loop for module in modules - if (member (cons category module) pkg-modules) - return t)) - (push `(doom-package-set ',(car pkg) :unpin t) forms)))))))) - (macroexp-progn forms)))) + (macroexp-progn + (mapcar + (lambda (target) + (when target + `(doom-package-set ',target :unpin t))) + (cl-loop for target in targets + if (or (keywordp target) (listp target)) + append + (cl-loop with (category . modules) = (doom-enlist target) + for (name . plist) in doom-packages + for pkg-modules = (plist-get plist :modules) + if (and (assq category pkg-modules) + (or (null modules) + (cl-loop for module in modules + if (member (cons category module) pkg-modules) + return t)) + name) + collect it) + else if (symbolp target) + collect target))))) (provide 'core-packages) ;;; core-packages.el ends here diff --git a/core/core-projects.el b/core/core-projects.el index 04310ffe7..dff3aec43 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -20,7 +20,6 @@ Emacs.") ;;; Packages (use-package! projectile - :after-call after-find-file dired-before-readin-hook minibuffer-setup-hook :commands (projectile-project-root projectile-project-name projectile-project-p @@ -146,8 +145,9 @@ c) are not valid projectile projects." ;; .gitignore. This is recommended in the projectile docs. ((executable-find doom-projectile-fd-binary) (setq projectile-generic-command - (format "%s . -0 -H -E .git --color=never --type file --type symlink --follow" - doom-projectile-fd-binary) + (concat (format "%s . -0 -H -E .git --color=never --type file --type symlink --follow" + doom-projectile-fd-binary) + (if IS-WINDOWS " --path-separator=/")) projectile-git-command projectile-generic-command projectile-git-submodule-command nil ;; ensure Windows users get fd's benefits diff --git a/core/core-ui.el b/core/core-ui.el index 9b590b668..ea788cd97 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -443,9 +443,10 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original (use-package! winner ;; undo/redo changes to Emacs' window layout - :after-call after-find-file doom-switch-window-hook + :hook (window-configuration-change . winner-mode) :preface (defvar winner-dont-bind-my-keys t) ; I'll bind keys myself - :config (winner-mode +1) + :config + (remove-hook 'window-configuration-change #'winner-mode) (appendq! winner-boring-buffers '("*Compile-Log*" "*inferior-lisp*" "*Fuzzy Completions*" "*Apropos*" "*Help*" "*cvs*" "*Buffer List*" "*Ibuffer*" @@ -454,13 +455,12 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original (use-package! paren ;; highlight matching delimiters - :after-call after-find-file doom-switch-buffer-hook + :hook (doom-first-buffer . show-paren-mode) :config (setq show-paren-delay 0.1 show-paren-highlight-openparen t show-paren-when-point-inside-paren t - show-paren-when-point-in-periphery t) - (show-paren-mode +1)) + show-paren-when-point-in-periphery t)) ;;;###package whitespace diff --git a/core/core.el b/core/core.el index 1ce4c6967..0fe78e949 100644 --- a/core/core.el +++ b/core/core.el @@ -1,8 +1,9 @@ ;;; core.el --- the heart of the beast -*- lexical-binding: t; -*- -(when (version< emacs-version "26.1") - (error "Detected Emacs %s. Doom only supports Emacs 26.1 and higher" - emacs-version)) +(eval-when-compile + (when (< emacs-major-version 26) + (error "Detected Emacs v%s. Doom only supports Emacs 26 and newer" + emacs-version))) (defconst doom-version "2.0.9" "Current version of Doom Emacs.") @@ -195,7 +196,7 @@ users).") ;; Emacs "updates" its ui more often than it needs to, so we slow it down ;; slightly from 0.5s: -(setq idle-update-delay 1) +(setq idle-update-delay 1.0) ;; Emacs is essentially one huge security vulnerability, what with all the ;; dependencies it pulls in from all corners of the globe. Let's try to be at @@ -291,7 +292,9 @@ users).") ;; Performance on Windows is considerably worse than elsewhere, especially if ;; WSL is involved. We'll need everything we can get. (when IS-WINDOWS - (setq w32-get-true-file-attributes nil)) ; slightly faster IO + (setq w32-get-true-file-attributes nil ; slightly faster IO + w32-pipe-read-delay 0 ; faster ipc + w32-pipe-buffer-size (* 64 1024))) ; read more at a time (was 4K) ;; Remove command line options that aren't relevant to our current OS; means ;; slightly less to process at startup. @@ -304,15 +307,13 @@ users).") ;; Adopt a sneaky garbage collection strategy of waiting until idle time to ;; collect; staving off the collector while the user is working. -(when doom-interactive-mode - (setq gc-cons-percentage 0.6) - (add-transient-hook! 'pre-command-hook (gcmh-mode +1)) - (with-eval-after-load 'gcmh - (setq gcmh-idle-delay 10 - gcmh-high-cons-threshold 16777216 - gcmh-verbose doom-debug-mode ; 16mb - gc-cons-percentage 0.1) - (add-hook 'focus-out-hook #'gcmh-idle-garbage-collect))) +(setq gcmh-idle-delay 5 + gcmh-high-cons-threshold 16777216 ; 16mb + gcmh-verbose doom-debug-mode + gc-cons-percentage 0.6) +(with-eval-after-load 'gcmh + ;; But restore this later, otherwise we risk freezing and stuttering! + (setq gc-cons-percentage 0.1)) ;; HACK `tty-run-terminal-initialization' is *tremendously* slow for some ;; reason. Disabling it completely could have many side-effects, so we @@ -335,18 +336,14 @@ users).") "Run MODE-local-vars-hook after local variables are initialized." (run-hook-wrapped (intern-soft (format "%s-local-vars-hook" major-mode)) #'doom-try-run-hook)) -(add-hook 'hack-local-variables-hook #'doom-run-local-var-hooks-h) ;; If the user has disabled `enable-local-variables', then ;; `hack-local-variables-hook' is never triggered, so we trigger it at the end ;; of `after-change-major-mode-hook': -(defun doom-run-local-var-hooks-if-necessary-h () +(defun doom-run-local-var-hooks-maybe-h () "Run `doom-run-local-var-hooks-h' if `enable-local-variables' is disabled." (unless enable-local-variables (doom-run-local-var-hooks-h))) -(add-hook 'after-change-major-mode-hook - #'doom-run-local-var-hooks-if-necessary-h - 'append) ;; @@ -425,7 +422,18 @@ If this is a daemon session, load them all immediately instead." nil #'doom-load-packages-incrementally (cdr doom-incremental-packages) t)))) -(add-hook 'emacs-startup-hook #'doom-load-packages-incrementally-h) + +;; +;;; Custom hooks + +(defvar doom-first-input-hook nil + "Transient hooks run before the first user input.") + +(defvar doom-first-file-hook nil + "Transient hooks run before the first interactively opened file.") + +(defvar doom-first-buffer-hook nil + "Transient hooks run before the first interactively opened buffer.") ;; @@ -443,7 +451,7 @@ If RETURN-P, return the message as a string instead of displaying it." (setq doom-init-time (float-time (time-subtract (current-time) before-init-time)))))) -(defun doom-initialize (&optional force-p noerror) +(defun doom-initialize (&optional force-p) "Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil). The bootstrap process ensures that the essential directories exist, all core @@ -471,6 +479,7 @@ for a list of all recognized module trees. Order defines precedence (from most to least)." (when (or force-p (not doom-init-p)) (setq doom-init-p t) + (doom-log "Initializing Doom") ;; Reset as much state as possible, so `doom-initialize' can be treated like ;; a reset function. e.g. when reloading the config. @@ -478,70 +487,55 @@ to least)." load-path doom--initial-load-path process-environment doom--initial-process-environment) - ;; Load shell environment, optionally generated from 'doom env'. No need to - ;; do so if we're in terminal Emacs, because Emacs will correctly inherit + (or (and + ;; `doom-autoload-file' tells Emacs where to load all its functions + ;; from. This includes everything in core/autoload/*.el and + ;; autoload files in enabled modules. + (or (doom-load-autoloads-file doom-autoload-file) + (ignore (warn "Doom's core autoloads file is missing"))) + ;; Loads `doom-package-autoload-file', which loads a concatenated + ;; package autoloads file which caches `load-path', + ;; `auto-mode-alist', `Info-directory-list', and + ;; `doom-disabled-packages'. A big reduction in startup time. + (or (doom-load-autoloads-file doom-package-autoload-file) + (ignore (warn "Doom's package autoloads file is missing")))) + ;; If neither autoloads file loads, then the user forgot to sync, or + ;; aborted a doom command midway! + (signal 'doom-error + (list "Doom is in an incomplete state" + "run 'bin/doom sync' on the command line to repair it"))) + + ;; Load shell environment, optionally generated from 'doom env'. No need + ;; to do so if we're in terminal Emacs, where Emacs correctly inherits ;; your shell environment there. - (when (and (or (display-graphic-p) - (daemonp)) - (file-exists-p doom-env-file)) - (doom-load-envvars-file doom-env-file)) + (and (or (display-graphic-p) + (daemonp)) + (doom-load-envvars-file doom-env-file 'noerror)) + ;; Loads `use-package' and our module helper library (require 'core-modules) - (let (;; `doom-autoload-file' tells Emacs where to load all its functions - ;; from. This includes everything in core/autoload/*.el and autoload - ;; files in enabled modules. - (core-autoloads-p (doom-load-autoloads-file doom-autoload-file noerror)) - ;; Loads `doom-package-autoload-file', which loads a concatenated - ;; package autoloads file which caches `load-path', `auto-mode-alist', - ;; `Info-directory-list', and `doom-disabled-packages'. A big - ;; reduction in startup time. - (pkg-autoloads-p (doom-load-autoloads-file doom-package-autoload-file noerror))) - (if (and core-autoloads-p pkg-autoloads-p (not force-p)) - ;; In case we want to use package.el or straight via M-x - (progn - (with-eval-after-load 'package - (require 'core-packages)) - (with-eval-after-load 'straight - (require 'core-packages) - (doom-initialize-packages))) + ;; In case we want to use package.el or straight via M-x later + (autoload 'doom-initialize-packages "core-packages") + (autoload 'doom-initialize-core-packages "core-packages") + (with-eval-after-load 'package (require 'core-packages)) + (with-eval-after-load 'straight (doom-initialize-packages)) - ;; Eagerly load these libraries because we may be in a session that hasn't been - ;; fully initialized (e.g. where autoloads files haven't been generated or - ;; `load-path' populated). - (mapc (doom-rpartial #'load nil (not doom-debug-mode) 'nosuffix) - (file-expand-wildcards (concat doom-core-dir "autoload/*.el"))) + ;; Bootstrap the interactive session + (add-hook! 'window-setup-hook + (add-hook 'hack-local-variables-hook #'doom-run-local-var-hooks-h) + (add-hook 'after-change-major-mode-hook #'doom-run-local-var-hooks-maybe-h 'append) + (add-hook 'doom-first-input-hook #'gcmh-mode) + (add-hook-trigger! 'doom-first-input-hook 'pre-command-hook) + (add-hook-trigger! 'doom-first-file-hook 'after-find-hook 'dired-initial-position-hook) + (add-hook-trigger! 'doom-first-buffer-hook 'after-find-hook 'doom-switch-buffer-hook)) + (add-hook 'emacs-startup-hook #'doom-load-packages-incrementally-h) + (add-hook 'window-setup-hook #'doom-display-benchmark-h 'append) - ;; Create all our core directories to quell file errors - (mapc (doom-rpartial #'make-directory 'parents) - (list doom-local-dir - doom-etc-dir - doom-cache-dir)) + ;; Load core/core-*.el, the user's private init.el and their config.el + (doom-initialize-modules force-p)) - ;; Ensure the package management system (and straight) are ready for - ;; action (and all core packages/repos are installed) - (require 'core-packages) - (doom-initialize-packages force-p)) - - (unless (or (and core-autoloads-p pkg-autoloads-p) - noerror) - (unless core-autoloads-p - (warn "Your Doom core autoloads file is missing")) - (unless pkg-autoloads-p - (warn "Your package autoloads file is missing")) - (signal 'doom-autoload-error (list "Run `bin/doom refresh' to generate them"))) - - (when doom-interactive-mode - (add-hook 'window-setup-hook #'doom-display-benchmark-h 'append) - (add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler)))) - t)) - -(defun doom-initialize-core () - "Load Doom's core files for an interactive session." - (require 'core-keybinds) - (require 'core-ui) - (require 'core-projects) - (require 'core-editor)) + doom-init-p) (provide 'core) ;;; core.el ends here diff --git a/core/packages.el b/core/packages.el index 4d6494314..cd6ebc6e0 100644 --- a/core/packages.el +++ b/core/packages.el @@ -5,8 +5,24 @@ (package! auto-minor-mode :pin "17cfa1b54800fdef2975c0c0531dad34846a5065") (package! gcmh :pin "b1bde5089169a74f62033d027e06e98cbeedd43f") +;; core-packages.el +(package! straight + :type 'core + :recipe `(:host github + :repo "raxod502/straight.el" + :branch ,straight-repository-branch + :local-repo "straight.el" + :files ("straight*.el") + :no-build t) + :pin "f606bfaf9330cfb1ef6971bb1f6ac4e2c39a39c2") + +;; core-modules.el +(package! use-package + :type 'core + :pin "c873d5529c9c80cb58222f22873a4f081c307cb2") + ;; core-ui.el -(package! all-the-icons :pin "0b74fc361817e885580c3f3408079f949f5830e1") +(package! all-the-icons :pin "d6cb6d4a779eaa3570d8e451fd4d38b2b4554860") (package! hide-mode-line :pin "88888825b5b27b300683e662fa3be88d954b1cea") (package! highlight-numbers :pin "8b4744c7f46c72b1d3d599d4fb75ef8183dee307") (package! rainbow-delimiters :pin "5125f4e47604ad36c3eb4706310fcafac729ca8c") @@ -14,8 +30,8 @@ ;; core-editor.el (package! better-jumper :pin "6d240032ca213ccb3347e25f26c29b6822bf03a7") -(package! dtrt-indent :pin "9163cd990fb1f43dafed3948c6e406c13a45a6be") -(package! helpful :pin "c54e9ddbd6a77858048c1a4c4b549de98af8f88e") +(package! dtrt-indent :pin "50c440c80e0d15303d8ab543bce4c56e9c2bf407") +(package! helpful :pin "c0662aa07266fe204f4e6d72ccaa6af089400556") (when IS-MAC (package! ns-auto-titlebar :pin "1efc30d38509647b417f05587fd7003457719256")) (package! pcre2el :pin "0b5b2a2c173aab3fd14aac6cf5e90ad3bf58fa7d") @@ -38,7 +54,7 @@ :pin "01b39044b9b65fa4ea7d3166f8b1ffab6f740362")) ;; core-projects.el -(package! projectile :pin "5cd261dd75f4d711c0016617621349e2a98b43aa") +(package! projectile :pin "768f0570cad57b6885c4472df803906d097cbc1a") ;; core-keybinds.el (package! general :pin "42e38034cd2305fa7432866323c923979d8f9b06") diff --git a/core/templates/QUICKSTART_INTRO b/core/templates/QUICKSTART_INTRO index f7d101ac7..2f7bd1e60 100644 --- a/core/templates/QUICKSTART_INTRO +++ b/core/templates/QUICKSTART_INTRO @@ -1,25 +1,19 @@ -Before you doom yourself, there are a few things you should know: +But before you doom yourself, here are some things you should know: -1. Whenever you edit your doom! block in ~/.doom.d/init.el or modify your - modules, run: +1. Don't forget to run 'doom sync', then restart Emacs, after modifying + ~/.doom.d/init.el or ~/.doom.d/packages.el. - bin/doom refresh + This command ensures needed packages are installed, orphaned packages are + removed, and your autoloads/cache files are up to date. When in doubt, run + 'doom sync'! - This will ensure all needed packages are installed, all orphaned packages are - removed, and your autoloads files are up to date. This is important! If you - forget to do this you will get errors! +2. If something goes wrong, run `doom doctor`. It diagnoses common issues with + your environment and setup, and may offer clues about what is wrong. -2. If something inexplicably goes wrong, try `bin/doom doctor` +3. Use 'doom upgrade' to update Doom. Doing it any other way will require + additional steps. Run 'doom help upgrade' to understand those extra steps. - This will diagnose common issues with your environment and setup, and may - give you clues about what is wrong. - -3. Use `bin/doom upgrade` to update Doom. Doing it any other way may require - additional work. When in doubt, run `bin/doom sync`. - -4. Check out `bin/doom help` to see what else `bin/doom` can do (and it is - recommended you add ~/.emacs.d/bin to your PATH). - -5. You can find Doom's documentation via `M-x doom/help` or `SPC h D`. +4. Access Doom's documentation from within Emacs via 'SPC h D' or 'C-h D' (or + 'M-x doom/help') Have fun! diff --git a/docs/api.org b/docs/api.org index 9c3f8e1c5..350249303 100644 --- a/docs/api.org +++ b/docs/api.org @@ -546,7 +546,7 @@ These are side-by-side comparisons, showing how to bind keys with and without (fullscreen . ,fullscreen)))))) (defun save-frame-dimensions () - (doom-store-set 'last-frame-size + (doom-store-put 'last-frame-size (list (frame-position) (frame-width) (frame-height) diff --git a/docs/getting_started.org b/docs/getting_started.org index 389f551ed..98b59e6e7 100644 --- a/docs/getting_started.org +++ b/docs/getting_started.org @@ -755,7 +755,7 @@ The ~package!~ macro possesses a ~:disable~ property: (package! rtags :disable t) #+END_SRC -Once a package is disabled, ~use-packages!~ and ~after!~ blocks for it will be +Once a package is disabled, ~use-package!~ and ~after!~ blocks for it will be ignored, and the package is removed the next time you run ~bin/doom sync~. Use this to disable Doom's packages that you don't want or need. diff --git a/docs/modules.org b/docs/modules.org index 3957d7e30..ecb451857 100644 --- a/docs/modules.org +++ b/docs/modules.org @@ -128,7 +128,7 @@ Modules that bring support for a language or group of languages to Emacs. + [[file:../modules/lang/perl/README.org][perl]] - TODO + [[file:../modules/lang/php/README.org][php]] =+lsp= - TODO + plantuml - TODO -+ purescript - TODO ++ purescript =+lsp= - TODO + [[file:../modules/lang/python/README.org][python]] =+lsp +pyenv +conda +poetry= - TODO + qt - TODO + racket - TODO diff --git a/init.el b/init.el index 13ed45647..78a37fc54 100644 --- a/init.el +++ b/init.el @@ -47,7 +47,3 @@ ;; And let 'er rip! (doom-initialize) -(if noninteractive - (doom-initialize-packages) - (doom-initialize-core) - (doom-initialize-modules)) diff --git a/modules/checkers/syntax/config.el b/modules/checkers/syntax/config.el index f6c52c0a1..9d9ab1049 100644 --- a/modules/checkers/syntax/config.el +++ b/modules/checkers/syntax/config.el @@ -5,7 +5,7 @@ (use-package! flycheck :commands flycheck-list-errors flycheck-buffer - :after-call doom-switch-buffer-hook after-find-file + :hook (doom-first-buffer . global-flycheck-mode) :config (setq flycheck-emacs-lisp-load-path 'inherit) @@ -39,9 +39,7 @@ :n "j" #'flycheck-error-list-next-error :n "k" #'flycheck-error-list-previous-error :n "RET" #'flycheck-error-list-goto-error - :n [return] #'flycheck-error-list-goto-error) - - (global-flycheck-mode +1)) + :n [return] #'flycheck-error-list-goto-error)) (use-package! flycheck-popup-tip diff --git a/modules/completion/company/config.el b/modules/completion/company/config.el index 06b2459a6..a83a13412 100644 --- a/modules/completion/company/config.el +++ b/modules/completion/company/config.el @@ -2,7 +2,7 @@ (use-package! company :commands company-complete-common company-manual-begin company-grab-line - :after-call pre-command-hook after-find-file + :hook (doom-first-input . global-company-mode) :init (setq company-idle-delay 0.25 company-minimum-prefix-length 2 @@ -10,7 +10,7 @@ company-tooltip-align-annotations t company-require-match 'never company-global-modes - '(not erc-mode message-mode help-mode gud-mode eshell-mode) + '(not erc-mode message-mode help-mode gud-mode) company-frontends '(company-pseudo-tooltip-frontend company-echo-metadata-frontend) @@ -37,7 +37,14 @@ (unless (featurep! +childframe) ;; Don't persist company popups when switching back to normal mode. ;; `company-box' aborts on mode switch so it doesn't need this. - (add-hook 'evil-normal-state-entry-hook #'company-abort)) + (add-hook! 'evil-normal-state-entry-hook + (defun +company-abort-h () + ;; HACK `company-abort' doesn't no-op if company isn't active; causing + ;; unwanted side-effects, like the suppression of messages in the + ;; echo-area. + ;; REVIEW Revisit this to refactor; shouldn't be necessary! + (when company-candidates + (company-abort))))) ;; Allow users to switch between backends on the fly. E.g. C-x C-s followed ;; by C-x C-n, will switch from `company-yasnippet' to ;; `company-dabbrev-code'. @@ -45,8 +52,7 @@ :before #'company-begin-backend (company-abort))) - (add-hook 'after-change-major-mode-hook #'+company-init-backends-h 'append) - (global-company-mode +1)) + (add-hook 'after-change-major-mode-hook #'+company-init-backends-h 'append)) (use-package! company-tng @@ -137,8 +143,7 @@ "This disables the company-box scrollbar, because: https://github.com/sebastiencs/company-box/issues/44" :around #'company-box--update-scrollbar - (cl-letf (((symbol-function #'display-buffer-in-side-window) - (symbol-function #'ignore))) + (letf! ((#'display-buffer-in-side-window #'ignore)) (apply orig-fn args)))) diff --git a/modules/completion/helm/config.el b/modules/completion/helm/config.el index 66e8d7b9a..24d3175c9 100644 --- a/modules/completion/helm/config.el +++ b/modules/completion/helm/config.el @@ -23,8 +23,7 @@ be negative.") ;;; Packages (use-package! helm-mode - :defer t - :after-call pre-command-hook + :hook (doom-first-input . helm-mode) :init (map! [remap apropos] #'helm-apropos [remap find-library] #'helm-locate-library @@ -43,7 +42,6 @@ be negative.") [remap recentf-open-files] #'helm-recentf [remap yank-pop] #'helm-show-kill-ring) :config - (helm-mode +1) ;; helm is too heavy for `find-file-at-point' (add-to-list 'helm-completing-read-handlers-alist (cons #'find-file-at-point nil))) diff --git a/modules/completion/ido/config.el b/modules/completion/ido/config.el index 22de041ae..5f0cb880b 100644 --- a/modules/completion/ido/config.el +++ b/modules/completion/ido/config.el @@ -8,12 +8,12 @@ ;;; Packages (use-package! ido - :after-call pre-command-hook + :hook (doom-first-input . ido-mode) :hook (ido-mode . ido-everywhere) :hook (ido-mode . ido-ubiquitous-mode) :preface ;; HACK `ido' is a really old package. It defines `ido-mode' manually and - ;; doesn't define a hook, so we define one for it.") + ;; doesn't define a hook, so we define one for it, so we can use it! (defadvice! +ido-run-hooks-a (&rest _) :after #'ido-mode (run-hooks 'ido-mode-hook)) @@ -42,9 +42,7 @@ ;; Go to $HOME with ~ "~" (λ! (if (looking-back "/" (point-min)) (insert "~/") - (call-interactively #'self-insert-command)))) - - (ido-mode +1)) + (call-interactively #'self-insert-command))))) (use-package! ido-vertical-mode diff --git a/modules/completion/ivy/autoload/ivy.el b/modules/completion/ivy/autoload/ivy.el index f646469af..8147c26d5 100644 --- a/modules/completion/ivy/autoload/ivy.el +++ b/modules/completion/ivy/autoload/ivy.el @@ -178,7 +178,7 @@ If ARG (universal argument), open selection in other-window." (user-error "%S doesn't support wgrep" caller))))) ;;;###autoload -(defun +ivy-yas-prompt (prompt choices &optional display-fn) +(defun +ivy-yas-prompt-fn (prompt choices &optional display-fn) (yas-completing-prompt prompt choices display-fn #'ivy-completing-read)) ;;;###autoload diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el index 2df48b827..23f95a7af 100644 --- a/modules/completion/ivy/config.el +++ b/modules/completion/ivy/config.el @@ -19,7 +19,7 @@ results buffer.") ;;; Packages (use-package! ivy - :after-call pre-command-hook + :hook (doom-first-input . ivy-mode) :init (let ((standard-search-fn (if (featurep! +prescient) @@ -88,7 +88,7 @@ results buffer.") (setq +ivy--origin nil))) (after! yasnippet - (add-hook 'yas-prompt-functions #'+ivy-yas-prompt)) + (add-hook 'yas-prompt-functions #'+ivy-yas-prompt-fn)) (defadvice! +ivy--inhibit-completion-in-region-a (orig-fn &rest args) "`ivy-completion-in-region' struggles with completing certain @@ -98,12 +98,10 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (apply orig-fn args))) (define-key! ivy-minibuffer-map - "C-c C-e" #'+ivy/woccur [remap doom/delete-backward-word] #'ivy-backward-kill-word + "C-c C-e" #'+ivy/woccur "C-o" #'ivy-dispatching-done - "M-o" #'hydra-ivy/body) - - (ivy-mode +1)) + "M-o" #'hydra-ivy/body)) (use-package! ivy-rich @@ -116,12 +114,12 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (cadr (plist-get ivy-rich-display-transformers-list 'ivy-switch-buffer)))) - ;; Include variable value in `counsel-describe-variable' + ;; Enahnce the appearance of a couple counsel commands (plist-put! ivy-rich-display-transformers-list 'counsel-describe-variable '(:columns ((counsel-describe-variable-transformer (:width 40)) ; the original transformer - (+ivy-rich-describe-variable-transformer (:width 50)) + (+ivy-rich-describe-variable-transformer (:width 50)) ; display variable value (ivy-rich-counsel-variable-docstring (:face font-lock-doc-face)))) 'counsel-M-x '(:columns @@ -141,10 +139,9 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." ;; Highlight buffers differently based on whether they're in the same project ;; as the current project or not. - (let* ((plist (plist-get ivy-rich-display-transformers-list 'ivy-switch-buffer)) - (switch-buffer-alist (assq 'ivy-rich-candidate (plist-get plist :columns)))) - (when switch-buffer-alist - (setcar switch-buffer-alist '+ivy-rich-buffer-name))) + (when-let* ((plist (plist-get ivy-rich-display-transformers-list 'ivy-switch-buffer)) + (switch-buffer-alist (assq 'ivy-rich-candidate (plist-get plist :columns)))) + (setcar switch-buffer-alist '+ivy-rich-buffer-name)) (ivy-rich-mode +1)) @@ -265,9 +262,10 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." :override #'counsel--find-return-list (cl-destructuring-bind (find-program . args) (cond ((executable-find doom-projectile-fd-binary) - (cons doom-projectile-fd-binary - (list "--color=never" "-E" ".git" - "--type" "file" "--type" "symlink" "--follow"))) + (append (list doom-projectile-fd-binary + "--color=never" "-E" ".git" + "--type" "file" "--type" "symlink" "--follow") + (if IS-WINDOWS '("--path-separator=/")))) ((executable-find "rg") (append (list "rg" "--files" "--follow" "--color=never" "--hidden" "--no-messages") (cl-loop for dir in projectile-globally-ignored-directories @@ -284,8 +282,8 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (let ((offset (if (member find-program (list "rg" doom-projectile-fd-binary)) 0 2)) files) (while (< (point) (point-max)) - (push (buffer-substring - (+ offset (line-beginning-position)) (line-end-position)) files) + (push (buffer-substring (+ offset (line-beginning-position)) (line-end-position)) + files) (forward-line 1)) (nreverse files))))))) diff --git a/modules/completion/ivy/packages.el b/modules/completion/ivy/packages.el index 05a724417..78f1a9b25 100644 --- a/modules/completion/ivy/packages.el +++ b/modules/completion/ivy/packages.el @@ -1,18 +1,18 @@ ;; -*- no-byte-compile: t; -*- ;;; completion/ivy/packages.el -(package! swiper :pin "84efa3a2cbb9c5b0bbcc8d2e90671434eed74f94") +(package! swiper :pin "04ca16420053a3a6d34a96f0d680dd449c2e5851") (package! ivy) (package! ivy-hydra) (package! counsel) (package! amx :pin "7fb7b874291e0cdeb1f0acb18564a686ec86788d") -(package! counsel-projectile :pin "b556ed8995f375e57496f3482aef4b0def565de8") +(package! counsel-projectile :pin "126e825bbab872b3befd9ef88660571391ebfdc3") (package! ivy-rich :pin "3f818b201769bc13cc75aa73645217e374136aca") -(package! wgrep :pin "5977b8e00051c9003ca96e9d35133e0dea68db2c") +(package! wgrep :pin "f0ef9bfa44db503cdb2f83fcfbd2fa4e2382ef1f") (if (featurep! +prescient) - (package! ivy-prescient :pin "0f4a89bdec61395138d968a38d375e63ccfbed63") + (package! ivy-prescient :pin "3ab7605d997fb8337bf5ded2ad960b98ac0e1fd7") (when (featurep! +fuzzy) (package! flx :pin "17f5c9cb2af18aa6f52910ff4a5a63591261ced5"))) diff --git a/modules/config/default/+emacs-bindings.el b/modules/config/default/+emacs-bindings.el index ac6310c75..f85c6850b 100644 --- a/modules/config/default/+emacs-bindings.el +++ b/modules/config/default/+emacs-bindings.el @@ -202,7 +202,14 @@ :desc "Send to Launchbar" "l" #'+macos/send-to-launchbar :desc "Send project to Launchbar" "L" #'+macos/send-project-to-launchbar) (:when (featurep! :tools docker) - :desc "Docker" "D" #'docker)) + :desc "Docker" "D" #'docker) + (:when (featurep! :email mu4e) + :desc "mu4e" "m" #'=mu4e) + (:when (featurep! :email notmuch) + :desc "notmuch" "m" #'=notmuch) + (:when (featurep! :email wanderlust) + :desc "wanderlust" "m" #'=wanderlust)) + ;;; p --- project (:prefix ("p" . "project") diff --git a/modules/config/default/+evil-bindings.el b/modules/config/default/+evil-bindings.el index 2cc5d3f40..b7d2f20c3 100644 --- a/modules/config/default/+evil-bindings.el +++ b/modules/config/default/+evil-bindings.el @@ -4,12 +4,12 @@ ;; NOTE SPC u replaces C-u as the universal argument. ;; Minibuffer - (define-key! :keymaps '(evil-ex-completion-map evil-ex-search-keymap) - "C-a" #'evil-beginning-of-line - "C-b" #'evil-backward-char - "C-f" #'evil-forward-char - "C-j" #'next-complete-history-element - "C-k" #'previous-complete-history-element) + (map! :map (evil-ex-completion-map evil-ex-search-keymap) + "C-a" #'evil-beginning-of-line + "C-b" #'evil-backward-char + "C-f" #'evil-forward-char + :gi "C-j" #'next-complete-history-element + :gi "C-k" #'previous-complete-history-element) (define-key! :keymaps +default-minibuffer-maps [escape] #'abort-recursive-edit @@ -26,6 +26,7 @@ "C-k" #'previous-line "C-S-j" #'scroll-up-command "C-S-k" #'scroll-down-command) + ;; For folks with `evil-collection-setup-minibuffer' enabled (define-key! :states 'insert :keymaps +default-minibuffer-maps "C-j" #'next-line "C-k" #'previous-line) @@ -549,7 +550,13 @@ :desc "Send to Launchbar" "l" #'+macos/send-to-launchbar :desc "Send project to Launchbar" "L" #'+macos/send-project-to-launchbar) (:when (featurep! :tools docker) - :desc "Docker" "D" #'docker)) + :desc "Docker" "D" #'docker) + (:when (featurep! :email mu4e) + :desc "mu4e" "m" #'=mu4e) + (:when (featurep! :email notmuch) + :desc "notmuch" "m" #'=notmuch) + (:when (featurep! :email wanderlust) + :desc "wanderlust" "m" #'=wanderlust)) ;;; p --- project (:prefix-map ("p" . "project") diff --git a/modules/config/default/autoload/files.el b/modules/config/default/autoload/files.el index e49e8255f..73272354c 100644 --- a/modules/config/default/autoload/files.el +++ b/modules/config/default/autoload/files.el @@ -28,13 +28,15 @@ (defun +default/browse-notes () "Browse files from `org-directory'." (interactive) - (require 'org) + (unless (bound-and-true-p org-directory) + (require 'org)) (doom-project-browse org-directory)) ;;;###autoload (defun +default/find-in-notes () "Find a file under `org-directory', recursively." (interactive) - (require 'org) + (unless (bound-and-true-p org-directory) + (require 'org)) (doom-project-find-file org-directory)) ;;;###autoload diff --git a/modules/config/default/autoload/text.el b/modules/config/default/autoload/text.el index 01141cc6e..6d28f224b 100644 --- a/modules/config/default/autoload/text.el +++ b/modules/config/default/autoload/text.el @@ -50,8 +50,8 @@ If `buffer-file-name' isn't set, uses `default-directory'." (abbreviate-file-name path) (file-name-nondirectory path))))) - -(defun doom--backward-delete-whitespace-to-column () +;;;###autoload +(defun doom/backward-delete-whitespace-to-column () "Delete back to the previous column of whitespace, or as much whitespace as possible, or just one char if that's not possible." (interactive) @@ -74,7 +74,7 @@ possible, or just one char if that's not possible." ;; point and bol. ((and (not indent-tabs-mode) (not (bolp)) - (not (sp-point-in-string)) + (not (doom-point-in-string-p)) (save-excursion (>= (- (skip-chars-backward " \t")) tab-width))) (let ((movement (% (current-column) tab-width))) (when (= movement 0) @@ -97,7 +97,7 @@ possible, or just one char if that's not possible." { | } => {|} -+ Otherwise, resort to `doom--backward-delete-whitespace-to-column'. ++ Otherwise, resort to `doom/backward-delete-whitespace-to-column'. + Resorts to `delete-char' if n > 1" (interactive "p\nP") (or (integerp n) @@ -120,12 +120,14 @@ possible, or just one char if that's not possible." (save-excursion (insert-char ?\s (- ocol (current-column)) nil)))) ;; - ((and (= n 1) (bound-and-true-p smartparens-mode)) - (cond ((and (memq (char-before) (list ?\ ?\t)) - (save-excursion - (and (/= (skip-chars-backward " \t" (line-beginning-position)) 0) - (bolp)))) - (doom--backward-delete-whitespace-to-column)) + ((= n 1) + (cond ((or (not (featurep! +smartparens)) + (not (bound-and-true-p smartparens-mode)) + (and (memq (char-before) (list ?\ ?\t)) + (save-excursion + (and (/= (skip-chars-backward " \t" (line-beginning-position)) 0) + (bolp))))) + (doom/backward-delete-whitespace-to-column)) ((let* ((pair (ignore-errors (sp-get-thing))) (op (plist-get pair :op)) (cl (plist-get pair :cl)) @@ -144,6 +146,6 @@ possible, or just one char if that's not possible." (sp-insert-pair op) t) ((run-hook-with-args-until-success 'doom-delete-backward-functions)) - ((doom--backward-delete-whitespace-to-column))))))) + ((doom/backward-delete-whitespace-to-column))))))) ;; Otherwise, do simple deletion. ((delete-char (- n) killflag)))) diff --git a/modules/config/default/config.el b/modules/config/default/config.el index bbfc1ade7..ac9e53ba4 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -204,52 +204,52 @@ ;; This keybind allows * to skip over **. (map! :map markdown-mode-map - :ig "*" (λ! (if (looking-at-p "\\*\\* *$") - (forward-char 2) - (call-interactively 'self-insert-command))))) - - ;; Highjacks backspace to: - ;; a) balance spaces inside brackets/parentheses ( | ) -> (|) - ;; b) delete up to nearest column multiple of `tab-width' at a time - ;; c) close empty multiline brace blocks in one step: - ;; { - ;; | - ;; } - ;; becomes {|} - ;; d) refresh smartparens' :post-handlers, so SPC and RET expansions work - ;; even after a backspace. - ;; e) properly delete smartparen pairs when they are encountered, without - ;; the need for strict mode. - ;; f) do none of this when inside a string - (advice-add #'delete-backward-char :override #'+default--delete-backward-char-a)) - - ;; HACK Makes `newline-and-indent' continue comments (and more reliably). - ;; Consults `doom-point-in-comment-functions' to detect a commented - ;; region and uses that mode's `comment-line-break-function' to continue - ;; comments. If neither exists, it will fall back to the normal behavior - ;; of `newline-and-indent'. - ;; - ;; We use an advice here instead of a remapping because many modes define - ;; and remap to their own newline-and-indent commands, and tackling all - ;; those cases was judged to be more work than dealing with the edge - ;; cases on a case by case basis. - (defadvice! +default--newline-indent-and-continue-comments-a (&rest _) - "A replacement for `newline-and-indent'. - -Continues comments if executed from a commented line. Consults -`doom-point-in-comment-functions' to determine if in a comment." - :before-until #'newline-and-indent - (interactive "*") - (when (and +default-want-RET-continue-comments - (doom-point-in-comment-p) - (fboundp comment-line-break-function)) - (funcall comment-line-break-function nil) - t))) + :ig "*" (general-predicate-dispatch nil + (looking-at-p "\\*\\* *") + (λ! (forward-char 2))))))) ;; ;;; Keybinding fixes +;; Highjacks backspace to delete up to nearest column multiple of `tab-width' at +;; a time. If you have smartparens enabled, it will also: +;; a) balance spaces inside brackets/parentheses ( | ) -> (|) +;; b) close empty multiline brace blocks in one step: +;; { +;; | +;; } +;; becomes {|} +;; c) refresh smartparens' :post-handlers, so SPC and RET expansions work even +;; after a backspace. +;; d) properly delete smartparen pairs when they are encountered, without the +;; need for strict mode. +;; e) do none of this when inside a string +(advice-add #'delete-backward-char :override #'+default--delete-backward-char-a) + +;; HACK Makes `newline-and-indent' continue comments (and more reliably). +;; Consults `doom-point-in-comment-functions' to detect a commented region +;; and uses that mode's `comment-line-break-function' to continue comments. +;; If neither exists, it will fall back to the normal behavior of +;; `newline-and-indent'. +;; +;; We use an advice here instead of a remapping because many modes define +;; and remap to their own newline-and-indent commands, and tackling all +;; those cases was judged to be more work than dealing with the edge cases +;; on a case by case basis. +(defadvice! +default--newline-indent-and-continue-comments-a (&rest _) + "A replacement for `newline-and-indent'. + +Continues comments if executed from a commented line. Consults +`doom-point-in-comment-functions' to determine if in a comment." + :before-until #'newline-and-indent + (interactive "*") + (when (and +default-want-RET-continue-comments + (doom-point-in-comment-p) + (fboundp comment-line-break-function)) + (funcall comment-line-break-function nil) + t)) + ;; This section is dedicated to "fixing" certain keys so that they behave ;; sensibly (and consistently with similar contexts). @@ -353,7 +353,7 @@ Continues comments if executed from a commented line. Consults "dL" #'doom/help-search-loaded-files "dm" #'doom/help-modules "dn" #'doom/help-news - "dN" #'doom/help-news-search + "dN" #'doom/help-search-news "dpc" #'doom/help-package-config "dpd" #'doom/goto-private-packages-file "dph" #'doom/help-package-homepage diff --git a/modules/editor/evil/config.el b/modules/editor/evil/config.el index 3d214619b..8ea569173 100644 --- a/modules/editor/evil/config.el +++ b/modules/editor/evil/config.el @@ -1,13 +1,10 @@ ;;; editor/evil/config.el -*- lexical-binding: t; -*- -;; I'm a vimmer at heart. Its modal philosophy suits me better, and this module -;; strives to make Emacs a much better vim than vim was. - (defvar +evil-repeat-keys (cons ";" ",") "The keys to use for universal repeating motions. This is a cons cell whose CAR is the key for repeating a motion forward, and -whose CDR is for repeating backward. They should both be kbd-able strings.") +whose CDR is for repeating backward. They should both be `kbd'-able strings.") (defvar +evil-want-o/O-to-continue-comments t "If non-nil, the o/O keys will continue comment lines if the point is on a @@ -137,8 +134,8 @@ directives. By default, this only recognizes C directives.") (and (>= char ?2) (<= char ?9))) ;; REVIEW Fix #2493: dir-locals cannot target fundamental-mode when evil-mode - ;; is active. See https://github.com/hlissner/doom-emacs/issues/2493. - ;; Revert this if this is ever fixed upstream. + ;; is active. See hlissner/doom-emacs#2493. Revert this if + ;; emacs-evil/evil#1268 is resolved upstream. (defadvice! +evil--fix-local-vars-a (&rest _) :before #'turn-on-evil-mode (when (eq major-mode 'fundamental-mode) @@ -227,12 +224,12 @@ directives. By default, this only recognizes C directives.") (use-package! evil-embrace :commands embrace-add-pair embrace-add-pair-regexp :hook (LaTeX-mode . embrace-LaTeX-mode-hook) + :hook (LaTeX-mode . +evil-embrace-latex-mode-hook-h) :hook (org-mode . embrace-org-mode-hook) :hook (ruby-mode . embrace-ruby-mode-hook) :hook (emacs-lisp-mode . embrace-emacs-lisp-mode-hook) :hook ((lisp-mode emacs-lisp-mode clojure-mode racket-mode hy-mode) . +evil-embrace-lisp-mode-hook-h) - :hook ((org-mode LaTeX-mode) . +evil-embrace-latex-mode-hook-h) :hook ((c++-mode rustic-mode csharp-mode java-mode swift-mode typescript-mode) . +evil-embrace-angle-bracket-modes-hook-h) :init @@ -369,14 +366,12 @@ directives. By default, this only recognizes C directives.") (defmacro set-repeater! (command next-func prev-func) "Makes ; and , the universal repeat-keys in evil-mode. To change these keys see `+evil-repeat-keys'." - (let ((fn-sym (intern (format "+evil/repeat-%s" (doom-unquote command))))) - `(progn - (defun ,fn-sym (&rest _) - (when +evil-repeat-keys - (evil-define-key* 'motion 'local - (kbd (car +evil-repeat-keys)) #',next-func - (kbd (cdr +evil-repeat-keys)) #',prev-func))) - (advice-add #',command :after-while #',fn-sym)))) + `(defadvice! ,(intern (format "+evil--repeat-%s-a" (doom-unquote command))) (&rest _) + :after-while #',command + (when +evil-repeat-keys + (evil-define-key* 'motion 'local + (kbd (car +evil-repeat-keys)) #',next-func + (kbd (cdr +evil-repeat-keys)) #',prev-func)))) ;; n/N (set-repeater! evil-ex-search-next evil-ex-search-next evil-ex-search-previous) diff --git a/modules/editor/evil/init.el b/modules/editor/evil/init.el index 462e35b39..3c629003b 100644 --- a/modules/editor/evil/init.el +++ b/modules/editor/evil/init.el @@ -1,5 +1,7 @@ ;;; editor/evil/init.el -*- lexical-binding: t; -*- +(defvar evil-collection-key-blacklist) + ;; We load evil-collection ourselves for these reasons: ;; ;; 1. To truly lazy load it. Some of its modules, like diff --git a/modules/editor/multiple-cursors/autoload/evil-mc.el b/modules/editor/multiple-cursors/autoload/evil-mc.el index 886ca9c0d..cab055caa 100644 --- a/modules/editor/multiple-cursors/autoload/evil-mc.el +++ b/modules/editor/multiple-cursors/autoload/evil-mc.el @@ -56,6 +56,7 @@ PATTERN as literal. PATTERN is a delimited regexp (the same that :g or :s uses). FLAGS can be g and/or i; which mean the same thing they do in `evil-ex-substitute'." :evil-mc t + :keep-visual t (interactive "") (unless (and (stringp pattern) (not (string-empty-p pattern))) diff --git a/modules/emacs/dired/config.el b/modules/emacs/dired/config.el index 3da7048bd..bf971bedc 100644 --- a/modules/emacs/dired/config.el +++ b/modules/emacs/dired/config.el @@ -40,7 +40,7 @@ This is because there's no guarantee the remote system has GNU ls, which is the only variant that supports --group-directories-first." (when (file-remote-p default-directory) - (setq-local dired-listing-switches (car args)))))) + (setq-local dired-actual-switches (car args)))))) ;; Don't complain about this command being disabled when we use it (put 'dired-find-alternate-file 'disabled nil) diff --git a/modules/emacs/ibuffer/config.el b/modules/emacs/ibuffer/config.el index 9ad9e56d2..6cb2f9a87 100644 --- a/modules/emacs/ibuffer/config.el +++ b/modules/emacs/ibuffer/config.el @@ -42,16 +42,16 @@ (when (featurep! :ui workspaces) (define-ibuffer-filter workspace-buffers "Filter for workspace buffers" - (:reader - (+workspace-get (read-string "workspace name: ")) :description "workspace") + (:reader (+workspace-get (read-string "workspace name: ")) + :description "workspace") (memq buf (+workspace-buffer-list qualifier))) - (defun +ibuffer/workspace (workspace-name) + (defun +ibuffer-workspace (workspace-name) "Open an ibuffer window for a workspace" (ibuffer nil (format "%s buffers" workspace-name) (list (cons 'workspace-buffers (+workspace-get workspace-name))))) - (defun +ibuffer-current-workspace () + (defun +ibuffer/open-for-current-workspace () "Open an ibuffer window for the current workspace" (interactive) (+ibuffer/workspace (+workspace-current-name)))) diff --git a/modules/emacs/undo/config.el b/modules/emacs/undo/config.el index 9a289f922..5b86f9605 100644 --- a/modules/emacs/undo/config.el +++ b/modules/emacs/undo/config.el @@ -2,8 +2,9 @@ (use-package! undo-fu :unless (featurep! +tree) - :after-call doom-switch-buffer after-find-file + :after-call pre-command-hook after-find-file :init + ;; `evil' activates undo-tree, so we must pre-emptively disable it. (after! undo-tree (global-undo-tree-mode -1)) :config diff --git a/modules/emacs/undo/packages.el b/modules/emacs/undo/packages.el index 8c21f4904..b40326f1e 100644 --- a/modules/emacs/undo/packages.el +++ b/modules/emacs/undo/packages.el @@ -3,5 +3,5 @@ (if (featurep! +tree) (package! undo-tree :pin "5b6df03781495d8a25695d846b0cce496d3d3058") - (package! undo-fu :pin "0ce9ac36144e80316fff50bfe1bc5dd7e5e7ded6") - (package! undo-fu-session :pin "b808ef0cdcdd2eef221c67eda567eed7fcb3d4af")) + (package! undo-fu :pin "2b1e53285a55ce50ca6fd60b050f2171e235d8f9") + (package! undo-fu-session :pin "0400f15f2a0cfcedb69c06c3ff62f3f8814b62fb")) diff --git a/modules/email/mu4e/README.org b/modules/email/mu4e/README.org index 93f2ab37c..ca88d65b5 100644 --- a/modules/email/mu4e/README.org +++ b/modules/email/mu4e/README.org @@ -80,7 +80,7 @@ environment.systemPackages = with pkgs; [ ]; #+END_SRC -[[https://github.com/Emiller88/dotfiles/blob/master/modules/shell/mail.nix][An example of setting up mbsync with home-manager]] +[[https://github.com/Emiller88/dotfiles/blob/5eaabedf1b141c80a8d32e1b496055231476f65e/modules/shell/mail.nix][An example of setting up mbsync and mu with home-manager]] ** openSUSE Remove ~#~ in ~#sync_program=offlineimap~ to choose ~offlineimap~ instead of diff --git a/modules/email/notmuch/README.org b/modules/email/notmuch/README.org new file mode 100644 index 000000000..9125f8dd4 --- /dev/null +++ b/modules/email/notmuch/README.org @@ -0,0 +1,110 @@ +#+TITLE: email/notmuch +#+DATE: May 5, 2019 +#+SINCE: v2.0 +#+STARTUP: inlineimages + +* Table of Contents :TOC: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] + - [[#macos][MacOS]] + - [[#arch-linux][Arch Linux]] + - [[#nixos][NixOS]] + - [[#opensuse][openSUSE]] + - [[#debianubuntu][Debian/Ubuntu]] +- [[#features][Features]] +- [[#configuration][Configuration]] + - [[#gmailier][Gmailier]] + - [[#offlineimap][offlineimap]] + - [[#mbsync][mbsync]] + - [[#notmuch][notmuch]] +- [[#troubleshooting][Troubleshooting]] + +* Description +This module makes Emacs an email client, using ~notmuch~. + +** Module Flags ++ This module install no module flags. + + +** Plugins ++ [[https://notmuchmail.org/][notmuch]] ++ [[https://github.com/org-mime/org-mime][org-mime]] + +* Prerequisites +This module requires: + ++ Either ~gmailieer~ (default), ~mbsync~ or ~offlineimap~ (to sync mail with) ++ ~notmuch~, to index and tag your downloaded messages. ++ ~afew~, optionally to initially tag your downloaded messages. + +** TODO MacOS + +** TODO Arch Linux +** NixOS +#+BEGIN_SRC nix +environment.systemPackages = with pkgs; [ + notmuch + # And one of the following + gmailieer + isync + offlineimap +]; +#+END_SRC + +[[https://github.com/Emiller88/dotfiles/blob/319841bd3b89e59b01d169137cceee3183aba4fc/modules/shell/mail.nix][An example of setting up mbsync and notmuch with home-manager]] + +** TODO openSUSE +** TODO Debian/Ubuntu +* TODO Features + +* Configuration +** TODO Gmailier +** offlineimap +This module uses =Gmailier= by default. To use =offlineimap=, change ~+notmuch-sync-backend~: + +#+BEGIN_SRC emacs-lisp +(setq +notmuch-sync-backend 'offlineimap) +#+END_SRC + +Next, you need to write a configuration file for =offlineimap=. Mine can be found +[[https://github.com/hlissner/dotfiles/tree/master/shell/mu][in my dotfiles repository]]. It is configured to download mail to ~\~/.mail~. I +use [[https://www.passwordstore.org/][unix pass]] to securely store my login credentials. You can find a *very* +detailed configuration [[https://github.com/OfflineIMAP/offlineimap/blob/master/offlineimap.conf][here]]. + +Next you can download your email with ~offlineimap -o~. This may take a while, +especially if you have thousands of mails. + +You can now proceed with the [[*mu and mu4e][mu and mu4e]] section. + +** mbsync +This module uses =Gmailier= by default. To use =mbsync=, change ~+notmuch-sync-backend~: + +#+BEGIN_SRC emacs-lisp +(setq +notmuch-sync-backend 'mbsync) +#+END_SRC + +The steps needed to set up =mu4e= with =mbsync= are very similar to the ones for +[[*offlineimap][offlineimap]]. + +Start with writing a ~\~/.mbsyncrc~. An example for GMAIL can be found on +[[http://pragmaticemacs.com/emacs/migrating-from-offlineimap-to-mbsync-for-mu4e/][pragmaticemacs.com]]. A non-GMAIL example is available as a gist [[https://gist.github.com/agraul/60977cc497c3aec44e10591f94f49ef0][here]]. The [[http://isync.sourceforge.net/mbsync.html][manual +page]] contains all needed information to set up your own. + +Next you can download your email with ~mbsync --all~. This may take a while, but +should be quicker than =offlineimap= ;). + +You can now proceed with the [[*mu and mu4e][mu and mu4e]] section. + +** notmuch +You should have your email downloaded already. If you have not, you need to set +=Gmailier=, =offlineimap= or =mbsync= up before you proceed. + +Before you can use =notmuch=, you need to index your email initially. + +#+BEGIN_SRC sh +notmuch new +#+END_SRC + +* Troubleshooting diff --git a/modules/lang/csharp/doctor.el b/modules/lang/csharp/doctor.el index 82c1c191d..b9a56317f 100644 --- a/modules/lang/csharp/doctor.el +++ b/modules/lang/csharp/doctor.el @@ -1,7 +1,7 @@ ;; -*- lexical-binding: t; no-byte-compile: t; -*- ;;; lang/csharp/doctor.el -(require 'omnisharp) -(let ((omnisharp-bin (or omnisharp-server-executable-path (omnisharp--server-installation-path t)))) - (unless (file-exists-p omnisharp-bin) - (warn! "Omnisharp server isn't installed, completion won't work"))) +(when (and (require 'omnisharp nil t) (not (featurep! +lsp))) + (let ((omnisharp-bin (or omnisharp-server-executable-path (omnisharp--server-installation-path t)))) + (unless (file-exists-p omnisharp-bin) + (warn! "Omnisharp server isn't installed, completion won't work")))) diff --git a/modules/lang/elixir/README.org b/modules/lang/elixir/README.org index 1ae98cf91..b1a703e2f 100644 --- a/modules/lang/elixir/README.org +++ b/modules/lang/elixir/README.org @@ -16,10 +16,10 @@ * Description This module provides support for [[https://elixir-lang.org/][Elixir programming language]] via [[https://github.com/tonini/alchemist.el][alchemist.el]] -or [[https://github.com/JakeBecker/elixir-ls/][elixir-ls]]. +or [[https://github.com/elixir-lsp/elixir-ls/][elixir-ls]]. ** Module flags -+ ~+lsp~ Enable LSP support. Requires [[https://github.com/JakeBecker/elixir-ls/][elixir-ls]]. ++ ~+lsp~ Enable LSP support. Requires [[https://github.com/elixir-lsp/elixir-ls/][elixir-ls]]. ** Plugins + [[https://github.com/elixir-editors/emacs-elixir][elixir-mode]] diff --git a/modules/lang/emacs-lisp/autoload.el b/modules/lang/emacs-lisp/autoload.el index 080bfbd02..2191695c2 100644 --- a/modules/lang/emacs-lisp/autoload.el +++ b/modules/lang/emacs-lisp/autoload.el @@ -67,6 +67,9 @@ library/userland functions" (byte-compile #'+emacs-lisp-highlight-vars-and-faces))) +;; +;;; Handlers + (defun +emacs-lisp--module-at-point () (let ((origin (point))) (save-excursion @@ -126,11 +129,52 @@ if it's callable, `apropos' otherwise." (thing (helpful-symbol (intern thing))) ((call-interactively #'helpful-at-point)))) -;; FIXME -;; (defun +emacs-lisp-lookup-file (thing) -;; (when-let (module (+emacs-lisp--module-at-point thing)) -;; (doom/help-modules (car module) (cadr module) 'visit-dir) -;; t)) +;;;###autoload +(defun +emacs-lisp-indent-function (indent-point state) + "A replacement for `lisp-indent-function'. + +Indents plists more sensibly. Adapted from +https://emacs.stackexchange.com/questions/10230/how-to-indent-keywords-aligned" + (let ((normal-indent (current-column)) + (orig-point (point)) + ;; TODO Refactor `target' usage (ew!) + target) + (goto-char (1+ (elt state 1))) + (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) + (cond ((and (elt state 2) + (or (not (looking-at-p "\\sw\\|\\s_")) + (eq (char-after) ?:))) + (unless (> (save-excursion (forward-line 1) (point)) + calculate-lisp-indent-last-sexp) + (goto-char calculate-lisp-indent-last-sexp) + (beginning-of-line) + (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)) + (backward-prefix-chars) + (current-column)) + ((and (save-excursion + (goto-char indent-point) + (skip-syntax-forward " ") + (not (eq (char-after) ?:))) + (save-excursion + (goto-char orig-point) + (and (eq (char-after) ?:) + (eq (char-before) ?\() + (setq target (current-column))))) + (save-excursion + (move-to-column target t) + target)) + ((let* ((function (buffer-substring (point) (progn (forward-sexp 1) (point)))) + (method (or (function-get (intern-soft function) 'lisp-indent-function) + (get (intern-soft function) 'lisp-indent-hook)))) + (cond ((or (eq method 'defun) + (and (null method) + (> (length function) 3) + (string-match-p "\\`def" function))) + (lisp-indent-defform state indent-point)) + ((integerp method) + (lisp-indent-specform method state indent-point normal-indent)) + (method + (funcall method indent-point state)))))))) ;; @@ -170,6 +214,18 @@ if it's callable, `apropos' otherwise." load-path))) (buttercup-run-discover))) +;;;###autoload +(defun +emacs-lisp/edebug-instrument-defun-on () + "Toggle on instrumentalisation for the function under `defun'." + (interactive) + (eval-defun 'edebugit)) + +;;;###autoload +(defun +emacs-lisp/edebug-instrument-defun-off () + "Toggle off instrumentalisation for the function under `defun'." + (interactive) + (eval-defun nil)) + ;; ;;; Hooks @@ -234,62 +290,3 @@ verbosity when editing a file in `doom-private-dir' or `doom-emacs-dir'." (when (and start finish) (put-text-property start finish 'display "..."))))) nil) - -;;;###autoload -(defun +emacs-lisp-indent-function (indent-point state) - "A replacement for `lisp-indent-function'. - -Indents plists more sensibly. Adapted from -https://emacs.stackexchange.com/questions/10230/how-to-indent-keywords-aligned" - (let ((normal-indent (current-column)) - (orig-point (point)) - ;; TODO Refactor `target' usage (ew!) - target) - (goto-char (1+ (elt state 1))) - (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) - (cond ((and (elt state 2) - (or (not (looking-at-p "\\sw\\|\\s_")) - (eq (char-after) ?:))) - (unless (> (save-excursion (forward-line 1) (point)) - calculate-lisp-indent-last-sexp) - (goto-char calculate-lisp-indent-last-sexp) - (beginning-of-line) - (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)) - (backward-prefix-chars) - (current-column)) - ((and (save-excursion - (goto-char indent-point) - (skip-syntax-forward " ") - (not (eq (char-after) ?:))) - (save-excursion - (goto-char orig-point) - (and (eq (char-after) ?:) - (eq (char-before) ?\() - (setq target (current-column))))) - (save-excursion - (move-to-column target t) - target)) - ((let* ((function (buffer-substring (point) (progn (forward-sexp 1) (point)))) - (method (or (function-get (intern-soft function) 'lisp-indent-function) - (get (intern-soft function) 'lisp-indent-hook)))) - (cond ((or (eq method 'defun) - (and (null method) - (> (length function) 3) - (string-match-p "\\`def" function))) - (lisp-indent-defform state indent-point)) - ((integerp method) - (lisp-indent-specform method state indent-point normal-indent)) - (method - (funcall method indent-point state)))))))) - -;;;###autoload -(defun +emacs-lisp/edebug-instrument-defun-on () - "Toggle on instrumentalisation for the function under `defun'." - (interactive) - (eval-defun 'edebugit)) - -;;;###autoload -(defun +emacs-lisp/edebug-instrument-defun-off () - "Toggle off instrumentalisation for the function under `defun'." - (interactive) - (eval-defun nil)) diff --git a/modules/lang/emacs-lisp/packages.el b/modules/lang/emacs-lisp/packages.el index a72ea6124..f4b918316 100644 --- a/modules/lang/emacs-lisp/packages.el +++ b/modules/lang/emacs-lisp/packages.el @@ -3,15 +3,16 @@ (package! elisp-mode :built-in t) -(package! highlight-quoted :pin "2410347815") +;; Fontification plugins +(package! highlight-quoted :pin "24103478158cd19fbcfb4339a3f1fa1f054f1469") ;; Tools -(package! macrostep :pin "424e3734a1") -(package! overseer :pin "02d49f582e") -(package! elisp-def :pin "368b04da68") -(package! elisp-demos :pin "57dd4ae3e4") +(package! macrostep :pin "424e3734a1ee526a1bd7b5c3cd1d3ef19d184267") +(package! overseer :pin "02d49f582e80e36b4334c9187801c5ecfb027789") +(package! elisp-def :pin "368b04da68783601b52e3169312183381871cf9e") +(package! elisp-demos :pin "4cd55a30d5dbd8d36a0e6f87261c4fef17fc6db0") (when (featurep! :checkers syntax) - (package! flycheck-cask :pin "3457ae553c")) + (package! flycheck-cask :pin "3457ae553c4feaf8168008f063d78fdde8fb5f94")) ;; Libraries -(package! buttercup :pin "a91f282025") +(package! buttercup :pin "532d082a363add4f6d9838ca3f924a773ce12136") diff --git a/modules/lang/go/packages.el b/modules/lang/go/packages.el index fe58f0329..2008ca56c 100644 --- a/modules/lang/go/packages.el +++ b/modules/lang/go/packages.el @@ -1,15 +1,15 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/go/packages.el -(package! go-eldoc :pin "cbbd2ea1e9") -(package! go-guru :pin "10d6ab43d9") -(package! go-mode :pin "10d6ab43d9") -(package! gorepl-mode :pin "6a73bf352e") -(package! go-tag :pin "59b243f2fa") -(package! go-gen-test :pin "44c202ac97") +(package! go-eldoc :pin "cbbd2ea1e94a36004432a9ac61414cb5a95a39bd") +(package! go-guru :pin "734d5232455ffde088021ea5908849ac570e890f") +(package! go-mode :pin "734d5232455ffde088021ea5908849ac570e890f") +(package! gorepl-mode :pin "6a73bf352e8d893f89cad36c958c4db2b5e35e07") +(package! go-tag :pin "59b243f2fa079d9de9d56f6e2d94397e9560310a") +(package! go-gen-test :pin "44c202ac97e728e93a35cee028a0ea8dd6e4292c") (when (featurep! :completion company) - (package! company-go :pin "4acdcbdea7")) + (package! company-go :pin "4acdcbdea79de6b3dee1c637eca5cbea0fdbe37c")) (when (featurep! :checkers syntax) - (package! flycheck-golangci-lint :pin "8e446c6831")) + (package! flycheck-golangci-lint :pin "8e446c68311048f0b87febf8ef0379e29d358851")) diff --git a/modules/lang/haskell/config.el b/modules/lang/haskell/config.el index eab9a2adf..a65ce74fb 100644 --- a/modules/lang/haskell/config.el +++ b/modules/lang/haskell/config.el @@ -31,7 +31,9 @@ (map! :map haskell-mode-map :n "o" #'+haskell/evil-open-below - :n "O" #'+haskell/evil-open-above) + :n "O" #'+haskell/evil-open-above + (:when (featurep! :tools lookup) + [remap haskell-mode-jump-to-def-or-tag] #'+lookup/definition)) (map! :localleader :map haskell-mode-map diff --git a/modules/lang/markdown/config.el b/modules/lang/markdown/config.el index 448710520..d82e396db 100644 --- a/modules/lang/markdown/config.el +++ b/modules/lang/markdown/config.el @@ -92,10 +92,6 @@ capture, the end position, and the output buffer.") (use-package! evil-markdown :when (featurep! :editor evil +everywhere) :hook (markdown-mode . evil-markdown-mode) - :init - ;; REVIEW Until Somelauw/evil-markdown#1 is resolved: - (defun evil-disable-insert-state-bindings () - evil-disable-insert-state-bindings) :config (add-hook 'evil-markdown-mode-hook #'evil-normalize-keymaps) (map! :map evil-markdown-mode-map diff --git a/modules/lang/markdown/packages.el b/modules/lang/markdown/packages.el index dfe2f4572..9b7e2f921 100644 --- a/modules/lang/markdown/packages.el +++ b/modules/lang/markdown/packages.el @@ -1,8 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/markdown/packages.el -(package! markdown-mode :pin "c927a114b1b23cf7538181d62fd14679cce7fa25") -(package! markdown-toc :pin "eda9650a1bf0015e52e9678bd92b0a8beb1d7d71") +(package! markdown-mode :pin "f47a2e9796dfdde6fae7920af23647fe027dc34e") +(package! markdown-toc :pin "a9f13eecd0c7d8be960055dbc2d6f5d3fe6f40ca") (package! edit-indirect :pin "935ded353b9ed3da67bc61abf245c21b58d88864") (when (featurep! +grip) @@ -11,4 +11,4 @@ (when (featurep! :editor evil +everywhere) (package! evil-markdown :recipe (:host github :repo "Somelauw/evil-markdown") - :pin "46cd81b37991c4325fc24015a610f832b0ff995d")) + :pin "685d7fbb81bc02fa32779d2a127b99a0c8c7436b")) diff --git a/modules/lang/org/autoload/org-link.el b/modules/lang/org/autoload/org-link.el index 59d6c477b..28e7bde1e 100644 --- a/modules/lang/org/autoload/org-link.el +++ b/modules/lang/org/autoload/org-link.el @@ -46,11 +46,15 @@ exist, and `org-link' otherwise." "Intepret LINK as an image file path and return its data." (setq link (expand-file-name - link - (pcase protocol - ("download" (or org-download-image-dir org-attach-id-dir default-directory)) - ("attachment" org-attach-id-dir) - (_ default-directory)))) + link (pcase protocol + ("download" + (or (if (require 'org-download nil t) org-download-image-dir) + (if (require 'org-attach) org-attach-id-dir) + default-directory)) + ("attachment" + (require 'org-attach) + org-attach-id-dir) + (_ default-directory)))) (when (and (file-exists-p link) (image-type-from-file-name link)) (with-temp-buffer diff --git a/modules/lang/org/autoload/org.el b/modules/lang/org/autoload/org.el index e66d03a09..b392851b5 100644 --- a/modules/lang/org/autoload/org.el +++ b/modules/lang/org/autoload/org.el @@ -141,7 +141,7 @@ current file). Only scans first 2048 bytes of the document." ;;; Commands ;;;###autoload -(defun +org/dwim-at-point () +(defun +org/dwim-at-point (&optional arg) "Do-what-I-mean at point. If on a: @@ -158,7 +158,7 @@ If on a: - latex fragment: toggle it. - link: follow it - otherwise, refresh all inline images in current tree." - (interactive) + (interactive "P") (let* ((context (org-element-context)) (type (org-element-type context))) ;; skip over unimportant contexts @@ -206,7 +206,7 @@ If on a: (`table-cell (org-table-blank-field) - (org-table-recalculate) + (org-table-recalculate arg) (when (and (string-empty-p (string-trim (org-table-get-field))) (bound-and-true-p evil-local-mode)) (evil-change-state 'insert))) @@ -215,13 +215,13 @@ If on a: (org-babel-lob-execute-maybe)) (`statistics-cookie - (save-excursion (org-update-statistics-cookies nil))) + (save-excursion (org-update-statistics-cookies arg))) ((or `src-block `inline-src-block) - (org-babel-execute-src-block)) + (org-babel-execute-src-block arg)) ((or `latex-fragment `latex-environment) - (org-latex-preview)) + (org-latex-preview arg)) (`link (let* ((lineage (org-element-lineage context '(link) t)) @@ -229,7 +229,7 @@ If on a: (if (or (equal (org-element-property :type lineage) "img") (and path (image-type-from-file-name path))) (+org--refresh-inline-images-in-subtree) - (org-open-at-point)))) + (org-open-at-point arg)))) ((guard (org-element-property :checkbox (org-element-lineage context '(item) t))) (let ((match (and (org-at-item-checkbox-p) (match-string 1)))) @@ -238,7 +238,7 @@ If on a: (_ (if (or (org-in-regexp org-ts-regexp-both nil t) (org-in-regexp org-tsr-regexp-both nil t) - (org-in-regexp org-any-link-re nil t)) + (org-in-regexp org-link-any-re nil t)) (call-interactively #'org-open-at-point) (+org--refresh-inline-images-in-subtree)))))) @@ -306,6 +306,20 @@ the prefix ARG changes this command's behavior." ;;;###autoload (defalias #'+org/close-fold #'outline-hide-subtree) +;;;###autoload +(defun +org/close-all-folds (&optional level) + "Close all folds in the buffer (or below LEVEL)." + (interactive "p") + (outline-hide-sublevels (or level 1))) + +;;;###autoload +(defun +org/open-all-folds (&optional level) + "Open all folds in the buffer (or up to LEVEL)." + (interactive "P") + (if (integerp level) + (outline-hide-sublevels level) + (outline-show-all))) + (defun +org--get-foldlevel () (let ((max 1)) (save-restriction @@ -321,22 +335,20 @@ the prefix ARG changes this command's behavior." max))) ;;;###autoload -(defun +org/show-next-fold-level () +(defun +org/show-next-fold-level (&optional count) "Decrease the fold-level of the visible area of the buffer. This unfolds another level of headings on each invocation." - (interactive) - (let* ((current-level (+org--get-foldlevel)) - (new-level (1+ current-level))) + (interactive "p") + (let ((new-level (+ (+org--get-foldlevel) (or count 1)))) (outline-hide-sublevels new-level) (message "Folded to level %s" new-level))) ;;;###autoload -(defun +org/hide-next-fold-level () +(defun +org/hide-next-fold-level (&optional count) "Increase the global fold-level of the visible area of the buffer. This folds another level of headings on each invocation." - (interactive) - (let* ((current-level (+org--get-foldlevel)) - (new-level (max 1 (1- current-level)))) + (interactive "p") + (let ((new-level (max 1 (- (+org--get-foldlevel) (or count 1))))) (outline-hide-sublevels new-level) (message "Folded to level %s" new-level))) @@ -434,20 +446,14 @@ with `org-cycle')." t)) ;;;###autoload -(defun +org-unfold-to-2nd-level-or-point-h () - "Alters '#+STARTUP overview' to only expand first-level headings. -Expands the first level, but no further. If a different startup option was -provided, do that instead." - (unless org-agenda-inhibit-startup - ;; TODO Implement a custom #+STARTUP option? - (when (eq org-startup-folded t) - (outline-hide-sublevels +org-initial-fold-level)) - ;; If point was left somewhere deeper, unfold to point on startup. - (when (outline-invisible-p) - (ignore-errors - (save-excursion - (outline-previous-visible-heading 1) - (org-show-subtree)))))) +(defun +org-make-last-point-visible-h () + "Unfold subtree around point if saveplace places it to a folded region." + (and (not org-agenda-inhibit-startup) + (outline-invisible-p) + (ignore-errors + (save-excursion + (outline-previous-visible-heading 1) + (org-show-subtree))))) ;;;###autoload (defun +org-remove-occur-highlights-h () diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index 1e556547a..3dc4acc22 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -50,9 +50,6 @@ Is relative to `org-directory', unless it is absolute. Is used in Doom's default (defvar +org-capture-projects-file "projects.org" "Default, centralized target for org-capture templates.") -(defvar +org-initial-fold-level 2 - "The initial fold level of org files when no #+STARTUP options for it.") - (defvar +org-habit-graph-padding 2 "The padding added to the end of the consistency graph") @@ -163,7 +160,7 @@ This forces it to read the background before rendering." ("HOLD" . +org-todo-onhold) ("PROJ" . +org-todo-project))) - (defadvice! +org-display-link-in-eldoc-a (&rest args) + (defadvice! +org-display-link-in-eldoc-a (&rest _) "Display full link in minibuffer when cursor/mouse is over it." :before-until #'org-eldoc-documentation-function (when-let (link (org-element-property :raw-link (org-element-context))) @@ -199,10 +196,10 @@ This forces it to read the background before rendering." ;; I prefer C-c C-c over C-c ' (more consistent) (define-key org-src-mode-map (kbd "C-c C-c") #'org-edit-src-exit) - (defadvice! +org-fix-newline-and-indent-in-src-blocks-a () + (defadvice! +org-fix-newline-and-indent-in-src-blocks-a (&optional indent _arg _interactive) "Mimic `newline-and-indent' in src blocks w/ lang-appropriate indentation." - :after #'org-return-indent - (when (org-in-src-block-p t) + :after #'org-return + (when (and indent (org-in-src-block-p t)) (org-babel-do-in-edit-buffer (call-interactively #'indent-for-tab-command)))) @@ -322,11 +319,17 @@ I like: (after! org-capture (org-capture-put :kill-buffer t)) + ;; Fix #462: when refiling from org-capture, Emacs prompts to kill the + ;; underlying, modified buffer. This fixes that. + (add-hook 'org-after-refile-insert-hook #'save-buffer) + ;; HACK Doom doesn't support `customize'. Best not to advertise it as an ;; option in `org-capture's menu. (defadvice! +org--remove-customize-option-a (orig-fn table title &optional prompt specials) :around #'org-mks - (funcall orig-fn table title prompt (remove '("C" "Customize org-capture-templates") specials))) + (funcall orig-fn table title prompt + (remove '("C" "Customize org-capture-templates") + specials))) (defadvice! +org--capture-expand-variable-file-a (file) "If a variable is used for a file path in `org-capture-template', it is used @@ -337,13 +340,6 @@ relative to `org-directory', unless it is an absolute path." (expand-file-name (symbol-value file) org-directory) file)) - (defadvice! +org--prevent-save-prompts-when-refiling-a (&rest _) - "Fix #462: when refiling from org-capture, Emacs prompts to kill the -underlying, modified buffer. This fixes that." - :after #'org-refile - (when (bound-and-true-p org-capture-is-refiling) - (org-save-all-org-buffers))) - (add-hook! 'org-capture-mode-hook (defun +org-show-target-in-capture-header-h () (setq header-line-format @@ -436,28 +432,39 @@ underlying, modified buffer. This fixes that." (setq org-pandoc-options '((standalone . t) (mathjax . t) - (variable . "revealjs-url=https://revealjs.com"))))) + (variable . "revealjs-url=https://revealjs.com")))) + + (defadvice! +org--fix-async-export-a (orig-fn &rest args) + :around #'org-export-to-file + (if (not org-export-in-background) + (apply orig-fn args) + (setq org-export-async-init-file (make-temp-file "doom-org-async-export")) + (with-temp-file org-export-async-init-file + (prin1 `(progn (setq org-export-async-debug ,debug-on-error + load-path ',load-path) + (load ,user-init-file nil t)) + (current-buffer))) + (apply orig-fn args)))) (defun +org-init-habit-h () - "TODO" (add-hook! 'org-agenda-mode-hook (defun +org-habit-resize-graph-h () "Right align and resize the consistency graphs based on `+org-habit-graph-window-ratio'" - (require 'org-habit) - (let* ((total-days (float (+ org-habit-preceding-days org-habit-following-days))) - (preceding-days-ratio (/ org-habit-preceding-days total-days)) - (graph-width (floor (* (window-width) +org-habit-graph-window-ratio))) - (preceding-days (floor (* graph-width preceding-days-ratio))) - (following-days (- graph-width preceding-days)) - (graph-column (- (window-width) (+ preceding-days following-days))) - (graph-column-adjusted (if (> graph-column +org-habit-min-width) - (- graph-column +org-habit-graph-padding) - nil))) - (setq-local org-habit-preceding-days preceding-days) - (setq-local org-habit-following-days following-days) - (setq-local org-habit-graph-column graph-column-adjusted))))) + (when (featurep 'org-habit) + (let* ((total-days (float (+ org-habit-preceding-days org-habit-following-days))) + (preceding-days-ratio (/ org-habit-preceding-days total-days)) + (graph-width (floor (* (window-width) +org-habit-graph-window-ratio))) + (preceding-days (floor (* graph-width preceding-days-ratio))) + (following-days (- graph-width preceding-days)) + (graph-column (- (window-width) (+ preceding-days following-days))) + (graph-column-adjusted (if (> graph-column +org-habit-min-width) + (- graph-column +org-habit-graph-padding) + nil))) + (setq-local org-habit-preceding-days preceding-days) + (setq-local org-habit-following-days following-days) + (setq-local org-habit-graph-column graph-column-adjusted)))))) (defun +org-init-hacks-h () @@ -467,10 +474,11 @@ underlying, modified buffer. This fixes that." ;; Open directory links in dired (add-to-list 'org-file-apps '(directory . emacs)) - ;; When you create a sparse tree and `org-indent-mode' is enabled, the - ;; highlighting destroys the invisibility added by `org-indent-mode'. - ;; Therefore, don't highlight when creating a sparse tree. - (setq org-highlight-sparse-tree-matches nil) + ;; HACK Org is known to use a lot of unicode symbols (and large org files tend + ;; to be especially memory hungry). Compounded with + ;; `inhibit-compacting-font-caches' being non-nil, org needs more memory + ;; to be performant. + (setq-hook! 'org-mode-hook gcmh-high-cons-threshold (* 2 gcmh-high-cons-threshold)) (add-hook! 'org-follow-link-hook (defun +org-delayed-recenter-h () @@ -707,12 +715,12 @@ between the two." :localleader "d" #'org-agenda-deadline (:prefix ("c" . "clock") - "c" #'org-agenda-clock-in - "C" #'org-agenda-clock-out + "c" #'org-agenda-clock-cancel "g" #'org-agenda-clock-goto + "i" #'org-agenda-clock-in + "o" #'org-agenda-clock-out "r" #'org-agenda-clockreport-mode - "s" #'org-agenda-show-clocking-issues - "x" #'org-agenda-clock-cancel) + "s" #'org-agenda-show-clocking-issues) "q" #'org-agenda-set-tags "r" #'org-agenda-refile "s" #'org-agenda-schedule @@ -770,6 +778,11 @@ compelling reason, so..." ) +(defun +org-init-smartparens-h () + ;; Disable the slow defaults + (provide 'smartparens-org)) + + ;; ;;; Packages @@ -891,8 +904,8 @@ compelling reason, so..." :ni "C-S-k" #'org-shiftup :ni "C-S-j" #'org-shiftdown ;; more intuitive RET keybinds - :i [return] #'org-return-indent - :i "RET" #'org-return-indent + :i [return] (λ! (org-return t)) + :i "RET" (λ! (org-return t)) :n [return] #'+org/dwim-at-point :n "RET" #'+org/dwim-at-point ;; more vim-esque org motion keys (not covered by evil-org-mode) @@ -911,11 +924,12 @@ compelling reason, so..." :n "zc" #'+org/close-fold :n "zC" #'outline-hide-subtree :n "zm" #'+org/hide-next-fold-level + :n "zM" #'+org/close-all-folds :n "zn" #'org-tree-to-indirect-buffer :n "zo" #'+org/open-fold :n "zO" #'outline-show-subtree :n "zr" #'+org/show-next-fold-level - :n "zR" #'outline-show-all + :n "zR" #'+org/open-all-folds :n "zi" #'org-toggle-inline-images :map org-read-date-minibuffer-local-map @@ -971,15 +985,8 @@ compelling reason, so..." )) ;;; Custom org modules - (if (featurep! +brain) (load! "contrib/brain")) - (if (featurep! +dragndrop) (load! "contrib/dragndrop")) - (if (featurep! +ipython) (load! "contrib/ipython")) - (if (featurep! +journal) (load! "contrib/journal")) - (if (featurep! +jupyter) (load! "contrib/jupyter")) - (if (featurep! +pomodoro) (load! "contrib/pomodoro")) - (if (featurep! +present) (load! "contrib/present")) - (if (featurep! +roam) (load! "contrib/roam")) - (if (featurep! +noter) (load! "contrib/noter")) + (dolist (flag doom--current-flags) + (load! (concat "contrib/" (substring (symbol-name flag) 1)) nil t)) ;; Add our general hooks after the submodules, so that any hooks the ;; submodules add run after them, and can overwrite any defaults if necessary. @@ -991,7 +998,7 @@ compelling reason, so..." #'doom-disable-show-trailing-whitespace-h #'+org-enable-auto-reformat-tables-h #'+org-enable-auto-update-cookies-h - #'+org-unfold-to-2nd-level-or-point-h) + #'+org-make-last-point-visible-h) (add-hook! 'org-load-hook #'+org-init-org-directory-h @@ -1009,7 +1016,8 @@ compelling reason, so..." #'+org-init-keybinds-h #'+org-init-popup-rules-h #'+org-init-protocol-h - #'+org-init-protocol-lazy-loader-h) + #'+org-init-protocol-lazy-loader-h + #'+org-init-smartparens-h) ;; (Re)activate eldoc-mode in org-mode a little later, because it disables ;; itself if started too soon (which is the case with `global-eldoc-mode'). diff --git a/modules/lang/org/contrib/present.el b/modules/lang/org/contrib/present.el index d05cf8985..48a6b395d 100644 --- a/modules/lang/org/contrib/present.el +++ b/modules/lang/org/contrib/present.el @@ -41,19 +41,19 @@ (defadvice! +org-present--narrow-to-subtree-a (orig-fn &rest args) "Narrow to the target subtree when you start the presentation." :around #'org-tree-slide--display-tree-with-narrow - (letf! ((defun org-narrow-to-subtree () - (save-excursion - (save-match-data - (org-with-limited-levels - (narrow-to-region - (progn - (when (org-before-first-heading-p) - (org-next-visible-heading 1)) - (ignore-errors (org-up-heading-all 99)) - (forward-line 1) - (point)) - (progn (org-end-of-subtree t t) - (when (and (org-at-heading-p) (not (eobp))) - (backward-char 1)) - (point)))))))) + (letf! (defun org-narrow-to-subtree () + (save-excursion + (save-match-data + (org-with-limited-levels + (narrow-to-region + (progn + (when (org-before-first-heading-p) + (org-next-visible-heading 1)) + (ignore-errors (org-up-heading-all 99)) + (forward-line 1) + (point)) + (progn (org-end-of-subtree t t) + (when (and (org-at-heading-p) (not (eobp))) + (backward-char 1)) + (point))))))) (apply orig-fn args)))) diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el index 62af344b5..0e5007246 100644 --- a/modules/lang/org/packages.el +++ b/modules/lang/org/packages.el @@ -27,7 +27,7 @@ :recipe (:host github :repo "emacs-straight/org-mode" :files ("*.el" "lisp/*.el" "contrib/lisp/*.el")) - :pin "b171ff02f6e69bcce0dec56ea23e11c75e558704") + :pin "93c50e3a7867a1a85fc78b337172585f7a10dcc6") ;; ...And prevent other packages from pulling org; org-plus-contrib satisfies ;; the dependency already: https://github.com/raxod502/straight.el/issues/352 (package! org :recipe (:local-repo nil)) @@ -51,7 +51,7 @@ (when (featurep! :tools magit) (package! orgit :pin "e147f055772cc934fe1f1d8619059badeb647c93")) (when (featurep! +brain) - (package! org-brain :pin "ae7fe0f628bd093526786ece6917f7a4310e5e4d")) + (package! org-brain :pin "ed99f7e38dd687800fb898f8934a0da0541ebcd9")) (when (featurep! +dragndrop) (package! org-download :pin "d248fcb8f2592a40507682e91eed9a31ead4e4a6")) (when (featurep! +gnuplot) @@ -62,7 +62,7 @@ (when (featurep! +jupyter) (package! jupyter :pin "785edbbff65abb0c929dc2fbd8b8305c77fd529e")) (when (featurep! +journal) - (package! org-journal :pin "8bf06b28d6f14f52d4968123e2b4b91930c8f947")) + (package! org-journal :pin "2c43b10eed0659f8e47797e5e53d2973f939284d")) (when (featurep! +noter) (package! org-noter :pin "9ead81d42dd4dd5074782d239b2efddf9b8b7b3d")) (when (featurep! +pomodoro) @@ -72,11 +72,11 @@ :recipe (:host github :repo "anler/centered-window-mode") :pin "f50859941ab5c7cbeaee410f2d38716252b552ac") (package! org-tree-slide :pin "7bf09a02bd2d8f1ccfcb5209bfb18fbe02d1f44e") - (package! org-re-reveal :pin "61549f4c00284a30e34caa3d76001b233ea5d2ad")) + (package! org-re-reveal :pin "a9e9d4ef88417b3af7741a8d8f444ece820e7a3b")) (when (featurep! +roam) - (package! org-roam :pin "689f55908048eede3cb65aa30ab990be3ac93263") + (package! org-roam :pin "1267a430431f11035798cc4007c5dd3efe543ced") (when (featurep! :completion company) - (package! company-org-roam :pin "0913d86f167164e18831206e611f44bb8e7297e3"))) + (package! company-org-roam :pin "674c2bd493f571c5323d69279557a6c18ccbd14e"))) ;;; Babel (package! ob-async :pin "80a30b96a007d419ece12c976a81804ede340311") diff --git a/modules/lang/purescript/config.el b/modules/lang/purescript/config.el index 02c83c4b0..96ebd48fb 100644 --- a/modules/lang/purescript/config.el +++ b/modules/lang/purescript/config.el @@ -1,6 +1,9 @@ ;;; lang/purescript/config.el -*- lexical-binding: t; -*- (after! purescript-mode + (when (featurep! +lsp) + (add-hook 'purescript-mode-local-vars-hook #'lsp!)) + (add-hook! 'purescript-mode-hook #'purescript-indentation-mode #'rainbow-delimiters-mode) diff --git a/modules/lang/python/autoload/python.el b/modules/lang/python/autoload/python.el index 4f6ef7f3b..052db2a29 100644 --- a/modules/lang/python/autoload/python.el +++ b/modules/lang/python/autoload/python.el @@ -4,6 +4,7 @@ (defun +python/open-repl () "Open the Python REPL." (interactive) + (require 'python) (unless python-shell-interpreter (user-error "`python-shell-interpreter' isn't set")) (pop-to-buffer @@ -23,6 +24,7 @@ (defun +python/open-ipython-repl () "Open an IPython REPL." (interactive) + (require 'python) (let ((python-shell-interpreter (or (+python-executable-find "ipython") "ipython")) (python-shell-interpreter-args (string-join +python-ipython-repl-args " "))) (+python/open-repl))) @@ -31,6 +33,7 @@ (defun +python/open-jupyter-repl () "Open a Jupyter console." (interactive) + (require 'python) (add-to-list 'python-shell-completion-native-disabled-interpreters "jupyter") (let ((python-shell-interpreter (or (+python-executable-find "jupyter") "jupyter")) (python-shell-interpreter-args (format "console %s" (string-join +python-jupyter-repl-args " ")))) diff --git a/modules/lang/python/doctor.el b/modules/lang/python/doctor.el index 588d3fb7a..546df9a8d 100644 --- a/modules/lang/python/doctor.el +++ b/modules/lang/python/doctor.el @@ -5,7 +5,7 @@ "This module requires (:tools lsp)") (if (not (executable-find "python")) - (error! "Python isn't installed.") + (error! "Couldn't find python in your PATH") (unless (featurep! +lsp) (unless (zerop (shell-command "python -c 'import setuptools'")) (warn! "setuptools wasn't detected, which anaconda-mode requires")))) diff --git a/modules/lang/ruby/config.el b/modules/lang/ruby/config.el index 02f0e400e..f9b29b84e 100644 --- a/modules/lang/ruby/config.el +++ b/modules/lang/ruby/config.el @@ -50,6 +50,11 @@ (set-lookup-handlers! 'ruby-mode :definition #'robe-jump :documentation #'robe-doc) + (when (boundp 'read-process-output-max) + ;; Robe can over saturate IPC, making interacting with it slow/clobbering + ;; the GC, so increase the amount of data Emacs reads from it at a time. + (setq-hook! '(robe-mode-hook inf-ruby-mode-hook) + read-process-output-max (* 1024 1024))) (when (featurep! :editor evil) (add-hook 'robe-mode-hook #'evil-normalize-keymaps)) (map! :localleader @@ -175,7 +180,9 @@ :when (featurep! +rails) :hook ((ruby-mode inf-ruby-mode projectile-rails-server-mode) . projectile-rails-mode) :hook (projectile-rails-server-mode . doom-mark-buffer-as-real-h) + :hook (projectile-rails-mode . auto-insert-mode) :init + (setq auto-insert-query nil) (setq inf-ruby-console-environment "development") (when (featurep! :lang web) (add-hook 'web-mode-hook #'projectile-rails-mode)) diff --git a/modules/lang/ruby/packages.el b/modules/lang/ruby/packages.el index 22060e9b3..69dd672a3 100644 --- a/modules/lang/ruby/packages.el +++ b/modules/lang/ruby/packages.el @@ -3,34 +3,34 @@ ;; Major modes (package! ruby-mode :built-in t) -(package! yard-mode :pin "ba74a47463") +(package! yard-mode :pin "ba74a47463b0320ae152bd42a7dd7aeecd7b5748") ;; REPL -(package! inf-ruby :pin "41e5ed3a88") +(package! inf-ruby :pin "41e5ed3a886fca56990486f1987bb3bae0dbd54b") (when (featurep! :completion company) - (package! company-inf-ruby :pin "fe3e4863bc")) + (package! company-inf-ruby :pin "fe3e4863bc971fbb81edad447efad5795ead1b17")) ;; Programming environment -(package! rubocop :pin "03bf15558a") -(package! robe :pin "68503b32bb") +(package! rubocop :pin "03bf15558a6eb65e4f74000cab29412efd46660e") +(package! robe :pin "68503b32bb3a005787ecb7a7fdeb3bb4a2317e2b") ;; Project tools -(package! bundler :pin "43efb6be4e") -(package! rake :pin "9c204334b0") +(package! bundler :pin "43efb6be4ed118b06d787ce7fbcffd68a31732a7") +(package! rake :pin "9c204334b03b4e899fadae6e59c20cf105404128") ;; Environment management (when (featurep! +rbenv) - (package! rbenv :pin "2ea1a5bdc1")) + (package! rbenv :pin "2ea1a5bdc1266caef1dd77700f2c8f42429b03f1")) (when (featurep! +rvm) - (package! rvm :pin "134497bc46")) + (package! rvm :pin "134497bc460990c71ab8fa75431156e62c17da2d")) (when (featurep! +chruby) - (package! chruby :pin "42bc6d521f")) + (package! chruby :pin "42bc6d521f832eca8e2ba210f30d03ad5529788f")) ;; Testing frameworks -(package! rspec-mode :pin "9a2a9d2935") -(package! minitest :pin "97d7d1760b") +(package! rspec-mode :pin "9a2a9d2935ae17b8570485bdea7c347533b464f6") +(package! minitest :pin "ddd152c990a528ad09a696bfad23afa4330ea4d7") ;; Rails (when (featurep! +rails) - (package! projectile-rails :pin "0398d940a2") - (package! inflections :pin "e4f1372cf2")) + (package! projectile-rails :pin "11980b2bcb99208888856a9b8666ff329b6f0142") + (package! inflections :pin "e4f1372cf22e811faca52fc86bdd5d817498a4d8")) diff --git a/modules/term/eshell/README.org b/modules/term/eshell/README.org new file mode 100644 index 000000000..ad3c5878b --- /dev/null +++ b/modules/term/eshell/README.org @@ -0,0 +1,61 @@ +#+TITLE: term/eshell +#+DATE: May 18, 2020 +#+SINCE: v2.0 +#+STARTUP: inlineimages nofold + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#maintainers][Maintainers]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] + - [[#hacks][Hacks]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] +- [[#configuration][Configuration]] +- [[#troubleshooting][Troubleshooting]] + +* Description +This module provides additional features for the built-in [[https://www.gnu.org/software/emacs/manual/html_mono/eshell.html][Emacs Shell]] + +The Emacs Shell or =eshell= is a shell-like command interpreter implemented in +Emacs Lisp. It is an alternative to traditional shells such as =bash=, =zsh=, +=fish=, etc. that is built into Emacs and entirely cross-platform. + +** Maintainers +This module has no dedicated maintainers. + +** Module Flags +This module provides no flags, but does gain auto-completion if =:completion +company= is enabled. + +** Plugins ++ [[https://github.com/peterwvj/eshell-up][eshell-up]] ++ [[https://github.com/xuchunyang/eshell-z][eshell-z]] ++ [[https://github.com/tom-tan/esh-help][esh-help]] ++ [[https://gitlab.com/bennya/shrink-path.el][shrink-path]] ++ [[https://github.com/xuchunyang/eshell-did-you-mean][eshell-did-you-mean]] ++ =:completion company= + + [[https://gitlab.com/ambrevar/emacs-fish-completion][fish-completion]] + + [[https://github.com/szermatt/emacs-bash-completion][bash-completion]] + +** Hacks ++ Even with =fish-completion-fallback-on-bash-p= non-nil, fish must be installed + for bash completion to work. Workaround in =config.el=. ++ =eshell-did-you-mean= does not work on first invocation, so we manually invoke + it once. + +* Prerequisites +[[https://fishshell.com/][=fish= shell]] for completions, falling back to [[https://www.gnu.org/software/bash/][=bash= shell]] if =fish= is not +found. If neither shell is found, completions may not be available. + +* Features ++ Command completion with Company ++ =fish=-style prompt with Git integration ++ [[https://github.com/rupa/z][=z=]]-like directory jumping ++ Command-not-found recommendations + +* TODO Configuration +# How to configure this module, including common problems and how to address them. + +* TODO Troubleshooting +# Common issues and their solution, or places to look for help. diff --git a/modules/term/eshell/autoload/eshell.el b/modules/term/eshell/autoload/eshell.el index 2468012e9..0a4604a0b 100644 --- a/modules/term/eshell/autoload/eshell.el +++ b/modules/term/eshell/autoload/eshell.el @@ -21,13 +21,15 @@ t)) (defun +eshell--bury-buffer (&optional dedicated-p) - (unless (switch-to-prev-buffer nil 'bury) - (switch-to-buffer (doom-fallback-buffer))) - (when (eq major-mode 'eshell-mode) - (switch-to-buffer (doom-fallback-buffer))) - (when +eshell-enable-new-shell-on-split - (when-let (win (get-buffer-window (+eshell/here))) - (set-window-dedicated-p win dedicated-p)))) + (let ((directory default-directory)) + (unless (switch-to-prev-buffer nil 'bury) + (switch-to-buffer (doom-fallback-buffer))) + (when (eq major-mode 'eshell-mode) + (switch-to-buffer (doom-fallback-buffer))) + (when +eshell-enable-new-shell-on-split + (let ((default-directory directory)) + (when-let (win (get-buffer-window (+eshell/here t))) + (set-window-dedicated-p win dedicated-p)))))) (defun +eshell--setup-window (window &optional flag) (when (window-live-p window) diff --git a/modules/term/eshell/config.el b/modules/term/eshell/config.el index 217fa6ff4..0b16b51f1 100644 --- a/modules/term/eshell/config.el +++ b/modules/term/eshell/config.el @@ -12,6 +12,10 @@ "Where to store eshell configuration files, as opposed to `eshell-directory-name', which is where Doom will store temporary/data files.") +(defvar eshell-directory-name (concat doom-etc-dir "eshell") + "Where to store temporary/data files, as opposed to `eshell-config-dir', +which is where Doom will store eshell configuration files.") + (defvar +eshell-enable-new-shell-on-split t "If non-nil, spawn a new eshell session after splitting from an eshell buffer.") @@ -22,11 +26,13 @@ buffer.") (defvar +eshell-aliases '(("q" "exit") ; built-in ("f" "find-file $1") + ("ff" "find-file $1") ("d" "dired $1") ("bd" "eshell-up $1") ("rg" "rg --color=always $*") ("l" "ls -lh $*") ("ll" "ls -lah $*") + ("gg" "magit-status") ("clear" "clear-scrollback")) ; more sensible than default "An alist of default eshell aliases, meant to emulate useful shell utilities, like fasd and bd. Note that you may overwrite these in your @@ -35,15 +41,11 @@ to define your aliases. You should use `set-eshell-alias!' to change this.") -;; -(defvar eshell-directory-name (concat doom-etc-dir "eshell")) - ;; These files are exceptions, because they may contain configuration (defvar eshell-aliases-file (concat +eshell-config-dir "aliases")) (defvar eshell-rc-script (concat +eshell-config-dir "profile")) (defvar eshell-login-script (concat +eshell-config-dir "login")) - (defvar +eshell--default-aliases nil) @@ -152,7 +154,11 @@ You should use `set-eshell-alias!' to change this.") [remap doom/backward-kill-to-bol-and-indent] #'eshell-kill-input [remap evil-delete-back-to-indentation] #'eshell-kill-input [remap evil-window-split] #'+eshell/split-below - [remap evil-window-vsplit] #'+eshell/split-right)))) + [remap evil-window-vsplit] #'+eshell/split-right + (:localleader + "b" #'eshell-insert-buffer-name + "e" #'eshell-insert-envvar + "s" #'+eshell/search-history))))) (use-package! eshell-up @@ -173,13 +179,26 @@ You should use `set-eshell-alias!' to change this.") :config (setup-esh-help-eldoc)) +(use-package! eshell-did-you-mean + :after esh-mode ; Specifically esh-mode, not eshell + :config + (eshell-did-you-mean-setup) + ;; HACK There is a known issue with `eshell-did-you-mean' where it does not + ;; work on first invocation, so we invoke it once manually by setting the + ;; last command and then calling the output filter. + (setq eshell-last-command-name "catt") + (eshell-did-you-mean-output-filter "catt: command not found")) + + (use-package! fish-completion + :unless IS-WINDOWS :hook (eshell-mode . fish-completion-mode) :init (setq fish-completion-fallback-on-bash-p t) :config - ;; HACK Even with `fish-completion-fallback-on-bash-p' non-nil, fish must be - ;; installed for bash completion to work. How frustrating. This way we - ;; can at least get bash completion whether or not fish is present. + ;; HACK Even with `fish-completion-fallback-on-bash-p' non-nil, + ;; `fish-completion--list-completions-with-desc' will throw an error if + ;; fish isn't installed (and so, will fail to fall back to bash), so we + ;; advise it to fail silently. (defadvice! +eshell--fallback-to-bash-a (&rest _) - :before-while #'fish-completion--list-completions-with-desc - (executable-find "fish"))) + :before-until #'fish-completion--list-completions-with-desc + (unless (executable-find "fish") ""))) diff --git a/modules/term/eshell/packages.el b/modules/term/eshell/packages.el index 3744666cb..82bfc2281 100644 --- a/modules/term/eshell/packages.el +++ b/modules/term/eshell/packages.el @@ -5,7 +5,9 @@ (package! eshell-z :pin "337cb241e17bd472bd3677ff166a0800f684213c") (package! shrink-path :pin "c14882c8599aec79a6e8ef2d06454254bb3e1e41") (package! esh-help :pin "417673ed18a983930a66a6692dbfb288a995cb80") +(package! eshell-did-you-mean :pin "7cb6ef8e2274d0a50a9e114d412307a6543533d5") -(when (featurep! :completion company) - (package! fish-completion :pin "10384881817b5ae38cf6197a077a663420090d2c") - (package! bash-completion :pin "96ce14af9674f3e605bacca87abc0c23b8f13cd5")) +(unless IS-WINDOWS + (when (featurep! :completion company) + (package! fish-completion :pin "10384881817b5ae38cf6197a077a663420090d2c") + (package! bash-completion :pin "96ce14af9674f3e605bacca87abc0c23b8f13cd5"))) diff --git a/modules/tools/debugger/packages.el b/modules/tools/debugger/packages.el index 9c0c5e9f7..40d021f1f 100644 --- a/modules/tools/debugger/packages.el +++ b/modules/tools/debugger/packages.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/debugger/packages.el -(when (package! realgud :pin "e03446f54c7ee0b4ed3ec7300597046cf1de2bb8") +(when (package! realgud :pin "1238d8e72945a84bb06cd39d7ded75f37105d4d2") (when (featurep! :lang javascript) (package! realgud-trepan-ni :pin "6e9cac5e8097018aadf41c88de541168036cc227"))) (when (featurep! +lsp) - (package! dap-mode :pin "cc2eb2fc1b2958ef01dad8c004d2f3bc4dc38bc3") + (package! dap-mode :pin "8f69dc2e3c850571758744d271061549c19e11fc") (package! posframe :pin "093b29a53cbeda6d637ccc9ef4dfc47123e79b9e")) diff --git a/modules/tools/direnv/config.el b/modules/tools/direnv/config.el index 04abdab15..8c50c9064 100644 --- a/modules/tools/direnv/config.el +++ b/modules/tools/direnv/config.el @@ -12,8 +12,8 @@ ;;; Packages (use-package! direnv - :hook (before-hack-local-variables . direnv--maybe-update-environment) - :hook (flycheck-before-syntax-check . direnv--maybe-update-environment) + :hook (before-hack-local-variables . direnv-update-environment) + :hook (flycheck-before-syntax-check . direnv-update-environment) :hook (direnv-envrc-mode . +direnv-envrc-fontify-keywords-h) :config (add-to-list 'direnv-non-file-modes 'vterm-mode) diff --git a/modules/tools/lookup/README.org b/modules/tools/lookup/README.org index b4ecf508e..6ccbf35be 100644 --- a/modules/tools/lookup/README.org +++ b/modules/tools/lookup/README.org @@ -82,7 +82,7 @@ export PATH="/usr/local/opt/sqlite/bin:$PATH" ** Arch Linux #+BEGIN_SRC sh sudo pacman -S sqlite ripgrep -sudo yay -S wordnet-cli +yay -S wordnet-cli #+END_SRC ** NixOS diff --git a/modules/tools/lookup/autoload/lookup.el b/modules/tools/lookup/autoload/lookup.el index a77ace491..33d5818f8 100644 --- a/modules/tools/lookup/autoload/lookup.el +++ b/modules/tools/lookup/autoload/lookup.el @@ -15,6 +15,12 @@ properties: :definition FN Run when jumping to a symbol's definition. Used by `+lookup/definition'. +:implementations FN + Run when looking for implementations of a symbol in the current project. Used + by `+lookup/implementations'. +:type-definition FN + Run when jumping to a symbol's type definition. Used by + `+lookup/type-definition'. :references FN Run when looking for usage references of a symbol in the current project. Used by `+lookup/references'. @@ -46,6 +52,7 @@ change the current buffer or window or return non-nil when it succeeds. If it doesn't change the current buffer, or it returns nil, the lookup module will fall back to the next handler in `+lookup-definition-functions', +`+lookup-implementations-functions', `+lookup-type-definition-functions', `+lookup-references-functions', `+lookup-file-functions' or `+lookup-documentation-functions'. @@ -57,7 +64,7 @@ This can be passed nil as its second argument to unset handlers for MODES. e.g. (set-lookup-handlers! 'python-mode nil) -\(fn MODES &key DEFINITION REFERENCES DOCUMENTATION FILE XREF-BACKEND ASYNC)" +\(fn MODES &key DEFINITION IMPLEMENTATIONS TYPE-DEFINITION REFERENCES DOCUMENTATION FILE XREF-BACKEND ASYNC)" (declare (indent defun)) (dolist (mode (doom-enlist modes)) (let ((hook (intern (format "%s-hook" mode))) @@ -69,15 +76,19 @@ This can be passed nil as its second argument to unset handlers for MODES. e.g. (fset fn (lambda () - (cl-destructuring-bind (&key definition references documentation file xref-backend async) + (cl-destructuring-bind (&key definition implementations type-definition references documentation file xref-backend async) plist (cl-mapc #'+lookup--set-handler (list definition + implementations + type-definition references documentation file xref-backend) (list '+lookup-definition-functions + '+lookup-implementations-functions + '+lookup-type-definition-functions '+lookup-references-functions '+lookup-documentation-functions '+lookup-file-functions @@ -133,6 +144,8 @@ This can be passed nil as its second argument to unset handlers for MODES. e.g. (let* ((origin (point-marker)) (handlers (plist-get (list :definition '+lookup-definition-functions + :implementations '+lookup-implementations-functions + :type-definition '+lookup-type-definition-functions :references '+lookup-references-functions :documentation '+lookup-documentation-functions :file '+lookup-file-functions) @@ -241,6 +254,30 @@ evil-mode is active." ((+lookup--jump-to :definition identifier nil arg)) ((error "Couldn't find the definition of %S" identifier)))) +;;;###autoload +(defun +lookup/implementations (identifier &optional arg) + "Jump to the implementations of IDENTIFIER (defaults to the symbol at point). + +Each function in `+lookup-implementations-functions' is tried until one changes +the point or current buffer." + (interactive (list (doom-thing-at-point-or-region) + current-prefix-arg)) + (cond ((null identifier) (user-error "Nothing under point")) + ((+lookup--jump-to :implementations identifier nil arg)) + ((error "Couldn't find the implementations of %S" identifier)))) + +;;;###autoload +(defun +lookup/type-definition (identifier &optional arg) + "Jump to the type definition of IDENTIFIER (defaults to the symbol at point). + +Each function in `+lookup-type-definition-functions' is tried until one changes +the point or current buffer." + (interactive (list (doom-thing-at-point-or-region) + current-prefix-arg)) + (cond ((null identifier) (user-error "Nothing under point")) + ((+lookup--jump-to :type-definition identifier nil arg)) + ((error "Couldn't find the definition of %S" identifier)))) + ;;;###autoload (defun +lookup/references (identifier &optional arg) "Show a list of usages of IDENTIFIER (defaults to the symbol at point) diff --git a/modules/tools/lookup/config.el b/modules/tools/lookup/config.el index 8f392cd76..22f944972 100644 --- a/modules/tools/lookup/config.el +++ b/modules/tools/lookup/config.el @@ -3,6 +3,8 @@ ;; "What am I looking at?" This module helps you answer this question. ;; ;; + `+lookup/definition': a jump-to-definition that should 'just work' +;; + `+lookup/implementations': find a symbol's implementations in the current +;; project ;; + `+lookup/references': find a symbol's references in the current project ;; + `+lookup/file': open the file referenced at point ;; + `+lookup/online'; look up a symbol on online resources @@ -52,6 +54,24 @@ If the argument is interactive (satisfies `commandp'), it is called with argument: the identifier at point. See `set-lookup-handlers!' about adding to this list.") +(defvar +lookup-implementations-functions () + "Function for `+lookup/implementations' to try. Stops at the first function to +return non-nil or change the current window/point. + +If the argument is interactive (satisfies `commandp'), it is called with +`call-interactively' (with no arguments). Otherwise, it is called with one +argument: the identifier at point. See `set-lookup-handlers!' about adding to +this list.") + +(defvar +lookup-type-definition-functions () + "Functions for `+lookup/type-definition' to try. Stops at the first function to +return non-nil or change the current window/point. + +If the argument is interactive (satisfies `commandp'), it is called with +`call-interactively' (with no arguments). Otherwise, it is called with one +argument: the identifier at point. See `set-lookup-handlers!' about adding to +this list.") + (defvar +lookup-references-functions '(+lookup-xref-references-backend-fn +lookup-project-search-backend-fn) diff --git a/modules/tools/lookup/packages.el b/modules/tools/lookup/packages.el index e42c7449d..e8efd6c7b 100644 --- a/modules/tools/lookup/packages.el +++ b/modules/tools/lookup/packages.el @@ -28,9 +28,8 @@ (if IS-MAC (package! osx-dictionary :pin "1b79ff64c72485cb078db9ab7ee3256b11a99f4b") (package! define-word :pin "08c71b1ff4fd07bf0c78d1fcf77efeaafc8f7443") - ;; HACK Fix #2945: the main package is broken (see - ;; SavchenkoValeriy/emacs-powerthesaurus). We use this fork until it is - ;; merged. + ;; HACK Fix #2945: the main package is broken due to + ;; SavchenkoValeriy/emacs-powerthesaurus#11 (package! powerthesaurus :recipe (:host github :repo "maxchaos/emacs-powerthesaurus" :branch "pt-api-change") :pin "4a834782a394f2dc70fc02d68b6962b44d87f0cf") diff --git a/modules/tools/lsp/README.org b/modules/tools/lsp/README.org index 6b2992bd0..8144e8138 100644 --- a/modules/tools/lsp/README.org +++ b/modules/tools/lsp/README.org @@ -51,6 +51,7 @@ As of this writing, this is the state of LSP support in Doom Emacs: | [[../../lang/sh/README.org][:lang sh]] | sh-mode | bash-language-server | | [[../../lang/swift/README.org][:lang swift]] | swift-mode | sourcekit | | [[../../lang/web/README.org][:lang web]] | web-mode, css-mode, scss-mode, sass-mode, less-css-mode | vscode-css-languageserver-bin, vscode-html-languageserver-bin | +| [[../../lang/purescript/README.org][:lang purescript]] | purescript-mode | purescript-language-server | ** Module Flags + =+peek= Use =lsp-ui-peek= when looking up definitions and references with diff --git a/modules/tools/lsp/config.el b/modules/tools/lsp/config.el index 51f18346e..fd1675bc2 100644 --- a/modules/tools/lsp/config.el +++ b/modules/tools/lsp/config.el @@ -54,6 +54,8 @@ working on that project after closing the last buffer.") (set-lookup-handlers! 'lsp-mode :async t :documentation #'lsp-describe-thing-at-point :definition #'lsp-find-definition + :implementations #'lsp-find-implementation + :type-definition #'lsp-find-type-definition :references #'lsp-find-references) ;; TODO Lazy load these. They don't need to be loaded all at once unless the @@ -91,6 +93,10 @@ This also logs the resolved project root, if found, so we know where we are." ;; development builds of Emacs 27 and above (or (not (boundp 'read-process-output-max)) (setq-local read-process-output-max (* 1024 1024))) + ;; REVIEW LSP causes a lot of allocations, with or without Emacs 27+'s + ;; native JSON library, so we up the GC threshold to stave off + ;; GC-induced slowdowns/freezes. + (setq-local gcmh-high-cons-threshold (* 2 gcmh-high-cons-threshold)) (prog1 (lsp-mode 1) (setq-local lsp-buffer-uri (lsp--buffer-uri)) ;; Announce what project root we're using, for diagnostic purposes @@ -189,6 +195,7 @@ auto-killed (which is a potentially expensive process)." (when (featurep! +peek) (set-lookup-handlers! 'lsp-ui-mode :async t :definition 'lsp-ui-peek-find-definitions + :implementations 'lsp-ui-peek-find-implementation :references 'lsp-ui-peek-find-references))) diff --git a/modules/tools/lsp/packages.el b/modules/tools/lsp/packages.el index c9c9e43cd..80abb7217 100644 --- a/modules/tools/lsp/packages.el +++ b/modules/tools/lsp/packages.el @@ -1,8 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/lsp/packages.el -(package! lsp-mode :pin "11750e7b118858b38417a538c1c6eff8759c12f3") -(package! lsp-ui :pin "1288be94b4c37f89e80a03b1cff1b81aba9560bb") +(package! lsp-mode :pin "bac42c50b370f3716f258506dc1ae9f62906313f") +(package! lsp-ui :pin "ab55e306af9dd9eb62fe7463e4e05d948ad3dfc6") (when (featurep! :completion ivy) (package! lsp-ivy :pin "81e81ced99829358674c5a6bbe2c3e15cecd4ed8")) (when (featurep! :completion helm) diff --git a/modules/tools/magit/autoload.el b/modules/tools/magit/autoload.el index f96b0ff9f..2b4c99359 100644 --- a/modules/tools/magit/autoload.el +++ b/modules/tools/magit/autoload.el @@ -1,10 +1,22 @@ ;;; tools/magit/autoload.el -*- lexical-binding: t; -*- -;; HACK Magit complains loudly when it can't determine its own version, which is -;; the case when magit is built through straight. The warning is harmless, -;; however, so we just need it to shut up. +;; HACK Magit complains loudly (but harmlessly) when it can't determine its own +;; version (in the case of a sparse clone). ;;;###autoload -(advice-add #'magit-version :override #'ignore) +(defadvice! +magit--ignore-version-a (&optional print-dest) + :override #'magit-version + (when print-dest + (defvar magit-git-debug) + (princ (format "Magit (unknown), Git %s, Emacs %s, %s" + (or (let ((magit-git-debug + (lambda (err) + (display-warning '(magit git) err :error)))) + (magit-git-version t)) + "(unknown)") + emacs-version + system-type) + print-dest)) + nil) ;;;###autoload (defun +magit-display-buffer-fn (buffer) diff --git a/modules/tools/pdf/config.el b/modules/tools/pdf/config.el index b1c74e782..9e9025dee 100644 --- a/modules/tools/pdf/config.el +++ b/modules/tools/pdf/config.el @@ -25,6 +25,20 @@ (setq pdf-view-use-scaling t pdf-view-use-imagemagick nil) + ;; Persist current page for PDF files viewed in Emacs + (defvar +pdf--page-restored-p nil) + (add-hook! 'pdf-view-change-page-hook + (defun +pdf-remember-page-number-h () + (when-let (page (and buffer-file-name (pdf-view-current-page))) + (doom-store-put buffer-file-name page nil "pdf-view")))) + (add-hook! 'pdf-view-mode-hook + (defun +pdf-restore-page-number-h () + (when-let (page (and buffer-file-name (doom-store-get buffer-file-name "pdf-view"))) + (and (not +pdf--page-restored-p) + (<= page (or (pdf-cache-number-of-pages) 1)) + (pdf-view-goto-page page) + (setq-local +pdf--page-restored-p t))))) + ;; Add retina support for MacOS users (when IS-MAC (advice-add #'pdf-util-frame-scale-factor :around #'+pdf--util-frame-scale-factor-a) @@ -51,29 +65,30 @@ (setq-hook! 'pdf-view-mode-hook evil-normal-state-cursor (list nil)) ;; Install epdfinfo binary if needed, blocking until it is finished - (require 'pdf-tools) - (unless (file-executable-p pdf-info-epdfinfo-program) - (let ((wconf (current-window-configuration))) - (pdf-tools-install) - (message "Building epdfinfo, this will take a moment...") - ;; HACK We reset all `pdf-view-mode' buffers to fundamental mode so that - ;; `pdf-tools-install' has a chance to reinitialize them as - ;; `pdf-view-mode' buffers. This is necessary because - ;; `pdf-tools-install' won't do this to buffers that are already in - ;; pdf-view-mode. - (dolist (buffer (doom-buffers-in-mode 'pdf-view-mode)) - (with-current-buffer buffer (fundamental-mode))) - (while compilation-in-progress - ;; Block until `pdf-tools-install' is done - (redisplay) - (sleep-for 1)) - ;; HACK If pdf-tools was loaded by you opening a pdf file, once - ;; `pdf-tools-install' completes, `pdf-view-mode' will throw an error - ;; because the compilation buffer is focused, not the pdf buffer. - ;; Therefore, it is imperative that the window config is restored. - (when (file-executable-p pdf-info-epdfinfo-program) - (set-window-configuration wconf)))) + (when doom-interactive-mode + (require 'pdf-tools) + (unless (file-executable-p pdf-info-epdfinfo-program) + (let ((wconf (current-window-configuration))) + (pdf-tools-install) + (message "Building epdfinfo, this will take a moment...") + ;; HACK We reset all `pdf-view-mode' buffers to fundamental mode so that + ;; `pdf-tools-install' has a chance to reinitialize them as + ;; `pdf-view-mode' buffers. This is necessary because + ;; `pdf-tools-install' won't do this to buffers that are already in + ;; pdf-view-mode. + (dolist (buffer (doom-buffers-in-mode 'pdf-view-mode)) + (with-current-buffer buffer (fundamental-mode))) + (while compilation-in-progress + ;; Block until `pdf-tools-install' is done + (redisplay) + (sleep-for 1)) + ;; HACK If pdf-tools was loaded by you opening a pdf file, once + ;; `pdf-tools-install' completes, `pdf-view-mode' will throw an error + ;; because the compilation buffer is focused, not the pdf buffer. + ;; Therefore, it is imperative that the window config is restored. + (when (file-executable-p pdf-info-epdfinfo-program) + (set-window-configuration wconf)))) - ;; Sets up `pdf-tools-enable-minor-modes', `pdf-occur-global-minor-mode' and - ;; `pdf-virtual-global-minor-mode'. - (pdf-tools-install-noverify)) + ;; Sets up `pdf-tools-enable-minor-modes', `pdf-occur-global-minor-mode' and + ;; `pdf-virtual-global-minor-mode'. + (pdf-tools-install-noverify))) diff --git a/modules/tools/pdf/packages.el b/modules/tools/pdf/packages.el index 046c2ea84..b2ea21233 100644 --- a/modules/tools/pdf/packages.el +++ b/modules/tools/pdf/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/pdf/packages.el -(package! pdf-tools :pin "d9712989fc4715443f674459199bdffa987054ac") +(package! pdf-tools :pin "c510442ab89c8a9e9881230eeb364f4663f59e76") diff --git a/modules/ui/ophints/config.el b/modules/ui/ophints/config.el index 2db9a2850..f1349a549 100644 --- a/modules/ui/ophints/config.el +++ b/modules/ui/ophints/config.el @@ -2,7 +2,7 @@ (use-package! evil-goggles :when (featurep! :editor evil) - :after-call pre-command-hook + :hook (doom-first-input . evil-goggles-mode) :init (setq evil-goggles-duration 0.1 evil-goggles-pulse nil ; too slow @@ -23,13 +23,12 @@ '(+eval:region :face evil-goggles-yank-face :switch evil-goggles-enable-yank - :advice evil-goggles--generic-async-advice)) - (evil-goggles-mode +1)) + :advice evil-goggles--generic-async-advice))) (use-package! volatile-highlights :unless (featurep! :editor evil) - :after-call pre-command-hook + :hook (doom-first-input . volatile-highlights-mode) :config (after! undo-fu (vhl/define-extension 'undo-fu 'undo-fu-only-undo 'undo-fu-only-redo)