diff --git a/.travis.yml b/.travis.yml index c3e5b5bba..f8a202d09 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,9 +10,6 @@ before_install: - export PATH="/home/travis/.evm/bin:$PATH" - evm config path /tmp - evm install $EVM_EMACS --use --skip - - mkdir -p ~/.config/doom - - cp init.test.el ~/.config/doom/init.el - - bin/doom -y -i install env: - EVM_EMACS=emacs-25.3-travis - EVM_EMACS=emacs-26.1-travis @@ -22,5 +19,5 @@ matrix: - env: EVM_EMACS=emacs-git-snapshot-travis script: - bin/doom version - - bin/doom -d test + - bin/doom test - bin/doom -y compile diff --git a/bin/doom b/bin/doom index 416c861e7..7a33bff35 100755 --- a/bin/doom +++ b/bin/doom @@ -82,7 +82,7 @@ ;; Bootstrap Doom (if (not noninteractive) - (progn + (let ((doom-interactive-mode t)) (load (expand-file-name "init.el" user-emacs-directory) nil 'nomessage) (doom-run-all-startup-hooks-h)) @@ -91,32 +91,31 @@ (doom-initialize 'force-p) (doom-initialize-modules) - (cond ((and (not (cdr args)) - (member (car args) '("help" "h"))) - (usage)) - ((not args) - (print! (error "No command detected.\n")) + (cond ((or (not args) + (and (not (cdr args)) + (member (car args) '("help" "h")))) + (unless args + (print! (error "No command detected.\n"))) (usage)) ((require 'core-cli) - (let ((default-directory user-emacs-directory)) - (setq argv nil) - (condition-case e - (doom-dispatch (car args) (cdr args)) - (user-error - (print! (error "%s\n") (error-message-string e)) - (print! (yellow "See 'doom help %s' for documentation on this command.") (car args))) - ((debug error) - (message "--------------------------------------------------\n") - (message "There was an unexpected error:") - (message " %s (%s)" (get (car e) 'error-message) (car e)) - (dolist (item (cdr e)) - (message " %s" item)) - (unless debug-on-error - (message - (concat "\nRun the command again with the -d (or --debug) option to enable debug\n" - "mode and, hopefully, generate a stack trace. If you decide to file a bug\n" - "report, please include it!\n\n" - "Emacs outputs to standard error, so you'll need to redirect stderr to\n" - "stdout to pipe this to a file or clipboard!\n\n" - " e.g. doom -d install 2>&1 | clipboard-program")) - (signal 'doom-error e))))))))) + (setq argv nil) + (condition-case e + (doom-dispatch (car args) (cdr args)) + (user-error + (print! (error "%s\n") (error-message-string e)) + (print! (yellow "See 'doom help %s' for documentation on this command.") (car args))) + ((debug error) + (message "--------------------------------------------------\n") + (message "There was an unexpected error:") + (message " %s (%s)" (get (car e) 'error-message) (car e)) + (dolist (item (cdr e)) + (message " %s" item)) + (unless debug-on-error + (message + (concat "\nRun the command again with the -d (or --debug) option to enable debug\n" + "mode and, hopefully, generate a stack trace. If you decide to file a bug\n" + "report, please include it!\n\n" + "Emacs outputs to standard error, so you'll need to redirect stderr to\n" + "stdout to pipe this to a file or clipboard!\n\n" + " e.g. doom -d install 2>&1 | clipboard-program")) + (signal 'doom-error e)))))))) diff --git a/bin/doom-doctor b/bin/doom-doctor index a3f54e98a..59e735e8a 100755 --- a/bin/doom-doctor +++ b/bin/doom-doctor @@ -1,7 +1,7 @@ #!/usr/bin/env sh ":"; command -v emacs >/dev/null || { >&2 echo "Emacs isn't installed"; exit 1; } # -*-emacs-lisp-*- ":"; VERSION=$(emacs --version | head -n1) -":"; case $VERSION in *\ 2[0-2].[0-1].[0-9]) echo "You're running $VERSION"; echo "That version is too old to run the doctor. Check your PATH"; echo; exit 2 ;; esac +":"; case $VERSION in *\ 2[0-2].[0-1].[0-9]) echo "You're running $VERSION"; echo "That version is too old to run the doctor (25.3 minimum). Check your PATH"; echo; exit 2 ;; esac ":"; exec emacs --quick --script "$0"; exit 0 ;; The Doom doctor is essentially one big, self-contained elisp shell script @@ -43,7 +43,7 @@ (defun sh (cmd &rest args) (ignore-errors (string-trim-right - (shell-command-to-string (apply #'format cmd args))))) + (shell-command-to-string (if args (apply #'format cmd args) cmd))))) (defun elc-check-dir (dir) (dolist (file (directory-files-recursively dir "\\.elc$")) @@ -139,18 +139,6 @@ (concat "\nMacOS users should use homebrew (https://brew.sh) to install Emacs\n" " brew install emacs --with-modules --with-imagemagick --with-cocoa")))) - (section! "Checking if your version of Emacs has changed recently...") - (let ((version-file (expand-file-name ".local/emacs-version.el" user-emacs-directory)) - doom--last-emacs-version) - (when (and (load version-file 'noerror 'nomessage 'nosuffix) - (not (equal emacs-version doom--last-emacs-version))) - (warn! "Your version of Emacs has changed from %S to %S. Recompile your packages!" - doom--last-emacs-version - emacs-version) - (explain! "Byte-code compiled in one version of Emacs may not work in another version." - "It is recommended that you reinstall your plugins or recompile them with" - "`bin/doom rebuild'."))) - (section! "Checking for Emacs config conflicts...") (when (file-exists-p "~/.emacs") (warn! "Detected an ~/.emacs file, which may prevent Doom from loading") diff --git a/bin/doom.cmd b/bin/doom.cmd index 132691e4b..7cba4ca5e 100644 --- a/bin/doom.cmd +++ b/bin/doom.cmd @@ -16,7 +16,7 @@ IF NOT [%1]==[] ( ) IF [%command%]==[run] ( - start runemacs -Q %args% -l ..\init.el -f "doom|run-all-startup-hooks" + start runemacs -Q %args% -l ..\init.el -f "doom-run-all-startup-hooks-h" ) ELSE ( emacs --quick --script .\doom -- %* ) diff --git a/core/autoload/config.el b/core/autoload/config.el index 9439bc96c..6bb9ae300 100644 --- a/core/autoload/config.el +++ b/core/autoload/config.el @@ -74,7 +74,7 @@ line." Uses the same mechanism as 'bin/doom env reload'." (interactive) - (compile (format "%s env refresh" (expand-file-name "bin/doom" doom-emacs-dir))) + (compile (format "%s env" (expand-file-name "bin/doom" doom-emacs-dir))) (while compilation-in-progress (sit-for 1)) (unless (file-readable-p doom-env-file) diff --git a/core/autoload/debug.el b/core/autoload/debug.el index bad9910a6..7d5ac5dba 100644 --- a/core/autoload/debug.el +++ b/core/autoload/debug.el @@ -39,7 +39,12 @@ ready to be pasted in a bug report on github." (version . ,emacs-version) (features ,@system-configuration-features) (build . ,(format-time-string "%b %d, %Y" emacs-build-time)) - (buildopts ,system-configuration-options)) + (buildopts ,system-configuration-options) + (windowsys . ,(if noninteractive 'batch window-system)) + (daemonp . ,(cond ((daemonp) 'daemon) + ((and (require 'server) + (server-running-p)) + 'server-running)))) (doom (version . ,doom-version) (build . ,(sh "git log -1 --format=\"%D %h %ci\""))) @@ -63,11 +68,11 @@ ready to be pasted in a bug report on github." (modules ,@(or (cl-loop with cat = nil for key being the hash-keys of doom-modules - if (or (not cat) (not (eq cat (car key)))) + if (or (not cat) + (not (eq cat (car key)))) do (setq cat (car key)) and collect cat - and collect (cdr key) - else collect + collect (let ((flags (doom-module-get cat (cdr key) :flags))) (if flags `(,(cdr key) ,@flags) @@ -75,18 +80,20 @@ ready to be pasted in a bug report on github." '("n/a"))) (packages ,@(or (ignore-errors - (require 'core-packages) - (doom-initialize-packages) - (cl-loop for (name . plist) in doom-packages - if (doom-package-private-p name) - collect - (format - "%s" (if-let (splist (doom-plist-delete (copy-sequence plist) - :modules)) - (cons name splist) - name)))) + (let ((doom-interactive-mode t) + doom-packages + doom-disabled-packages) + (doom--read-module-packages-file + (doom-path doom-private-dir "packages.el") + nil t) + (cl-loop for (name . plist) in (nreverse doom-packages) + collect + (if-let (splist (doom-plist-delete (copy-sequence plist) + :modules)) + (prin1-to-string (cons name splist)) + name)))) '("n/a"))) - (elpa-packages + (elpa ,@(or (ignore-errors (cl-loop for (name . _) in package-alist collect (format "%s" name))) @@ -203,24 +210,23 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!" (setq-default buffer-undo-tree (make-undo-tree)))) (pcase mode (`vanilla-doom+ ; Doom core + modules - private config - `((setq doom-private-dir "/tmp/does/not/exist") + `((setq doom-init-modules-p t) (load-file ,user-init-file) (setq doom-modules ',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))) + (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" (plist-get plist :path) t))) + (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 - `((setq doom-private-dir "/tmp/does/not/exist" - doom-init-modules-p t) + `((setq doom-init-modules-p t) (load-file ,user-init-file) (doom-run-all-startup-hooks-h))) (`vanilla ; nothing loaded @@ -378,42 +384,6 @@ will be automatically appended to the result." (profiler-stop)) (setq doom--profiler (not doom--profiler))) -;;;###autoload -(defun doom/profile-emacs () - "Profile the startup time of Emacs in the background with ESUP. -If INIT-FILE is non-nil, profile that instead of USER-INIT-FILE." - (interactive) - (require 'esup) - (let ((init-file esup-user-init-file)) - (message "Starting esup...") - (esup-reset) - (setq esup-server-process (esup-server-create (esup-select-port))) - (setq esup-server-port (process-contact esup-server-process :service)) - (message "esup process started on port %s" esup-server-port) - (let ((process-args - (append `("*esup-child*" - "*esup-child*" - ,esup-emacs-path - "-Q" - "--eval=(setq after-init-time nil)" - "-L" ,esup-load-path) - (when (bound-and-true-p early-init-file) - `("-l" ,early-init-file)) - `("-l" "esup-child" - ,(format "--eval=(let ((load-file-name \"%s\")) (esup-child-run \"%s\" \"%s\" %d))" - init-file - init-file - esup-server-port - esup-depth) - "--eval=(doom-run-all-startup-hooks-h)")))) - (when esup-run-as-batch-p - (setq process-args (append process-args '("--batch")))) - (setq esup-child-process (apply #'start-process process-args))) - (set-process-sentinel esup-child-process 'esup-child-process-sentinel))) - -;;;###autoload -(advice-add #'esup :override #'doom/profile-emacs) - ;;;###autoload (defun doom/toggle-debug-mode (&optional arg) "Toggle `debug-on-error' and `doom-debug-mode' for verbose logging." diff --git a/core/autoload/fonts.el b/core/autoload/fonts.el index 6a17919e2..0c2cfd6cc 100644 --- a/core/autoload/fonts.el +++ b/core/autoload/fonts.el @@ -72,7 +72,8 @@ See `doom-init-fonts-h'." (interactive) (when doom-font (set-frame-font doom-font t)) - (mapc #'doom-init-fonts-h (frame-list))) + (doom-init-fonts-h) + (mapc #'doom-init-extra-fonts-h (frame-list))) ;;;###autoload (defun doom/increase-font-size (count) diff --git a/core/autoload/format.el b/core/autoload/format.el index 7de5af711..8e4df3882 100644 --- a/core/autoload/format.el +++ b/core/autoload/format.el @@ -91,6 +91,7 @@ Accepts 'ansi and 'text-properties. nil means don't render colors.") ;; ;;; Library +;;;###autoload (defun doom--format (output) (if (string-empty-p (string-trim output)) "" @@ -99,6 +100,7 @@ Accepts 'ansi and 'text-properties. nil means don't render colors.") "\n" (concat "\n" (make-string doom-format-indent 32)) output t t)))) +;;;###autoload (defun doom--format-print (output) (unless (string-empty-p output) (if (not noninteractive) @@ -107,6 +109,7 @@ Accepts 'ansi and 'text-properties. nil means don't render colors.") (terpri)) ; newline t)) +;;;###autoload (defun doom--format-indent (width text &optional prefix) "Indent TEXT by WIDTH spaces. If ARGS, format TEXT with them." (with-temp-buffer @@ -121,6 +124,7 @@ Accepts 'ansi and 'text-properties. nil means don't render colors.") (insert prefix))) (buffer-string))) +;;;###autoload (defun doom--format-autofill (&rest msgs) "Ensure MSG is split into lines no longer than `fill-column'." (with-temp-buffer @@ -131,6 +135,7 @@ Accepts 'ansi and 'text-properties. nil means don't render colors.") (fill-region (point-min) (point-max)) (buffer-string)))) +;;;###autoload (defun doom--format-color (style format &rest args) "Apply STYLE to formatted MESSAGE with ARGS. @@ -159,6 +164,7 @@ Otherwise, it maps colors to a term-color-* face." ((cddr (assq style doom-format-ansi-alist))))))) (_ message)))) +;;;###autoload (defun doom--format-class (class format &rest args) "Apply CLASS to formatted format with ARGS. @@ -172,6 +178,7 @@ transformative logic." (args (apply #'format format args)) (format)))) +;;;###autoload (defun doom--format-apply (forms &optional sub) "Replace color-name functions with calls to `doom--format-color'." (cond ((null forms) nil) diff --git a/core/autoload/help.el b/core/autoload/help.el index 72d738483..81f484204 100644 --- a/core/autoload/help.el +++ b/core/autoload/help.el @@ -147,7 +147,7 @@ selection of all minor-modes, active or not." (list (or (+org-get-property "TITLE") (file-relative-name buffer-file-name)))) path - (list (replace-regexp-in-string org-any-link-re "\\4" text))) + (list (replace-regexp-in-string org-link-any-re "\\4" text))) " > ") tags) " ") @@ -366,15 +366,6 @@ current file is in, or d) the module associated with the current major mode (see (recenter) (message "Couldn't find the config block")))))))) -(defvar doom--help-packages-list nil) -(defun doom--help-packages-list (&optional refresh) - (or (unless refresh - doom--help-packages-list) - (setq doom--help-packages-list - (append (cl-loop for package in doom-core-packages - collect (list package :modules '((:core internal)))) - (doom-package-list 'all))))) - (defun doom--help-package-configs (package) ;; TODO Add git checks, in case ~/.emacs.d isn't a git repo (let ((default-directory doom-emacs-dir)) @@ -394,16 +385,16 @@ defined and configured. If prefix arg is present, refresh the cache." (interactive - (let* ((guess (or (function-called-at-point) - (symbol-at-point)))) + (let ((guess (or (function-called-at-point) + (symbol-at-point)))) (require 'finder-inf nil t) - (require 'package) - (unless package--initialized - (package-initialize t)) - (let ((packages (cl-delete-duplicates - (append (mapcar 'car package-alist) - (mapcar 'car package--builtins) - (mapcar 'car (doom--help-packages-list)) + (require 'core-packages) + (doom-initialize-packages) + (let ((packages (delete-dups + (append (mapcar #'car package-alist) + (mapcar #'car package--builtins) + (mapcar #'intern (hash-table-keys straight--build-cache)) + (mapcar #'car (doom-package-list 'all)) nil)))) (unless (memq guess packages) (setq guess nil)) @@ -415,6 +406,8 @@ If prefix arg is present, refresh the cache." "Describe package: ") packages nil t nil nil (if guess (symbol-name guess)))))))) + (require 'core-packages) + (doom-initialize-packages) (if (or (package-desc-p package) (and (symbolp package) (or (assq package package-alist) @@ -425,8 +418,7 @@ If prefix arg is present, refresh the cache." (with-help-window (help-buffer))) (save-excursion (with-current-buffer (help-buffer) - (let ((doom-packages (doom--help-packages-list)) - (inhibit-read-only t) + (let ((inhibit-read-only t) (indent (make-string 13 ? ))) (goto-char (point-max)) (if (re-search-forward "^ *Status: " nil t) @@ -441,7 +433,10 @@ If prefix arg is present, refresh the cache." (package--print-help-section "Source") (insert (or (pcase (doom-package-backend package) (`straight - (format! "Straight\n%s" + (format! "Straight (%s)\n%s" + (let ((default-directory (straight--build-dir (symbol-name package)))) + (string-trim + (shell-command-to-string "git log -1 --format=\"%D %h %ci\""))) (indent 13 (string-trim (pp-to-string @@ -453,7 +448,7 @@ If prefix arg is present, refresh the cache." "unknown") "\n") - (when (assq package doom-packages) + (when (gethash (symbol-name package) straight--build-cache) (package--print-help-section "Modules") (insert "Declared by the following Doom modules:\n") (dolist (m (doom-package-get package :modules)) @@ -548,7 +543,7 @@ If prefix arg is present, refresh the cache." ;;;###autoload (defun doom/help-package-config (package) - "Jump to any `def-package!', `after!' or ;;;###package block for PACKAGE. + "Jump to any `use-package!', `after!' or ;;;###package block for PACKAGE. This only searches `doom-emacs-dir' (typically ~/.emacs.d) and does not include config blocks in your private config." diff --git a/core/autoload/packages.el b/core/autoload/packages.el index b2ef5f1d1..bdf655d08 100644 --- a/core/autoload/packages.el +++ b/core/autoload/packages.el @@ -131,10 +131,10 @@ was installed with." ;; ;;; Package list getters -(defun doom--read-module-packages-file (file &optional eval noerror) +(defun doom--read-module-packages-file (file &optional noeval noerror) (with-temp-buffer ; prevent buffer-local settings from propagating (condition-case e - (if (not eval) + (if (not noeval) (load file noerror t t) (when (file-readable-p file) (insert-file-contents file) @@ -165,7 +165,7 @@ This excludes core packages listed in `doom-core-packages'. If ALL-P, gather packages unconditionally across all modules, including disabled ones." - (let ((noninteractive t) + (let ((doom-interactive-mode t) (doom-modules (doom-modules)) doom-packages doom-disabled-packages) diff --git a/core/autoload/sessions.el b/core/autoload/sessions.el index 45dd18ae0..432d6a7b3 100644 --- a/core/autoload/sessions.el +++ b/core/autoload/sessions.el @@ -92,8 +92,8 @@ (let ((session-file (doom-session-file))) (list (or (read-file-name "Session to restore: " (file-name-directory session-file) - nil t - (file-name-nondirectory session-file)) + (file-name-nondirectory session-file) + t) (user-error "No session selected. Aborting"))))) (unless file (error "No session file selected")) @@ -107,7 +107,6 @@ (let ((session-file (doom-session-file))) (list (or (read-file-name "Save session to: " (file-name-directory session-file) - nil nil (file-name-nondirectory session-file)) (user-error "No session selected. Aborting"))))) (unless file diff --git a/core/autoload/themes.el b/core/autoload/themes.el index ef9d9a4b5..6000e4c4c 100644 --- a/core/autoload/themes.el +++ b/core/autoload/themes.el @@ -4,7 +4,7 @@ (cond ((listp (car spec)) (cl-loop for face in (car spec) collect - (doom--custom-theme-set-face `(,face ,(cdr spec))))) + (car (doom--custom-theme-set-face (cons face (cdr spec)))))) ((keywordp (cadr spec)) `((,(car spec) ((t ,(cdr spec)))))) (`((,(car spec) ,(cdr spec)))))) @@ -44,7 +44,5 @@ face format." (let ((theme (or (car-safe custom-enabled-themes) doom-theme))) (when theme (mapc #'disable-theme custom-enabled-themes)) - (when (and doom-theme (not (memq doom-theme custom-enabled-themes))) - (let (doom--prefer-theme-elc) - (load-theme doom-theme t))) - (doom-init-fonts-h))) + (load-theme doom-theme 'noconfirm) + (doom/reload-font))) diff --git a/core/autoload/ui.el b/core/autoload/ui.el index d1cb2dd97..b83dfc31b 100644 --- a/core/autoload/ui.el +++ b/core/autoload/ui.el @@ -169,35 +169,67 @@ OPACITY is an integer between 0 to 100, inclusive." 100)))) (set-frame-parameter nil 'alpha opacity)) -(defvar-local doom--buffer-narrowed-origin nil) -(defvar-local doom--buffer-narrowed-window-start nil) +(defvar doom--narrowed-base-buffer nil) ;;;###autoload -(defun doom/clone-and-narrow-buffer (beg end &optional clone-p) - "Restrict editing in this buffer to the current region, indirectly. With CLONE-P, -clone the buffer and hard-narrow the selection. If mark isn't active, then widen -the buffer (if narrowed). +(defun doom/narrow-buffer-indirectly (beg end) + "Restrict editing in this buffer to the current region, indirectly. + +This recursively creates indirect clones of the current buffer so that the +narrowing doesn't affect other windows displaying the same buffer. Call +`doom/widen-indirectly-narrowed-buffer' to undo it (incrementally). Inspired from http://demonastery.org/2013/04/emacs-evil-narrow-region/" (interactive (list (or (bound-and-true-p evil-visual-beginning) (region-beginning)) (or (bound-and-true-p evil-visual-end) (region-end)) current-prefix-arg)) - (cond ((or (region-active-p) - (not (buffer-narrowed-p))) - (unless (region-active-p) - (setq beg (line-beginning-position) - end (line-end-position))) - (setq deactivate-mark t) - (when clone-p - (let ((old-buf (current-buffer))) - (switch-to-buffer (clone-indirect-buffer nil nil)) - (setq doom--buffer-narrowed-origin old-buf))) - (setq doom--buffer-narrowed-window-start (window-start)) - (narrow-to-region beg end)) - (doom--buffer-narrowed-origin - (kill-current-buffer) - (switch-to-buffer doom--buffer-narrowed-origin) - (setq doom--buffer-narrowed-origin nil)) - (t - (widen) - (set-window-start nil doom--buffer-narrowed-window-start)))) + (unless (region-active-p) + (setq beg (line-beginning-position) + end (line-end-position))) + (deactivate-mark) + (let ((orig-buffer (current-buffer))) + (with-current-buffer (switch-to-buffer (clone-indirect-buffer nil nil)) + (narrow-to-region beg end) + (setq-local doom--narrowed-base-buffer orig-buffer)))) + +;;;###autoload +(defun doom/widen-indirectly-narrowed-buffer (&optional arg) + "Widens narrowed buffers. + +This command will incrementally kill indirect buffers (under the assumption they +were created by `doom/narrow-buffer-indirectly') and switch to their base +buffer. + +If ARG, then kill all indirect buffers, return the base buffer and widen it. + +If the current buffer is not an indirect buffer, it is `widen'ed." + (interactive "P") + (unless (buffer-narrowed-p) + (user-error "Buffer isn't narrowed")) + (let ((orig-buffer (current-buffer)) + (base-buffer doom--narrowed-base-buffer)) + (cond ((or (not base-buffer) + (not (buffer-live-p base-buffer))) + (widen)) + (arg + (let ((buffer orig-buffer) + (buffers-to-kill (list orig-buffer))) + (while (setq buffer (buffer-local-value 'doom--narrowed-base-buffer buffer)) + (push buffer buffers-to-kill)) + (switch-to-buffer (buffer-base-buffer)) + (mapc #'kill-buffer (remove (current-buffer) buffers-to-kill)))) + ((switch-to-buffer base-buffer) + (kill-buffer orig-buffer))))) + +;;;###autoload +(defun doom/toggle-narrow-buffer (beg end) + "Narrow the buffer to BEG END. If narrowed, widen it." + (interactive + (list (or (bound-and-true-p evil-visual-beginning) (region-beginning)) + (or (bound-and-true-p evil-visual-end) (region-end)))) + (if (buffer-narrowed-p) + (widen) + (unless (region-active-p) + (setq beg (line-beginning-position) + end (line-end-position))) + (narrow-to-region beg end))) diff --git a/core/cli/autoloads.el b/core/cli/autoloads.el index b28fed6c1..155757330 100644 --- a/core/cli/autoloads.el +++ b/core/cli/autoloads.el @@ -27,6 +27,8 @@ byte-compiles `doom-autoload-file', as well as `doom-package-autoload-file' It also caches `load-path', `Info-directory-list', `doom-disabled-packages', `package-activated-list' and `auto-mode-alist'." + ;; REVIEW Can we avoid calling `straight-check-all' everywhere? + (straight-check-all) (doom-reload-autoloads nil 'force)) @@ -357,7 +359,7 @@ This should be run whenever your `doom!' block or update your packages." (print-group! (if (and (not force-p) (file-exists-p doom-package-autoload-file) - (not (file-newer-than-file-p doom-elpa-dir doom-package-autoload-file)) + (not (file-newer-than-file-p package-user-dir doom-package-autoload-file)) (not (cl-loop for dir in (straight--directory-files (straight--build-dir)) if (cl-find-if (lambda (dir) @@ -377,7 +379,7 @@ This should be run whenever your `doom!' block or update your packages." (noninteractive t) (backup-inhibited t) (version-control 'never) - (case-fold-search nil) ; reduce magit + (case-fold-search nil) ; reduce magic (autoload-timestamps nil)) (print! (start "Regenerating package autoloads file")) diff --git a/core/cli/byte-compile.el b/core/cli/byte-compile.el index 40fff8b81..23dfd00ec 100644 --- a/core/cli/byte-compile.el +++ b/core/cli/byte-compile.el @@ -92,10 +92,9 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files." ;; 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 (noninteractive) + (let ((doom-interactive-mode 'byte-compile)) (doom-initialize 'force) - (doom-initialize-core) - (doom-initialize-modules 'force)) + (doom-initialize-core)) ;; (unless target-dirs diff --git a/core/cli/env.el b/core/cli/env.el index 9a1240bda..83cc678c9 100644 --- a/core/cli/env.el +++ b/core/cli/env.el @@ -22,22 +22,23 @@ This file is automatically regenerated when you run this command or 'doom refresh'. However, 'doom refresh' will only regenerate this file if it exists. Use the -c or --clear switch to delete your envvar file." - (let ((default-directory doom-emacs-dir)) - (when (member "clear" args) ; DEPRECATED - (message "'doom env clear' is deprecated. Use 'doom env -c' or 'doom env --clear' instead") - (push "-c" args)) - + (when (member "clear" args) ; DEPRECATED + (message "'doom env clear' is deprecated. Use 'doom env -c' or 'doom env --clear' instead") + (push "-c" args)) + (let ((env-file (or (cadr (member "-o" args)) + doom-env-file))) (cond ((or (member "-c" args) (member "--clear" args)) - (unless (file-exists-p doom-env-file) + (unless (file-exists-p env-file) (user-error! "%S does not exist to be cleared" - (relpath doom-env-file))) - (delete-file doom-env-file) + (path env-file))) + (delete-file env-file) (print! (success "Successfully deleted %S") - (relpath doom-env-file))) + (path env-file))) - ((null args) - (doom-reload-env-file 'force)) + ((or (null args) + (member "-o" args)) + (doom-reload-env-file 'force env-file)) ((user-error "I don't understand 'doom env %s'" (string-join args " ")))))) @@ -58,6 +59,7 @@ Use the -c or --clear switch to delete your envvar file." "^INSECURE$" "^DEBUG$" "^YES$" + "^TERM$" "^__") "Environment variables to not save in `doom-env-file'. @@ -79,64 +81,67 @@ It is rare that you'll need to change this.") This is a list of strings. Each entry is run separately and in sequence with `doom-env-executable' to scrape envvars from your shell environment.") -;; Borrows heavily from Spacemacs' `spacemacs//init-spacemacs-env'. -(defun doom-reload-env-file (&optional force-p) +(defun doom-reload-env-file (&optional force-p env-file) "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." - (when (or force-p (not (file-exists-p doom-env-file))) - (with-temp-file doom-env-file - (print! (start "%s envvars file at %S") - (if (file-exists-p doom-env-file) - "Regenerating" - "Generating") - (relpath doom-env-file doom-emacs-dir)) - (let ((process-environment doom--initial-process-environment)) - (let ((shell-command-switch doom-env-switches) - (error-buffer (get-buffer-create "*env errors*"))) - (print! (info "Scraping shell environment with '%s %s %s'") - (filename shell-file-name) - shell-command-switch - (filename doom-env-executable)) - (save-excursion - (shell-command doom-env-executable (current-buffer) error-buffer)) - (print-group! - (let ((errors (with-current-buffer error-buffer (buffer-string)))) - (unless (string-empty-p errors) - (print! (info "Error output:\n\n%s") (indent 4 errors)))) - ;; Remove undesireable variables - (insert - (concat - "# -*- mode: dotenv -*-\n" - (format "# Generated with: %s %s %s\n" - shell-file-name - doom-env-switches - doom-env-executable) - "# ---------------------------------------------------------------------------\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" - "# It is NOT safe to edit this file. Changes will be overwritten next time that\n" - "# `doom refresh` is executed. Alternatively, create your own env file and load\n" - "# it with `(doom-load-envvars-file FILE)` in your private config.el.\n" - "# ---------------------------------------------------------------------------\n\n")) - (goto-char (point-min)) - (while (re-search-forward "\n\\([^= \n]+\\)=" nil t) - (save-excursion - (let* ((valend (or (save-match-data - (when (re-search-forward "^\\([^= ]+\\)=" nil t) - (line-beginning-position))) - (point-max))) - (var (match-string 1))) - (when (cl-loop for regexp in doom-env-ignored-vars - if (string-match-p regexp var) - return t) - (print! (info "Ignoring %s") var) - (delete-region (match-beginning 0) (1- valend))))))) - (print! (success "Successfully generated %S") - (relpath doom-env-file doom-emacs-dir)) - t))))) + (let ((env-file (if env-file + (expand-file-name env-file) + doom-env-file))) + (when (or force-p (not (file-exists-p env-file))) + (with-temp-file env-file + (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)) + (let ((shell-command-switch doom-env-switches) + (error-buffer (get-buffer-create "*env errors*"))) + (print! (info "Scraping shell environment with '%s %s %s'") + (filename shell-file-name) + shell-command-switch + (filename doom-env-executable)) + (save-excursion + (shell-command doom-env-executable (current-buffer) error-buffer)) + (print-group! + (let ((errors (with-current-buffer error-buffer (buffer-string)))) + (unless (string-empty-p errors) + (print! (info "Warnings:\n\n%s") (indent 4 errors)))) + ;; Remove undesireable variables + (insert + (concat + "# -*- mode: dotenv -*-\n" + (format "# Generated with: %s %s %s\n" + shell-file-name + doom-env-switches + doom-env-executable) + "# ---------------------------------------------------------------------------\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" + "# It is NOT safe to edit this file. Changes will be overwritten next time that\n" + "# `doom refresh` is executed. Alternatively, create your own env file with\n" + "# `doom env -o ~/.doom.d/myenv`, then load it with (doom-load-envvars-file FILE)\n" + "# in your private config.el.\n" + "# ---------------------------------------------------------------------------\n\n")) + (goto-char (point-min)) + (while (re-search-forward "\n\\([^= \n]+\\)=" nil t) + (save-excursion + (let* ((valend (or (save-match-data + (when (re-search-forward "^\\([^= ]+\\)=" nil t) + (line-beginning-position))) + (point-max))) + (var (match-string 1))) + (when (cl-loop for regexp in doom-env-ignored-vars + if (string-match-p regexp var) + return t) + (print! (info "Ignoring %s") var) + (delete-region (match-beginning 0) (1- valend))))))) + (print! (success "Successfully generated %S") + (path env-file)) + t)))))) diff --git a/core/cli/install.el b/core/cli/install.el index 4059aa7e5..b50b1522f 100644 --- a/core/cli/install.el +++ b/core/cli/install.el @@ -8,15 +8,15 @@ See 'doom help install' instead." (apply #'doom-cli-install args)) (defcli! (install i) (&rest args) - "A wizard for installing Doom for the first time. + "Installs and sets up Doom Emacs for the first time. This command does the following: 1. Creates DOOMDIR at ~/.doom.d, - 2. Copies ~/.emacs.d/init.example.el to DOOMDIR/init.el (if it doesn't exist), - 3. Creates dummy files for DOOMDIR/{config,packages}.el, - 4. Prompts you to generate an envvar file (via 'doom env refresh'), - 5. Installs any dependencies of enabled modules (specified by DOOMDIR/init.el), + 2. Copies ~/.emacs.d/init.example.el to $DOOMDIR/init.el (if it doesn't exist), + 3. Creates dummy files for $DOOMDIR/{config,packages}.el, + 4. Prompts you to generate an envvar file (same as 'doom env'), + 5. Installs any dependencies of enabled modules (specified by $DOOMDIR/init.el), 6. And prompts to install all-the-icons' fonts This command is idempotent and safe to reuse. @@ -27,7 +27,7 @@ DOOMDIR environment variable. e.g. doom -p ~/.config/doom install DOOMDIR=~/.config/doom doom install -install understands the following switches: +The following switches are recognized: --no-config Don't create DOOMDIR or dummy files therein --no-install Don't auto-install packages diff --git a/core/cli/packages.el b/core/cli/packages.el index 6137cf60b..2b2faf15d 100644 --- a/core/cli/packages.el +++ b/core/cli/packages.el @@ -3,6 +3,7 @@ (defmacro doom--ensure-autoloads-while (&rest body) `(progn + (straight-check-all) (doom-reload-core-autoloads) (when (progn ,@body) (doom-reload-package-autoloads 'force-p)) @@ -19,11 +20,16 @@ This works by fetching all installed package repos and checking the distance between HEAD and FETCH_HEAD. This can take a while. This excludes packages whose `package!' declaration contains a non-nil :freeze -or :ignore property." +or :ignore property. + +Switches: + -t/--timeout TTL Seconds until a thread is timed out (default: 45) + --threads N How many threads to use (default: 8)" (doom--ensure-autoloads-while - (straight-check-all) (doom-packages-update doom-auto-accept + (when-let (threads (cadr (member "--threads" args))) + (string-to-number threads)) (when-let (timeout (cadr (or (member "--timeout" args) (member "-t" args)))) (string-to-number timeout))))) @@ -32,26 +38,33 @@ or :ignore property." "Rebuilds all installed packages. This ensures that all needed files are symlinked from their package repo and -their elisp files are byte-compiled." +their elisp files are byte-compiled. + +Switches: + -f Forcibly rebuild autoloads files, even if they're up-to-date" (doom--ensure-autoloads-while (doom-packages-rebuild doom-auto-accept (member "-f" args)))) (defcli! (purge p) (&rest args) "Deletes any unused ELPA packages, straight builds, and (optionally) repos. -By default, this does not purge repos. +By default, this does not purge ELPA packages or repos. It is a good idea to run +'doom purge --all' once in a while, to stymy build-up of repos and ELPA +packages that could be taking up precious space. -Available options: - ---no-elpa Don't purge ELPA packages ---no-builds Don't purge unneeded (built) packages ---repos Purge unused repos" +Switches: + --no-builds Don't purge unneeded (built) packages + -e / --elpa Don't purge ELPA packages + -r / --repos Purge unused repos + --all Purge builds, elpa packages and repos" (doom--ensure-autoloads-while - (straight-check-all) - (doom-packages-purge (not (member "--no-elpa" args)) + (doom-packages-purge (or (member "-e" args) + (member "--elpa" args) + (member "--all" args)) (not (member "--no-builds" args)) (or (member "-r" args) - (member "--repos" args)) + (member "--repos" args) + (member "--all" args)) doom-auto-accept))) ;; (defcli! rollback () ; TODO rollback @@ -126,7 +139,7 @@ a list of packages that will be installed." (lambda (&rest _) (cl-incf n))) (let ((straight--packages-to-rebuild :all) (straight--packages-not-to-rebuild (make-hash-table :test #'equal))) - (straight-use-package (intern package) nil nil " ")) + (straight-use-package (intern package) nil nil " ")) (straight--byte-compile-package recipe) (dolist (dep (straight--get-dependencies package)) (when-let (recipe (gethash dep straight--recipe-cache)) @@ -147,12 +160,11 @@ a list of packages that will be installed." (condition-case e (let (packages errors) (load ,(concat doom-core-dir "core.el")) - (doom-initialize 'force-p) + (doom-initialize 'force) (dolist (recipe ',group) (when (straight--repository-is-available-p recipe) (straight-vc-git--destructure recipe - (package local-repo nonrecursive upstream-remote upstream-repo upstream-host - branch remote) + (package local-repo nonrecursive upstream-remote upstream-repo upstream-host branch) (condition-case e (let ((default-directory (straight--repos-dir local-repo))) ;; HACK We normalize packages to avoid certain scenarios @@ -162,7 +174,7 @@ a list of packages that will be installed." ;; can't use `straight-normalize-package' because could ;; create popup prompts too, so we do it manually: (shell-command-to-string "git merge --abort") - (straight--get-call "git" "reset" "--hard" (format "%s/%s" remote branch)) + (straight--get-call "git" "reset" "--hard" branch) (straight--get-call "git" "clean" "-ffd") (unless nonrecursive (shell-command-to-string "git submodule update --init --recursive")) @@ -208,7 +220,7 @@ a list of packages that will be installed." (cons 'error e)))))) -(defun doom-packages-update (&optional auto-accept-p timeout) +(defun doom-packages-update (&optional auto-accept-p threads timeout) "Updates packages. Unless AUTO-ACCEPT-P is non-nil, this function will prompt for confirmation with @@ -217,14 +229,17 @@ a list of packages that will be updated." (print-group! (when timeout (print! (info "Using %S as timeout value" timeout))) + (when threads + (print! (info "Limiting to %d thread(s)" threads))) ;; REVIEW Does this fail gracefully enough? Is it error tolerant? ;; TODO Add version-lock checks; don't want to spend all this effort on ;; packages that shouldn't be updated (let* ((futures + ;; REVIEW We can do better "thread" management here (or (cl-loop for group in (seq-partition (hash-table-values straight--repo-cache) (/ (hash-table-count straight--repo-cache) - 16)) + (or threads 8))) for future = (doom--packages-remove-outdated-f group) if (processp future) collect (cons future group) @@ -422,18 +437,21 @@ a list of packages that will be updated." (defun doom--packages-purge-elpa (&optional auto-accept-p) (unless (bound-and-true-p package--initialized) (package-initialize)) - (if (not package-alist) - (progn (print! (info "No ELPA packages to purge")) - 0) - (doom--prompt-columns-p - (lambda (p) (format " + %-20.20s" p)) - (mapcar #'car package-alist) nil - (format! "Found %d orphaned ELPA packages. Purge them?" - (length package-alist))) - (mapc (doom-rpartial #'delete-directory 'recursive) - (mapcar #'package-desc-dir - (mapcar #'cadr package-alist))) - (length package-alist))) + (let ((packages (cl-loop for (package desc) in package-alist + for dir = (package-desc-dir desc) + if (file-in-directory-p dir package-user-dir) + collect (cons package dir)))) + (if (not package-alist) + (progn (print! (info "No ELPA packages to purge")) + 0) + (doom--prompt-columns-p + (lambda (p) (format " + %-20.20s" p)) + (mapcar #'car packages) nil + (format! "Found %d orphaned ELPA packages. Purge them?" + (length package-alist))) + (mapc (doom-rpartial #'delete-directory 'recursive) + (mapcar #'cdr packages)) + (length packages)))) (defun doom-packages-purge (&optional elpa-p builds-p repos-p auto-accept-p) "Auto-removes orphaned packages and repos. diff --git a/core/cli/patch-macos.el b/core/cli/patch-macos.el deleted file mode 100644 index 4e1816398..000000000 --- a/core/cli/patch-macos.el +++ /dev/null @@ -1,72 +0,0 @@ -;;; core/cli/patch-macos.el -*- lexical-binding: t; -*- - -(defcli! patch-macos () ; DEPRECATED - "Patches Emacs.app to respect your shell environment. - -WARNING: This command is deprecated. Use 'doom env' instead. - -A common issue with GUI Emacs on MacOS is that it launches in an environment -independent of your shell configuration, including your PATH and any other -utilities like rbenv, rvm or virtualenv. - -This patch fixes this by patching Emacs.app (in /Applications or -~/Applications). It will: - - 1. Move Contents/MacOS/Emacs to Contents/MacOS/RunEmacs - 2. And replace Contents/MacOS/Emacs with the following wrapper script: - - #!/user/bin/env bash - args=\"$@\" - pwd=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\"; pwd -P)\" - exec \"$SHELL\" -l -c \"$pwd/RunEmacs $args\" - -This ensures that Emacs is always aware of your shell environment, regardless of -how it is launched. - -It can be undone with the --undo or -u options. - -Alternatively, you can install the exec-path-from-shell Emacs plugin, which will -scrape your shell environment remotely, at startup. However, this can be slow -depending on your shell configuration and isn't always reliable." - :hidden t - (doom-patch-macos (or (member "--undo" args) - (member "-u" args)) - (doom--find-emacsapp-path))) - - -;; -;; Library - -(defun doom--find-emacsapp-path () - (or (getenv "EMACS_APP_PATH") - (cl-loop for dir in (list "/usr/local/opt/emacs" - "/usr/local/opt/emacs-plus" - "/Applications" - "~/Applications") - for appdir = (concat dir "/Emacs.app") - if (file-directory-p appdir) - return appdir) - (user-error "Couldn't find Emacs.app"))) - -(defun doom-patch-macos (undo-p appdir) - "Patches Emacs.app to respect your shell environment." - (unless IS-MAC - (user-error "You don't seem to be running MacOS")) - (unless (file-directory-p appdir) - (user-error "Couldn't find '%s'" appdir)) - (let ((oldbin (expand-file-name "Contents/MacOS/Emacs" appdir)) - (newbin (expand-file-name "Contents/MacOS/RunEmacs" appdir))) - (cond (undo-p - (unless (file-exists-p newbin) - (user-error "Emacs.app is not patched")) - (copy-file newbin oldbin 'ok-if-already-exists nil nil 'preserve-permissions) - (unless (file-exists-p oldbin) - (error "Failed to copy %s to %s" newbin oldbin)) - (delete-file newbin) - (message "%s successfully unpatched" appdir)) - - ((file-exists-p newbin) - (user-error "%s is already patched. Use 'doom patch-macos --undo' to unpatch it" - appdir)) - - ((user-error "patch-macos has been disabled. Please use 'doom env refresh' instead"))))) diff --git a/core/cli/test.el b/core/cli/test.el index 003705222..daf1dd815 100644 --- a/core/cli/test.el +++ b/core/cli/test.el @@ -1,5 +1,12 @@ ;;; core/cli/test.el -*- lexical-binding: t; -*- +(defun doom--emacs-binary () + (let ((emacs-binary-path (doom-path invocation-directory invocation-name)) + (runemacs-binary-path (if IS-WINDOWS (doom-path invocation-directory "runemacs.exe")))) + (if (and runemacs-binary-path (file-exists-p runemacs-binary-path)) + runemacs-binary-path + emacs-binary-path))) + (defcli! test (&rest targets) "Run Doom unit tests." (let (files error) @@ -14,6 +21,7 @@ (cdr (doom-module-load-path))))))) (while targets (let ((target (pop targets))) + ;; FIXME Module targets don't work (cond ((equal target ":core") (appendq! files (nreverse (doom-glob doom-core-dir "test/test-*.el")))) ((file-directory-p target) @@ -21,26 +29,24 @@ (appendq! files (nreverse (doom-glob target "test/test-*.el")))) ((file-exists-p target) (push target files))))) - (require 'restart-emacs) (with-temp-buffer - (setenv "DOOMDIR" (concat doom-core-dir "test/")) - (setenv "DOOMLOCALDIR" (concat doom-local-dir "test/")) (print! (start "Bootstrapping test environment, if necessary...")) (if (zerop (call-process - (restart-emacs--get-emacs-binary) + (doom--emacs-binary) nil t nil "--batch" - "-l" (concat doom-core-dir "core.el") "--eval" (prin1-to-string - `(progn (doom-initialize 'force) - (doom-initialize-modules) - (require 'core-cli) - (unless (package-installed-p 'buttercup) - (package-refresh-contents) - (package-install 'buttercup)) - (doom-reload-core-autoloads 'force) - (when (doom-packages-install 'auto-accept) - (doom-reload-package-autoloads 'force)))))) + `(progn + (setq doom-emacs-dir ,doom-emacs-dir + doom-local-dir ,(concat doom-local-dir "test/") + doom-private-dir ,(concat doom-core-dir "test/")) + (require 'core ,(locate-library "core")) + (doom-initialize 'force) + (doom-initialize-modules) + (require 'core-cli) + (doom-reload-core-autoloads 'force) + (when (doom-packages-install 'auto-accept) + (doom-reload-package-autoloads 'force)))))) (message "%s" (buffer-string)) (message "%s" (buffer-string)) (error "Failed to bootstrap unit tests"))) @@ -49,18 +55,21 @@ (with-temp-buffer (unless (zerop - (call-process - (restart-emacs--get-emacs-binary) - nil t nil "--batch" - "-l" (concat doom-core-dir "core.el") - "-l" (concat doom-core-dir "test/helpers.el") - "--eval" (prin1-to-string `(doom-initialize 'force)) - "-l" "buttercup" - "-l" file - "-f" "buttercup-run")) + (apply #'call-process + (doom--emacs-binary) + nil t nil "--batch" + (append (list + "-L" doom-core-dir + "-l" "core" + "-l" (concat doom-core-dir "test/helpers.el")) + (when (file-in-directory-p file doom-modules-dir) + (list "-f" "doom-initialize-core")) + (list + "-l" file + "-f" "buttercup-run")))) (setq error t)) (message "%s" (buffer-string))) (print! (info "Ignoring %s" (relpath file))))) (if error - (error "A test failed") + (user-error "A test failed") t))) diff --git a/core/cli/upgrade.el b/core/cli/upgrade.el index 3098151db..893012f41 100644 --- a/core/cli/upgrade.el +++ b/core/cli/upgrade.el @@ -10,15 +10,26 @@ following shell commands: git pull --rebase bin/doom clean bin/doom refresh - bin/doom update" - (and (doom-upgrade (or (member "-f" args) + bin/doom update + +Switches: + -t/--timeout TTL Seconds until a thread is timed out (default: 45) + --threads N How many threads to use (default: 8)" + (and (doom-upgrade doom-auto-accept + (or (member "-f" args) (member "--force" args))) - (doom-packages-update doom-auto-accept) + (doom-packages-update + doom-auto-accept + (when-let (threads (cadr (member "--threads" args))) + (string-to-number threads)) + (when-let (timeout (cadr (or (member "--timeout" args) + (member "-t" args)))) + (string-to-number timeout))) (doom-reload-package-autoloads 'force-p))) ;; -;;; Library +;;; library (defvar doom-repo-url "https://github.com/hlissner/doom-emacs" "The git repo url for Doom Emacs.") @@ -33,7 +44,7 @@ following shell commands: (error "Failed to check working tree in %s" dir)))) -(defun doom-upgrade (&optional force-p) +(defun doom-upgrade (&optional auto-accept-p force-p) "Upgrade Doom to the latest version non-destructively." (require 'vc-git) (let ((default-directory doom-emacs-dir) @@ -82,12 +93,15 @@ following shell commands: (substring new-rev 0 10) (cdr (doom-sh "git" "log" "-1" "--format=%cr" target-remote)))) - (when (y-or-n-p "View the comparison diff in your browser?") + (when (and (not auto-accept-p) + (y-or-n-p "View the comparison diff in your browser?")) (print! (info "Opened github in your browser.")) (browse-url (format "https://github.com/hlissner/doom-emacs/compare/%s...%s" this-rev new-rev))) - (if (not (y-or-n-p "Proceed with upgrade?")) + + (if (not (or auto-accept-p + (y-or-n-p "Proceed with upgrade?"))) (ignore (print! (error "Aborted"))) (print! (start "Upgrading Doom Emacs...")) (print-group! @@ -97,6 +111,7 @@ following shell commands: (error "Failed to check out %s" (substring new-rev 0 10))) (print! (success "Finished upgrading Doom Emacs"))) (doom-delete-autoloads-file doom-autoload-file) + (doom-delete-autoloads-file doom-package-autoload-file) (doom-cli-refresh "-f") t) diff --git a/core/core-cli.el b/core/core-cli.el index f4410de96..2aec15099 100644 --- a/core/core-cli.el +++ b/core/core-cli.el @@ -186,7 +186,7 @@ stale." (setq success t)) (and (doom-packages-rebuild doom-auto-accept) (setq success t)) - (and (doom-packages-purge 'elpa-p 'builds-p nil doom-auto-accept) + (and (doom-packages-purge nil 'builds-p nil doom-auto-accept) (setq success t))) (doom-reload-package-autoloads (or success force-p)) (doom-byte-compile nil 'recompile)) @@ -212,8 +212,7 @@ enabled modules.") (load! "cli/env") (load! "cli/upgrade") (load! "cli/packages") - (load! "cli/autoloads") - (load! "cli/patch-macos")) + (load! "cli/autoloads")) (defcligroup! "Byte compilation" "For byte-compiling Doom and your config" diff --git a/core/core-editor.el b/core/core-editor.el index c131c0f7b..d1f9a78b7 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -111,8 +111,8 @@ successfully sets indent_style/indent_size.") ;; ;; This is because autorevert abuses the heck out of inotify handles which can ;; grind Emacs to a halt if you do expensive IO (outside of Emacs) on the - ;; files you have open (like compression). We only really need revert changes - ;; when we switch to a buffer or when we focus the Emacs frame. + ;; files you have open (like compression). We only really need to revert + ;; changes when we switch to a buffer or when we focus the Emacs frame. (defun doom-auto-revert-buffer-h () "Auto revert current buffer, if necessary." (unless auto-revert-mode @@ -168,7 +168,7 @@ successfully sets indent_style/indent_size.") "Add dired directory to recentf file list." (recentf-add-file default-directory))) - (unless noninteractive + (when doom-interactive-mode (add-hook 'kill-emacs-hook #'recentf-cleanup) (quiet! (recentf-mode +1)))) @@ -198,12 +198,22 @@ successfully sets indent_style/indent_size.") :after-call after-find-file dired-initial-position-hook :config (setq save-place-file (concat doom-cache-dir "saveplace") - save-place-forget-unreadable-files t - save-place-limit 200) + save-place-limit 100) + (defadvice! doom--recenter-on-load-saveplace-a (&rest _) "Recenter on cursor when loading a saved place." :after-while #'save-place-find-file-hook (if buffer-file-name (ignore-errors (recenter)))) + + (defadvice! doom--dont-prettify-saveplace-cache-a (orig-fn) + "`save-place-alist-to-file' uses `pp' to prettify the contents of its cache. +`pp' can be expensive for longer lists, and there's no reason to prettify cache +files, so we replace calls to `pp' with the much faster `prin1'." + :around #'save-place-alist-to-file + (cl-letf (((symbol-function #'pp) + (symbol-function #'prin1))) + (funcall orig-fn))) + (save-place-mode +1)) @@ -223,6 +233,10 @@ successfully sets indent_style/indent_size.") (use-package! better-jumper :after-call pre-command-hook + :preface + ;; REVIEW Suppress byte-compiler warning spawning a *Compile-Log* buffer at + ;; startup. This can be removed once gilbertw1/better-jumper#2 is merged. + (defvar better-jumper-local-mode nil) :init (global-set-key [remap evil-jump-forward] #'better-jumper-jump-forward) (global-set-key [remap evil-jump-backward] #'better-jumper-jump-backward) @@ -259,18 +273,9 @@ successfully sets indent_style/indent_size.") nil)) -(use-package! command-log-mode - :commands global-command-log-mode - :config - (setq command-log-mode-auto-show t - command-log-mode-open-log-turns-on-mode nil - command-log-mode-is-global t - command-log-mode-window-size 50)) - - (use-package! dtrt-indent ;; Automatic detection of indent settings - :unless noninteractive + :when doom-interactive-mode :defer t :init (add-hook! '(change-major-mode-after-body-hook read-only-mode-hook) @@ -389,8 +394,17 @@ successfully sets indent_style/indent_size.") (sp-local-pair 'minibuffer-inactive-mode "`" nil :actions nil) ;; Smartparens breaks evil-mode's replace state - (add-hook 'evil-replace-state-entry-hook #'turn-off-smartparens-mode) - (add-hook 'evil-replace-state-exit-hook #'turn-on-smartparens-mode) + (defvar doom-buffer-smartparens-mode nil) + (add-hook! 'evil-replace-state-exit-hook + (defun doom-enable-smartparens-mode-maybe-h () + (when doom-buffer-smartparens-mode + (turn-on-smartparens-mode) + (kill-local-variable 'doom-buffer-smartparens-mode)))) + (add-hook! 'evil-replace-state-entry-hook + (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)) @@ -412,11 +426,20 @@ successfully sets indent_style/indent_size.") undo-tree-history-directory-alist `(("." . ,(concat doom-cache-dir "undo-tree-hist/")))) + ;; Compress undo-tree history files with zstd, if available. File size isn't + ;; the (only) concern here: the file IO barrier is slow for Emacs to cross; + ;; reading a tiny file and piping it in-memory through zstd is *slightly* + ;; faster than Emacs reading the entire undo-tree file from the get go (on + ;; SSDs). Whether or not that's true in practice, we still enjoy zstd's ~80% + ;; file savings (these files add up over time and zstd is so incredibly fast). (when (executable-find "zstd") (defadvice! doom--undo-tree-make-history-save-file-name-a (file) :filter-return #'undo-tree-make-history-save-file-name (concat file ".zst"))) + ;; Strip text properties from undo-tree data to stave off bloat. File size + ;; isn't the concern here; undo cache files bloat easily, which can cause + ;; freezing, crashes, GC-induced stuttering or delays when opening files. (defadvice! doom--undo-tree-strip-text-properties-a (&rest _) :before #'undo-list-transfer-to-tree (dolist (item buffer-undo-list) diff --git a/core/core-keybinds.el b/core/core-keybinds.el index 507e7276e..fb305bd23 100644 --- a/core/core-keybinds.el +++ b/core/core-keybinds.el @@ -23,6 +23,14 @@ and Emacs states, and for non-evil users.") "An overriding keymap for keys.") +;; +;;; Keybind settings + +(when IS-MAC + (setq mac-command-modifier 'super + mac-option-modifier 'meta)) + + ;; ;;; Universal, non-nuclear escape @@ -411,18 +419,7 @@ Properties :unless [CONDITION] [...] Any of the above properties may be nested, so that they only apply to a - certain group of keybinds. - -Example - (map! :map magit-mode-map - :m \"C-r\" 'do-something ; C-r in motion state - :nv \"q\" 'magit-mode-quit-window ; q in normal+visual states - \"C-x C-r\" 'a-global-keybind - :g \"C-x C-r\" 'another-global-keybind ; same as above - - (:when IS-MAC - :n \"M-s\" 'some-fn - :i \"M-o\" (lambda (interactive) (message \"Hi\"))))" + certain group of keybinds." (doom--map-process rest)) (provide 'core-keybinds) diff --git a/core/core-lib.el b/core/core-lib.el index ed2abf35b..254c778d7 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -1,8 +1,7 @@ ;;; core-lib.el -*- lexical-binding: t; -*- -(let ((load-path doom--initial-load-path)) - (require 'subr-x) - (require 'cl-lib)) +(require 'cl-lib) +(require 'subr-x) ;; Polyfills (unless EMACS26+ @@ -175,7 +174,7 @@ at the values with which this function was called." "Push VALUES sequentially into PLACE, if they aren't already present. This is a variadic `cl-pushnew'." (let ((var (make-symbol "result"))) - `(dolist (,var (list ,@values)) + `(dolist (,var (list ,@values) (with-no-warnings ,place)) (cl-pushnew ,var ,place :test #'equal)))) (defmacro prependq! (sym &rest lists) @@ -243,7 +242,7 @@ This macro accepts, in order: 3. The function(s) to be added: this can be one function, a list thereof, a list of `defun's, or body forms (implicitly wrapped in a closure). -\(fn [:append :local] HOOKS FUNCTIONS)" +\(fn HOOKS [:append :local] FUNCTIONS)" (declare (indent (lambda (indent-point state) (goto-char indent-point) (when (looking-at-p "\\s-*(") @@ -295,17 +294,13 @@ Takes the same arguments as `add-hook!'. If N and M = 1, there's no benefit to using this macro over `remove-hook'. -\(fn [:append :local] HOOKS FUNCTIONS)" +\(fn HOOKS [:append :local] FUNCTIONS)" (declare (indent defun) (debug t)) `(add-hook! ,hooks :remove ,@rest)) (defmacro setq-hook! (hooks &rest var-vals) "Sets buffer-local variables on HOOKS. - (setq-hook! 'markdown-mode-hook - line-spacing 2 - fill-column 80) - \(fn HOOKS &rest [SYM VAL]...)" (declare (indent 1)) (macroexp-progn @@ -338,7 +333,10 @@ If NOERROR is non-nil, don't throw an error if the file doesn't exist." (setq path (or (dir!) (error "Could not detect path to look for '%s' in" filename)))) - (let ((file (if path `(expand-file-name ,filename ,path) filename))) + (let ((file (if path + `(let (file-name-handler-alist) + (expand-file-name ,filename ,path)) + filename))) `(condition-case e (load ,file ,noerror ,(not doom-debug-mode)) ((debug doom-error) (signal (car e) (cdr e))) @@ -372,36 +370,37 @@ serve as a predicated alternative to `after!'." (put ',fn 'permanent-local-hook t) (add-hook 'after-load-functions #',fn))))) -(defmacro defer-feature! (feature &optional mode) - "Pretend FEATURE hasn't been loaded yet, until FEATURE-hook is triggered. +(defmacro defer-feature! (feature &optional fn) + "Pretend FEATURE hasn't been loaded yet, until FEATURE-hook or FN runs. Some packages (like `elisp-mode' and `lisp-mode') are loaded immediately at startup, which will prematurely trigger `after!' (and `with-eval-after-load') blocks. To get around this we make Emacs believe FEATURE hasn't been loaded yet, -then wait until FEATURE-hook (or MODE-hook, if MODE is provided) is triggered to +then wait until FEATURE-hook (or MODE-hook, if FN is provided) is triggered to reverse this and trigger `after!' blocks at a more reasonable time." (let ((advice-fn (intern (format "doom--defer-feature-%s-a" feature))) - (mode (or mode feature))) + (fn (or fn feature))) `(progn (setq features (delq ',feature features)) - (advice-add #',mode :before #',advice-fn) + (advice-add #',fn :before #',advice-fn) (defun ,advice-fn (&rest _) - ;; Some plugins (like yasnippet) will invoke a mode early to parse + ;; Some plugins (like yasnippet) will invoke a fn early to parse ;; code, which would prematurely trigger this. In those cases, well ;; behaved plugins will use `delay-mode-hooks', which we can check for: - (when (and ,(intern (format "%s-hook" mode)) + (when (and ,(intern (format "%s-hook" fn)) (not delay-mode-hooks)) ;; ...Otherwise, announce to the world this package has been loaded, ;; so `after!' handlers can react. (provide ',feature) - (advice-remove #',mode #',advice-fn)))))) + (advice-remove #',fn #',advice-fn)))))) (defmacro quiet! (&rest forms) "Run FORMS without generating any output. This silences calls to `message', `load-file', `write-region' and anything that writes to `standard-output'." - `(cond (noninteractive + `(cond (doom-debug-mode ,@forms) + ((not doom-interactive-mode) (let ((old-fn (symbol-function 'write-region))) (cl-letf ((standard-output (lambda (&rest _))) ((symbol-function 'load-file) (lambda (file) (load file nil t))) @@ -411,8 +410,6 @@ writes to `standard-output'." (unless visit (setq visit 'no-message)) (funcall old-fn start end filename append visit lockname mustbenew)))) ,@forms))) - ((or doom-debug-mode debug-on-error debug-on-quit) - ,@forms) ((let ((inhibit-message t) (save-silently t)) (prog1 ,@forms (message "")))))) diff --git a/core/core-modules.el b/core/core-modules.el index 1ca47f678..d55010e61 100644 --- a/core/core-modules.el +++ b/core/core-modules.el @@ -48,7 +48,7 @@ syntax-checker modules obsolete. e.g. If :feature version-control is found in your `doom!' block, a warning is emitted before replacing it with :emacs vc and :ui vc-gutter.") -(defvar doom-inhibit-module-warnings (not noninteractive) +(defvar doom-inhibit-module-warnings doom-interactive-mode "If non-nil, don't emit deprecated or missing module warnings at startup.") ;;; Custom hooks @@ -84,7 +84,7 @@ non-nil." (load! "init" (plist-get plist :path) t))) doom-modules)) (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook) - (unless noninteractive + (when doom-interactive-mode (when doom-modules (maphash (lambda (key plist) (let ((doom--current-module key) @@ -224,7 +224,7 @@ those directories. The first returned path is always `doom-private-dir'." "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 ((noninteractive t) + (let (doom-interactive-mode doom-modules doom-init-modules-p) (load! "init" doom-private-dir t) @@ -243,30 +243,16 @@ This value is cached. If REFRESH-P, then don't use the cached value." (setq use-package-compute-statistics doom-debug-mode use-package-verbose doom-debug-mode use-package-minimum-reported-time (if doom-debug-mode 0 0.1) - use-package-expand-minimally (not noninteractive))) + use-package-expand-minimally doom-interactive-mode)) -;; Adds four new keywords to `use-package' (and consequently, `use-package!') to -;; expand its lazy-loading capabilities. They are: -;; -;; Check out `use-package!'s documentation for more about these two. -;; :after-call SYMBOL|LIST -;; :defer-incrementally SYMBOL|LIST|t -;; -;; Provided by `auto-minor-mode' package: -;; :minor -;; :magic-minor -;; (defvar doom--deferred-packages-alist '(t)) (with-eval-after-load 'use-package-core ;; Macros are already fontified, no need for this (font-lock-remove-keywords 'emacs-lisp-mode use-package-font-lock-keywords) - ;; Register all new keywords - (dolist (keyword '(:defer-incrementally :after-call)) - (push keyword use-package-deferring-keywords) - (setq use-package-keywords - (use-package-list-insert keyword use-package-keywords :after))) + ;; 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)) (setq use-package-keywords (use-package-list-insert keyword use-package-keywords :commands))) @@ -279,6 +265,17 @@ This value is cached. If REFRESH-P, then don't use the cached value." (defun use-package-handler/:magic-minor (name _ arg rest state) (use-package-handle-mode name 'auto-minor-mode-magic-alist arg rest state)) + ;; Adds two keywords to `use-package' to expand its lazy-loading capabilities: + ;; + ;; :after-call SYMBOL|LIST + ;; :defer-incrementally SYMBOL|LIST|t + ;; + ;; Check out `use-package!'s documentation for more about these two. + (dolist (keyword '(:defer-incrementally :after-call)) + (push keyword use-package-deferring-keywords) + (setq use-package-keywords + (use-package-list-insert keyword use-package-keywords :after))) + (defalias 'use-package-normalize/:defer-incrementally #'use-package-normalize-symlist) (defun use-package-handler/:defer-incrementally (name _keyword targets rest state) (use-package-concat @@ -410,7 +407,7 @@ to least)." (if-let (path (doom-module-locate-path category module)) (doom-module-set category module :flags flags :path path) (message "WARNING Couldn't find the %s %s module" category module))))))) - (when noninteractive + (unless doom-interactive-mode (setq doom-inhibit-module-warnings t)) `(setq doom-modules ',doom-modules))) @@ -428,26 +425,17 @@ two extra properties: :after-call SYMBOL|LIST Takes a symbol or list of symbols representing functions or hook variables. The first time any of these functions or hooks are executed, the package is - loaded. e.g. - - (use-package! projectile - :after-call (pre-command-hook after-find-file dired-before-readin-hook) - ...) + loaded. :defer-incrementally SYMBOL|LIST|t Takes a symbol or list of symbols representing packages that will be loaded incrementally at startup before this one. This is helpful for large packages like magit or org, which load a lot of dependencies on first load. This lets you load them piece-meal during idle periods, so that when you finally do need - the package, it'll load quicker. e.g. + the package, it'll load quicker. NAME is implicitly added if this property is present and non-nil. No need to - specify it. A value of `t' implies NAME, e.g. - - (use-package! abc - ;; This is equivalent to :defer-incrementally (abc) - :defer-incrementally t - ...)" + specify it. A value of `t' implies NAME." (declare (indent 1)) (unless (or (memq name doom-disabled-packages) ;; At compile-time, use-package will forcibly load packages to @@ -462,7 +450,8 @@ two extra properties: (defmacro use-package-hook! (package when &rest body) "Reconfigures a package's `use-package!' block. -Only use this macro in a module's init.el file. +This macro must be used *before* PACKAGE's `use-package!' block. Often, this +means using it from your DOOMDIR/init.el. Under the hood, this uses use-package's `use-package-inject-hooks'. @@ -470,9 +459,13 @@ PACKAGE is a symbol; the package's name. WHEN should be one of the following: :pre-init :post-init :pre-config :post-config -WARNING: If :pre-init or :pre-config hooks return nil, the original -`use-package!''s :init/:config block (respectively) is overwritten, so remember -to have them return non-nil (or exploit that to overwrite Doom's config)." +WARNINGS: +- The use of this macro is more often than not a code smell. Use it as last + resort. There is almost always a better alternative. +- If you are using this solely for :post-config, stop! `after!' is much better. +- If :pre-init or :pre-config hooks return nil, the original `use-package!''s + :init/:config block (respectively) is overwritten, so remember to have them + return non-nil (or exploit that to overwrite Doom's config)." (declare (indent defun)) (unless (memq when '(:pre-init :post-init :pre-config :post-config)) (error "'%s' isn't a valid hook for use-package-hook!" when)) @@ -493,7 +486,7 @@ CATEGORY is a keyword, MODULE is a symbol and FLAGS are symbols. This is for testing and internal use. This is not the correct way to enable a module." - `(let ((doom-modules ,doom-modules) + `(let ((doom-modules (or ,doom-modules (doom-modules))) (module-path (doom-module-locate-path ,category ',module))) (doom-module-set ,category ',module diff --git a/core/core-packages.el b/core/core-packages.el index 39dd4aa7d..ae8283283 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -74,8 +74,8 @@ missing) and shouldn't be deleted.") ;; shouldn't be using it, but it may be convenient for quick package testing. (setq package--init-file-ensured t package-enable-at-startup nil - package-user-dir doom-elpa-dir - package-gnupghome-dir (expand-file-name "gpg" doom-elpa-dir) + package-user-dir (concat doom-local-dir "elpa/") + package-gnupghome-dir (expand-file-name "gpg" package-user-dir) ;; I omit Marmalade because its packages are manually submitted rather ;; than pulled, so packages are often out of date with upstream. package-archives @@ -112,12 +112,6 @@ missing) and shouldn't be deleted.") ;; we just don't have to deal with them at all. autoload-compute-prefixes nil) -;; Straight is hardcoded to operate out of ~/.emacs.d/straight. Not on my watch! -(defadvice! doom--straight-use-local-dir-a (orig-fn &rest args) - :around #'straight--emacs-dir - (let ((user-emacs-directory doom-local-dir)) - (apply orig-fn args))) - (defun doom--finalize-straight () (mapc #'funcall (delq nil (mapcar #'cdr straight--transaction-alist))) (setq straight--transaction-alist nil)) @@ -155,8 +149,6 @@ necessary package metadata is initialized and available for them." :branch ,straight-repository-branch :no-byte-compile t)) (mapc #'straight-use-package doom-core-packages) - (when noninteractive - (add-hook 'kill-emacs-hook #'doom--finalize-straight))) (doom-log "Initializing doom-packages") (setq doom-disabled-packages nil doom-packages (doom-package-list)) @@ -171,7 +163,9 @@ necessary package metadata is initialized and available for them." (if-let (recipe (plist-get plist :recipe)) (let ((plist (straight-recipes-retrieve pkg))) `(,pkg ,@(doom-plist-merge recipe (cdr plist)))) - pkg))))) + pkg)))) + (unless doom-interactive-mode + (add-hook 'kill-emacs-hook #'doom--finalize-straight)))) (defun doom-ensure-straight () "Ensure `straight' is installed and was compiled with this version of Emacs." @@ -214,7 +208,7 @@ Accepts the following properties: Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example); for packages to be installed from external sources. :disable BOOL - Do not install or update this package AND disable all of its `def-package!' + Do not install or update this package AND disable all of its `use-package!' blocks. :ignore FORM Do not install this package. diff --git a/core/core-projects.el b/core/core-projects.el index ba5a4a5f6..af03b3964 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -30,7 +30,7 @@ Emacs.") projectile-add-known-project) ; TODO PR autoload upstream :init (setq projectile-cache-file (concat doom-cache-dir "projectile.cache") - projectile-enable-caching (not noninteractive) + projectile-enable-caching doom-interactive-mode projectile-known-projects-file (concat doom-cache-dir "projectile.projects") projectile-require-project-root t projectile-globally-ignored-files '(".DS_Store" "Icon " "TAGS") @@ -51,6 +51,11 @@ Emacs.") (push ".project" projectile-project-root-files-bottom-up) (push (abbreviate-file-name doom-local-dir) projectile-globally-ignored-directories) + ;; Disable commands that won't work, as is, and that Doom already provides a + ;; better alternative for. + (put 'projectile-ag 'disabled "Use +{ivy,helm}/project-search or +{ivy,helm}/ag instead") + (put 'projectile-ripgrep 'disabled "Use +{ivy,helm}/project-search or +{ivy,helm}/rg instead") + ;; Treat current directory in dired as a "file in a project" and track it (add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook) @@ -67,7 +72,7 @@ b) represent blacklisted directories that are too big, change too often or are private. (see `doom-projectile-cache-blacklist'), c) are not valid projectile projects." (when (and (bound-and-true-p projectile-projects-cache) - (not noninteractive)) + doom-interactive-mode) (cl-loop with blacklist = (mapcar #'file-truename doom-projectile-cache-blacklist) for proot in (hash-table-keys projectile-projects-cache) if (or (not (stringp proot)) @@ -239,14 +244,16 @@ Relevant: `doom-project-hook'." ,(if (stringp (car files)) (cons 'and files) files)))) ,(or when t) (,name 1))))) - `((dolist (mode ,modes) - (let ((hook-name - (intern (format "doom--enable-%s%s-h" ',name - (if (eq mode t) "" (format "-in-%s" mode)))))) - (fset hook-name #',fn) - (if (eq mode t) - (add-to-list 'auto-minor-mode-magic-alist (cons hook-name #',name)) - (add-hook (intern (format "%s-hook" mode)) hook-name))))))) + (if modes + `((dolist (mode ,modes) + (let ((hook-name + (intern (format "doom--enable-%s%s-h" ',name + (if (eq mode t) "" (format "-in-%s" mode)))))) + (fset hook-name #',fn) + (if (eq mode t) + (add-to-list 'auto-minor-mode-magic-alist (cons hook-name #',name)) + (add-hook (intern (format "%s-hook" mode)) hook-name))))) + `((add-hook 'change-major-mode-after-body-hook #',fn))))) (match `((add-to-list 'auto-minor-mode-alist (cons ,match #',name))))))))) diff --git a/core/core-ui.el b/core/core-ui.el index a26d13c9b..5dffb9b4e 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -344,7 +344,7 @@ treat Emacs as a non-application window." (setq mode-line-default-help-echo nil show-help-function nil) -;; y/n is easier to type than yes/no +;; Typing yes/no is obnoxious when y/n will do (fset #'yes-or-no-p #'y-or-n-p) ;; Try really hard to keep the cursor from getting stuck in the read-only prompt @@ -372,11 +372,11 @@ treat Emacs as a non-application window." (use-package! ediff :defer t - :init + :config (setq ediff-diff-options "-w" ; turn off whitespace checking ediff-split-window-function #'split-window-horizontally ediff-window-setup-function #'ediff-setup-windows-plain) - :config + (defvar doom--ediff-saved-wconf nil) ;; Restore window config after quitting ediff (add-hook! 'ediff-before-setup-hook @@ -579,26 +579,26 @@ character that looks like a space that `whitespace-mode' won't affect.") behavior). Do not set this directly, this is let-bound in `doom-init-theme-h'.") (defun doom-init-fonts-h () - "Loads fonts. + "Loads `doom-font'." + (cond (doom-font + (add-to-list + 'default-frame-alist + (cons 'font + (cond ((stringp doom-font) doom-font) + ((fontp doom-font) (font-xlfd-name doom-font)) + ((signal 'wrong-type-argument (list '(fontp stringp) doom-font))))))) + ((display-graphic-p) + (setq doom-font (face-attribute 'default :font))))) -Fonts are specified by `doom-font', `doom-variable-pitch-font', -`doom-serif-font' and `doom-unicode-font'." +(defun doom-init-extra-fonts-h (&optional frame) + "Loads `doom-variable-pitch-font',`doom-serif-font' and `doom-unicode-font'." (condition-case e - (progn - (cond (doom-font - (add-to-list - 'default-frame-alist - (cons 'font - (cond ((stringp doom-font) doom-font) - ((fontp doom-font) (font-xlfd-name doom-font)) - ((signal 'wrong-type-argument (list '(fontp stringp) doom-font))))))) - ((display-graphic-p) - (setq doom-font (face-attribute 'default :font)))) + (with-selected-frame (or frame (selected-frame)) (when doom-serif-font - (set-face-attribute 'fixed-pitch-serif t :font doom-serif-font)) + (set-face-attribute 'fixed-pitch-serif nil :font doom-serif-font)) (when doom-variable-pitch-font - (set-face-attribute 'variable-pitch t :font doom-variable-pitch-font)) - (when doom-unicode-font + (set-face-attribute 'variable-pitch nil :font doom-variable-pitch-font)) + (when (and doom-unicode-font (fboundp 'set-fontset-font)) (set-fontset-font t 'unicode doom-unicode-font nil 'prepend))) ((debug error) (if (string-prefix-p "Font not available: " (error-message-string e)) @@ -656,10 +656,15 @@ startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil." (dolist (fn '(switch-to-buffer display-buffer)) (advice-add fn :around #'doom-run-switch-buffer-hooks-a))) -;; Apply `doom-theme' -(add-hook 'doom-init-ui-hook #'doom-init-theme-h) ;; Apply `doom-font' et co (add-hook 'doom-after-init-modules-hook #'doom-init-fonts-h) +(add-hook 'doom-load-theme-hook #'doom-init-extra-fonts-h) + +;; Apply `doom-theme' +(add-hook (if (daemonp) + 'after-make-frame-functions + 'doom-init-ui-hook) + #'doom-init-theme-h) (add-hook 'window-setup-hook #'doom-init-ui-h) @@ -669,7 +674,7 @@ startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil." ;; doesn't exist in terminal Emacs; we define it to prevent errors (unless (fboundp 'define-fringe-bitmap) - (defun define-fringe-bitmap (&rest _))) + (fset 'define-fringe-bitmap #'ignore)) (after! whitespace (defun doom-disable-whitespace-mode-in-childframes-a (orig-fn) diff --git a/core/core.el b/core/core.el index 5f57fb1dd..8a1b80dae 100644 --- a/core/core.el +++ b/core/core.el @@ -1,5 +1,16 @@ ;;; core.el --- the heart of the beast -*- lexical-binding: t; -*- +(defconst doom-version "2.0.9" + "Current version of Doom Emacs.") + +(defconst EMACS26+ (> emacs-major-version 25)) +(defconst EMACS27+ (> emacs-major-version 26)) +(defconst IS-MAC (eq system-type 'darwin)) +(defconst IS-LINUX (eq system-type 'gnu/linux)) +(defconst IS-WINDOWS (memq system-type '(cygwin windows-nt ms-dos))) +(defconst IS-BSD (or IS-MAC (eq system-type 'berkeley-unix))) + +;; (defvar doom-init-p nil "Non-nil if Doom has been initialized.") @@ -12,22 +23,13 @@ Use `doom/toggle-debug-mode' to toggle it. The --debug-init flag and setting the DEBUG envvar will enable this at startup.") +(defvar doom-interactive-mode (not noninteractive) + "If non-nil, Emacs is in interactive mode.") + (defvar doom-gc-cons-threshold 16777216 ; 16mb "The default value to use for `gc-cons-threshold'. If you experience freezing, decrease this. If you experience stuttering, increase this.") -;;; Constants -(defconst doom-version "2.0.9" - "Current version of Doom Emacs.") - -(defconst EMACS26+ (> emacs-major-version 25)) -(defconst EMACS27+ (> emacs-major-version 26)) - -(defconst IS-MAC (eq system-type 'darwin)) -(defconst IS-LINUX (eq system-type 'gnu/linux)) -(defconst IS-WINDOWS (memq system-type '(cygwin windows-nt ms-dos))) -(defconst IS-BSD (or IS-MAC (eq system-type 'berkeley-unix))) - ;;; Directories/files (defvar doom-emacs-dir (eval-when-compile (file-truename user-emacs-directory)) @@ -59,11 +61,6 @@ dependencies or long-term shared data. Must end with a slash.") Use this for files that change often, like cache files. Must end with a slash.") -(defvar doom-elpa-dir (concat doom-local-dir "elpa/") - "Where package.el and quelpa plugins (and their caches) are stored. - -Must end with a slash.") - (defvar doom-docs-dir (concat doom-emacs-dir "docs/") "Where Doom's documentation files are stored. Must end with a slash.") @@ -146,9 +143,6 @@ users).") ;; to, it's our (the user's) failure. One case for all! (setq auto-mode-case-fold nil) -;; Enable all disabled commands. -(setq disabled-command-function nil) - ;; Display the bare minimum at startup. We don't need all that noise. The ;; dashboard/empty scratch buffer is good enough. (setq inhibit-startup-message t @@ -202,6 +196,13 @@ users).") url-cache-directory (concat doom-cache-dir "url/") url-configuration-directory (concat doom-etc-dir "url/") gamegrid-user-score-file-directory (concat doom-etc-dir "games/")) +;; HACK +(with-eval-after-load 'x-win + (defun emacs-session-filename (session-id) + "Construct a filename to save a session based on SESSION-ID. +Doom Emacs overrides this function to stop sessions from littering the user +directory. The session files are placed by default in `doom-cache-dir'" + (concat doom-cache-dir "emacs-session." session-id))) ;; @@ -222,8 +223,8 @@ users).") (setq fast-but-imprecise-scrolling t) ;; Resizing the Emacs frame can be a terribly expensive part of changing the -;; font. By inhibiting this, we easily halve startup times with fonts that are -;; larger than the system default. +;; font. By inhibiting this, we halve startup times, particularly when we use +;; fonts that are larger than the system default (which would resize the frame). (setq frame-inhibit-implied-resize t) ;; Don't ping things that look like domain names. @@ -320,7 +321,8 @@ This is already done by the lang/org module, however. If you want to disable incremental loading altogether, either remove `doom-load-packages-incrementally-h' from `emacs-startup-hook' or set -`doom-incremental-first-idle-timer' to nil.") +`doom-incremental-first-idle-timer' to nil. Incremental loading does not occur +in daemon sessions (they are loaded immediately at startup).") (defvar doom-incremental-first-idle-timer 2 "How long (in idle seconds) until incremental loading starts. @@ -401,7 +403,8 @@ If RETURN-P, return the message as a string instead of displaying it." (- (length load-path) (length doom--initial-load-path)) (if doom-modules (hash-table-count doom-modules) 0) (or doom-init-time - (setq doom-init-time (float-time (time-subtract (current-time) before-init-time)))))) + (setq doom-init-time + (float-time (time-subtract (current-time) before-init-time)))))) (defun doom-load-autoloads-file (file) "Tries to load FILE (an autoloads file). Return t on success, throws an error @@ -410,7 +413,7 @@ in interactive sessions, nil otherwise (but logs a warning)." (let (command-switch-alist) (load (substring file 0 -3) 'noerror 'nomessage)) ((debug error) - (if noninteractive + (if doom-interactive-mode (message "Autoload file warning: %s -> %s" (car e) (error-message-string e)) (signal 'doom-autoload-error (list (file-name-nondirectory file) e)))))) @@ -419,25 +422,27 @@ in interactive sessions, nil otherwise (but logs a warning)." (if (not (file-readable-p file)) (unless noerror (signal 'file-error (list "Couldn't read envvar file" file))) - (with-temp-buffer - (insert-file-contents file) - (search-forward "\n\n" nil t) - (while (re-search-forward "\n\\([^= \n]+\\)=" nil t) - (save-excursion - (let ((var (match-string 1)) - (value (buffer-substring-no-properties - (point) - (1- (or (when (re-search-forward "^\\([^= ]+\\)=" nil t) - (line-beginning-position)) - (point-max)))))) - (setenv var value))))) - (setq-default - exec-path (append (split-string (getenv "PATH") - (if IS-WINDOWS ";" ":")) - (list exec-directory)) - shell-file-name (or (getenv "SHELL") - shell-file-name)) - t)) + (let (vars) + (with-temp-buffer + (insert-file-contents file) + (while (re-search-forward "\n *\\([^#][^= \n]+\\)=" nil t) + (save-excursion + (let ((var (string-trim-left (match-string 1))) + (value (buffer-substring-no-properties + (point) + (1- (or (when (re-search-forward "^\\([^= ]+\\)=" nil t) + (line-beginning-position)) + (point-max)))))) + (push (cons var value) vars) + (setenv var value))))) + (when vars + (setq-default + exec-path (append (split-string (getenv "PATH") + (if IS-WINDOWS ";" ":")) + (list exec-directory)) + shell-file-name (or (getenv "SHELL") + shell-file-name)) + (nreverse vars))))) (defun doom-initialize (&optional force-p) "Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil). @@ -495,7 +500,7 @@ to least)." ;; `Info-directory-list', and `doom-disabled-packages'. A big ;; reduction in startup time. (pkg-autoloads-p - (unless noninteractive + (when doom-interactive-mode (doom-load-autoloads-file doom-package-autoload-file)))) (if (and core-autoloads-p (not force-p)) @@ -507,17 +512,16 @@ to least)." (require 'core-packages) (doom-initialize-packages))) - ;; Eagerly load these libraries because this module may be loaded in a session - ;; that hasn't been fully initialized (where autoloads files haven't been - ;; generated or `load-path' populated). + ;; 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 'noerror 'nomessage) (file-expand-wildcards (concat doom-core-dir "autoload/*.el"))) ;; Create all our core directories to quell file errors (dolist (dir (list doom-local-dir doom-etc-dir - doom-cache-dir - doom-elpa-dir)) + doom-cache-dir)) (unless (file-directory-p dir) (make-directory dir 'parents))) @@ -528,12 +532,13 @@ to least)." (unless (or (and core-autoloads-p pkg-autoloads-p) force-p - noninteractive) + (not doom-interactive-mode)) (unless core-autoloads-p - (message "Your Doom core autoloads file is missing")) + (warn "Your Doom core autoloads file is missing")) (unless pkg-autoloads-p - (message "Your package autoloads file is missing")) - (user-error "Run `bin/doom refresh' to generate them"))))) + (warn "Your package autoloads file is missing")) + (signal 'doom-autoload-error "Run `bin/doom refresh' to generate them"))) + t)) (defun doom-initialize-core () "Load Doom's core files for an interactive session." diff --git a/core/packages.el b/core/packages.el index 3cef86642..844e557d5 100644 --- a/core/packages.el +++ b/core/packages.el @@ -9,6 +9,8 @@ (package! all-the-icons) (package! hide-mode-line) (package! highlight-numbers) +;; Some early 26.x builds of Emacs do not have `display-line-numbers' yet, so +;; check for it instead of Emacs' version. (unless (locate-library "display-line-numbers") (package! nlinum) (package! nlinum-hl) @@ -18,7 +20,6 @@ ;; core-editor.el (package! better-jumper) -(package! command-log-mode) (package! dtrt-indent) (package! helpful) (package! ns-auto-titlebar :ignore (not IS-MAC)) @@ -26,11 +27,14 @@ (package! smartparens) (package! so-long :built-in 'prefer - :recipe (:repo "https://git.savannah.gnu.org/git/so-long.git")) + ;; REVIEW so-long is slated to be published to ELPA eventually, but until then + ;; I've created my own mirror for it because git.savannah.gnu.org runs on a + ;; potato. + :recipe (:host github :repo "hlissner/emacs-so-long")) (package! osx-clipboard :ignore (not IS-MAC)) (package! undo-tree) (package! ws-butler) -(package! xclip :ignore IS-LINUX) +(package! xclip :ignore (not IS-LINUX)) ;; core-projects.el (package! projectile) @@ -38,6 +42,3 @@ ;; core-keybinds.el (package! general) (package! which-key) - -;; autoload/debug.el -(package! esup) diff --git a/core/test/helpers.el b/core/test/helpers.el index 89b1401db..a4d9986eb 100644 --- a/core/test/helpers.el +++ b/core/test/helpers.el @@ -1,6 +1,64 @@ -;; -*- no-byte-compile: t; -*- +;; -*- lexical-binding: t; no-byte-compile: t; -*- ;;; core/test/helpers.el +(eval-and-compile + (setq doom-interactive-mode 'test) + (doom-initialize 'force) + (require 'buttercup) + (setq split-width-threshold 0 + split-height-threshold 0 + window-min-width 0 + window-min-height 0)) + +;; +;;; Buttercup extensions + +(buttercup-define-matcher :to-expand-into (form expected) + (cl-destructuring-bind (form expected) + (mapcar #'funcall (list form expected)) + (let ((expanded (macroexpand-1 form))) + (if (equal expanded expected) + (cons t (format "Expected `%S' to not expand to `%S'" + form expected)) + (cons nil (format "Expected `%S' to not expand to `%S', but got `%S' instead" + form expected expanded)))))) + +(buttercup-define-matcher :to-output (form &optional expected-output) + (let ((expected-output (and (functionp expected-output) + (funcall expected-output))) + output) + (with-current-buffer (get-buffer "*Messages*") + (let ((standard-output (current-buffer)) + (start (point))) + (let ((inhibit-message t)) + (funcall form)) + (setq output (buffer-substring-no-properties start (point-max))) + (with-silent-modifications (erase-buffer)))) + (cond ((null expected-output) + (if (string-empty-p output) + (cons nil (format "Expected output %S, but got none" + expected-output)) + (cons t (format "Expected no output, but got %S" + output)))) + ((not (equal expected-output output)) + (cons nil (format "Expected output %S, but got %S instead" + expected-output output))) + ((cons t (format "Expected to not get %S as output" + expected-output)))))) + +(buttercup-define-matcher :to-contain-items (items expected) + (cl-destructuring-bind (items expected) + (mapcar #'funcall (list items expected)) + (if-let (missing (cl-set-difference expected items)) + (cons nil (format "Expected list to contain %S, but it was missing %S" + expected missing)) + (cons t (format "Expected list to not contain %S, but it did: %S" + expected items))))) + + +;; +;;; Helper macros + (defmacro insert!! (&rest text) "Insert TEXT in buffer, then move cursor to last {0} marker." `(progn diff --git a/core/test/packages.el b/core/test/packages.el new file mode 100644 index 000000000..a5f245786 --- /dev/null +++ b/core/test/packages.el @@ -0,0 +1,4 @@ +;; -*- no-byte-compile: t; -*- +;;; core/test/packages.el + +(package! buttercup) diff --git a/core/test/test-autoload-buffers.el b/core/test/test-autoload-buffers.el index f80e2538c..5c44ce232 100644 --- a/core/test/test-autoload-buffers.el +++ b/core/test/test-autoload-buffers.el @@ -1,16 +1,12 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-autoload-buffers.el -(require 'core-projects) -(load! "autoload/buffers" doom-core-dir) - -;; (describe "core/autoload/buffers" :var (a b c d) - (before-all - (spy-on 'buffer-list :and-call-fake - (lambda (&optional _) - (cl-remove-if-not #'buffer-live-p (list a b c d))))) + + (require 'core-projects) + (load! "autoload/buffers" doom-core-dir) + (before-each (delete-other-windows) (setq a (switch-to-buffer (get-buffer-create "a")) @@ -25,7 +21,7 @@ (describe "buffer-list" (it "should only see four buffers" - (expect (doom-buffer-list) :to-have-same-items-as (list a b c d)))) + (expect (doom-buffer-list) :to-contain-items (list a b c d)))) (describe "project-buffer-list" :var (projectile-projects-cache-time projectile-projects-cache) @@ -34,7 +30,7 @@ (before-each (with-current-buffer a (setq default-directory doom-emacs-dir)) - (with-current-buffer b (setq default-directory doom-emacs-dir)) + (with-current-buffer b (setq default-directory doom-core-dir)) (with-current-buffer c (setq default-directory "/tmp/")) (with-current-buffer d (setq default-directory "~")) (projectile-mode +1)) @@ -44,7 +40,7 @@ (it "returns buffers in the same project" (with-current-buffer a (expect (doom-project-buffer-list) - :to-have-same-items-as (list a b)))) + :to-contain-items (list a b)))) (it "returns all buffers if not in a project" (with-current-buffer c @@ -53,7 +49,10 @@ (describe "fallback-buffer" (it "returns a live buffer" - (expect (buffer-live-p (doom-fallback-buffer))))) + (expect (buffer-live-p (doom-fallback-buffer)))) + + (it "returns the scratch buffer" + (expect (doom-fallback-buffer) :to-equal (get-buffer "*scratch*")))) (describe "real buffers" (before-each @@ -72,7 +71,7 @@ (describe "real-buffer-list" (it "returns only real buffers" - (expect (doom-real-buffer-list) :to-have-same-items-as (list a b))))) + (expect (doom-real-buffer-list) :to-contain-items (list a b))))) (describe "buffer/window management" (describe "buffer search methods" @@ -85,14 +84,17 @@ (it "can match buffers by regexp" (expect (doom-matching-buffers "^[ac]$") :to-have-same-items-as (list a c))) + (it "can match buffers by major-mode" (expect (doom-buffers-in-mode 'text-mode) :to-have-same-items-as (list b c))) + (it "can find all buried buffers" - (expect (doom-buried-buffers) - :to-have-same-items-as (list c d))) + (expect (doom-buried-buffers) :to-contain-items (list c d))) + (it "can find all visible buffers" (expect (doom-visible-buffers) :to-have-same-items-as (list a b))) + (it "can find all visible windows" (expect (doom-visible-windows) :to-have-same-items-as @@ -109,7 +111,7 @@ (expect (length (doom-visible-windows)) :to-be 1))) ;; TODO - (describe "kill-all-buffers") - (describe "kill-other-buffers") - (describe "kill-matching-buffers") - (describe "cleanup-session"))) + (xdescribe "kill-all-buffers") + (xdescribe "kill-other-buffers") + (xdescribe "kill-matching-buffers") + (xdescribe "cleanup-session"))) diff --git a/core/test/test-autoload-files.el b/core/test/test-autoload-files.el index 4251e6f0d..87a63edde 100644 --- a/core/test/test-autoload-files.el +++ b/core/test/test-autoload-files.el @@ -1,54 +1,152 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-autoload-files.el -;;; -(require 'core-projects) -(require 'projectile) (describe "core/autoload/files" - :var (src dest projectile-projects-cache-time projectile-projects-cache) - (before-each - (setq src (make-temp-file "test-src") - existing (make-temp-file "test-existing") - dest (expand-file-name "test-dest" temporary-file-directory)) - (quiet! (find-file-literally src)) - (spy-on 'y-or-n-p :and-return-value nil) - (projectile-mode +1)) - (after-each - (projectile-mode -1) - (switch-to-buffer (doom-fallback-buffer)) - (ignore-errors (delete-file src)) - (ignore-errors (delete-file existing)) - (ignore-errors (delete-file dest))) + (load! "autoload/files" doom-core-dir) - (describe "move-this-file" - (it "won't move to itself" - (expect (quiet! (doom/move-this-file src)) :to-throw)) - (it "will move to another file" - (expect (quiet! (doom/move-this-file dest t))) - (expect (file-exists-p dest)) - (expect (file-exists-p src) :to-be nil)) - (it "will prompt if overwriting a file" - (quiet! (doom/move-this-file existing)) - (expect 'y-or-n-p :to-have-been-called-times 1) - (expect (file-exists-p src)))) + (xdescribe "doom-glob") + (xdescribe "doom-path") + (xdescribe "doom-dir") + (xdescribe "doom-files-in") - (describe "copy-this-file" - (it "refuses to copy to itself" - (expect (quiet! (doom/copy-this-file src)) :to-throw)) - (it "copies to another file" - (expect (quiet! (doom/copy-this-file dest t))) - (expect (file-exists-p! src dest))) - (it "prompts if overwriting a file" - (quiet! (doom/copy-this-file existing)) - (expect 'y-or-n-p :to-have-been-called-times 1))) + (describe "library" + (describe "file-exists-p!" + (it "is a (quasi) drop-in replacement for `file-exists-p'" + (let ((default-directory doom-emacs-dir) + (init-file "init.el")) + (expect (file-exists-p "init.el")) + (expect (and (file-exists-p! "init.el") + (file-exists-p "init.el"))) + (expect (and (file-exists-p! init-file) + (file-exists-p init-file))) + (expect (and (file-exists-p! doom-emacs-dir) + (file-exists-p doom-emacs-dir))) + (expect (and (not (file-exists-p! "/cant/possibly/exist/please/dont/exist")) + (not (file-exists-p "/cant/possibly/exist/please/dont/exist")))))) - (describe "delete-this-file" - (it "fails gracefully on non-existent files" - (expect (quiet! (doom/delete-this-file dest)) :to-throw)) - (it "deletes existing files" - (quiet! (doom/delete-this-file existing t)) - (expect (file-exists-p existing) :to-be nil)) - (it "prompts to delete any existing file" - (quiet! (doom/delete-this-file existing)) - (expect 'y-or-n-p :to-have-been-called-times 1)))) + (it "returns the file path if it exists" + (expect (file-exists-p! "init.example.el" + doom-emacs-dir) + :to-equal (expand-file-name "init.example.el" doom-emacs-dir))) + + (it "understands compound statements" + (let ((default-directory doom-emacs-dir)) + (expect (file-exists-p! (and "init.el" "init.example.el"))) + (expect (file-exists-p! (or "doesnotexist" "init.example.el"))) + (expect (not (file-exists-p! (or "doesnotexist" "DOESNOTEXIST"))))) + (expect (file-exists-p! (and "init.el" "init.example.el") + doom-emacs-dir)) + (expect (file-exists-p! (and "init.el" "init.example.el") + doom-emacs-dir)) + (expect (file-exists-p! (or "doesnotexist" "init.example.el") + doom-emacs-dir)) + (expect (not (file-exists-p! (or "doesnotexist" "DOESNOTEXIST") + doom-emacs-dir)))) + + (it "understands nested compound statements" + (expect (file-exists-p! (and "init.el" "init.example.el" + (or "doesnotexist" "LICENSE")) + doom-emacs-dir)) + (expect (file-exists-p! (and "init.el" "init.example.el" + (and "LICENSE" "README.md" + (or "doesnotexist" + "early-init.el"))) + doom-emacs-dir)) + (expect (file-exists-p! (and "init.el" "init.example.el" + (or "edoesnotexist" "DOESNOTEXIST" + (and "idontexist" + "doanyofusexist?"))) + doom-emacs-dir) + :to-be nil)) + + (it "returns the last form if a compound file check succeeds" + (expect (file-exists-p! (and "init.el" "init.example.el" + (or "doesnotexist" "LICENSE")) + doom-emacs-dir) + :to-equal (expand-file-name "LICENSE" doom-emacs-dir)) + (expect (file-exists-p! (and "init.el" "init.example.el" + (or (or "doesnotexist" "DOESNOTEXIST") + "doanyofusreallyexist" + (or "cantexist" "LICENSE"))) + doom-emacs-dir) + :to-equal (expand-file-name "LICENSE" doom-emacs-dir))) + + (it "disregards the directory argument if given absolute path" + (expect (file-exists-p! "/tmp" "/directory/that/doesnt/exist")) + (expect (file-exists-p! doom-core-dir "/directory/that/doesnt/exist")) + (expect (file-exists-p! (and "/tmp" doom-core-dir) "/directory/that/doesnt/exist")) + (expect (file-exists-p! (or "/tmp" doom-core-dir) "/directory/that/doesnt/exist"))) + + (it "interpolates variables" + (let ((file-1 "init.el") + (file-2 "init.example.el") + (file-3 "LICENSE") + (file-404 "doesnotexistlikenoreally")) + (expect (file-exists-p! file-1 doom-emacs-dir)) + (expect (file-exists-p! (and file-1 file-2) doom-emacs-dir)) + (expect (file-exists-p! (and file-1 (or file-404 file-2)) doom-emacs-dir)) + (expect (file-exists-p! (or (and file-404 file-2) (and file-3 file-1)) + doom-emacs-dir)))) + + (it "interpolates forms" + (cl-letf (((symbol-function 'getfilename) + (lambda () "init.example.el"))) + (expect (file-exists-p! (and (or (if nil "init.el" "doesnotexist") + (getfilename)) + "LICENSE") + doom-emacs-dir) + :to-equal (expand-file-name "LICENSE" doom-emacs-dir)))))) + + (describe "interactive file operations" + :var (src dest projectile-projects-cache-time projectile-projects-cache) + + (require 'core-projects) + (require 'projectile) + + (before-each + (setq src (make-temp-file "test-src") + existing (make-temp-file "test-existing") + dest (expand-file-name "test-dest" temporary-file-directory)) + (quiet! (find-file-literally src)) + (spy-on 'y-or-n-p :and-return-value nil) + (projectile-mode +1)) + + (after-each + (projectile-mode -1) + (switch-to-buffer (doom-fallback-buffer)) + (ignore-errors (delete-file src)) + (ignore-errors (delete-file existing)) + (ignore-errors (delete-file dest))) + + (describe "move-this-file" + (it "won't move to itself" + (expect (quiet! (doom/move-this-file src)) :to-throw)) + (it "will move to another file" + (expect (quiet! (doom/move-this-file dest t))) + (expect (file-exists-p dest)) + (expect (file-exists-p src) :to-be nil)) + (it "will prompt if overwriting a file" + (quiet! (doom/move-this-file existing)) + (expect 'y-or-n-p :to-have-been-called-times 1) + (expect (file-exists-p src)))) + + (describe "copy-this-file" + (it "refuses to copy to itself" + (expect (quiet! (doom/copy-this-file src)) :to-throw)) + (it "copies to another file" + (expect (quiet! (doom/copy-this-file dest t))) + (expect (file-exists-p! src dest))) + (it "prompts if overwriting a file" + (quiet! (doom/copy-this-file existing)) + (expect 'y-or-n-p :to-have-been-called-times 1))) + + (describe "delete-this-file" + (it "fails gracefully on non-existent files" + (expect (quiet! (doom/delete-this-file dest)) :to-throw)) + (it "deletes existing files" + (quiet! (doom/delete-this-file existing t)) + (expect (file-exists-p existing) :to-be nil)) + (it "prompts to delete any existing file" + (quiet! (doom/delete-this-file existing)) + (expect 'y-or-n-p :to-have-been-called-times 1))))) diff --git a/core/test/test-autoload-message.el b/core/test/test-autoload-format.el similarity index 80% rename from core/test/test-autoload-message.el rename to core/test/test-autoload-format.el index a7d90c122..237d26324 100644 --- a/core/test/test-autoload-message.el +++ b/core/test/test-autoload-format.el @@ -1,11 +1,11 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-autoload-message.el -(describe "core/autoload/message" +(describe "core/autoload/format" (describe "format!" - :var (doom-message-backend) + :var (doom-format-backend) (before-all - (setq doom-message-backend 'ansi)) + (setq doom-format-backend 'ansi)) (it "should be a drop-in replacement for `format'" (expect (format! "Hello %s" "World") @@ -16,7 +16,7 @@ :to-equal "Hello World")) (it "supports text properties in interactive sessions" - (let ((doom-message-backend 'text-properties)) + (let ((doom-format-backend 'text-properties)) (expect (get-text-property 0 'face (format! (red "Hello %s") "World")) :to-equal (list :foreground (face-foreground 'term-color-red))))) @@ -35,4 +35,10 @@ (expect (format! (color 'red "Hello %s") "World") :to-equal (format! (red "Hello %s") "World")) (expect (format! (color (if nil 'red 'blue) "Hello %s") "World") - :to-equal (format! (blue "Hello %s") "World"))))) + :to-equal (format! (blue "Hello %s") "World")))) + + (xdescribe "insert!") + (xdescribe "print!") + (xdescribe "print-group!") + (xdescribe "error!") + (xdescribe "user-error!")) diff --git a/core/test/test-autoload-help.el b/core/test/test-autoload-help.el deleted file mode 100644 index 8df3a9a3a..000000000 --- a/core/test/test-autoload-help.el +++ /dev/null @@ -1,10 +0,0 @@ -;; -*- no-byte-compile: t; -*- -;;; core/test/test-autoload-help.el - -;; (load! "autoload/help" doom-core-dir) - -;; -;; (describe "core/autoload/help" -;; :var (a) -;; (before-each (setq a (switch-to-buffer (get-buffer-create "a")))) -;; (after-each (kill-buffer a))) diff --git a/core/test/test-autoload-package.el b/core/test/test-autoload-package.el index ac5c81534..02923304c 100644 --- a/core/test/test-autoload-package.el +++ b/core/test/test-autoload-package.el @@ -1,138 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-autoload-package.el +;;;###if nil -(describe "core/autoload/packages" - :var (package-alist - package-archive-contents - package-selected-packages - doom-packages - quelpa-cache - quelpa-initialized-p - doom-packages-dir - doom-core-packages - package-user-dir - quelpa-dir - pkg) - - (before-all - (fset 'pkg - (lambda (name version &optional reqs) - (package-desc-create - :name name :version version :reqs reqs - :dir (expand-file-name (format "%s/" name) package-user-dir)))) - (require 'package) - (require 'quelpa) - (setq doom-packages-dir (expand-file-name "packages/" (file-name-directory load-file-name)) - package-user-dir (expand-file-name "elpa" doom-packages-dir) - quelpa-dir (expand-file-name "quelpa" doom-packages-dir) - quelpa-initialized-p t - doom-core-packages nil) - (spy-on #'package--user-installed-p :and-call-fake (lambda (_p) t)) - (spy-on #'doom-initialize-packages :and-call-fake (lambda (&optional _))) - (spy-on #'package-refresh-contents :and-call-fake (lambda (&optional _))) - (spy-on #'quelpa-checkout :and-call-fake - (lambda (rcp _dir) - (when (eq (car rcp) 'doom-quelpa-dummy) - "20170405.1234")))) - - (after-all - (unload-feature 'package t) - (unload-feature 'quelpa t)) - - (before-each - (setq package-alist - `((doom-dummy ,(pkg 'doom-dummy '(20160405 1234))) - (doom-uptodate-dummy ,(pkg 'doom-uptodate-dummy '(20160605 1234))) - (doom-unwanted-dummy ,(pkg 'doom-unwanted-dummy '(20160605 1234))) - (doom-quelpa-dummy ,(pkg 'doom-quelpa-dummy '(20160405 1234))) - (doom-noquelpa-dummy ,(pkg 'doom-noquelpa-dummy '(20160405 1234)))) - package-archive-contents - `((doom-dummy ,(pkg 'doom-dummy '(20170405 1234))) - (doom-uptodate-dummy ,(pkg 'doom-uptodate-dummy '(20160605 1234)))) - doom-packages - '((doom-dummy) - (doom-uptodate-dummy) - (doom-missing-dummy) - (doom-noquelpa-dummy) - (doom-disabled-dummy :disable t) - (doom-private-dummy :modules ((:private))) - (doom-disabled-private-dummy :modules ((:private)) :disable t) - (doom-quelpa-dummy :recipe (doom-quelpa-dummy :fetcher github :repo "hlissner/does-not-exist"))) - quelpa-cache - '((doom-quelpa-dummy :fetcher github :repo "hlissner/does-not-exist") - (doom-noquelpa-dummy :fetcher github :repo "hlissner/does-not-exist-3") - (doom-new-quelpa-dummy :fetcher github :repo "hlissner/does-not-exist-2")) - package-selected-packages (mapcar #'car doom-packages))) - - (describe "package-backend" - (it "determines the correct backend of a package" - (expect (doom-package-backend 'doom-dummy) :to-be 'elpa) - (expect (doom-package-backend 'doom-quelpa-dummy) :to-be 'quelpa) - (expect (doom-package-backend 'org) :to-be 'emacs)) - (it "errors out if package isn't installed" - (expect (doom-package-backend 'xyz) :to-throw))) - - (describe "package-outdated-p (elpa)" - (it "detects outdated ELPA packages and returns both versions" - (expect (doom-package-outdated-p 'doom-dummy) - :to-equal '(doom-dummy (20160405 1234) (20170405 1234)))) - (it "ignores up-to-date ELPA packages" - (expect (doom-package-outdated-p 'doom-uptodate-dummy) :to-be nil)) - - (it "detects outdated QUELPA packages and returns both versions" - (expect (doom-package-outdated-p 'doom-quelpa-dummy) - :to-equal '(doom-quelpa-dummy (20160405 1234) (20170405 1234)))) - (it "ignores up-to-date QUELPA packages" - (expect (doom-package-outdated-p 'doom-uptodate-dummy) :to-be nil)) - - (it "returns nil if package isn't installed" - (expect (doom-package-outdated-p 'xyz) :to-be nil))) - - (describe "get-packages" - (before-all - ;; In addition to `package-installed-p', `doom-package-installed-p' does - ;; file existence checks which won't work here, so we simplify it - (spy-on #'doom-package-installed-p :and-call-fake #'package-installed-p)) - - (it "returns all packages" - (expect (mapcar #'car (doom-find-packages)) - :to-have-same-items-as - (mapcar #'car doom-packages))) - (it "returns only disabled packages" - (expect (mapcar #'car (doom-find-packages :disabled t)) - :to-have-same-items-as - '(doom-disabled-dummy doom-disabled-private-dummy))) - (it "returns only non-disabled packages" - (expect (mapcar #'car (doom-find-packages :disabled nil)) - :to-have-same-items-as - '(doom-dummy doom-uptodate-dummy doom-quelpa-dummy doom-missing-dummy doom-noquelpa-dummy doom-private-dummy))) - (it "returns only installed packages" - (expect (mapcar #'car (doom-find-packages :disabled nil :installed t)) - :to-have-same-items-as - '(doom-dummy doom-uptodate-dummy doom-quelpa-dummy doom-noquelpa-dummy))) - (it "returns only non-installed packages" - (expect (mapcar #'car (doom-find-packages :disabled nil :installed nil)) - :to-have-same-items-as - '(doom-missing-dummy doom-private-dummy))) - (it "returns only private packages" - (expect (mapcar #'car (doom-find-packages :private t)) - :to-have-same-items-as - '(doom-private-dummy doom-disabled-private-dummy))) - (it "returns only disabled and private packages" - (expect (mapcar #'car (doom-find-packages :disabled t :private t)) - :to-have-same-items-as - '(doom-disabled-private-dummy)))) - - (describe "get-orphaned-packages" - (it "returns orphaned packages" - (expect (doom-get-orphaned-packages) :to-contain 'doom-unwanted-dummy)) - (it "returns packages that have changed backends" - (expect (doom-get-orphaned-packages) :to-contain 'doom-noquelpa-dummy))) - - (describe "get-missing-packages" - (it "returns packages that haven't been installed" - (expect (mapcar #'car (doom-get-missing-packages)) - :to-contain 'doom-missing-dummy)) - (it "returns packages that have changed backends" - (expect (mapcar #'car (doom-get-missing-packages)) - :to-contain 'doom-noquelpa-dummy)))) +(xdescribe "core/autoload/packages") diff --git a/core/test/test-core-keybinds.el b/core/test/test-core-keybinds.el index 2db47792e..5482205b2 100644 --- a/core/test/test-core-keybinds.el +++ b/core/test/test-core-keybinds.el @@ -1,40 +1,34 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-core-keybinds.el -(require 'core-keybinds) - -(buttercup-define-matcher :to-expand-into (src result) - (let ((src (funcall src)) - (result (funcall result))) - (or (equal (macroexpand-1 src) result) - (error "'%s' expanded into '%s' instead of '%s'" - src (macroexpand-1 src) result)))) - (describe "core/keybinds" + (require 'core-keybinds) + (describe "map!" - :var (doom--map-evil-p doom-map-states) + :var (doom--map-evil-p states-alist) (before-each (setq doom--map-evil-p t - doom-map-states '((:n . normal) - (:v . visual) - (:i . insert) - (:e . emacs) - (:o . operator) - (:m . motion) - (:r . replace)))) + states-alist '((:n . normal) + (:v . visual) + (:i . insert) + (:e . emacs) + (:o . operator) + (:m . motion) + (:r . replace)))) (describe "Single keybinds" (it "binds a global key" - (expect '(map! "C-." #'a) :to-expand-into '(general-define-key "C-." #'a))) + (expect '(map! "C-." #'a) + :to-expand-into '(general-define-key "C-." #'a))) (it "binds a key in one evil state" - (dolist (state doom-map-states) + (dolist (state states-alist) (expect `(map! ,(car state) "C-." #'a) :to-expand-into `(general-define-key :states ',(cdr state) "C-." #'a)))) (it "binds a key in multiple evil states" - (expect `(map! :nvi "C-." #'a) + (expect '(map! :nvi "C-." #'a) :to-expand-into '(progn (general-define-key :states 'insert "C-." #'a) (general-define-key :states 'visual "C-." #'a) @@ -54,7 +48,7 @@ '(general-define-key "C-." #'a "C-," #'b "C-/" #'c))) (it "binds multiple keybinds in an evil state and preserve order" - (dolist (state doom-map-states) + (dolist (state states-alist) (expect `(map! ,(car state) "a" #'a ,(car state) "b" #'b ,(car state) "c" #'c) diff --git a/core/test/test-core-lib.el b/core/test/test-core-lib.el index 608fdb66e..06a35649e 100644 --- a/core/test/test-core-lib.el +++ b/core/test/test-core-lib.el @@ -1,28 +1,130 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-core-lib.el -(require 'core-lib) +(describe "core-lib" + (before-all + (require 'core-lib)) -(describe "core/lib" ;; --- Helpers ---------------------------- (describe "doom-unquote" (it "unquotes a quoted form" (expect (doom-unquote '(quote hello)) :to-be 'hello)) - (it "unquotes nested-quoted forms" + (it "unquotes nested quoted forms" (expect (doom-unquote '(quote (quote (a b c)))) :to-equal '(a b c))) (it "unquotes function-quoted forms" (expect (doom-unquote '(function a)) :to-be 'a)) (it "does nothing to unquoted forms" - (expect (doom-unquote 'hello) :to-be 'hello))) + (expect (doom-unquote 'hello) :to-be 'hello) + (expect (doom-unquote 5) :to-be 5) + (expect (doom-unquote t) :to-be t))) (describe "doom-enlist" + (it "returns nil if given nil" + (expect (doom-enlist nil) :to-be nil)) (it "creates a list out of non-lists" (expect (doom-enlist 'a) :to-equal '(a))) - (it "does nothing to lists" + (it "returns lists as-is" (expect (doom-enlist '(a)) :to-equal '(a)))) + (describe "doom-keyword-intern" + (it "returns a keyword" + (expect (doom-keyword-intern "test") :to-equal :test)) + (it "errors if given anything but a string" + (expect (doom-keyword-intern t) :to-throw 'wrong-type-argument))) + + (describe "doom-keyword-name" + (it "returns the string name of a keyword" + (expect (doom-keyword-name :test) :to-equal "test")) + (it "errors if given anything but a keyword" + (expect (doom-keyword-name "test") :to-throw 'wrong-type-argument))) + + (describe "doom-partial" + (it "returns a closure" + (expect (functionp (doom-partial #'+ 1)))) + (it "returns a partial closure" + (expect (funcall (doom-partial #'+ 1) 2) :to-be 3))) + + (describe "doom-rpartial" + (it "returns a closure" + (expect (functionp (doom-rpartial #'+ 1)))) + (it "returns a partial closure with right-aligned arguments" + (expect (funcall (doom-rpartial #'/ 2) 10) :to-be 5))) + + + ;; --- Sugars ----------------------------- + (describe "lambda!" + (it "returns an interactive function" + (expect (commandp (lambda!))) + (expect (funcall (lambda! 5)) :to-equal 5))) + + (describe "lambda!!" + (it "returns an interactive function with a prefix argument" + (expect (commandp (lambda! #'ignore t))) + (expect (funcall (lambda!! (lambda (arg) + (interactive "P") + arg) + 5)) + :to-equal 5))) + + (describe "file!" + (it "returns the executing file" + (expect (eval-and-compile (file!)) + :to-equal (expand-file-name "test/test-core-lib.el" + doom-core-dir)))) + + (describe "dir!" + (it "returns the executing directory" + (expect (eval-and-compile (dir!)) + :to-equal (expand-file-name "test" doom-core-dir)))) + + (describe "pushnew!" + (it "pushes values onto a list symbol, in order" + (let ((a '(1 2 3))) + (expect (pushnew! a 9 8 7) + :to-equal '(7 8 9 1 2 3)))) + (it "only adds values that aren't already in the list" + (let ((a '(1 symbol 3.14 "test"))) + (expect (pushnew! a "test" 'symbol 3.14 1) + :to-equal '(1 symbol 3.14 "test"))))) + + (describe "prependq!" + (it "prepends a list to a list symbol" + (let ((list '(a b c))) + (expect (prependq! list '(d e f)) + :to-equal '(d e f a b c))))) + + (describe "append!" + (it "appends a list to a list symbol" + (let ((list '(a b c))) + (expect (appendq! list '(d e f)) + :to-equal '(a b c d e f))))) + + (describe "nconcq!" + (it "nconc's a list to a list symbol" + (let ((list '(a b c))) + (expect (nconcq! list '(d e f)) + :to-equal '(a b c d e f))))) + + (describe "delq!" + (it "delete's a symbol from a list" + (let ((list '(a b c))) + (delq! 'b list) + (expect list :to-equal '(a c)))) + (it "delete's an element from an alist by key" + (let ((alist '((a 1) (b 2) (c 3)))) + (delq! 'b alist 'assq) + (expect alist :to-equal '((a 1) (c 3)))))) + + (describe "delete!" + (it "delete's a string from a list" + (let ((list '("a" "b" "c"))) + (delete! "b" list) + (expect list :to-equal '("a" "c")))) + (it "delete's an element from an alist by key" + (let ((alist '(("a" 1) ("b" 2) ("c" 3)))) + (delete! (assoc "b" alist) alist) + (expect alist :to-equal '(("a" 1) ("c" 3)))))) - ;; --- Macros ----------------------------- (describe "hooks" (describe "add-hook!" :var (fake-mode-hook other-mode-hook some-mode-hook) @@ -31,15 +133,22 @@ other-mode-hook nil some-mode-hook '(first-hook second-hook))) + (it "resolves quoted hooks literally" + (expect '(add-hook! 'fake-mode-hook #'ignore) :to-expand-into + `(add-hook 'fake-mode-hook #'ignore nil nil))) + (it "resolves unquoted modes to their hook variables" + (expect '(add-hook! fake-mode #'ignore) :to-expand-into + `(add-hook 'fake-mode-hook #'ignore nil nil))) + (it "adds one-to-one hook" (add-hook! fake-mode #'hook-2) (add-hook! 'fake-mode-hook #'hook-1) (expect fake-mode-hook :to-equal '(hook-1 hook-2 first-hook))) - (it "adds many-to-one hook" + (it "adds one-to-many hook" (add-hook! (fake-mode other-mode some-mode) #'hook-2) (add-hook! '(fake-mode-hook other-mode-hook some-mode-hook) #'hook-1) - (add-hook! :append (fake-mode other-mode some-mode) #'last-hook) + (add-hook! (fake-mode other-mode some-mode) :append #'last-hook) (expect fake-mode-hook :to-equal '(hook-1 hook-2 first-hook last-hook)) (expect other-mode-hook :to-equal '(hook-1 hook-2 last-hook)) (expect some-mode-hook :to-equal '(hook-1 hook-2 first-hook second-hook last-hook))) @@ -47,7 +156,7 @@ (it "adds many-to-many hooks and preserve provided order" (add-hook! (fake-mode other-mode some-mode) #'(hook-3 hook-4)) (add-hook! '(fake-mode-hook other-mode-hook some-mode-hook) #'(hook-1 hook-2)) - (add-hook! :append '(fake-mode-hook other-mode-hook some-mode-hook) #'(last-hook-1 last-hook-2)) + (add-hook! '(fake-mode-hook other-mode-hook some-mode-hook) :append #'(last-hook-1 last-hook-2)) (expect fake-mode-hook :to-equal '(hook-1 hook-2 hook-3 hook-4 first-hook last-hook-1 last-hook-2)) (expect other-mode-hook :to-equal '(hook-1 hook-2 hook-3 hook-4 last-hook-1 last-hook-2)) (expect some-mode-hook :to-equal '(hook-1 hook-2 hook-3 hook-4 first-hook second-hook last-hook-1 last-hook-2))) @@ -55,10 +164,18 @@ (it "adds implicit lambda to one hook" (add-hook! fake-mode (progn)) (add-hook! 'other-mode-hook (ignore)) - (add-hook! :append 'some-mode-hook (ignore)) + (add-hook! 'some-mode-hook :append (ignore)) (expect (caar fake-mode-hook) :to-be 'lambda) (expect (caar other-mode-hook) :to-be 'lambda) - (expect (caar (last other-mode-hook)) :to-be 'lambda))) + (expect (caar (last other-mode-hook)) :to-be 'lambda)) + + (it "handles inline defuns as hook symbols" + (add-hook! fake-mode (defun hook-a ())) + (add-hook! 'other-mode-hook + (defun hook-b ()) + (defun hook-c ())) + (expect (car fake-mode-hook) :to-be 'hook-a) + (expect other-mode-hook :to-equal '(hook-b hook-c)))) (describe "remove-hook!" :var (fake-mode-hook) @@ -71,22 +188,92 @@ (it "removes multiple hooks" (remove-hook! fake-mode #'(first-hook third-hook)) (remove-hook! 'fake-mode-hook #'(second-hook fourth-hook)) - (expect fake-mode-hook :to-be nil)))) + (expect fake-mode-hook :to-be nil))) - (describe "add-transient-hook!" - (it "adds a transient function to hooks" - (let (hooks value) - (add-transient-hook! 'hooks (setq value t)) - (run-hooks 'hooks) - (expect value) - (expect hooks :to-be nil))) - (it "advises a function with a transient advisor" - (let (value) - (add-transient-hook! #'ignore (setq value (not value))) - (ignore t) - (expect value) - ;; repeat to ensure it was only run once - (ignore t) - (expect value)))) + (describe "add-transient-hook!" + (it "adds a transient function to hooks" + (let (hooks value) + (add-transient-hook! 'hooks (setq value t)) + (run-hooks 'hooks) + (expect value) + (expect hooks :to-be nil))) + (it "advises a function with a transient advisor" + (let (value) + (add-transient-hook! #'ignore (setq value (not value))) + (ignore t) + (expect value) + ;; repeat to ensure it was only run once + (ignore t) + (expect value)))) - (xdescribe "associate!")) ; TODO + (describe "(un)setq-hook!" + :var (fake-hook x y z) + (before-each + (setq x 10 y 20 z 30)) + + (it "sets variables buffer-locally" + (setq-hook! 'fake-hook x 1) + (with-temp-buffer + (run-hooks 'fake-hook) + (expect (local-variable-p 'x)) + (expect (= x 1))) + (expect (= x 10))) + + (it "overwrites earlier hooks" + (setq-hook! 'fake-hook x 1 y 0) + (setq-hook! 'fake-hook x 5 y -1) + (with-temp-buffer + (run-hooks 'fake-hook) + (expect (= x 5)) + (expect (= y -1)))) + + (it "unset setq hooks" + (setq-hook! 'fake-hook x 1 y 0) + (unsetq-hook! 'fake-hook y) + (with-temp-buffer + (run-hooks 'fake-hook) + (expect (local-variable-p 'x)) + (expect (= x 1)) + (expect (not (local-variable-p 'y))) + (expect (= y 20)))))) + + (describe "load!" + (before-each + (spy-on 'load :and-return-value t)) + + (it "loads a file relative to the current directory" + (load! "path") + (expect 'load :to-have-been-called) + (expect 'load :to-have-been-called-with (expand-file-name "path" (eval-when-compile (dir!))) nil t)) + + (it "loads a file relative to a specified directory" + (load! "path" doom-etc-dir) + (expect 'load :to-have-been-called-with (expand-file-name "path" doom-etc-dir) nil t))) + + (describe "quiet!" + (it "suppresses output from message" + (expect (message "hello world") :to-output "hello world\n") + (expect (message "hello world") :to-output) + (let (doom-interactive-mode) + (expect (quiet! (message "hello world")) :not :to-output)) + (let ((doom-interactive-mode t)) + (expect (quiet! inhibit-message)) + (expect (quiet! save-silently)))) + + (it "suppresses load messages from `load' & `load-file'" + (let ((tmpfile (make-temp-file "test" nil ".el"))) + (with-temp-file tmpfile) + (let (doom-interactive-mode) + (expect (load-file tmpfile) :to-output (format "Loading %s (source)...\n" tmpfile)) + (expect (quiet! (load-file tmpfile)) :not :to-output)) + (delete-file tmpfile))) + + (it "won't suppress output in debug mode" + (let ((doom-debug-mode t) + (tmpfile (make-temp-file "test" nil ".el"))) + (dolist (doom-interactive-mode (list t nil)) + (expect (quiet! (message "hello world")) + :to-output "hello world\n") + (with-temp-file tmpfile) + (expect (quiet! (load-file tmpfile)) + :to-output (format "Loading %s (source)...\n" tmpfile))))))) diff --git a/core/test/test-core-modules.el b/core/test/test-core-modules.el index a786ad2ea..9263f305a 100644 --- a/core/test/test-core-modules.el +++ b/core/test/test-core-modules.el @@ -1,6 +1,7 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-core-modules.el +;;;###if nil ;; (require 'core-modules) -(describe "core-modules") +(xdescribe "core-modules") diff --git a/core/test/test-core-packages.el b/core/test/test-core-packages.el index ff3104b76..d4434ad9f 100644 --- a/core/test/test-core-packages.el +++ b/core/test/test-core-packages.el @@ -1,4 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-core-packages.el +;;;###if nil -(describe "core-packages") +(xdescribe "core-packages") diff --git a/core/test/test-core-projects.el b/core/test/test-core-projects.el index 3dbd953d5..8febc5cdd 100644 --- a/core/test/test-core-projects.el +++ b/core/test/test-core-projects.el @@ -1,12 +1,17 @@ ;; -*- no-byte-compile: t; -*- -;;; ../core/test/test-core-projects.el - -(require 'core-projects) -(require 'projectile) +;;; core/test/test-core-projects.el (describe "core/projects" - (before-each (projectile-mode +1)) - (after-each (projectile-mode -1)) + :var (projectile-enable-caching) + + (require 'core-projects) + (require 'projectile) + + (before-each + (setq projectile-enable-caching nil) + (projectile-mode +1)) + (after-each + (projectile-mode -1)) (describe "project-p" (it "Should detect when in a valid project" diff --git a/core/test/test-core-ui.el b/core/test/test-core-ui.el index 0ace0bc73..5079aa996 100644 --- a/core/test/test-core-ui.el +++ b/core/test/test-core-ui.el @@ -1,17 +1,106 @@ ;; -*- no-byte-compile: t; -*- ;;; ../core/test/test-core-ui.el -(require 'core-ui) - (describe "core/ui" - (describe "doom|protect-fallback-buffer" - :var (kill-buffer-query-functions a b) + (before-all + (with-demoted-errors "Import error: %s" + (require 'core-ui))) + + (describe "doom-protect-fallback-buffer-h" + :var (kill-buffer-query-functions) (before-all - (setq kill-buffer-query-functions '(doom|protect-fallback-buffer))) + (setq kill-buffer-query-functions '(doom-protect-fallback-buffer-h))) (it "should kill other buffers" (expect (kill-buffer (get-buffer-create "a")))) (it "shouldn't kill the fallback buffer" - (expect (not (kill-buffer (doom-fallback-buffer))))))) + (expect (not (kill-buffer (doom-fallback-buffer)))))) + (describe "custom hooks" + (describe "switch hooks" + :var (before-hook after-hook a b) + (before-each + (setq a (switch-to-buffer (get-buffer-create "a")) + b (get-buffer-create "b")) + (spy-on 'hook) + (add-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h) + (add-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h) + (dolist (fn '(switch-to-buffer display-buffer)) + (advice-add fn :around #'doom-run-switch-buffer-hooks-a))) + (after-each + (remove-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h) + (remove-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h) + (dolist (fn '(switch-to-buffer display-buffer)) + (advice-remove fn #'doom-run-switch-buffer-hooks-a)) + (kill-buffer a) + (kill-buffer b)) + + (describe "switch-buffer" + :var (doom-switch-buffer-hook) + (before-each + (setq doom-switch-buffer-hook '(hook))) + (after-each + (setq doom-switch-buffer-hook nil)) + + (it "should trigger when switching buffers" + (switch-to-buffer b) + (switch-to-buffer a) + (switch-to-buffer b) + (expect 'hook :to-have-been-called-times 3)) + + (it "should trigger only once on the same buffer" + (switch-to-buffer b) + (switch-to-buffer b) + (switch-to-buffer a) + (expect 'hook :to-have-been-called-times 2))) + + + (describe "switch-window" + :var (doom-switch-window-hook x y) + (before-each + (delete-other-windows) + (setq x (get-buffer-window a) + y (save-selected-window (split-window))) + (with-selected-window y + (switch-to-buffer b)) + (select-window x) + (spy-calls-reset 'hook) + (setq doom-switch-window-hook '(hook))) + + (it "should trigger when switching windows" + (select-window y) + (select-window x) + (select-window y) + (expect 'hook :to-have-been-called-times 3)) + + (it "should trigger only once on the same window" + (select-window y) + (select-window y) + (select-window x) + (expect 'hook :to-have-been-called-times 2))) + + + (xdescribe "switch-frame" + :var (doom-switch-frame-hook x y) + (before-each + (delete-other-windows) + (setq x (get-buffer-window a) + y (save-selected-window (split-window))) + (with-selected-window y + (switch-to-buffer b)) + (select-window x) + (spy-calls-reset 'hook) + (setq doom-switch-window-hook '(hook))) + + (it "should trigger when switching windows" + (select-window y) + (select-window x) + (select-window y) + (expect 'hook :to-have-been-called-times 3)) + + (it "should trigger only once on the same window" + (select-window y) + (select-window y) + (select-window x) + (expect 'hook :to-have-been-called-times 2)))))) diff --git a/core/test/test-core.el b/core/test/test-core.el index 07361463a..22892b4fd 100644 --- a/core/test/test-core.el +++ b/core/test/test-core.el @@ -2,118 +2,116 @@ ;;; core/test/test-core.el (describe "core" - (xdescribe "initialize" - :var (doom-init-p doom-init-modules-p doom-private-dir) - (before-each - (setq doom-init-p nil - doom-init-modules-p nil - doom-private-dir doom-emacs-dir) + :var (doom-interactive-mode) + (before-each + (setq doom-interactive-mode nil)) - (spy-on 'require) - (spy-on 'load) - (spy-on 'doom-reload-doom-autoloads) - (spy-on 'doom-reload-package-autoloads) - (spy-on 'doom-initialize-autoloads) - (spy-on 'doom-ensure-core-directories) - (spy-on 'doom-ensure-core-packages) - (spy-on 'doom-ensure-packages-initialized) - (spy-on 'doom-ensure-same-emacs-version-p)) - - (describe "in interactive session" - :var (noninteractive) - (before-each (setq noninteractive t)) - - (it "initializes once, unless forced") - (it "does not initialize on consecutive invokations") - (it "loads all core libraries" ) - (it "loads autoloads file" ) - (it "does not load autoloads file if forced" ) - (it "regenerates missing autoloads" )) - - (describe "in non-interactive session" - :var (noninteractive) - (before-each (setq noninteractive nil)) - - (it "initializes once, unless forced") - (it "does not initialize on consecutive invokations") - (it "does not load all core libraries" ) - (it "loads autoloads file" ) - (it "does not load autoloads file if forced" ) - (it "does not regenerate missing autoloads" ))) - - (xdescribe "initialize-packages" - (before-each (spy-on 'quelpa-setup-p)) - - (it "initializes package.el once, unless forced" ) - (it "initializes quelpa once, unless forced" ) - (it "initializes doom-packages once, unless forced" )) - - (xdescribe "initialize-modules" - (it "loads private init.el once, unless forced" )) - - (xdescribe "initialize-autoloads" - (it "loads autoloads file" ) - (it "ignores autoloads file if cleared" )) - - (describe "custom hooks" - (describe "switch hooks" - :var (before-hook after-hook a b) + (describe "initialization" + (describe "doom-initialize" + :var (doom-init-p) (before-each - (setq a (switch-to-buffer (get-buffer-create "a")) - b (get-buffer-create "b")) - (spy-on 'hook) - (add-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h) - (add-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h) - (dolist (fn '(switch-to-buffer display-buffer)) - (advice-add fn :around #'doom-run-switch-buffer-hooks-a))) - (after-each - (remove-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h) - (remove-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h) - (dolist (fn '(switch-to-buffer display-buffer)) - (advice-remove fn #'doom-run-switch-buffer-hooks-a)) - (kill-buffer a) - (kill-buffer b)) + (setq doom-init-p nil)) - (describe "switch-buffer" - :var (doom-switch-buffer-hook) + (it "initializes once" + (expect (doom-initialize)) + (expect (not (doom-initialize))) + (expect (not (doom-initialize))) + (expect doom-init-p)) + + (it "initializes multiple times, if forced" + (expect (doom-initialize)) + (expect (not (doom-initialize))) + (expect (doom-initialize 'force))) + + (describe "package initialization" (before-each - (setq doom-switch-buffer-hook '(hook))) - (after-each - (setq doom-switch-buffer-hook nil)) + (spy-on 'doom-initialize-packages :and-return-value t)) - (it "should trigger when switching buffers" - (switch-to-buffer b) - (switch-to-buffer a) - (switch-to-buffer b) - (expect 'hook :to-have-been-called-times 3)) + (it "initializes packages if core autoload file doesn't exist" + (let ((doom-autoload-file "doesnotexist")) + (doom-initialize)) + (expect 'doom-initialize-packages :to-have-been-called)) - (it "should trigger only once on the same buffer" - (switch-to-buffer b) - (switch-to-buffer b) - (switch-to-buffer a) - (expect 'hook :to-have-been-called-times 2))) + (it "doesn't initialize packages if core autoload file was loaded" + (let ((doom-interactive-mode t)) + (spy-on 'doom-load-autoloads-file :and-return-value t) + (doom-initialize) + (expect 'doom-load-autoloads-file :to-have-been-called-with doom-package-autoload-file) + (expect 'doom-initialize-packages :to-have-been-called))) + (it "initializes packages when forced" + (doom-initialize 'force) + (expect 'doom-initialize-packages :to-have-been-called))) - (describe "switch-window" - :var (doom-switch-window-hook x y) + (describe "autoloads files" (before-each - (delete-other-windows) - (setq x (get-buffer-window a) - y (save-selected-window (split-window))) - (with-selected-window y - (switch-to-buffer b)) - (select-window x) - (spy-calls-reset 'hook) - (setq doom-switch-window-hook '(hook))) + (spy-on 'doom-load-autoloads-file) + (spy-on 'warn :and-return-value t)) - (it "should trigger when switching windows" - (select-window y) - (select-window x) - (select-window y) - (expect 'hook :to-have-been-called-times 3)) + (it "loads autoloads file" + (let ((doom-interactive-mode t)) + (ignore-errors (doom-initialize))) + (expect 'doom-load-autoloads-file + :to-have-been-called-with doom-autoload-file) + (expect 'doom-load-autoloads-file + :to-have-been-called-with doom-package-autoload-file)) - (it "should trigger only once on the same window" - (select-window y) - (select-window y) - (select-window x) - (expect 'hook :to-have-been-called-times 2)))))) + (it "does not load package autoloads file if noninteractive" + (doom-initialize) + (expect 'doom-load-autoloads-file + :to-have-been-called-with doom-autoload-file) + (expect 'doom-load-autoloads-file + :not :to-have-been-called-with doom-package-autoload-file)) + + (it "throws doom-autoload-error in interactive session where autoload files don't exist" + (let ((doom-interactive-mode t) + (doom-autoload-file "doesnotexist") + (doom-package-autoload-file "doesnotexist")) + (expect (doom-initialize) :to-throw 'doom-autoload-error))))) + + (describe "doom-initialize-core" + (before-each + (spy-on 'require)) + + (it "loads all doom core libraries" + (doom-initialize-core) + (expect 'require :to-have-been-called-with 'core-keybinds) + (expect 'require :to-have-been-called-with 'core-ui) + (expect 'require :to-have-been-called-with 'core-projects) + (expect 'require :to-have-been-called-with 'core-editor)))) + + (describe "doom-load-autoloads-file" + (before-each + (spy-on 'load :and-return-value t)) + + (it "loads the autoloads file" + (doom-load-autoloads-file doom-autoload-file) + (expect 'load :to-have-been-called-with (file-name-sans-extension doom-autoload-file) + 'noerror 'nomessage))) + + (describe "doom-load-envvars-file" + :var (envvarfile process-environment) + (before-each + (setq process-environment (copy-sequence process-environment)) + (with-temp-file doom-env-file + (insert "\n\n\nA=1\nB=2\nC=3\n"))) + (after-each + (delete-file doom-env-file)) + + (it "throws a file-error if file doesn't exist" + (expect (doom-load-envvars-file "/tmp/envvardoesnotexist") + :to-throw 'file-error)) + + (it "to fail silently if NOERROR is non-nil" + (expect (doom-load-envvars-file "/tmp/envvardoesnotexist" 'noerror) + :not :to-throw)) + + (it "loads a well-formed envvar file" + (expect (getenv "A") :not :to-be-truthy) + (expect (doom-load-envvars-file doom-env-file) + :to-equal '(("A" . "1") ("B" . "2") ("C" . "3"))) + (expect (getenv "A") :to-equal "1")) + + (it "fails on an invalid envvar file" + (with-temp-file doom-env-file (insert "A=1\nB=2\nC=3\n")) + (expect (doom-load-envvars-file doom-env-file) :to-throw)))) diff --git a/docs/api.org b/docs/api.org index 7a55d6d26..0e08525c1 100644 --- a/docs/api.org +++ b/docs/api.org @@ -1,9 +1,40 @@ #+TITLE: API Demos +#+PROPERTY: header-args:elisp :results pp This appendix serves as a reference on how to use Doom Emacs' standard library. It is integrated into Helpful, in Doom. -* add-hook! +* Table of Contents :TOC_3: +- [[#examples-for-dooms-core-library][Examples for Doom's core library]] + - [[#core-lib][core-lib]] + - [[#add-hook][add-hook!]] + - [[#add-transient-hook][add-transient-hook!]] + - [[#after][after!]] + - [[#custom-set-faces][custom-set-faces!]] + - [[#custom-theme-set-faces][custom-theme-set-faces!]] + - [[#defer-feature][defer-feature!]] + - [[#defer-until][defer-until!]] + - [[#disable-packages][disable-packages!]] + - [[#doom][doom!]] + - [[#file-exists-p][file-exists-p!]] + - [[#lambda][lambda!]] + - [[#lambda-1][lambda!!]] + - [[#load][load!]] + - [[#map][map!]] + - [[#package][package!]] + - [[#pushnew][pushnew!]] + - [[#quiet][quiet!]] + - [[#remove-hook][remove-hook!]] + - [[#setq-hook][setq-hook!]] + - [[#unsetq-hook][unsetq-hook!]] + - [[#use-package][use-package!]] +- [[#interesting-snippets][Interesting snippets]] + - [[#persist-emacs-initial-frame-size-across-sessions][Persist Emacs' initial frame size across sessions]] + - [[#persist-emacs-initial-frame-position-dimensions-andor-full-screen-state-across-sessions][Persist Emacs' initial frame position, dimensions and/or full-screen state across sessions]] + +* Examples for Doom's core library +** core-lib +*** add-hook! #+BEGIN_SRC elisp :eval no ;; With only one hook and one function, this is identical to `add-hook'. In that ;; case, use that instead. @@ -31,33 +62,35 @@ It is integrated into Helpful, in Doom. ...)) #+END_SRC -* custom-theme-set-faces! +*** TODO add-transient-hook! +*** after! #+BEGIN_SRC elisp :eval no -(custom-theme-set-faces! 'doom-one-theme - '(outline-1 :weight normal) - '(outline-2 :weight normal) - '(outline-3 :weight normal) - '(outline-4 :weight normal) - '(outline-5 :weight normal) - '(outline-6 :weight normal) - '(default :background "red" :weight bold) - '(region :background "red" :weight bold)) +;;; `after!' will take: -(custom-theme-set-faces! '(doom-one-theme doom-one-light-theme) - '((outline-1 outline-2 outline-3 outline-4 outline-5 outline-6) - :weight normal) - '((default region) - :background "red" :weight bold)) +;; An unquoted package symbol (the name of a package) +(after! helm ...) -(let ((red-bg-faces '(default region))) - (custom-theme-set-faces! '(doom-one-theme doom-one-light-theme) - `(,(cl-loop for i from 0 to 6 collect (intern (format "outline-%d" i))) - :weight normal) - `(,red-bg-faces - :background "red" :weight bold))) +;; An unquoted list of package symbols (i.e. BODY is evaluated once both magit +;; and git-gutter have loaded) +(after! (magit git-gutter) ...) + +;; An unquoted, nested list of compound package lists, using any combination of +;; :or/:any and :and/:all +(after! (:or package-a package-b ...) ...) +(after! (:and package-a package-b ...) ...) +(after! (:and package-a (:or package-b package-c) ...) ...) +;; (Without :or/:any/:and/:all, :and/:all are implied.) + +;; A common mistake is to pass it the names of major or minor modes, e.g. +(after! rustic-mode ...) +(after! python-mode ...) +;; But the code in them will never run! rustic-mode is in the `rustic' package +;; and python-mode is in the `python' package. This is what you want: +(after! rustic ...) +(after! python ...) #+END_SRC -* custom-set-faces! +*** custom-set-faces! #+BEGIN_SRC elisp :eval no (custom-set-faces! '(outline-1 :weight normal) @@ -81,9 +114,59 @@ It is integrated into Helpful, in Doom. :weight normal) `(,red-bg-faces :background "red" :weight bold))) + +;; If you want to make use of the `doom-themes' package API (e.g. `doom-color', +;; `doom-lighten', `doom-darken', etc.), you must use `custom-set-faces!' +;; *after* the theme has been loaded. e.g. +(load-theme 'doom-one t) +(custom-set-faces! + `(outline-1 :foreground ,(doom-color 'red)) + `(outline-2 :background ,(doom-color 'blue))) #+END_SRC -* doom! +*** custom-theme-set-faces! +#+BEGIN_SRC elisp :eval no +(custom-theme-set-faces! 'doom-one-theme + '(outline-1 :weight normal) + '(outline-2 :weight normal) + '(outline-3 :weight normal) + '(outline-4 :weight normal) + '(outline-5 :weight normal) + '(outline-6 :weight normal) + '(default :background "red" :weight bold) + '(region :background "red" :weight bold)) + +(custom-theme-set-faces! '(doom-one-theme doom-one-light-theme) + '((outline-1 outline-2 outline-3 outline-4 outline-5 outline-6) + :weight normal) + '((default region) + :background "red" :weight bold)) + +(let ((red-bg-faces '(default region))) + (custom-theme-set-faces! '(doom-one-theme doom-one-light-theme) + `(,(cl-loop for i from 0 to 6 collect (intern (format "outline-%d" i))) + :weight normal) + `(,red-bg-faces + :background "red" :weight bold))) + +;; If you want to make use of the `doom-themes' package API (e.g. `doom-color', +;; `doom-lighten', `doom-darken', etc.), you must use `custom-set-faces!' +;; *after* the theme has been loaded. e.g. +(load-theme 'doom-one t) +(custom-theme-set-faces! 'doom-one + `(outline-1 :foreground ,(doom-color 'red)) + `(outline-2 :background ,(doom-color 'blue))) +#+END_SRC + +*** TODO defer-feature! +*** TODO defer-until! +*** disable-packages! +#+BEGIN_SRC elisp :eval no +;; Disable packages enabled by DOOM +(disable-packages! some-package second-package) +#+END_SRC + +*** doom! #+BEGIN_SRC elisp :eval no (doom! :completion company @@ -113,7 +196,7 @@ It is integrated into Helpful, in Doom. (default +bindings +smartparens)) #+END_SRC -* file-exists-p! +*** file-exists-p! #+BEGIN_SRC elisp (file-exists-p! "init.el" doom-emacs-dir) #+END_SRC @@ -130,7 +213,86 @@ It is integrated into Helpful, in Doom. #+RESULTS: : /home/hlissner/.emacs.d/LICENSE -* remove-hook! +*** TODO lambda! +*** TODO lambda!! +*** load! +#+BEGIN_SRC elisp :eval no +;;; Lets say we're in ~/.doom.d/config.el +(load! "lisp/module") ; loads ~/.doom.d/lisp/module.el +(load! "somefile" doom-emacs-dir) ; loads ~/.emacs.d/somefile.el +(load! "anotherfile" doom-private-dir) ; loads ~/.doom.d/anotherfile.el + +;; If you don't want a `load!' call to throw an error if the file doesn't exist: +(load! "~/.maynotexist" nil t) +#+END_SRC + +*** map! +#+BEGIN_SRC elisp :eval no +(map! :map magit-mode-map + :m "C-r" 'do-something ; C-r in motion state + :nv "q" 'magit-mode-quit-window ; q in normal+visual states + "C-x C-r" 'a-global-keybind + :g "C-x C-r" 'another-global-keybind ; same as above + + (:when IS-MAC + :n "M-s" 'some-fn + :i "M-o" (lambda (interactive) (message "Hi")))) + +(map! (:when (featurep! :completion company) ; Conditional loading + :i "C-@" #'+company/complete + (:prefix "C-x" ; Use a prefix key + :i "C-l" #'+company/whole-lines))) + +(map! (:when (featurep! :lang latex) ; local conditional + (:map LaTeX-mode-map + :localleader ; Use local leader + :desc "View" "v" #'TeX-view)) ; Add which-key description + :leader ; Use leader key from now on + :desc "Eval expression" ";" #'eval-expression) +#+END_SRC + +*** package! +#+BEGIN_SRC elisp :eval no +;; To install a package that can be found on ELPA or any of the sources +;; specified in `doom-core-package-sources': +(package! evil) +(package! js2-mode) +(package! rainbow-delimiters) + +;; To disable a package included with Doom (which will no-op all its `after!' +;; and `use-package!' blocks): +(package! evil :disable t) +(package! rainbow-delimiters :disable t) + +;; To install a package from a github repo +(package! so-long :recipe (:host github :repo "hlissner/emacs-so-long")) + +;; If a package is particularly big and comes with submodules you don't need, +;; you can tell the package manager not to clone the repo recursively: +(package! ansible :recipe (:nonrecursive t)) + +;; To install a particular branch, commit or tag: +(package! evil + ;; if :host and :fetcher aren't specified, the package manager will fall back + ;; to evil's default source provided by their (M)ELPA recipes: + :recipe (:commit "e7bc39de2f961505e8e112da8c1b315ae8afce52")) + +(package! evil :recipe (:branch "stable")) + +(package! evil :recipe (:tag "1.2.9")) + +;; If you share your config between two computers, and don't want bin/doom +;; refresh to delete packages used only on one system, use :ignore +(package! evil :ignore (not (equal system-name "my-desktop"))) +#+END_SRC + +*** TODO pushnew! +*** quiet! +#+BEGIN_SRC elisp :eval no +;; Enters recentf-mode without extra output +(quiet! (recentf-mode +1)) +#+END_SRC +*** remove-hook! #+BEGIN_SRC elisp :eval no ;; With only one hook and one function, this is identical to `remove-hook'. In ;; that case, use that instead. @@ -148,3 +310,78 @@ It is integrated into Helpful, in Doom. ;; Removing arbitrary forms (must be exactly the same as the definition) (remove-hook! (one-mode second-mode) (setq v 5) (setq a 2)) #+END_SRC +*** setq-hook! +#+BEGIN_SRC elisp :eval no +;; Set multiple variables after a hook +(setq-hook! 'markdown-mode-hook + line-spacing 2 + fill-column 80) + +;; Set variables after multiple hooks +(setq-hook! '(eshell-mode-hook term-mode-hook) + hscroll-margin 0) +#+END_SRC + +*** unsetq-hook! +#+BEGIN_SRC elisp :eval no +(unsetq-hook! 'markdown-mode-hook line-spacing) + +;; Removes the following variable hook +(setq-hook! 'markdown-mode-hook line-spacing 2) + +;; Removing N variables from M hooks +(unsetq-hook! some-mode enable-something and-another) +(unsetq-hook! some-mode (enable-something and-another)) +(unsetq-hook! '(one-mode-hook second-mode-hook) enable-something) +(unsetq-hook! (one-mode second-mode) enable-something) +#+END_SRC + +*** use-package! +#+BEGIN_SRC elisp :eval no +;; Use after-call to load package before hook +(use-package! projectile + :after-call (pre-command-hook after-find-file dired-before-readin-hook)) + +;; defer recentf packages one by one +(use-package! recentf + :defer-incrementally easymenu tree-widget timer + :after-call after-find-file) + +;; This is equivalent to :defer-incrementally (abc) +(use-package! abc + :defer-incrementally t) +#+END_SRC +* Interesting snippets +** Persist Emacs' initial frame size across sessions +#+BEGIN_SRC elisp +(let ((display-height (display-pixel-height)) + (display-width (display-pixel-width))) + (add-to-list 'initial-frame-alist + `((left . ,(/ new-frame-width 2)) + (top . ,(/ new-frame-height 2)) + (width . ,(/ display-width 2)) + (height . ,(/ display-height 2))))) +#+END_SRC + +** Persist Emacs' initial frame position, dimensions and/or full-screen state across sessions +#+BEGIN_SRC elisp +;; add to ~/.doom.d/config.el +(when-let* ((dims (doom-cache-get 'last-frame-size))) + (cl-destructuring-bind ((left . top) width height fullscreen) dims + (setq initial-frame-alist + (append initial-frame-alist + `((left . ,left) + (top . ,top) + (width . ,width) + (height . ,height) + (fullscreen . ,fullscreen)))))) + +(defun save-frame-dimensions () + (doom-cache-set 'last-frame-size + (list (frame-position) + (frame-width) + (frame-height) + (frame-parameter nil 'fullscreen)))) + +(add-hook 'kill-emacs-hook #'save-frame-dimensions) +#+END_SRC diff --git a/early-init.el b/early-init.el index a4b3682b8..25005c3ef 100644 --- a/early-init.el +++ b/early-init.el @@ -20,3 +20,8 @@ ;; font. By inhibiting this, we easily halve startup times with fonts that are ;; larger than the system default. (setq frame-inhibit-implied-resize t) + +;; Ignore X resources; its settings would be redundant with the other settings +;; in this file and can conflict with later config (particularly where the +;; cursor color is concerned). +(advice-add #'x-apply-session-resources :override #'ignore) diff --git a/init.el b/init.el index 179629068..9f236f0c0 100644 --- a/init.el +++ b/init.el @@ -27,13 +27,6 @@ ;; ;;; License: MIT -(when (version< emacs-version "25.3") - (error "Detected Emacs %s. Doom only supports Emacs 25.3 and higher" - emacs-version)) - -;; Ensure Doom is running out of this file's directory -(setq user-emacs-directory (file-name-directory load-file-name)) - ;; A big contributor to startup times is garbage collection. We up the gc ;; threshold to temporarily prevent it from running, then reset it later with ;; `doom-restore-garbage-collection-h'. Not resetting it will cause @@ -45,8 +38,17 @@ ;; to skip the mtime checks on every *.elc file we load. (setq load-prefer-newer noninteractive) +(let (file-name-handler-alist) + (when (version< emacs-version "25.3") + (error "Detected Emacs %s. Doom only supports Emacs 25.3 and higher" + emacs-version)) + + ;; Ensure Doom is running out of this file's directory + (setq user-emacs-directory (file-name-directory load-file-name))) + ;; Load the heart of Doom Emacs -(require 'core (concat user-emacs-directory "core/core")) +(load (concat user-emacs-directory "core/core") + nil 'nomessage) ;; And let 'er rip! (add-hook 'window-setup-hook #'doom-display-benchmark-h) diff --git a/init.example.el b/init.example.el index fec33a81c..c854a7f4e 100644 --- a/init.example.el +++ b/init.example.el @@ -118,6 +118,7 @@ ;;julia ; a better, faster MATLAB ;;kotlin ; a better, slicker Java(Script) ;;latex ; writing papers in Emacs has never been so fun + ;;lean ;;ledger ; an accounting system in Emacs ;;lua ; one-based indices? one-based indices markdown ; writing docs for people to ignore @@ -125,10 +126,10 @@ ;;nix ; I hereby declare "nix geht mehr!" ;;ocaml ; an objective camel (org ; organize your plain life in plain text - +dragndrop ; file drag & drop support - +ipython ; ipython support for babel - +pandoc ; pandoc integration into org's exporter - +present) ; using Emacs for presentations + +dragndrop ; drag & drop files/images into org buffers + +ipython ; ipython/jupyter support for babel + +pandoc ; export-with-pandoc support + +present) ; using org-mode for presentations ;;perl ; write code no one else can comprehend ;;php ; perl's insecure younger brother ;;plantuml ; diagrams for confusing people more diff --git a/modules/README.org b/modules/README.org index aab14fca3..2b329620a 100644 --- a/modules/README.org +++ b/modules/README.org @@ -42,7 +42,7 @@ Aesthetic modules that affect the Emacs interface or user experience. + [[file:ui/unicode/README.org][unicode]]: + vc-gutter: + vi-tilde-fringe: -+ [[file:ui/window-select/README.org][window-select]]: ++ [[file:ui/window-select/README.org][window-select]] =+switch-window +numbers=: + [[file:ui/workspaces/README.org][workspaces]]: Isolated workspaces * :editor @@ -64,7 +64,6 @@ Modules that reconfigure or augment packages or features built into Emacs. + dired =+ranger +icons=: + electric: -+ imenu: + vc: * :term diff --git a/modules/completion/company/README.org b/modules/completion/company/README.org index 6d796ba00..595a8bf88 100644 --- a/modules/completion/company/README.org +++ b/modules/completion/company/README.org @@ -34,7 +34,7 @@ https://assets.doomemacs.org/completion/company/overlay.png + [[https://github.com/company-mode/company-mode][company-mode]] + [[https://github.com/hlissner/emacs-company-dict][company-dict]] + [[https://github.com/raxod502/prescient.el][company-prescient]] -+ [[https://github.com/sebastiencs/company-box][company-box]] ++ [[https://github.com/sebastiencs/company-box][company-box]]* (=+childframe=) * Prerequisites This module has no direct prerequisites. diff --git a/modules/completion/helm/config.el b/modules/completion/helm/config.el index eae06e513..ca4d26e52 100644 --- a/modules/completion/helm/config.el +++ b/modules/completion/helm/config.el @@ -41,6 +41,7 @@ be negative.") [remap bookmark-jump] #'helm-bookmarks [remap execute-extended-command] #'helm-M-x [remap find-file] #'helm-find-files + [remap locate] #'helm-locate [remap imenu] #'helm-semantic-or-imenu [remap noop-show-kill-ring] #'helm-show-kill-ring [remap persp-switch-to-buffer] #'+helm/workspace-mini @@ -158,7 +159,23 @@ be negative.") ;;;###package helm-locate (defvar helm-generic-files-map (make-sparse-keymap)) -(after! helm-locate (set-keymap-parent helm-generic-files-map helm-map)) +(after! helm-locate + (when (and IS-MAC + (null helm-locate-command) + (executable-find "mdfind")) + (setq helm-locate-command "mdfind -name %s")) + (set-keymap-parent helm-generic-files-map helm-map)) + + +;;;###package helm-org +(use-package! helm-org + :when (featurep! :lang org) + :defer t + :init + (after! helm-mode + (pushnew! helm-completing-read-handlers-alist + '(org-capture . helm-org-completing-read-tags) + '(org-set-tags . helm-org-completing-read-tags)))) ;;;###package helm-projectile diff --git a/modules/completion/helm/packages.el b/modules/completion/helm/packages.el index 2021ecf93..870759b66 100644 --- a/modules/completion/helm/packages.el +++ b/modules/completion/helm/packages.el @@ -12,3 +12,5 @@ (package! helm-flx)) (when (and EMACS26+ (featurep! +childframe)) (package! posframe)) +(when (featurep! :lang org) + (package! helm-org)) diff --git a/modules/completion/ivy/README.org b/modules/completion/ivy/README.org index 4f817733e..49f99e929 100644 --- a/modules/completion/ivy/README.org +++ b/modules/completion/ivy/README.org @@ -12,6 +12,7 @@ - [[#install][Install]] - [[#macos][MacOS]] - [[#arch-linux][Arch Linux]] + - [[#opensuse][openSUSE]] - [[#features][Features]] - [[#jump-to-file-project-navigation][Jump-to-file project navigation]] - [[#project-search--replace][Project search & replace]] @@ -90,6 +91,11 @@ brew install ripgrep the_silver_searcher sudo pacman --needed --noconfirm -S ripgrep the_silver_searcher #+END_SRC +*** openSUSE +#+BEGIN_SRC sh :dir /sudo:: +sudo zypper install ripgrep the_silver_searcher +#+END_SRC + * Features Ivy and its ilk are large plugins. Covering everything about them is outside of this documentation's scope, so only Doom-specific Ivy features are listed here: diff --git a/modules/completion/ivy/autoload/ivy.el b/modules/completion/ivy/autoload/ivy.el index 9ccfb9df6..4a4b8aac8 100644 --- a/modules/completion/ivy/autoload/ivy.el +++ b/modules/completion/ivy/autoload/ivy.el @@ -214,7 +214,7 @@ If ARG (universal argument), open selection in other-window." (defun +ivy--tasks-open-action (x) "Jump to the file and line of the current task." - (cl-destructuring-bind (label type file line) x + (cl-destructuring-bind (_label type file line) x (with-ivy-window (find-file (expand-file-name file (doom-project-root))) (goto-char (point-min)) diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el index fa7b46653..f39009d9a 100644 --- a/modules/completion/ivy/config.el +++ b/modules/completion/ivy/config.el @@ -62,8 +62,6 @@ immediately runs it on the current candidate (ending the ivy session)." projectile-completion-system 'ivy ;; Don't use ^ as initial input ivy-initial-inputs-alist nil - ;; highlight til EOL - ivy-format-function #'ivy-format-function-line ;; disable magic slash on non-match ivy-magic-slash-non-match-action nil ;; don't show recent files in switch-buffer @@ -75,6 +73,9 @@ immediately runs it on the current candidate (ending the ivy session)." ;; enable ability to select prompt (alternative to `ivy-immediate-done') ivy-use-selectable-prompt t) + (setf (alist-get 't ivy-format-functions-alist) + #'ivy-format-function-line) + ;; REVIEW Move this somewhere else and perhaps generalize this so both ;; ivy/helm users can enjoy it. (defadvice! +ivy--counsel-file-jump-use-fd-rg-a (args) @@ -220,10 +221,13 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." [remap org-capture] #'counsel-org-capture [remap swiper] #'counsel-grep-or-swiper [remap evil-ex-registers] #'counsel-evil-registers - [remap yank-pop] #'counsel-yank-pop) + [remap yank-pop] #'counsel-yank-pop + [remap locate] #'counsel-locate) :config (set-popup-rule! "^\\*ivy-occur" :size 0.35 :ttl 0 :quit nil) + (when IS-MAC + (setq counsel-locate-cmd #'counsel-locate-cmd-mdfind)) (setq counsel-find-file-ignore-regexp "\\(?:^[#.]\\)\\|\\(?:[#~]$\\)\\|\\(?:^Icon?\\)" counsel-describe-function-function #'helpful-callable counsel-describe-variable-function #'helpful-variable diff --git a/modules/config/default/+emacs-bindings.el b/modules/config/default/+emacs-bindings.el index 3d541e07d..93ab1e067 100644 --- a/modules/config/default/+emacs-bindings.el +++ b/modules/config/default/+emacs-bindings.el @@ -20,20 +20,7 @@ ;;; Leader keys (map! :leader - :desc "Find file in project" "C-f" #'projectile-find-file :desc "Evaluate line/region" "e" #'+eval/line-or-region - :desc "Open scratch buffer" "x" #'doom/open-scratch-buffer - :desc "Open project scratch buffer" "X" #'doom/switch-to-scratch-buffer - - (:when (featurep! :term term) - :desc "Toggle term popup" "`" #'+term/toggle - :desc "Open term here" "~" #'+term/here) - (:when (featurep! :term vterm) - :desc "Toggle vterm popup" "`" #'+vterm/toggle - :desc "Open vterm here" "~" #'+vterm/here) - (:when (featurep! :term eshell) - :desc "Toggle eshell popup" "`" #'+eshell/toggle - :desc "Open eshell here" "~" #'+eshell/here) (:prefix ("l" . "")) ; bound locally (:prefix ("!" . "checkers")) ; bound by flycheck @@ -49,6 +36,8 @@ :desc "Browse emacs.d" "E" #'+default/browse-emacsd :desc "Find file from here" "f" (if (fboundp 'counsel-file-jump) #'counsel-file-jump #'find-file) :desc "Find file in other project" "F" #'doom/browse-in-other-project + :desc "Delete this file" "K" #'doom/delete-this-file + :desc "Move this file" "m" #'doom/move-this-file :desc "Find file in project" "p" #'projectile-find-file :desc "Find file in other project" "P" #'doom/find-file-in-other-project :desc "Recent files" "r" #'recentf-open-files @@ -56,11 +45,25 @@ :desc "Sudo this file" "s" #'doom/sudo-this-file :desc "Sudo find file" "S" #'doom/sudo-find-file :desc "Yank filename" "y" #'+default/yank-buffer-filename - :desc "Delete this file" "X" #'doom/delete-this-file) + :desc "Open scratch buffer" "x" #'doom/open-scratch-buffer + :desc "Open project scratch buffer" "X" #'doom/switch-to-scratch-buffer) + + ;;; g --- lookup + (:when (featurep! :tools lookup) + (:prefix-map ("g" . "lookup") + "k" #'+lookup/documentation + "d" #'+lookup/definition + "D" #'+lookup/references + "f" #'+lookup/file + "o" #'+lookup/online-select + "i" #'+lookup/in-docsets + "I" #'+lookup/in-all-docsets)) ;;; o --- org "o" nil ; we need to unbind it first as Org claims this (:prefix-map ("o". "org") + :desc "Do what I mean" "o" #'+org/dwim-at-point + :desc "Display inline images" "i" #'org-display-inline-images :desc "Search notes for symbol" "." #'+default/search-notes-for-symbol-at-point (:prefix ("a" . "org agenda") :desc "Agenda" "a" #'org-agenda @@ -104,6 +107,17 @@ :desc "Create Temp Template" "c" #'aya-create :desc "Use Temp Template" "e" #'aya-expand) + (:prefix-map ("t" . "terminal") + (:when (featurep! :term term) + :desc "Toggle term popup" "t" #'+term/toggle + :desc "Open term here" "T" #'+term/here) + (:when (featurep! :term vterm) + :desc "Toggle vterm popup" "t" #'+vterm/toggle + :desc "Open vterm here" "T" #'+vterm/here) + (:when (featurep! :term eshell) + :desc "Toggle eshell popup" "t" #'+eshell/toggle + :desc "Open eshell here" "T" #'+eshell/here)) + ;;; v --- versioning (:prefix-map ("v" . "versioning") :desc "Git revert file" "R" #'vc-revert diff --git a/modules/config/default/+evil-bindings.el b/modules/config/default/+evil-bindings.el index 573352e92..303ec77fa 100644 --- a/modules/config/default/+evil-bindings.el +++ b/modules/config/default/+evil-bindings.el @@ -54,7 +54,7 @@ [remap quit-window] #'kill-current-buffer) (:map (help-mode-map helpful-mode-map) - :n "o" 'ace-link-help) + :n "o" #'ace-link-help) ;; misc :n "C-S-f" #'toggle-frame-fullscreen @@ -116,6 +116,8 @@ :v "g+" #'evil-numbers/inc-at-pt ;; custom evil keybinds + :n "zn" #'+evil:narrow-buffer + :n "zN" #'doom/widen-indirectly-narrowed-buffer :n "zx" #'kill-current-buffer :n "ZX" #'bury-buffer ;; repeat in visual mode (FIXME buggy) @@ -544,6 +546,7 @@ :desc "Search buffer" "b" #'swiper :desc "Search current directory" "d" #'+default/search-cwd :desc "Search other directory" "D" #'+default/search-other-cwd + :desc "Locate file" "f" #'locate :desc "Jump to symbol" "i" #'imenu :desc "Jump to link" "l" #'ace-link :desc "Look up online" "o" #'+lookup/online @@ -581,7 +584,7 @@ ;;; b --- buffer (:prefix-map ("b" . "buffer") - :desc "Toggle narrowing" "-" #'doom/clone-and-narrow-buffer + :desc "Toggle narrowing" "-" #'doom/toggle-narrow-buffer :desc "Previous buffer" "[" #'previous-buffer :desc "Next buffer" "]" #'next-buffer (:when (featurep! :ui workspaces) @@ -594,7 +597,7 @@ :desc "Switch to last buffer" "l" #'evil-switch-to-windows-last-buffer :desc "Next buffer" "n" #'next-buffer :desc "New empty buffer" "N" #'evil-buffer-new - :desc "Kill other buffers" "o" #'doom/kill-other-buffers + :desc "Kill other buffers" "O" #'doom/kill-other-buffers :desc "Previous buffer" "p" #'previous-buffer :desc "Save buffer" "s" #'save-buffer :desc "Sudo edit this file" "S" #'doom/sudo-this-file @@ -615,7 +618,9 @@ :desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace :desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines (:when (featurep! :tools flycheck) - :desc "List errors" "x" #'flycheck-list-errors)) + :desc "List errors" "x" #'flycheck-list-errors) + (:unless (featurep! :tools flycheck) + :desc "List errors" "x" #'flymake-show-diagnostics-buffer)) ;;; f --- file (:prefix-map ("f" . "file") @@ -629,6 +634,7 @@ :desc "Find file in emacs.d" "e" #'+default/find-in-emacsd :desc "Browse emacs.d" "E" #'+default/browse-emacsd :desc "Find file from here" "f" #'find-file + :desc "Locate file" "l" #'locate :desc "Move/rename file" "m" #'doom/move-this-file :desc "Find file in private config" "p" #'doom/find-file-in-private-config :desc "Browse private config" "P" #'doom/open-private-config @@ -642,6 +648,8 @@ ;;; g --- git (:prefix-map ("g" . "git") :desc "Git revert file" "R" #'vc-revert + :desc "Copy git link" "y" #'git-link + :desc "Copy git link to homepage" "Y" #'git-link-homepage (:when (featurep! :ui vc-gutter) :desc "Git revert hunk" "r" #'git-gutter:revert-hunk :desc "Git stage hunk" "s" #'git-gutter:stage-hunk @@ -667,7 +675,7 @@ :desc "Find issue" "i" #'forge-visit-issue :desc "Find pull request" "p" #'forge-visit-pullreq) (:prefix ("o" . "open in browser") - :desc "Browse region or line" "." #'+vc/git-browse-region-or-line + :desc "Browse region or line" "o" #'+vc/git-browse-region-or-line :desc "Browse remote" "r" #'forge-browse-remote :desc "Browse commit" "c" #'forge-browse-commit :desc "Browse an issue" "i" #'forge-browse-issue @@ -707,7 +715,12 @@ :desc "Search org agenda headlines" "h" #'+default/org-notes-headlines :desc "Find file in notes" "n" #'+default/find-in-notes :desc "Browse notes" "N" #'+default/browse-notes - :desc "Org store link" "l" #'org-store-link) + :desc "Org store link" "l" #'org-store-link + + (:when (featurep! :lang org +journal) + (:prefix ("j" . "journal") + :desc "New Entry" "j" #'org-journal-new-entry + :desc "Search Forever" "s" #'org-journal-search-forever))) ;;; o --- open (:prefix-map ("o" . "open") @@ -813,6 +826,10 @@ ;;; t --- toggle (:prefix-map ("t" . "toggle") :desc "Big mode" "b" #'doom-big-font-mode + (:when (featurep! :tools flycheck) + :desc "Flycheck" "f" #'flycheck-mode) + (:unless (featurep! :tools flycheck) + :desc "Flymake" "f" #'flymake-mode) :desc "Frame fullscreen" "F" #'toggle-frame-fullscreen :desc "Evil goggles" "g" #'evil-goggles-mode :desc "Indent style" "I" #'doom/toggle-indent-style diff --git a/modules/config/default/+evil.el b/modules/config/default/+evil.el index e1c7eddb6..afb67451d 100644 --- a/modules/config/default/+evil.el +++ b/modules/config/default/+evil.el @@ -1,9 +1,9 @@ ;;; config/default/+evil.el -*- lexical-binding: t; -*- -(defun +default|disable-delete-selection-mode () +(defun +default-disable-delete-selection-mode-h () (delete-selection-mode -1)) (add-hook 'evil-insert-state-entry-hook #'delete-selection-mode) -(add-hook 'evil-insert-state-exit-hook #'+default|disable-delete-selection-mode) +(add-hook 'evil-insert-state-exit-hook #'+default-disable-delete-selection-mode-h) ;; diff --git a/modules/config/default/autoload/default.el b/modules/config/default/autoload/default.el index 89fa2f4dd..5adcb3ba7 100644 --- a/modules/config/default/autoload/default.el +++ b/modules/config/default/autoload/default.el @@ -109,7 +109,7 @@ If ARG (universal argument), runs `compile' from the current directory." ((error "No kill-ring search backend available. Enable ivy or helm!"))))) ;;;###autoload -(defun +default*newline-indent-and-continue-comments () +(defun +default--newline-indent-and-continue-comments-a () "A replacement for `newline-and-indent'. Continues comments if executed from a commented line, with special support for @@ -159,7 +159,7 @@ possible, or just one char if that's not possible." ((delete-char -1))))) ;;;###autoload -(defun +default*delete-backward-char (n &optional killflag) +(defun +default--delete-backward-char-a (n &optional killflag) "Same as `delete-backward-char', but preforms these additional checks: + If point is surrounded by (balanced) whitespace and a brace delimiter ({} [] @@ -283,11 +283,11 @@ If prefix ARG is set, prompt for a known project to search from." ((rgrep (regexp-quote symbol)))))) ;;;###autoload -(defun +default/search-notes-for-symbol-at-point (&optional arg symbol) +(defun +default/search-notes-for-symbol-at-point (&optional symbol) "Conduct a text search in the current project for symbol at point. If prefix ARG is set, prompt for a known project to search from." (interactive - (list current-prefix-arg (thing-at-point 'symbol t))) + (list (thing-at-point 'symbol t))) (require 'org) (let ((default-directory org-directory)) (+default/search-project-for-symbol-at-point diff --git a/modules/config/default/config.el b/modules/config/default/config.el index e3671d1d9..46976180f 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -34,6 +34,10 @@ ;; prompt for the key passphrase. epa-pinentry-mode 'loopback)) +;;;###package tramp +(unless IS-WINDOWS + (setq tramp-default-method "ssh")) ; faster than the default scp + ;; ;;; Smartparens config @@ -195,10 +199,10 @@ ;; 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)) + (advice-add #'delete-backward-char :override #'+default--delete-backward-char-a)) ;; Makes `newline-and-indent' continue comments (and more reliably) - (advice-add #'newline-and-indent :override #'+default*newline-indent-and-continue-comments)) + (advice-add #'newline-and-indent :override #'+default--newline-indent-and-continue-comments-a)) ;; @@ -260,7 +264,6 @@ (define-key! help-map ;; new keybinds "'" #'describe-char - "B" #'doom/open-bug-report "D" #'doom/help "E" #'doom/sandbox "M" #'doom/describe-active-minor-mode @@ -312,8 +315,6 @@ "F" #'describe-face ;; replaces `view-hello-file' b/c annoying "h" #'doom/help - ;; replaces `describe-language-environment' b/c remapped to C-l - "L" #'global-command-log-mode ;; replaces `view-emacs-news' b/c it's on C-n too "n" #'doom/help-news ;; replaces `finder-by-keyword' diff --git a/modules/editor/evil/+everywhere.el b/modules/editor/evil/+everywhere.el index 3edab99aa..6a05adacf 100644 --- a/modules/editor/evil/+everywhere.el +++ b/modules/editor/evil/+everywhere.el @@ -53,7 +53,12 @@ variable for an explanation of the defaults (in comments). See ;; (url-retrieve-synchronously "https://raw.githubusercontent.com/emacs-evil/evil-collection/master/evil-collection.el" t t) ;; (goto-char (point-min)) ;; (when (re-search-forward "^(defcustom evil-collection-mode-list\n[^(]+") -;; (kill-new (thing-at-point 'sexp t)))) +;; (let ((list (sexp-at-point))) +;; ;; Fixes +;; (when (assq 'pdf list) +;; (setf (alist-get 'pdf list) '(pdf-tools))) +;; (kill-new (prin1-to-string list))))) + (defvar evil-collection-mode-list `(2048-game ag @@ -143,7 +148,7 @@ variable for an explanation of the defaults (in comments). See p4 (package-menu package) pass - (pdf pdf-view) + (pdf pdf-tools) popup proced process-menu diff --git a/modules/editor/evil/autoload/evil.el b/modules/editor/evil/autoload/evil.el index 4cb4bd7c3..183c82fae 100644 --- a/modules/editor/evil/autoload/evil.el +++ b/modules/editor/evil/autoload/evil.el @@ -135,10 +135,18 @@ integration." ;;;###autoload (autoload '+evil:narrow-buffer "editor/evil/autoload/evil" nil t) (evil-define-operator +evil:narrow-buffer (beg end &optional bang) - "Wrapper around `doom/clone-and-narrow-buffer'." + "Narrow the buffer to region between BEG and END. + +Widens narrowed buffers first. If BANG, use indirect buffer clones instead." :move-point nil (interactive "") - (doom/clone-and-narrow-buffer beg end bang)) + (if (not bang) + (if (buffer-narrowed-p) + (widen) + (narrow-to-region beg end)) + (when (buffer-narrowed-p) + (doom/widen-indirectly-narrowed-buffer t)) + (doom/narrow-buffer-indirectly beg end))) ;;;###autoload (defun +evil/next-beginning-of-method (count) @@ -192,13 +200,13 @@ See `+evil/next-preproc-directive' for details." (dotimes (_ (abs count)) (cond ((> count 0) (while (and (not (eobp)) (sp-point-in-comment)) - (next-line)) + (forward-line 1)) (unless (comment-search-forward (point-max) 'noerror) (goto-char orig-pt) (user-error "No comment after point"))) (t (while (and (not (bobp)) (sp-point-in-comment)) - (previous-line)) + (forward-line -1)) (unless (comment-search-backward nil 'noerror) (goto-char orig-pt) (user-error "No comment before point"))))))) diff --git a/modules/editor/evil/autoload/ex.el b/modules/editor/evil/autoload/ex.el index ab7c4919a..5412a3039 100644 --- a/modules/editor/evil/autoload/ex.el +++ b/modules/editor/evil/autoload/ex.el @@ -177,7 +177,7 @@ If BANG, search Doom documentation." 'module (list cat mod)))) (module (completing-read "Describe module: " modules nil t query)) (key (get-text-property 0 'module module))) - (doom/help-modules key))) + (doom/help-modules (car key) (cdr key)))) ((and (string-match-p "\\(?:SPC\\|[CMsSH]-[^ ]\\|<[^>]+>\\)" query) (helpful-key (kbd (string-trim query))))) ((apropos query t))))) diff --git a/modules/editor/evil/autoload/unimpaired.el b/modules/editor/evil/autoload/unimpaired.el index 5a3fe6ce0..448a59719 100644 --- a/modules/editor/evil/autoload/unimpaired.el +++ b/modules/editor/evil/autoload/unimpaired.el @@ -85,31 +85,31 @@ ;;; ]u / [u ;;;###autoload (autoload '+evil:url-encode "editor/evil/autoload/unimpaired" nil t) -(evil-define-operator +evil:url-encode (count &optional beg end type) +(evil-define-operator +evil:url-encode (_count &optional beg end) "TODO" - (interactive "") + (interactive "") (+evil--encode beg end #'url-encode-url)) ;;;###autoload (autoload '+evil:url-decode "editor/evil/autoload/unimpaired" nil t) -(evil-define-operator +evil:url-decode (count &optional beg end type) +(evil-define-operator +evil:url-decode (_count &optional beg end) "TODO" - (interactive "") + (interactive "") (+evil--encode beg end #'url-unhex-string)) ;;; ]y / [y ;;;###autoload (autoload '+evil:c-string-encode "editor/evil/autoload/unimpaired" nil t) -(evil-define-operator +evil:c-string-encode (count &optional beg end type) +(evil-define-operator +evil:c-string-encode (_count &optional beg end) "TODO" - (interactive "") + (interactive "") (+evil--encode beg end (lambda (text) (replace-regexp-in-string "[\"\\]" (lambda (ch) (concat "\\" ch)) text)))) ;;;###autoload (autoload '+evil:c-string-decode "editor/evil/autoload/unimpaired" nil t) -(evil-define-operator +evil:c-string-decode (count &optional beg end type) +(evil-define-operator +evil:c-string-decode (_count &optional beg end) "TODO" - (interactive "") + (interactive "") (+evil--encode beg end (lambda (text) diff --git a/modules/editor/evil/config.el b/modules/editor/evil/config.el index 720e0514e..4a7f417f6 100644 --- a/modules/editor/evil/config.el +++ b/modules/editor/evil/config.el @@ -257,12 +257,12 @@ directives. By default, this only recognizes C directives.") (use-package! evil-escape :commands evil-escape - :after-call evil-normal-state-exit-hook + :after-call pre-command-hook :init (setq evil-escape-excluded-states '(normal visual multiedit emacs motion) evil-escape-excluded-major-modes '(neotree-mode treemacs-mode vterm-mode) evil-escape-key-sequence "jk" - evil-escape-delay 0.25) + evil-escape-delay 0.15) (evil-define-key* '(insert replace visual operator) 'global "\C-g" #'evil-escape) :config ;; no `evil-escape' in minibuffer diff --git a/modules/editor/evil/test/test-evil.el b/modules/editor/evil/test/test-evil.el index 240a74b13..99694f11e 100644 --- a/modules/editor/evil/test/test-evil.el +++ b/modules/editor/evil/test/test-evil.el @@ -3,12 +3,11 @@ (describe "feature/evil" :var (resv project-root) - (before-all - (require! :editor evil) - (require 'evil) - (load! "../autoload/evil")) - (after-all - (unload-feature 'evil t)) + + (require! :editor evil) + (require 'evil) + (load! "../autoload/evil") + (before-each (fset 'resv #'+evil-resolve-vim-path-a) (spy-on 'doom-project-root :and-call-fake (lambda () project-root))) diff --git a/modules/editor/multiple-cursors/autoload/evil-mc.el b/modules/editor/multiple-cursors/autoload/evil-mc.el index 5b5928182..036d7547e 100644 --- a/modules/editor/multiple-cursors/autoload/evil-mc.el +++ b/modules/editor/multiple-cursors/autoload/evil-mc.el @@ -5,6 +5,8 @@ (defun +multiple-cursors/evil-mc-toggle-cursors () "Toggle frozen state of evil-mc cursors." (interactive) + (unless (evil-mc-has-cursors-p) + (user-error "No cursors exist to be toggled")) (setq evil-mc-frozen (not (and (evil-mc-has-cursors-p) evil-mc-frozen))) (if evil-mc-frozen @@ -45,12 +47,14 @@ pauses cursors." (evil-mc-make-cursor-here)))) ;;;###autoload (autoload '+multiple-cursors:evil-mc "editor/multiple-cursors/autoload/evil-mc" nil t) -(evil-define-command +multiple-cursors:evil-mc (beg end type pattern &optional bang) - "Create mc cursors at each match of PATTERN within BEG and END, and leave the -cursor at the final match. If BANG, then treat PATTERN as literal." +(evil-define-command +multiple-cursors:evil-mc (beg end type pattern &optional flags bang) + "Create mc cursors at each match of PATTERN within BEG and END. + +This leaves the cursor at the final match. If BANG, then treat PATTERN as +literal. PATTERN is a delimited regexp (the same that :g or :s uses)." :move-point nil :evil-mc t - (interactive "") + (interactive "") (unless (and (stringp pattern) (not (string-empty-p pattern))) (user-error "A regexp pattern is required")) @@ -59,9 +63,19 @@ cursor at the final match. If BANG, then treat PATTERN as literal." (cons (evil-ex-make-search-pattern (if bang (regexp-quote pattern) pattern)) (list beg end type))) - (save-excursion - (evil-with-restriction beg end - (evil-mc-make-cursors-for-all))) + (evil-with-restriction beg end + (let ((point (point))) + (save-excursion + (goto-char (point-min)) + (while (eq (evil-ex-find-next (evil-mc-get-pattern) 'forward t) t) + (goto-char (1- (point))) + (when (/= point (point)) + (evil-mc-run-cursors-before) + (evil-mc-make-cursor-at-pos (point))) + (goto-char + (if (memq ?g flags) + (line-beginning-position 2) + (1+ (point)))))))) (evil-exit-visual-state) (evil-mc-goto-cursor (if (= (evil-visual-direction) 1) diff --git a/modules/editor/multiple-cursors/config.el b/modules/editor/multiple-cursors/config.el index 9ceb9e394..362705c94 100644 --- a/modules/editor/multiple-cursors/config.el +++ b/modules/editor/multiple-cursors/config.el @@ -59,11 +59,7 @@ ;; Forward declare these so that ex completion and evil-mc support is ;; recognized before the autoloaded functions are loaded. (evil-add-command-properties '+evil:align :evil-mc t) - (evil-set-command-properties '+multiple-cursors:evil-mc - :move-point nil - :ex-arg 'global-match - :ex-bang t - :evil-mc t)) + (evil-add-command-properties '+multiple-cursors:evil-mc :evil-mc t)) (after! multiple-cursors-core diff --git a/modules/editor/snippets/autoload/settings.el b/modules/editor/snippets/autoload/settings.el index 09880ed66..f83a8df9d 100644 --- a/modules/editor/snippets/autoload/settings.el +++ b/modules/editor/snippets/autoload/settings.el @@ -5,6 +5,6 @@ "Register minor MODES (one mode symbol or a list of them) with yasnippet so it can have its own snippets category, if the folder exists." (dolist (mode (doom-enlist modes)) - (let ((fn (intern (format "+snippets|register-%s" mode)))) + (let ((fn (intern (format "+snippets-register-%s-h" mode)))) (fset fn (lambda () (yas-activate-extra-mode mode))) (add-hook (intern (format "%s-hook" mode)) fn)))) diff --git a/modules/editor/snippets/autoload/snippets.el b/modules/editor/snippets/autoload/snippets.el index 685ccce18..f2b49f266 100644 --- a/modules/editor/snippets/autoload/snippets.el +++ b/modules/editor/snippets/autoload/snippets.el @@ -225,7 +225,7 @@ You will be prompted for a snippet to alias." "# condition: t}\n" "# type: command\n" "# --\n" - "(%alias \"${4:" (or template "uuid") "}\")")) + "(%alias \"${4:" (or template-uuid "uuid") "}\")")) (when (bound-and-true-p evil-local-mode) (evil-insert-state))))) diff --git a/modules/emacs/dired/config.el b/modules/emacs/dired/config.el index e4682e506..0fab59283 100755 --- a/modules/emacs/dired/config.el +++ b/modules/emacs/dired/config.el @@ -24,7 +24,7 @@ ;; Use GNU ls as `gls' from `coreutils' if available. Add `(setq ;; dired-use-ls-dired nil)' to your config to suppress the Dired warning ;; when not using GNU ls. - (if-let* ((gls (executable-find "gls"))) + (if-let (gls (executable-find "gls")) (setq insert-directory-program gls) ;; BSD ls doesn't support --group-directories-first (setq args (delete "--group-directories-first" args)))) @@ -45,30 +45,12 @@ :hook (dired-mode . diredfl-mode)) -(use-package! dired-k - :hook (dired-initial-position . dired-k) - :hook (dired-after-readin . dired-k-no-revert) +(use-package! diff-hl + :hook (dired-mode . diff-hl-dired-mode) + :hook (magit-post-refresh . diff-hl-magit-post-refresh) :config - (setq dired-k-style 'git - dired-k-padding 1) - - ;; Don't highlight based on mtime, this interferes with diredfl and is more - ;; confusing than helpful. - (advice-add #'dired-k--highlight-by-file-attribyte :override #'ignore) - - (defadvice! +dired--interrupt-process-a (orig-fn &rest args) - "Fixes dired-k killing git processes too abruptly, leaving behind disruptive -.git/index.lock files." - :around #'dired-k--start-git-status - (cl-letf (((symbol-function #'kill-process) - (symbol-function #'interrupt-process))) - (apply orig-fn args))) - - (defadvice! +dired--dired-k-highlight-a (orig-fn &rest args) - "Butt out if the requested directory is remote (i.e. through tramp)." - :around #'dired-k--highlight - (unless (file-remote-p default-directory) - (apply orig-fn args)))) + ;; use margin instead of fringe + (diff-hl-margin-mode)) (use-package! ranger @@ -117,6 +99,7 @@ we have to clean it up ourselves." (use-package! dired-x + :unless (featurep! +ranger) :hook (dired-mode . dired-omit-mode) :config (setq dired-omit-verbose nil) diff --git a/modules/emacs/dired/packages.el b/modules/emacs/dired/packages.el index b786e6fde..8e912c9c8 100644 --- a/modules/emacs/dired/packages.el +++ b/modules/emacs/dired/packages.el @@ -2,7 +2,7 @@ ;;; emacs/dired/packages.el (package! diredfl) -(package! dired-k) +(package! diff-hl) (package! dired-rsync) (when (featurep! +ranger) (package! ranger)) diff --git a/modules/email/mu4e/README.org b/modules/email/mu4e/README.org index 2132fba1d..06c43b859 100644 --- a/modules/email/mu4e/README.org +++ b/modules/email/mu4e/README.org @@ -4,16 +4,18 @@ #+STARTUP: inlineimages * Table of Contents :TOC: -- [[Description][Description]] - - [[Module Flags][Module Flags]] - - [[Plugins][Plugins]] -- [[Prerequisites][Prerequisites]] - - [[MacOS][MacOS]] - - [[Arch Linux][Arch Linux]] -- [[Features][Features]] -- [[Configuration][Configuration]] - - [[offlineimap][offlineimap]] - - [[mbsync][mbsync]] +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] + - [[#macos][MacOS]] + - [[#arch-linux][Arch Linux]] + - [[#nixos][NixOS]] + - [[#opensuse][openSUSE]] +- [[#features][Features]] +- [[#configuration][Configuration]] + - [[#offlineimap][offlineimap]] + - [[#mbsync][mbsync]] * Description This module makes Emacs an email client, using ~mu4e~. @@ -66,6 +68,15 @@ environment.systemPackages = with pkgs; [ [[https://github.com/Emiller88/dotfiles/blob/master/modules/shell/mail.nix][An example of setting up mbsync with home-manager]] +** openSUSE + +Remove ~#~ in ~#sync_program=offlineimap~ to choose ~offlineimap~ instead of ~mbsync~. +#+BEGIN_SRC sh :dir /sudo:: +sync_program=isync # mbsync +#sync_program=offlineimap +sudo zypper install maildir-utils $sync_programm +#+END_SRC + * TODO Features * Configuration diff --git a/modules/input/chinese/config.el b/modules/input/chinese/config.el index 204bc0513..666cfe395 100644 --- a/modules/input/chinese/config.el +++ b/modules/input/chinese/config.el @@ -36,15 +36,15 @@ when exporting org-mode to html." :filter-args #'org-html-paragraph (cl-destructuring-bind (paragraph contents info) args - (let* ((fix-regexp "[[:multibyte:]a-zA-Z0-9]") - (origin-contents contents) + (let* ((fix-regexp "[[:multibyte:]]") + (origin-contents + (replace-regexp-in-string + "<[Bb][Rr] */>" + "" + contents)) (fixed-contents (replace-regexp-in-string - (concat "\\(" - fix-regexp - "\\) *\\(<[Bb][Rr] */>\\)?\n *\\(" - fix-regexp - "\\)") - "\\1\\3" + (concat "\\(" fix-regexp "\\) *\n *\\(" fix-regexp "\\)") + "\\1\\2" origin-contents))) (list paragraph fixed-contents info)))) diff --git a/modules/input/japanese/config.el b/modules/input/japanese/config.el index e94078c00..407c029e3 100644 --- a/modules/input/japanese/config.el +++ b/modules/input/japanese/config.el @@ -22,7 +22,7 @@ (after! helm (helm-migemo-mode +1))))) -(use-package pangu-spacing +(use-package! pangu-spacing :hook (text-mode . pangu-spacing-mode) :init ;; replacing `chinese-two-byte' by `japanese' @@ -46,15 +46,15 @@ when exporting org-mode to html." :filter-args #'org-html-paragraph (cl-destructuring-bind (paragraph contents info) args - (let* ((fix-regexp "[[:multibyte:]a-zA-Z0-9]") - (origin-contents contents) + (let* ((fix-regexp "[[:multibyte:]]") + (origin-contents + (replace-regexp-in-string + "<[Bb][Rr] */>" + "" + contents)) (fixed-contents (replace-regexp-in-string - (concat "\\(" - fix-regexp - "\\) *\\(<[Bb][Rr] */>\\)?\n *\\(" - fix-regexp - "\\)") - "\\1\\3" + (concat "\\(" fix-regexp "\\) *\n *\\(" fix-regexp "\\)") + "\\1\\2" origin-contents))) (list paragraph fixed-contents info)))) diff --git a/modules/lang/agda/README.org b/modules/lang/agda/README.org index 1812f748d..6713085bf 100644 --- a/modules/lang/agda/README.org +++ b/modules/lang/agda/README.org @@ -1,9 +1,4 @@ #+TITLE: :lang agda -This module adds support for the [[http://wiki.portal.chalmers.se/agda/pmwiki.php][agda]] programming language. - -Emacs support is included in the agda release (you can find installation -instructions [[https://agda.readthedocs.io/en/latest/getting-started/installation.html][here]]). This module attempts to find the location of ~agda2.el~ via -the ~agda-mode locate~ command that comes with the agda release. Users can set -this manually by setting the ~+agda2-dir~ variable. - +This module adds support for the [[http://wiki.portal.chalmers.se/agda/pmwiki.php][agda]] programming language. The Emacs support +exists directly in the agda repository but not in melpa. diff --git a/modules/lang/agda/config.el b/modules/lang/agda/config.el index 715e33658..0fa2bdbc7 100644 --- a/modules/lang/agda/config.el +++ b/modules/lang/agda/config.el @@ -1,41 +1,31 @@ ;;; lang/agda/config.el -*- lexical-binding: t; -*- -(defvar +agda-dir - (when (executable-find "agda-mode") - (file-name-directory (shell-command-to-string "agda-mode locate")))) - -(use-package! agda2 - :when +agda-dir - :load-path +agda-dir) - -(use-package! agda2-mode - :defer t - :config - (map! :map agda2-mode-map - :localleader - "?" #'agda2-show-goals - "." #'agda2-goal-and-context-and-inferred - "," #'agda2-goal-and-context - "=" #'agda2-show-constraints - "SPC" #'agda2-give - "a" #'agda2-auto-maybe-all - "b" #'agda2-previous-goal - "c" #'agda2-make-case - "d" #'agda2-infer-type-maybe-toplevel - "e" #'agda2-show-context - "f" #'agda2-next-goal - "gG" #'agda2-go-back - "h" #'agda2-helper-function-type - "l" #'agda2-load - "n" #'agda2-compute-normalised-maybe-toplevel - "p" #'agda2-module-contents-maybe-toplevel - "r" #'agda2-refine - "s" #'agda2-solveAll - "t" #'agda2-goal-type - "w" #'agda2-why-in-scope-maybe-toplevel - (:prefix "x" - "c" #'agda2-compile - "d" #'agda2-remove-annotations - "h" #'agda2-display-implicit-arguments - "q" #'agda2-quit - "r" #'agda2-restart))) +(map! :after agda2-mode + :map agda2-mode-map + :localleader + "?" #'agda2-show-goals + "." #'agda2-goal-and-context-and-inferred + "," #'agda2-goal-and-context + "=" #'agda2-show-constraints + "SPC" #'agda2-give + "a" #'agda2-auto-maybe-all + "b" #'agda2-previous-goal + "c" #'agda2-make-case + "d" #'agda2-infer-type-maybe-toplevel + "e" #'agda2-show-context + "f" #'agda2-next-goal + "gG" #'agda2-go-back + "h" #'agda2-helper-function-type + "l" #'agda2-load + "n" #'agda2-compute-normalised-maybe-toplevel + "p" #'agda2-module-contents-maybe-toplevel + "r" #'agda2-refine + "s" #'agda2-solveAll + "t" #'agda2-goal-type + "w" #'agda2-why-in-scope-maybe-toplevel + (:prefix "x" + "c" #'agda2-compile + "d" #'agda2-remove-annotations + "h" #'agda2-display-implicit-arguments + "q" #'agda2-quit + "r" #'agda2-restart)) diff --git a/modules/lang/agda/doctor.el b/modules/lang/agda/doctor.el deleted file mode 100644 index e38ccbc03..000000000 --- a/modules/lang/agda/doctor.el +++ /dev/null @@ -1,5 +0,0 @@ -;; -*- lexical-binding: t; no-byte-compile: t; -*- -;;; lang/agda/doctor.el - -(unless (executable-find "agda-mode") - (warn! "Couldn't find agda-mode. Agda support won't work")) diff --git a/modules/lang/agda/packages.el b/modules/lang/agda/packages.el new file mode 100644 index 000000000..35bead2a1 --- /dev/null +++ b/modules/lang/agda/packages.el @@ -0,0 +1,15 @@ +;; -*- no-byte-compile: t; -*- +;;; lang/agda/packages.el + + +(package! agda-input + :recipe + (:host github :repo "agda/agda" + :files ("src/data/emacs-mode/agda-input.el"))) + +(package! agda2-mode + :recipe + (:host github :repo "agda/agda" + :files + ("src/data/emacs-mode/*.el" + (:exclude "agda-input.el")))) diff --git a/modules/lang/cc/README.org b/modules/lang/cc/README.org index f965fb24d..c4b846a9f 100644 --- a/modules/lang/cc/README.org +++ b/modules/lang/cc/README.org @@ -11,6 +11,7 @@ - [[#irony-server][irony-server]] - [[#macos][MacOS]] - [[#arch-linux][Arch Linux]] + - [[#opensuse][openSUSE]] - [[#rtags][rtags]] - [[#configure][Configure]] - [[#project-compile-settings][Project compile settings]] @@ -93,6 +94,11 @@ rm -rf irony-mode pacman -S clang cmake #+END_SRC +*** openSUSE +#+BEGIN_SRC sh :dir /sudo:: +sudo zypper install clang cmake +#+END_SRC + ** rtags Code navigation requires an [[https://github.com/Andersbakken/rtags][rtags]] server (~rdm~) installed. This should be available through your OS's package manager. diff --git a/modules/lang/cc/autoload.el b/modules/lang/cc/autoload.el index 079d541b6..251840b85 100644 --- a/modules/lang/cc/autoload.el +++ b/modules/lang/cc/autoload.el @@ -103,7 +103,7 @@ simpler." (rtags-call-rc :silent t "-J" (or (doom-project-root) default-directory)))) ;; then irony (when (and (featurep 'irony) irony-mode) - (+cc|irony-init-compile-options))) + (+cc-init-irony-compile-options-h))) ;;;###autoload (defun +cc/imenu () diff --git a/modules/lang/clojure/config.el b/modules/lang/clojure/config.el index e115f4c15..e813f709c 100644 --- a/modules/lang/clojure/config.el +++ b/modules/lang/clojure/config.el @@ -1,22 +1,22 @@ ;;; lang/clojure/config.el -*- lexical-binding: t; -*- -;;;###package clojure-mode -(add-hook 'clojure-mode-hook #'rainbow-delimiters-mode) +(after! clojure-mode + (add-hook 'clojure-mode-hook #'rainbow-delimiters-mode) + + (set-repl-handler! 'clojure-mode #'+clojure/repl) + (set-eval-handler! 'clojure-mode #'cider-eval-region)) (use-package! cider ;; NOTE: if you don't have an org directory set (the dir doesn't exist), ;; cider jack in won't work. - :commands (cider-jack-in cider-jack-in-clojurescript) + :commands cider-jack-in cider-jack-in-clojurescript :hook (clojure-mode-local-vars . cider-mode) - :init - (set-repl-handler! 'clojure-mode #'+clojure/repl) - (set-eval-handler! 'clojure-mode #'cider-eval-region) + :config + (add-hook 'cider-mode-hook #'eldoc-mode) (set-lookup-handlers! 'cider-mode :definition #'+clojure-cider-lookup-definition :documentation #'cider-doc) - (add-hook 'cider-mode-hook #'eldoc-mode) - :config (set-popup-rules! '(("^\\*cider-error*" :ignore t) ("^\\*cider-repl" :quit nil) @@ -134,10 +134,9 @@ (use-package! clj-refactor :hook (clojure-mode . clj-refactor-mode) - :init + :config (set-lookup-handlers! 'clj-refactor-mode :references #'cljr-find-usages) - :config (map! :map clojure-mode-map :localleader :desc "refactor" "R" #'hydra-cljr-help-menu/body)) diff --git a/modules/lang/coq/autoload.el b/modules/lang/coq/autoload.el index 624185018..988b0558e 100644 --- a/modules/lang/coq/autoload.el +++ b/modules/lang/coq/autoload.el @@ -1,4 +1,15 @@ ;;; lang/coq/autoload.el -*- lexical-binding: t; -*- +;; HACK `proof-general' ascertains its own library path at compile time in its +;; autoloads file using `byte-compile-current-file' (and stores it in +;; `pg-init--script-full-path'). This means that when +;; `doom-package-autoload-file' is created and byte-compiled, +;; `pg-init--script-full-path' will be wrong, causing file-missing errors as it +;; tries to load `proof-site'. We prevent this by defining these two variables +;; early, in our own autoloads file. +;;;###autoload +(setq pg-init--script-full-path (locate-library "proof-general") + pg-init--pg-root (file-name-directory pg-init--script-full-path)) + ;;;###autoload (add-hook 'coq-mode-hook #'company-coq-mode) diff --git a/modules/lang/coq/config.el b/modules/lang/coq/config.el index 8bfdcf90e..565f7c4b1 100644 --- a/modules/lang/coq/config.el +++ b/modules/lang/coq/config.el @@ -7,6 +7,45 @@ ;; library included with Doom). (setq coq-mode-abbrev-table '()) +(map! :after coq-mode + :map coq-mode-map + :localleader + "]" #'proof-assert-next-command-interactive + "[" #'proof-undo-last-successful-command + "." #'proof-goto-point + (:prefix ("l" . "layout") + "c" #'pg-response-clear-displays + "l" #'proof-layout-windows + "p" #'proof-prf) + (:prefix ("p" . "proof") + "i" #'proof-interrupt-process + "p" #'proof-process-buffer + "q" #'proof-shell-exit + "r" #'proof-retract-buffer) + (:prefix ("a" . "about/print/check") + "a" #'coq-Print + "A" #'coq-Print-with-all + "b" #'coq-About + "B" #'coq-About-with-all + "c" #'coq-Check + "C" #'coq-Check-show-all + "f" #'proof-find-theorems + (:prefix ("i" . "implicits") + "b" #'coq-About-with-implicits + "c" #'coq-Check-show-implicits + "i" #'coq-Print-with-implicits)) + (:prefix ("g" . "goto") + "e" #'proof-goto-command-end + "l" #'proof-goto-end-of-locked + "s" #'proof-goto-command-start) + (:prefix ("i" . "insert") + "c" #'coq-insert-command + "e" #'coq-end-Section + "i" #'coq-insert-intros + "r" #'coq-insert-requires + "s" #'coq-insert-section-or-module + "t" #'coq-insert-tactic + "T" #'coq-insert-tactical)) (after! company-coq (set-popup-rule! "^\\*\\(?:response\\|goals\\)\\*" :ignore t) @@ -15,4 +54,15 @@ :references #'company-coq-grep-symbol :documentation #'company-coq-doc) (unless (featurep! :completion company) - (setq company-coq-disabled-features '(company company-defaults)))) + (setq company-coq-disabled-features '(company company-defaults))) + + (map! :map coq-mode-map + :localleader + (:prefix ("i" . "insert") + "l" #'company-coq-lemma-from-goal + "m" #'company-coq-insert-match-construct) + "ao" #'company-coq-occur + (:prefix ("h" . "help") + "e" #'company-coq-document-error + "E" #'company-coq-browse-error-messages + "h" #'company-coq-doc))) diff --git a/modules/lang/csharp/README.org b/modules/lang/csharp/README.org index 6006e1689..0b24c0fa6 100644 --- a/modules/lang/csharp/README.org +++ b/modules/lang/csharp/README.org @@ -1,28 +1,39 @@ #+TITLE: :lang csharp -This module adds C# support to Emacs. - -#+begin_quote -I don't use C# for much else than Unity3D and, seldomly, for Mono game -development on Linux. -#+end_quote - * Table of Contents :TOC: -- [[Install][Install]] - - [[MacOS][MacOS]] - - [[Arch Linux][Arch Linux]] +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] + - [[#macos][MacOS]] + - [[#arch-linux][Arch Linux]] + - [[#nixos][NixOS]] -* Install +* Description +This module adds C# support to Emacs. Powered by omnisharp (directly or through +LSP). + +** Module Flags ++ =+lsp= Enables omnisharp through LSP support (requires omnisharp). ++ =+unity= Enables special support for the [[https://unity.com/][Unity game engine]] (particularly, + support for HLSL shaders). + +** Plugins ++ [[https://github.com/josteink/csharp-mode][csharp-mode]] ++ [[https://github.com/OmniSharp/omnisharp-emacs][omnisharp]]* (not =+lsp=) ++ [[https://github.com/midnightSuyama/shader-mode][shader-mode]]* (=+unity=) + +* Prerequisites This module needs: -+ omnisharp-roslyn (install with ~M-x omnisharp-install-server~) ++ omnisharp (with the ~+lsp~ flag, this must be installed externally. Without + it, use ~M-x omnisharp-install-server~) + .NET SDKs (on Windows) + Mono (on UNIX platforms) -** MacOS -=TODO= - +** TODO MacOS ** Arch Linux -#+BEGIN_SRC sh :dir /sudo:: :tangle (if (doom-system-os 'arch) "yes") +#+BEGIN_SRC sh sudo pacman --needed --noconfirm -S mono #+END_SRC +** TODO NixOS diff --git a/modules/lang/csharp/config.el b/modules/lang/csharp/config.el index 960b4b943..d0852d899 100644 --- a/modules/lang/csharp/config.el +++ b/modules/lang/csharp/config.el @@ -3,6 +3,9 @@ (after! csharp-mode (add-hook 'csharp-mode-hook #'rainbow-delimiters-mode) + (when (featurep! +lsp) + (add-hook 'csharp-mode-local-vars-hook #'lsp!)) + (set-electric! 'csharp-mode :chars '(?\n ?\})) (set-rotate-patterns! 'csharp-mode :symbols '(("public" "protected" "private") @@ -13,6 +16,7 @@ (use-package! omnisharp + :unless (featurep! +lsp) :hook (csharp-mode . omnisharp-mode) :commands omnisharp-install-server :preface @@ -56,9 +60,10 @@ "b" #'omnisharp-unit-test-buffer))) +;;;###package shader-mode (when (featurep! +unity) - ;; `shader-mode' --- unity shaders - (add-to-list 'auto-mode-alist '("\\.shader$" . shader-mode)) + ;; Unity shaders + (add-to-list 'auto-mode-alist '("\\.shader\\'" . shader-mode)) (def-project-mode! +csharp-unity-mode :modes '(csharp-mode shader-mode) diff --git a/modules/lang/csharp/packages.el b/modules/lang/csharp/packages.el index 49b94bed4..07db938df 100644 --- a/modules/lang/csharp/packages.el +++ b/modules/lang/csharp/packages.el @@ -2,7 +2,9 @@ ;;; lang/csharp/packages.el (package! csharp-mode) -(package! omnisharp) + +(unless (featurep! +lsp) + (package! omnisharp)) (when (featurep! +unity) (package! shader-mode)) diff --git a/modules/lang/data/config.el b/modules/lang/data/config.el index 2692fac8c..035c96f0d 100644 --- a/modules/lang/data/config.el +++ b/modules/lang/data/config.el @@ -18,7 +18,7 @@ ;; ;;; Third-party plugins -;; `csv-mode' +;;;###package csv-mode (map! :after csv-mode :localleader :map csv-mode-map @@ -37,13 +37,12 @@ :config (set-electric! 'json-mode :chars '(?\n ?: ?{ ?}))) -(use-package! jsonnet-mode - :defer t - :config +(after! jsonnet-mode (set-electric! 'jsonnet-mode :chars '(?\n ?: ?{ ?}))) + ;; -;; Frameworks +;;; Frameworks (def-project-mode! +data-vagrant-mode :files ("Vagrantfile")) diff --git a/modules/lang/elixir/README.org b/modules/lang/elixir/README.org index 9a6b0aaa0..b20a65e9d 100644 --- a/modules/lang/elixir/README.org +++ b/modules/lang/elixir/README.org @@ -11,6 +11,7 @@ - [[#with-asdf][With ~asdf~]] - [[#arch-linux][Arch Linux]] - [[#gentoo-linux][Gentoo Linux]] + - [[#opensuse][openSUSE]] - [[#features][Features]] * Description @@ -47,6 +48,11 @@ sudo pacman -S elixir #+BEGIN_SRC sh :dir /sudo:: sudo emerge -v dev-lang/elixir #+END_SRC + +*** openSUSE +#+BEGIN_SRC sh :dir /sudo:: +sudo zypper install elixir +#+END_SRC * Features - Code completion (~:completion company~) - Documentation lookup (~:tools lookup~) diff --git a/modules/lang/elixir/config.el b/modules/lang/elixir/config.el index b6750c24c..04d78ad5f 100644 --- a/modules/lang/elixir/config.el +++ b/modules/lang/elixir/config.el @@ -32,18 +32,6 @@ (when (featurep! +lsp) (add-hook 'elixir-mode-local-vars-hook #'lsp!)) - (use-package! alchemist-company - :when (featurep! :completion company) - :commands alchemist-company - :init - (set-company-backend! 'elixir-mode '(alchemist-company company-yasnippet)) - :config - ;; Alchemist doesn't use hook symbols to add these backends, so we have to - ;; use the entire closure to get rid of it. - (let ((fn (byte-compile (lambda () (add-to-list (make-local-variable 'company-backends) 'alchemist-company))))) - (remove-hook 'alchemist-mode-hook fn) - (remove-hook 'alchemist-iex-mode-hook fn))) - (use-package! flycheck-credo :when (featurep! :tools flycheck) :config (flycheck-credo-setup))) @@ -51,9 +39,24 @@ (use-package! alchemist :hook (elixir-mode . alchemist-mode) + :init + (after! elixir-mode + (set-lookup-handlers! 'elixir-mode + :definition #'alchemist-goto-definition-at-point + :documentation #'alchemist-help-search-at-point) + (set-eval-handler! 'elixir-mode #'alchemist-eval-region) + (set-repl-handler! 'elixir-mode #'alchemist-iex-project-run))) + + +(use-package! alchemist-company + :when (featurep! :completion company) + :commands alchemist-company + :init + (after! elixir-mode + (set-company-backend! 'elixir-mode '(alchemist-company company-yasnippet))) :config - (set-lookup-handlers! 'elixir-mode - :definition #'alchemist-goto-definition-at-point - :documentation #'alchemist-help-search-at-point) - (set-eval-handler! 'elixir-mode #'alchemist-eval-region) - (set-repl-handler! 'elixir-mode #'alchemist-iex-project-run)) + ;; Alchemist doesn't use hook symbols to add these backends, so we have to use + ;; the entire closure to get rid of it. + (let ((fn (byte-compile (lambda () (add-to-list (make-local-variable 'company-backends) 'alchemist-company))))) + (remove-hook 'alchemist-mode-hook fn) + (remove-hook 'alchemist-iex-mode-hook fn))) diff --git a/modules/lang/elm/config.el b/modules/lang/elm/config.el index c96baafb4..479800eed 100644 --- a/modules/lang/elm/config.el +++ b/modules/lang/elm/config.el @@ -1,8 +1,5 @@ ;;; lang/elm/config.el -*- lexical-binding: t; -*- -;; `elm-mode' -(setq elm-format-on-save t) - (after! elm-mode (add-hook 'elm-mode-hook #'rainbow-delimiters-mode) diff --git a/modules/lang/emacs-lisp/autoload.el b/modules/lang/emacs-lisp/autoload.el index 9432513ae..87be59029 100644 --- a/modules/lang/emacs-lisp/autoload.el +++ b/modules/lang/emacs-lisp/autoload.el @@ -135,7 +135,7 @@ if it's callable, `apropos' otherwise." `(("Section" "^[ \t]*;;;;*[ \t]+\\([^\n]+\\)" 1) ("Evil commands" "^\\s-*(evil-define-\\(?:command\\|operator\\|motion\\) +\\(\\_<[^ ()\n]+\\_>\\)" 1) ("Unit tests" "^\\s-*(\\(?:ert-deftest\\|describe\\) +\"\\([^\")]+\\)\"" 1) - ("Package" "^\\s-*(\\(?:;;;###package\\|def-package!\\|package!\\|use-package\\|after!\\) +\\(\\_<[^ ()\n]+\\_>\\)" 1) + ("Package" "^\\s-*(\\(?:;;;###package\\|package!\\|use-package!?\\|after!\\) +\\(\\_<[^ ()\n]+\\_>\\)" 1) ("Major modes" "^\\s-*(define-derived-mode +\\([^ ()\n]+\\)" 1) ("Minor modes" "^\\s-*(define-\\(?:global\\(?:ized\\)?-minor\\|generic\\|minor\\)-mode +\\([^ ()\n]+\\)" 1) ("Modelines" "^\\s-*(def-modeline! +\\([^ ()\n]+\\)" 1) @@ -174,3 +174,15 @@ verbosity when editing a file in `doom-private-dir' or `doom-emacs-dir'." " " (default-value 'flycheck-emacs-lisp-check-form) ")")))) + +;;;###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/config.el b/modules/lang/emacs-lisp/config.el index d673197b9..6c734424f 100644 --- a/modules/lang/emacs-lisp/config.el +++ b/modules/lang/emacs-lisp/config.el @@ -77,8 +77,10 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") (map! :localleader :map emacs-lisp-mode-map - "e" #'macrostep-expand)) - + "e" #'macrostep-expand + (:prefix ("d" . "debug") + ("f" #'+emacs-lisp/edebug-instrument-defun-on) + ("F" #'+emacs-lisp/edebug-instrument-defun-off)))) ;; ;;; Packages @@ -112,8 +114,20 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") "Add Doom's own demos to help buffers." :around #'elisp-demos--search (or (funcall orig-fn symbol) - (when-let* ((elisp-demos--elisp-demos.org (doom-glob doom-docs-dir "api.org"))) - (funcall orig-fn symbol))))) + (when-let (demos-file (doom-glob doom-docs-dir "api.org")) + (with-temp-buffer + (insert-file-contents demos-file) + (goto-char (point-min)) + (when (re-search-forward + (format "^\\*\\*\\* %s$" (regexp-quote (symbol-name symbol))) + nil t) + (let (beg end) + (forward-line 1) + (setq beg (point)) + (if (re-search-forward "^\\*" nil t) + (setq end (line-beginning-position)) + (setq end (point-max))) + (string-trim (buffer-substring-no-properties beg end))))))))) (use-package! buttercup diff --git a/modules/lang/erlang/config.el b/modules/lang/erlang/config.el index 7e748fbba..6324785fc 100644 --- a/modules/lang/erlang/config.el +++ b/modules/lang/erlang/config.el @@ -1,9 +1,9 @@ ;;; lang/erlang/config.el -*- lexical-binding: t; -*- (use-package! erlang - :mode ("\\.erlang$" . erlang-mode) - :mode ("/rebar\\.config\\(?:\\.script\\)?$" . erlang-mode) - :mode ("/\\(?:app\\|sys\\)\\.config$" . erlang-mode)) + :mode ("\\.erlang\\'" . erlang-mode) + :mode ("/rebar\\.config\\(?:\\.script\\)?\\'" . erlang-mode) + :mode ("/\\(?:app\\|sys\\)\\.config\\'" . erlang-mode)) (use-package! flycheck-rebar3 diff --git a/modules/lang/ess/config.el b/modules/lang/ess/config.el index afbde141b..1041a5130 100644 --- a/modules/lang/ess/config.el +++ b/modules/lang/ess/config.el @@ -1,7 +1,7 @@ ;;; lang/ess/config.el -*- lexical-binding: t; -*- (use-package! ess - :commands (stata SAS) + :commands stata SAS :init (setq ess-smart-S-assign-key nil) (unless (featurep! :lang julia) diff --git a/modules/lang/go/README.org b/modules/lang/go/README.org index 685462917..d8c1d5cd4 100644 --- a/modules/lang/go/README.org +++ b/modules/lang/go/README.org @@ -4,15 +4,15 @@ #+STARTUP: inlineimages * Table of Contents :TOC: -- [[Description][Description]] - - [[Module Flags][Module Flags]] - - [[Plugins][Plugins]] -- [[Prerequisites][Prerequisites]] - - [[Go][Go]] - - [[Dependencies][Dependencies]] -- [[Features][Features]] -- [[Configuration][Configuration]] -- [[Troubleshooting][Troubleshooting]] +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] + - [[#go][Go]] + - [[#dependencies][Dependencies]] +- [[#features][Features]] +- [[#configuration][Configuration]] +- [[#troubleshooting][Troubleshooting]] * Description This module adds [[https://golang.org][Go]] support. @@ -35,6 +35,7 @@ This module provides no flags. + [[https://github.com/syohex/emacs-go-eldoc][go-eldoc]] + [[https://github.com/dominikh/go-mode.el][go-guru]] + [[https://github.com/manute/gorepl-mode][gorepl-mode]] ++ [[https://github.com/syohex/emacs-go-add-tags][go-add-tags]] + [[https://github.com/mdempsky/gocode][company-go]]* * Prerequisites @@ -51,6 +52,11 @@ brew install go sudo pacman -S go #+END_SRC +*** openSUSE +#+BEGIN_SRC sh :dir /sudo:: +sudo zypper install go +#+END_SRC + ** Dependencies This module requires a valid ~GOPATH~, and the following Go packages: diff --git a/modules/lang/go/autoload.el b/modules/lang/go/autoload.el index 8ef830833..709bd2a2e 100644 --- a/modules/lang/go/autoload.el +++ b/modules/lang/go/autoload.el @@ -6,17 +6,21 @@ (defvar +go-test-last nil "The last test run.") -(defun +go--run-tests (args) - (require 'async) +(defun +go--spawn (cmd) (save-selected-window - (async-shell-command (concat "go test " args)))) + (compile cmd))) + +(defun +go--run-tests (args) + (let ((cmd (concat "go test " args))) + (setq +go-test-last (concat "cd " default-directory ";" cmd)) + (+go--spawn cmd))) ;;;###autoload (defun +go/test-rerun () (interactive) (if +go-test-last - (funcall +go-test-last) - (+go/run-all-tests))) + (+go--spawn +go-test-last) + (+go/test-all))) ;;;###autoload (defun +go/test-all () diff --git a/modules/lang/go/config.el b/modules/lang/go/config.el index cb999a169..eaa37e862 100644 --- a/modules/lang/go/config.el +++ b/modules/lang/go/config.el @@ -1,7 +1,7 @@ ;;; lang/go/config.el -*- lexical-binding: t; -*- ;; -;; Packages +;;; Packages (after! go-mode (set-docsets! 'go-mode "Go") @@ -25,6 +25,7 @@ (map! :map go-mode-map :localleader + "a" #'go-add-tags "e" #'+go/play-buffer-or-region "i" #'go-goto-imports ; Go to imports (:prefix ("h" . "help") @@ -58,8 +59,8 @@ (use-package! company-go - :when (and (featurep! :completion company) - (not (featurep! +lsp))) + :when (featurep! :completion company) + :unless (featurep! +lsp) :after go-mode :config (set-company-backend! 'go-mode 'company-go) diff --git a/modules/lang/go/packages.el b/modules/lang/go/packages.el index 09dba5325..c5bb1bc70 100644 --- a/modules/lang/go/packages.el +++ b/modules/lang/go/packages.el @@ -5,6 +5,7 @@ (package! go-guru) (package! go-mode) (package! gorepl-mode) +(package! go-add-tags) (when (featurep! :completion company) (package! company-go)) diff --git a/modules/lang/haskell/README.org b/modules/lang/haskell/README.org index 88a4c09b0..9f160ca88 100644 --- a/modules/lang/haskell/README.org +++ b/modules/lang/haskell/README.org @@ -72,7 +72,6 @@ To use Intero, you need =stack=: brew install haskell-stack stack setup #+END_SRC - *** Arch Linux #+BEGIN_SRC sh sudo pacman -S stack @@ -81,6 +80,12 @@ pacaur -S ncurses5-compat-lib stack setup #+END_SRC +*** openSUSE +#+BEGIN_SRC sh :dir /sudo:: +sudo zypper install stack +stack setup +#+END_SRC + ** Cabal To use Dante, you need =cabal= (the haskell package builder) and =ghci= (the compiler, syntax checker & repl): @@ -95,6 +100,11 @@ brew install cabal-install ghc sudo pacman -S cabal-install ghc #+END_SRC +*** openSUSE +#+BEGIN_SRC sh :dir /sudo:: +sudo zypper install cabal-install ghc +#+END_SRC + ** LSP You will need =stack= and =git= installed. diff --git a/modules/lang/javascript/README.org b/modules/lang/javascript/README.org index 7e2f68898..cb1add7ab 100644 --- a/modules/lang/javascript/README.org +++ b/modules/lang/javascript/README.org @@ -10,6 +10,7 @@ - [[#prerequisites][Prerequisites]] - [[#macos][MacOS]] - [[#arch-linux][Arch Linux]] + - [[#opensuse][openSUSE]] - [[#appendix][Appendix]] - [[#commands][Commands]] @@ -56,6 +57,11 @@ brew install node sudo pacman --needed --noconfirm -S nodejs npm #+END_SRC +** openSUSE +#+BEGIN_SRC sh :dir /sudo:: +sudo zypper install nodejs npm +#+END_SRC + * Appendix ** Commands *** JS2-mode diff --git a/modules/lang/javascript/config.el b/modules/lang/javascript/config.el index a569871b5..55e8daff1 100644 --- a/modules/lang/javascript/config.el +++ b/modules/lang/javascript/config.el @@ -145,8 +145,7 @@ to tide." ;; necessary because `tide-setup' and `lsp' will error if not a ;; file-visiting buffer (add-hook 'after-save-hook #'+javascript-init-tide-or-lsp-maybe-h nil 'local) - (or (and (featurep! +lsp) - (progn (lsp!) lsp-mode)) + (or (and (featurep! +lsp) (lsp!)) ;; fall back to tide (if (executable-find "node") (and (require 'tide nil t) @@ -238,7 +237,7 @@ to tide." ;;;###package npm-mode -(use-package npm-mode +(use-package! npm-mode :hook ((js-mode typescript-mode) . npm-mode) :config (map! :localleader diff --git a/modules/lang/julia/config.el b/modules/lang/julia/config.el index 385b49b7a..fa5944f31 100644 --- a/modules/lang/julia/config.el +++ b/modules/lang/julia/config.el @@ -1,6 +1,6 @@ ;;; lang/julia/config.el -*- lexical-binding: t; -*- -(use-package julia-mode +(use-package! julia-mode :interpreter "julia" :config (set-repl-handler! 'julia-mode #'+julia/repl) diff --git a/modules/lang/kotlin/config.el b/modules/lang/kotlin/config.el index d1b2ac1ab..06a1588b8 100644 --- a/modules/lang/kotlin/config.el +++ b/modules/lang/kotlin/config.el @@ -10,7 +10,7 @@ :desc "gradlew build" "b" (λ! (+kotlin/run-gradlew "build")) :desc "gradlew test" "t" (λ! (+kotlin/run-gradlew "test")))) + (use-package! flycheck-kotlin :when (featurep! :tools flycheck) - :after kotlin-mode - :config (add-hook 'kotlin-mode-hook #'flycheck-kotlin-setup)) + :hook (kotlin-mode . flycheck-kotlin-setup)) diff --git a/modules/lang/latex/+viewers.el b/modules/lang/latex/+viewers.el index 012ceeafd..1e92f3bd9 100644 --- a/modules/lang/latex/+viewers.el +++ b/modules/lang/latex/+viewers.el @@ -4,7 +4,7 @@ (add-to-list 'TeX-view-program-selection '(output-pdf "preview-pane") 'append) (add-to-list 'TeX-view-program-list '("preview-pane" latex-preview-pane-mode)) -(dolist (viewer +latex-viewers) +(dolist (viewer (reverse +latex-viewers)) (pcase viewer (`skim (when (and IS-MAC @@ -39,7 +39,7 @@ ;; PDF Tools isn't in `TeX-view-program-list-builtin' on macs (add-to-list 'TeX-view-program-list '("PDF Tools" TeX-pdf-tools-sync-view))) ;; Update PDF buffers after successful LaTeX runs - (add-hook 'TeX-after-compilation-finished-function #'TeX-revert-document-buffer))))) + (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer))))) (after! latex-preview-pane diff --git a/modules/lang/latex/README.org b/modules/lang/latex/README.org index 202c69c11..209af8708 100644 --- a/modules/lang/latex/README.org +++ b/modules/lang/latex/README.org @@ -4,13 +4,13 @@ #+STARTUP: inlineimages * Table of Contents :TOC_3:noexport: -- [[Description][Description]] - - [[Module Flags][Module Flags]] - - [[Plugins][Plugins]] -- [[Features][Features]] -- [[Customization][Customization]] - - [[Specifying the location of a bibtex file & corresponding PDFs][Specifying the location of a bibtex file & corresponding PDFs]] - - [[Changing the PDFs viewer][Changing the PDFs viewer]] +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#features][Features]] +- [[#customization][Customization]] + - [[#specifying-the-location-of-a-bibtex-file--corresponding-pdfs][Specifying the location of a bibtex file & corresponding PDFs]] + - [[#changing-the-pdfs-viewer][Changing the PDFs viewer]] * Description Provide a helping hand when working with LaTeX documents. @@ -56,8 +56,10 @@ PDFs: This module provides integration for four supported pdf viewers. They are + [[https://skim-app.sourceforge.io/][Skim.app]] (MacOS only) -+ Okular ++ Evince ++ Sumatra PDF + Zathura ++ Okular + pdf-tools (requires =:tools pdf= module) They are searched for in this order. See ~+latex-viewers~ to change the order, diff --git a/modules/lang/latex/config.el b/modules/lang/latex/config.el index 8fae86080..1279b51d7 100644 --- a/modules/lang/latex/config.el +++ b/modules/lang/latex/config.el @@ -13,8 +13,8 @@ package to be installed.") (defvar +latex-viewers '(skim evince sumatrapdf zathura okular pdf-tools) "A list of enabled latex viewers to use, in this order. If they don't exist, -they will be ignored. Recognized viewers are skim, zathura, okular and -pdf-tools. +they will be ignored. Recognized viewers are skim, evince, sumatrapdf, zathura, +okular and pdf-tools. If no viewers are found, `latex-preview-pane' is used.") @@ -25,7 +25,7 @@ If no viewers are found, `latex-preview-pane' is used.") ;; ;; Packages -(add-to-list 'auto-mode-alist '("\\.tex\\'" . TeX-latex-mode)) +(add-to-list 'auto-mode-alist '("\\.tex\\'" . LaTeX-mode)) (after! tex @@ -66,10 +66,10 @@ If no viewers are found, `latex-preview-pane' is used.") (cl-find-if #'byte-code-function-p find-file-hook) 'local)) (add-hook 'latex-mode-local-vars-hook #'flyspell-mode!) - ;; All these excess pairs dramatically slow down typing in latex buffers, so - ;; we remove them. Let snippets do their job. (after! smartparens-latex (let ((modes '(tex-mode plain-tex-mode latex-mode LaTeX-mode))) + ;; All these excess pairs dramatically slow down typing in latex buffers, + ;; so we remove them. Let snippets do their job. (dolist (open '("\\left(" "\\left[" "\\left\\{" "\\left|" "\\bigl(" "\\biggl(" "\\Bigl(" "\\Biggl(" "\\bigl[" "\\biggl[" "\\Bigl[" "\\Biggl[" "\\bigl\\{" "\\biggl\\{" @@ -77,6 +77,8 @@ If no viewers are found, `latex-preview-pane' is used.") "\\lfloor" "\\lceil" "\\langle" "\\lVert" "\\lvert" "`")) (sp-local-pair modes open nil :actions :rem)) + ;; And tweak these so that users can decide whether they want use latex + ;; quotes or not, via `+latex-enable-plain-double-quotes' (sp-local-pair modes "``" nil :unless '(:add sp-in-math-p))))) diff --git a/modules/lang/lean/config.el b/modules/lang/lean/config.el new file mode 100644 index 000000000..3cff278d5 --- /dev/null +++ b/modules/lang/lean/config.el @@ -0,0 +1,36 @@ +;;; lang/lean/config.el -*- lexical-binding: t; -*- + +(after! lean-mode + (set-lookup-handlers! 'lean-mode + :definition #'lean-find-definition) + (sp-with-modes 'lean-mode + (sp-local-pair "/-" "-/") + (sp-local-pair "`" "`") + (sp-local-pair "{" "}") + (sp-local-pair "«" "»") + (sp-local-pair "⟨" "⟩") + (sp-local-pair "⟪" "⟫")) + (map! :map lean-mode-map + :localleader + "g" #'lean-toggle-show-goal + "n" #'lean-toggle-next-error + (:prefix ("s" . "server") + "r" #'lean-server-restart + "s" #'lean-server-stop + "v" #'lean-server-switch-version) + (:prefix ("p" . "leanpkg") + "t" #'lean-leanpkg-test + "b" #'lean-leanpkg-build + "c" #'lean-leanpkg-configure) + "f" #'lean-fill-placeholder + "h" #'lean-hole + "m" #'lean-message-boxes-toggle + "e" #'lean-execute)) + + +(use-package! company-lean + :init + (advice-add #'company-lean-hook :override #'ignore) + (set-company-backend! 'lean-mode 'company-lean) + :when (featurep! :completion company) + :after lean-mode) diff --git a/modules/lang/lean/packages.el b/modules/lang/lean/packages.el new file mode 100644 index 000000000..6061c61f6 --- /dev/null +++ b/modules/lang/lean/packages.el @@ -0,0 +1,7 @@ +;; -*- no-byte-compile: t; -*- +;;; lang/lean/packages.el + +(package! lean-mode) + +(when (featurep! :completion company) + (package! company-lean)) diff --git a/modules/lang/ledger/config.el b/modules/lang/ledger/config.el index d520a4d4e..35f6b83dd 100644 --- a/modules/lang/ledger/config.el +++ b/modules/lang/ledger/config.el @@ -1,14 +1,14 @@ ;;; lang/ledger/config.el -*- lexical-binding: t; -*- -;; `ledger-mode' +;;;###package ledger-mode (setq ledger-clear-whole-transactions 1) -(defun +ledger*check-version (orig-fn) +(defadvice! +ledger--check-version-a (orig-fn) "Fail gracefully if ledger binary isn't available." + :around #'ledger-check-version (if (executable-find ledger-binary-path) (funcall orig-fn) (message "Couldn't find '%s' executable" ledger-binary-path))) -(advice-add #'ledger-check-version :around #'+ledger*check-version) ;; Restore leader key in ledger reports (map! :after ledger-mode @@ -51,7 +51,8 @@ (:prefix "g" "s" #'ledger-display-ledger-stats "b" #'ledger-display-balance-at-point)) - ;; Fix inaccurate keybind message - (defun +ledger*fix-key-help (&rest _) - (message "q to quit; gr to redo; RET to edit; C-c C-s to save")) - (advice-add #'ledger-report :after #'+ledger*fix-key-help)) + + (defadvice! +ledger--fix-key-help-a (&rest _) + "Fix inaccurate keybind message." + :after #'ledger-report + (message "q to quit; gr to redo; RET to edit; C-c C-s to save"))) diff --git a/modules/lang/lua/autoload/moonscript.el b/modules/lang/lua/autoload/moonscript.el index 77f9ae07a..a3597db4c 100644 --- a/modules/lang/lua/autoload/moonscript.el +++ b/modules/lang/lua/autoload/moonscript.el @@ -2,13 +2,13 @@ ;;;###if (featurep! +moonscript) ;;;###autoload -(defun +lua|moonscript-fix-single-quotes () +(defun +lua-moonscript-fix-single-quotes-h () "Single-quoted strings aren't treated as strings." ;; (modify-syntax-entry ?\" "\"" moonscript-mode-syntax-table) (modify-syntax-entry ?\' "\"" moonscript-mode-syntax-table)) ;;;###autoload -(defun +lua|moonscript-fontify-interpolation () +(defun +lua-moonscript-fontify-interpolation-h () "Highlight interpolated expressions in moonscript strings." (font-lock-add-keywords nil '(("#{\\([^}]+\\)}" diff --git a/modules/lang/lua/config.el b/modules/lang/lua/config.el index ec8d58591..b69e286e5 100644 --- a/modules/lang/lua/config.el +++ b/modules/lang/lua/config.el @@ -26,8 +26,10 @@ (setq-hook! 'moonscript-mode-hook moonscript-indent-offset tab-width) (add-hook! 'moonscript-mode-hook - #'+lua|moonscript-fix-single-quotes - #'+lua|moonscript-fontify-interpolation)) + #'+lua-moonscript-fix-single-quotes-h + #'+lua-moonscript-fontify-interpolation-h) + (when (featurep! :tools flycheck) + (require 'flycheck-moonscript nil t))) ;; diff --git a/modules/lang/lua/packages.el b/modules/lang/lua/packages.el index c5fe05e07..c4e8e4f7b 100644 --- a/modules/lang/lua/packages.el +++ b/modules/lang/lua/packages.el @@ -2,7 +2,12 @@ ;;; lang/lua/packages.el (package! lua-mode) -(package! moonscript) + +(when (featurep! +moonscript) + (package! moonscript) + (when (featurep! :tools flycheck) + (package! flycheck-moonscript + :recipe (:host github :repo "hlissner/emacs-flycheck-moonscript")))) (when (featurep! :completion company) (package! company-lua)) diff --git a/modules/lang/markdown/autoload.el b/modules/lang/markdown/autoload.el index 1ae173fc1..84d5533c8 100644 --- a/modules/lang/markdown/autoload.el +++ b/modules/lang/markdown/autoload.el @@ -36,10 +36,7 @@ otherwise throws an error." Returns its exit code." (when (executable-find "marked") (apply #'call-process-region - beg end - shell-file-name nil output-buffer nil - shell-command-switch - "marked" + beg end "marked" nil output-buffer nil (when (eq major-mode 'gfm-mode) (list "--gfm" "--tables" "--breaks"))))) @@ -48,21 +45,18 @@ Returns its exit code." "Compiles markdown with the pandoc program, if available. Returns its exit code." (when (executable-find "pandoc") - (call-process-region beg end - shell-file-name nil output-buffer nil - shell-command-switch - "pandoc" "-f" "markdown" "-t" "html" - "--standalone" "--mathjax" "--highlight-style=pygments"))) + (call-process-region beg end "pandoc" nil output-buffer nil + "-f" "markdown" + "-t" "html" + "--mathjax" + "--highlight-style=pygments"))) ;;;###autoload (defun +markdown-compile-multimarkdown (beg end output-buffer) "Compiles markdown with the multimarkdown program, if available. Returns its exit code." (when (executable-find "multimarkdown") - (call-process-region beg end - shell-file-name nil output-buffer nil - shell-command-switch - "multimarkdown"))) + (call-process-region beg end "multimarkdown" nil output-buffer))) ;;;###autoload (defun +markdown-compile-markdown (beg end output-buffer) @@ -70,10 +64,7 @@ exit code." available. Returns its exit code." (when-let (exe (or (executable-find "Markdown.pl") (executable-find "markdown"))) - (call-process-region beg end - shell-file-name nil output-buffer nil - shell-command-switch - exe))) + (call-process-region beg end exe nil output-buffer nil))) ;; ;;; Commands diff --git a/modules/lang/markdown/config.el b/modules/lang/markdown/config.el index 2f41a7a9f..37608ce5f 100644 --- a/modules/lang/markdown/config.el +++ b/modules/lang/markdown/config.el @@ -38,6 +38,7 @@ capture, the end position, and the output buffer.") (set-lookup-handlers! '(markdown-mode gfm-mode) :file #'markdown-follow-thing-at-point) + ;; Enable hard-wrapping. By convention, Doom does this for all textual modes. (add-hook 'markdown-mode-hook #'auto-fill-mode) ;; Prevent mis-fontification of YAML metadata blocks in `markdown-mode' which diff --git a/modules/lang/nim/README.org b/modules/lang/nim/README.org index bd12a1fc3..67ac7c744 100644 --- a/modules/lang/nim/README.org +++ b/modules/lang/nim/README.org @@ -11,10 +11,10 @@ This module adds [[https://nim-lang.org][Nim]] support to Emacs. + Babel support (~ob-nim~) * Table of Contents :TOC: -- [[Module Flags][Module Flags]] -- [[Prerequisites][Prerequisites]] - - [[Nim][Nim]] -- [[Configuration][Configuration]] +- [[#module-flags][Module Flags]] +- [[#prerequisites][Prerequisites]] + - [[#nim][Nim]] +- [[#configuration][Configuration]] * Module Flags This module provides no flags. @@ -44,4 +44,9 @@ brew install nim sudo pacman --needed --noconfirm -S nim nimble #+END_SRC +*** openSUSE +#+BEGIN_SRC sh :dir /sudo:: +sudo zypper install nim +#+END_SRC + * Configuration diff --git a/modules/lang/nim/config.el b/modules/lang/nim/config.el index 02b81aba3..2435c93d0 100644 --- a/modules/lang/nim/config.el +++ b/modules/lang/nim/config.el @@ -1,14 +1,14 @@ ;;; lang/nim/config.el -*- lexical-binding: t; -*- (after! nim-mode - (defun +nim|init-nimsuggest-mode () - "Conditionally load `nimsuggest-mode', instead of clumsily erroring out if + (add-hook! 'nim-mode-hook + (defun +nim-init-nimsuggest-mode-h () + "Conditionally load `nimsuggest-mode', instead of clumsily erroring out if nimsuggest isn't installed." - (unless (stringp nimsuggest-path) - (setq nimsuggest-path (executable-find "nimsuggest"))) - (when (and nimsuggest-path (file-executable-p nimsuggest-path)) - (nimsuggest-mode))) - (add-hook 'nim-mode-hook #'+nim|init-nimsuggest-mode) + (unless (stringp nimsuggest-path) + (setq nimsuggest-path (executable-find "nimsuggest"))) + (when (and nimsuggest-path (file-executable-p nimsuggest-path)) + (nimsuggest-mode)))) (when IS-WINDOWS ;; TODO File PR/report upstream (https://github.com/nim-lang/nim-mode) diff --git a/modules/lang/nix/autoload.el b/modules/lang/nix/autoload.el new file mode 100644 index 000000000..bce624513 --- /dev/null +++ b/modules/lang/nix/autoload.el @@ -0,0 +1,41 @@ +;;; lang/nix/autoload.el -*- lexical-binding: t; -*- + +(defun +nix--options-action (candidate) + (switch-to-buffer-other-window + (nixos-options-doc-buffer + (nixos-options-get-documentation-for-option candidate)))) + +;;;###autoload +(defun +nix/lookup-option (&optional initial-input) + "Look up documentation on a nix option." + (interactive + (list + ;; REVIEW Must be a better way to do this + (when (and (looking-at-p "[a-zA-Z0-9-_\\.]") + (not (doom-point-in-string-or-comment-p))) + (buffer-substring-no-properties + (save-excursion + (skip-chars-backward "^ ") + (point)) + (save-excursion + (skip-chars-forward "^ ") + (point)))))) + (cond ((featurep! :completion helm) + (require 'helm-nixos-options) + ;; REVIEW We reimplment `helm-nixos-options' so we can supply + ;; `initial-input'. Maybe use `helm-attrset' instead? + (helm :sources `(,(helm-source-nixos-options-search)) + :buffer "*helm-nixos-options*" + :input initial-input)) + ((featurep! :completion ivy) + (require 'nixos-options) + (ivy-read "NixOS options: " + nixos-options + :require-match t + :initial-input initial-input + :action #'+nix--options-action + :caller '+nix/options)) + ;; TODO Add general `completing-read' support + ((user-error "No search engine is enabled. Enable helm or ivy!"))) + ;; Tell lookup module to let us handle things from here + 'deferred) diff --git a/modules/lang/nix/config.el b/modules/lang/nix/config.el index 40ffe56d5..8c3c3dc8c 100644 --- a/modules/lang/nix/config.el +++ b/modules/lang/nix/config.el @@ -4,6 +4,8 @@ :mode "\\.nix\\'" :config (set-company-backend! 'nix-mode 'company-nixos-options) + (set-lookup-handlers! 'nix-mode + :documentation '(+nix/lookup-option :async t)) (map! :localleader :map nix-mode-map @@ -13,8 +15,7 @@ "s" #'nix-shell "b" #'nix-build "u" #'nix-unpack - (:when (featurep! :completion helm) - "o" #'helm-nixos-options))) + "o" #'+nix/lookup-option)) (use-package! nix-drv-mode :mode "\\.drv\\'") diff --git a/modules/lang/ocaml/README.org b/modules/lang/ocaml/README.org index e05d2764f..9f6755935 100644 --- a/modules/lang/ocaml/README.org +++ b/modules/lang/ocaml/README.org @@ -1,8 +1,23 @@ -#+TITLE: :lang ocaml +#+TITLE: lang/ocaml +#+DATE: June 27, 2017 +#+SINCE: v2.0.3 -This module adds [[https://ocaml.org/][OCaml]] support, powered by [[https://github.com/ocaml/tuareg][tuareg-mode]]. +* Table of Contents :TOC: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] +- [[#configuration][Configuration]] +- [[#appendix][Appendix]] + - [[#commands][Commands]] + - [[#hacks][Hacks]] -+ Code completion, documentation look-up, code navigation and refactoring ([[https://github.com/ocaml/merlin/wiki/emacs-from-scratch][merlin]]) +* Description +This module adds [[https://ocaml.org/][OCaml]] support to Doom Emacs, powered by [[https://github.com/ocaml/tuareg][tuareg-mode]]. + ++ Code completion, documentation look-up, code navigation and refactoring + ([[https://github.com/ocaml/merlin/wiki/emacs-from-scratch][merlin]]) + Type, documentation and function argument display on idle ([[https://github.com/Khady/merlin-eldoc][merlin-eldoc]]) + REPL ([[https://github.com/ocaml-community/utop][utop]]) + Syntax-checking (~merlin~ with [[https://github.com/flycheck/flycheck-ocaml][flycheck-ocaml]]) @@ -10,75 +25,66 @@ This module adds [[https://ocaml.org/][OCaml]] support, powered by [[https://gi + Code formatting ([[https://github.com/ocaml-ppx/ocamlformat][ocamlformat]]) + Dune file format ([[http://dune.build/][dune]]) -* Table of Contents :TOC: -- [[Module Flags][Module Flags]] -- [[Prerequisites][Prerequisites]] -- [[Features][Features]] -- [[Configuration][Configuration]] -- [[Appendix][Appendix]] - - [[Commands][Commands]] - - [[Hacks][Hacks]] +** Module Flags +This module provides no flags. -* Module Flags -This module provides the ~+opam-site-lisp~ flag to compile editor support ~.el~ -files from opam's =site-lisp= directory. -By default all editor plugins are installed from MELPA/GitHub even if the -corresponding =opam= package isn't installed. -If this flag is enabled then you must have all of the packages listed in -=package.el= installed via =opam=. - -To enable this, use: -#+BEGIN_SRC emacs-lisp -(doom! :lang (ocaml +opam-site-lisp)) -#+END_SRC +** Plugins ++ [[https://github.com/ocaml/tuareg][tuareg]] ++ [[https://github.com/ocaml/merlin][merlin]] ++ [[https://github.com/Khady/merlin-eldoc][merlin-eldoc]] ++ [[https://github.com/OCamlPro/ocp-indent][ocp-indent]] ++ [[https://github.com/flycheck/flycheck-ocaml][flycheck-ocaml]]* ++ [[https://github.com/ocaml-community/utop][utop]]* ++ [[https://github.com/ocaml-ppx/ocamlformat][ocamlformat]]* ++ [[https://github.com/ocaml/dune][dune]] * Prerequisites -It is highly recommended to install [[http://opam.ocaml.org/][opam]]. -To get all the features you should also install these ~opam~ packages, however -they are not all required (features should be disabled gracefully when a tool is -missing): +This module has no hard dependencies, but it is recommanded that you install +[[http://opam.ocaml.org/][opam]] and the following opam (optional) packages: + #+BEGIN_SRC shell opam install merlin utop ocp-indent dune ocamlformat #+END_SRC * Features -+ the following files should have syntax highlighting support: -~.ml{i,p,y,}~, ~.eliom{i,}~, ~jbuild~, ~dune~, ~opam~ ++ The following files should have syntax highlighting support: ~.ml{i,p,y,}~, + ~.eliom{i,}~, ~jbuild~, ~dune~, ~opam~ + =merlin-mode= is activated whenever a =.merlin= file is found (including in a parent directory) and =ocamlmerlin= executable is present -+ line-based auto-indentation is provided by =ocp-indent= when installed ++ Line-based auto-indentation is provided by =ocp-indent=, if it is available. * Configuration -+ if =:completion company= is enabled then autocomplete is provided by =merlin= -+ when =:tools flycheck= is enabled then =flycheck-ocaml= is activated to do ++ If =:completion company= is enabled then autocomplete is provided by =merlin= ++ When =:tools flycheck= is enabled then =flycheck-ocaml= is activated to do on-the-fly syntax/type checking via =merlin=, otherwise this is only done when the file is saved. -+ spell checking is activated in comments if =:tools flyspell= is active -+ a REPL is provided if =utop= is installed and =:tools eval= is active -+ if =:editor format= is enabled, the =ocamlformat= executable is available and ++ Spell checking is activated in comments if =:tools flyspell= is active ++ A REPL is provided if =utop= is installed and =:tools eval= is active ++ If =:editor format= is enabled, the =ocamlformat= executable is available and there is an =.ocamlformat= file present then =format-all-buffer= is bound to =ocamlformat=, otherwise to =ocp-indent= -+ if =:editor multiple-cursors= is enabled then identifiers can be refactored ++ If =:editor multiple-cursors= is enabled then identifiers can be refactored with =v R= and multiple cursors (this correctly matches identifier occurrences according to scope, it is not purely a textual match) -+ if =:emacs imenu= is enabled then top level symbols (modules, type, functions, etc.) can - be looked up using =SPC / i= ++ If =:emacs imenu= is enabled then top level symbols (modules, type, functions, + etc.) can be looked up using =SPC / i= -Run =make install= to install all packages, and =make doctor= to diagnose missing tools. +Run =bin/doom refresh= to install all packages and =make doctor= to diagnose +missing tools. * Appendix ** Commands - | command | key / ex command | description | - |------------------------------+------------------+-----------------------------------------------------------| - | =merlin-type-enclosing= | =SPC m t= | display type under point | - | =tuareg-find-alternate-file= | =SPC m a= | switch between =.ml= and =.mli= | - | =merlin-locate= | =gd= | lookup definition | - | =merlin-occurences= | =SPC c D= | lookup references | - | =merlin-document= | =K= | lookup documentation | - | =merlin-imenu= | =SPC / i= | symbol lookup in file | - | =merlin-iedit-occurrences= | =v R= | visual refactor identifier under point (multiple cursors) | - | =utop= | =SPC o r= | open =utop= as REPL | - | =utop-eval-region= | =SPC c e= | evaluate selected region in =utop= | + | Command | Key | Description | + |------------------------------+-----------+-----------------------------------------------------------| + | =merlin-type-enclosing= | =SPC m t= | display type under point | + | =tuareg-find-alternate-file= | =SPC m a= | switch between =.ml= and =.mli= | + | =merlin-locate= | =gd= | lookup definition | + | =merlin-occurences= | =SPC c D= | lookup references | + | =merlin-document= | =K= | lookup documentation | + | =merlin-imenu= | =SPC / i= | symbol lookup in file | + | =merlin-iedit-occurrences= | =v R= | visual refactor identifier under point (multiple cursors) | + | =utop= | =SPC o r= | open =utop= as REPL | + | =utop-eval-region= | =SPC c e= | evaluate selected region in =utop= | ** Hacks + =set-pretty-symbols!= is called with the full tuareg prettify symbol list, this diff --git a/modules/lang/ocaml/config.el b/modules/lang/ocaml/config.el index 3394fa4f6..25b7d0cad 100644 --- a/modules/lang/ocaml/config.el +++ b/modules/lang/ocaml/config.el @@ -28,20 +28,20 @@ (use-package! utop :when (featurep! :tools eval) - :hook (tuareg-mode . +ocaml|init-utop) + :hook (tuareg-mode . +ocaml-init-utop-h) :init (set-repl-handler! 'tuareg-mode #'utop) (set-eval-handler! 'tuareg-mode #'utop-eval-region) - (defun +ocaml|init-utop () + (defun +ocaml-init-utop-h () (when (executable-find "utop") (utop-minor-mode))))) (use-package! merlin :unless (featurep! +lsp) - :hook (tuareg-mode . +ocaml|init-merlin) + :hook (tuareg-mode . +ocaml-init-merlin-h) :init - (defun +ocaml|init-merlin () + (defun +ocaml-init-merlin-h () "Activate `merlin-mode' if the ocamlmerlin executable exists." (when (executable-find "ocamlmerlin") (merlin-mode))) @@ -62,9 +62,9 @@ (use-package! flycheck-ocaml :when (featurep! :tools flycheck) - :hook (merlin-mode . +ocaml|init-flycheck) + :hook (merlin-mode . +ocaml-init-flycheck-h) :config - (defun +ocaml|init-flycheck () + (defun +ocaml-init-flycheck-h () "Activate `flycheck-ocaml` if the current project possesses a .merlin file." (when (projectile-locate-dominating-file default-directory ".merlin") ;; Disable Merlin's own error checking @@ -90,9 +90,9 @@ (use-package! ocp-indent ;; must be careful to always defer this, it has autoloads that adds hooks ;; which we do not want if the executable can't be found - :hook (tuareg-mode . +ocaml|init-ocp-indent) + :hook (tuareg-mode . +ocaml-init-ocp-indent-h) :config - (defun +ocaml|init-ocp-indent () + (defun +ocaml-init-ocp-indent-h () "Run `ocp-setup-indent', so long as the ocp-indent binary exists." (when (executable-find "ocp-indent") (ocp-setup-indent)))) @@ -101,12 +101,12 @@ (use-package! ocamlformat :when (featurep! :editor format) :commands ocamlformat - :hook (tuareg-mode . +ocaml|init-ocamlformat) + :hook (tuareg-mode . +ocaml-init-ocamlformat-h) :config (set-formatter! 'ocamlformat #'ocamlformat :modes '(caml-mode tuareg-mode)) ;; TODO Fix region-based formatting support - (defun +ocaml|init-ocamlformat () + (defun +ocaml-init-ocamlformat-h () (setq +format-with 'ocp-indent) (when (and (executable-find "ocamlformat") (locate-dominating-file default-directory ".ocamlformat")) diff --git a/modules/lang/org/autoload/contrib-present.el b/modules/lang/org/autoload/contrib-present.el index cd50de4f5..cf4e28f4f 100644 --- a/modules/lang/org/autoload/contrib-present.el +++ b/modules/lang/org/autoload/contrib-present.el @@ -31,42 +31,46 @@ (+org-present--make-invisible (match-beginning 1) (match-end 1))))) ;;;###autoload -(defun +org-present|remove-overlays () +(defun +org-present-remove-overlays-h () (mapc #'delete-overlay +org-present--overlays) (remove-from-invisibility-spec '(+org-present))) ;;;###autoload (defun +org-present-detect-slide-h () (outline-show-all) - (if (member "title" (org-get-tags-at)) + (if (member "title" (org-get-tags)) (text-scale-set 10) (text-scale-set +org-present-text-scale))) +(defvar cwm-use-vertical-padding) +(defvar cwm-frame-internal-border) +(defvar cwm-left-fringe-ratio) +(defvar cwm-centered-window-width) ;;;###autoload (defun +org-present-init-org-tree-window-h () "Set up the org window for presentation." (doom/window-maximize-buffer) - (let ((cwm-use-vertical-padding t) - (cwm-frame-internal-border 110) - (cwm-left-fringe-ratio -10) - (cwm-centered-window-width 240) - (arg (if org-tree-slide-mode +1 -1))) + (let ((arg (if org-tree-slide-mode +1 -1))) (when (fboundp 'centered-window-mode) - (centered-window-mode arg)) + (let ((cwm-use-vertical-padding t) + (cwm-frame-internal-border 110) + (cwm-left-fringe-ratio -10) + (cwm-centered-window-width 240)) + (centered-window-mode arg))) (window-divider-mode (* arg -1)) (hide-mode-line-mode arg) (+org-pretty-mode arg) (cond (org-tree-slide-mode (org-indent-mode -1) (text-scale-set +org-present-text-scale) - (ignore-errors (org-toggle-latex-fragment '(4))) + (ignore-errors (org-latex-preview '(4))) (set-face-attribute 'org-level-2 nil :height 1.4)) (t (org-indent-mode +1) (text-scale-set 0) - (org-remove-latex-fragment-image-overlays) + (org-clear-latex-preview) (set-face-attribute 'org-level-2 nil :height 1.0) - (+org-present|remove-overlays) + (+org-present-remove-overlays-h) (org-remove-inline-images))))) diff --git a/modules/lang/org/autoload/org-attach.el b/modules/lang/org/autoload/org-attach.el index 303c85de3..566640d98 100644 --- a/modules/lang/org/autoload/org-attach.el +++ b/modules/lang/org/autoload/org-attach.el @@ -19,7 +19,7 @@ "A list of all indexed attachments in `org-directory'.") (defvar +org-attachments-files nil - "A list of all attachments in `org-attach-directory'.") + "A list of all attachments in `org-attach-id-dir'.") (defun +org-attachments--list (&optional beg end) "Return a list of all attachment file names in the current buffer between BEG @@ -33,20 +33,20 @@ and END (defaults to `point-min' and `point-max')." (while (search-forward "[[attach:" end t) (let* ((context (save-match-data (org-element-context))) (link (expand-file-name (org-link-unescape (org-element-property :path context)) - org-attach-directory))) + org-attach-id-dir))) (when (and (equal "file" (org-element-property :type context)) - (file-in-directory-p link org-attach-directory)) + (file-in-directory-p link org-attach-id-dir)) (push (file-name-nondirectory link) attachments)))))) (cl-delete-duplicates attachments :test #'string=))) ;;;###autoload (defun +org-attach/sync (arg) "Reindex all attachments in `org-directory' and delete orphaned attachments in -`org-attach-directory'. If ARG (universal arg), conduct a dry run." +`org-attach-id-dir'. If ARG (universal arg), conduct a dry run." (declare (interactive-only t)) (interactive "P") (message "Reloading") - (setq +org-attachments-files (directory-files org-attach-directory nil "^[^.]" t)) + (setq +org-attachments-files (directory-files org-attach-id-dir nil "^[^.]" t)) (with-temp-buffer (delay-mode-hooks (org-mode)) (dolist (org-file (directory-files-recursively org-directory "\\.org$")) @@ -59,14 +59,14 @@ and END (defaults to `point-min' and `point-max')." (message "Deleting orphaned attachment: %s" file) (cl-incf deleted) (unless arg - (delete-file (expand-file-name file org-attach-directory)))) + (delete-file (expand-file-name file org-attach-id-dir)))) (message "Buffer's attachments synced (%d deleted)" deleted))) ;;;###autoload (defun +org-attach/find-file () - "Open a file from `org-attach-directory'." + "Open a file from `org-attach-id-dir'." (interactive) - (doom-project-browse org-attach-directory)) + (doom-project-browse org-attach-id-dir)) ;;;###autoload (defun +org-attach/file (path) diff --git a/modules/lang/org/autoload/org-capture.el b/modules/lang/org/autoload/org-capture.el index fa19ee6b7..a5041362c 100644 --- a/modules/lang/org/autoload/org-capture.el +++ b/modules/lang/org/autoload/org-capture.el @@ -40,6 +40,7 @@ you're done. This can be called from an external shell script." (frame (if (+org-capture-frame-p) (selected-frame) (make-frame +org-capture-frame-parameters)))) + (select-frame-set-input-focus frame) ; fix MacOS not focusing new frames (with-selected-frame frame (require 'org-capture) (condition-case ex diff --git a/modules/lang/org/autoload/org.el b/modules/lang/org/autoload/org.el index a06a19259..64aaf584d 100644 --- a/modules/lang/org/autoload/org.el +++ b/modules/lang/org/autoload/org.el @@ -111,8 +111,8 @@ If on a: 'done))) (t (+org/refresh-inline-images) - (org-remove-latex-fragment-image-overlays) - (org-toggle-latex-fragment '(4))))) + (org-clear-latex-preview) + (org-latex-preview '(4))))) (`clock (org-clock-update-time-maybe)) @@ -150,7 +150,7 @@ If on a: (org-babel-execute-src-block)) ((or `latex-fragment `latex-environment) - (org-toggle-latex-fragment)) + (org-latex-preview)) (`link (let* ((lineage (org-element-lineage context '(link) t)) @@ -298,15 +298,14 @@ wrong places)." (defun +org/remove-link () "Unlink the text at point." (interactive) - (unless (org-in-regexp org-bracket-link-regexp 1) + (unless (org-in-regexp org-link-bracket-re 1) (user-error "No link at point")) (save-excursion - (let ((remove (list (match-beginning 0) (match-end 0))) - (description (if (match-end 3) - (match-string-no-properties 3) - (match-string-no-properties 1)))) - (apply #'delete-region remove) - (insert description)))) + (let ((label (if (match-end 2) + (match-string-no-properties 2) + (org-link-unescape (match-string-no-properties 1))))) + (delete-region (match-beginning 0) (match-end 0)) + (insert label)))) ;;;###autoload (defun +org/toggle-checkbox () diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index 2115ec2b7..07fdfe08e 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -7,6 +7,8 @@ (unless org-agenda-files (setq org-agenda-files (list org-directory))) (setq-default + ;; Don't monopolize the whole frame just for the agenda + org-agenda-window-setup 'current-window ;; Hide blocked tasks in the agenda view. org-agenda-dim-blocked-tasks 'invisible org-agenda-inhibit-startup t @@ -22,8 +24,7 @@ (defun +org-init-appearance-h () "Configures the UI for `org-mode'." (setq-default - org-adapt-indentation nil - org-cycle-include-plain-lists t + org-indirect-buffer-display 'current-window org-eldoc-breadcrumb-separator " → " org-enforce-todo-dependencies t org-entities-user @@ -33,16 +34,10 @@ org-fontify-quote-and-verse-blocks t org-fontify-whole-heading-line t org-footnote-auto-label 'plain - org-hidden-keywords nil - org-hide-emphasis-markers nil org-hide-leading-stars t org-hide-leading-stars-before-indent-mode t org-image-actual-width nil - org-indent-indentation-per-level 2 - org-indent-mode-turns-on-hiding-stars t org-list-description-max-indent 4 - org-pretty-entities nil - org-pretty-entities-include-sub-superscripts t org-priority-faces '((?a . error) (?b . warning) @@ -50,10 +45,7 @@ org-refile-targets '((nil :maxlevel . 3) (org-agenda-files :maxlevel . 3)) - org-startup-folded t org-startup-indented t - org-startup-with-inline-images nil - org-tags-column 0 org-todo-keywords '((sequence "TODO(t)" "PROJ(p)" "|" "DONE(d)") (sequence "[ ](T)" "[-](P)" "[?](M)" "|" "[X](D)") @@ -69,6 +61,7 @@ ;; Scale up LaTeX previews a bit (default is too small) org-format-latex-options (plist-put org-format-latex-options :scale 1.5)) + ;; Show the full link destination in minibuffer when cursor/mouse is over it (advice-add #'org-eldoc-documentation-function :around #'+org-display-link-in-eldoc-a) ;; Don't do automatic indent detection in org files @@ -93,11 +86,11 @@ (defun +org-init-babel-h () - (setq org-src-fontify-natively t ; make code pretty - org-src-preserve-indentation t ; use native major-mode indentation + (setq org-src-preserve-indentation t ; use native major-mode indentation org-src-tab-acts-natively t - org-src-window-setup 'current-window - org-confirm-babel-evaluate nil) ; you don't need my permission + org-confirm-babel-evaluate nil ; you don't need my permission + ;; Show src buffer in popup, and don't monopolize the frame + org-src-window-setup 'other-window) ;; 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) @@ -106,7 +99,8 @@ (advice-add #'org-return-indent :after #'+org-fix-newline-and-indent-in-src-blocks-a) ;; `org-babel-get-header' was removed from org in 9.0. Quite a few babel - ;; plugins use it, so until those plugins update, this polyfill will do: + ;; plugins use it (like ob-spice), so until those plugins update, this + ;; polyfill will do: (defun org-babel-get-header (params key &optional others) (cl-loop with fn = (if others #'not #'identity) for p in params @@ -148,10 +142,12 @@ at the first function to return non-nil.") "Load babel libraries lazily when babel blocks are executed." :after-while #'org-babel-confirm-evaluate (let* ((lang (nth 0 info)) - (lang (if (symbolp lang) lang (intern lang))) + (lang (cond ((symbolp lang) lang) + ((stringp lang) (intern lang)))) (lang (or (cdr (assq lang +org-babel-mode-alist)) lang))) - (when (and (not (cdr (assq lang org-babel-load-languages))) + (when (and lang + (not (cdr (assq lang org-babel-load-languages))) (or (run-hook-with-args-until-success '+org-babel-load-functions lang) (require (intern (format "ob-%s" lang)) nil t))) (when (assq :async (nth 2 info)) @@ -258,7 +254,7 @@ underlying, modified buffer. This fixes that." "I believe Org's native attachment system is over-complicated and litters files with metadata I don't want. So I wrote my own, which: -+ Places attachments in a centralized location (`org-attach-directory' in ++ Places attachments in a centralized location (`org-attach-id-dir' in `org-directory'). + Adds attach:* link abbreviation for quick links to these files from anywhere. + Use `+org-attach/sync' to index all attachments in `org-directory' that use @@ -272,30 +268,30 @@ Some commands of interest: + `+org-attach/file' + `+org-attach/url' + `+org-attach/sync'" - (setq org-attach-directory (doom-path org-directory org-attach-directory)) + (setq org-attach-id-dir (doom-path org-directory org-attach-id-dir)) ;; A shorter link to attachments (add-to-list 'org-link-abbrev-alist (cons "attach" - (abbreviate-file-name org-attach-directory))) + (abbreviate-file-name org-attach-id-dir))) (org-link-set-parameters "attach" - :follow (lambda (link) (find-file (doom-path org-attach-directory link))) + :follow (lambda (link) (find-file (doom-path org-attach-id-dir link))) :complete (lambda (&optional _arg) - (+org--relpath (+org-link-read-file "attach" org-attach-directory) - org-attach-directory)) + (+org--relpath (+org-link-read-file "attach" org-attach-id-dir) + org-attach-id-dir)) :face (lambda (link) - (if (file-exists-p (expand-file-name link org-attach-directory)) + (if (file-exists-p (expand-file-name link org-attach-id-dir)) 'org-link 'error))) (after! projectile - (add-to-list 'projectile-globally-ignored-directories org-attach-directory)) + (add-to-list 'projectile-globally-ignored-directories org-attach-id-dir)) (after! recentf (add-to-list 'recentf-exclude - (lambda (file) (file-in-directory-p file org-attach-directory))))) + (lambda (file) (file-in-directory-p file org-attach-id-dir))))) (defun +org-init-centralized-exports-h () @@ -387,8 +383,8 @@ file isn't in `org-directory'." (add-to-list 'org-export-backends 'md)) (use-package! ox-pandoc - :when (and (featurep! +pandoc) - (executable-find "pandoc")) + :when (featurep! +pandoc) + :when (executable-find "pandoc") :after ox :init (add-to-list 'org-export-backends 'pandoc) @@ -459,7 +455,7 @@ eldoc string." for n from 0 for face = (nth (% n org-n-level-faces) org-level-faces) collect - (org-add-props (replace-regexp-in-string org-any-link-re "\\4" part) + (org-add-props (replace-regexp-in-string org-link-any-re "\\4" part) nil 'face `(:foreground ,(face-foreground face nil t) :weight bold))) separator))) @@ -477,7 +473,20 @@ current workspace." "Prevent temporarily opened agenda buffers from polluting recentf." :around #'org-get-agenda-file-buffer (let ((recentf-exclude (list (lambda (_file) t)))) - (funcall orig-fn file)))) + (funcall orig-fn file))) + + ;; HACK With https://code.orgmode.org/bzg/org-mode/commit/48da60f4, inline + ;; image previews broke for users with imagemagick support built in. This + ;; reverses the problem, but should be removed once it is addressed upstream + ;; (if ever). + (defadvice! +org--fix-inline-images-for-imagemagick-users-a (orig-fn &rest args) + :around #'org-display-inline-images + (cl-letf* ((old-create-image (symbol-function #'create-image)) + ((symbol-function #'create-image) + (lambda (file-or-data &optional type data-p &rest props) + (let ((type (if (plist-get props :width) type))) + (apply old-create-image file-or-data type data-p props))))) + (apply orig-fn args)))) (defun +org-init-keybinds-h () @@ -603,7 +612,7 @@ between the two." "t" #'org-agenda-todo)) -(defun +org-init-keybinds-for-evil-h (&rest args) +(defun +org-init-keybinds-for-evil-h (&rest _) "TODO" (when (featurep! :editor evil +everywhere) (use-package! evil-org @@ -657,18 +666,18 @@ between the two." (org-at-table-p) 'org-table-insert-column) :ni "C-S-h" (general-predicate-dispatch 'org-shiftmetaleft (org-at-table-p) '+org/table-insert-column-left) - :ni "C-S-k" (general-predicate-dispatch 'org-shiftmetaup + :ni "C-S-k" (general-predicate-dispatch 'org-metaup (org-at-table-p) 'org-table-insert-row) - :ni "C-S-j" (general-predicate-dispatch 'org-shiftmetadown + :ni "C-S-j" (general-predicate-dispatch 'org-metadown (org-at-table-p) '+org/table-insert-row-below) ;; moving/(de|pro)moting single headings & shifting table rows/columns :ni "C-M-S-l" (general-predicate-dispatch 'org-metaright (org-at-table-p) 'org-table-move-column-right) :ni "C-M-S-h" (general-predicate-dispatch 'org-metaleft (org-at-table-p) 'org-table-move-column-left) - :ni "C-M-S-k" (general-predicate-dispatch 'org-metaup + :ni "C-M-S-k" (general-predicate-dispatch 'org-shiftmetaup (org-at-table-p) 'org-table-move-row-up) - :ni "C-M-S-j" (general-predicate-dispatch 'org-metadown + :ni "C-M-S-j" (general-predicate-dispatch 'org-shiftmetadown (org-at-table-p) 'org-table-move-row-down) ;; more intuitive RET keybinds :i [return] #'org-return-indent @@ -693,8 +702,7 @@ between the two." :n "zc" #'+org/close-fold :n "zC" #'outline-hide-subtree :n "zm" #'+org/hide-next-fold-level - :n "zn" #'org-narrow-to-subtree - :n "zN" #'org-tree-to-indirect-buffer + :n "zn" #'org-tree-to-indirect-buffer :n "zo" #'+org/open-fold :n "zO" #'outline-show-subtree :n "zr" #'+org/show-next-fold-level @@ -717,8 +725,8 @@ between the two." '(("^\\*Org Links" :slot -1 :vslot -1 :size 2 :ttl 0) ("^\\*\\(?:Agenda Com\\|Calendar\\|Org \\(?:Export Dispatcher\\|Select\\)\\)" :slot -1 :vslot -1 :size #'+popup-shrink-to-fit :ttl 0) - ("^\\*Org Agenda" :size 0.35 :select t :ttl nil) - ("^\\*Org Src" :size 0.3 :quit nil :select t :autosave t :ttl nil) + ("^\\*Org Agenda" :ignore t) + ("^\\*Org Src" :size 0.4 :quit nil :select t :autosave t :modeline t :ttl nil) ("^CAPTURE.*\\.org$" :size 0.2 :quit nil :select t :autosave t)))) @@ -774,13 +782,17 @@ compelling reason, so..." (skip-chars-backward "*") (bolp)))) + (defun +org-sp-in-src-block-p (_id action _context) + (and (eq action 'insert) + (org-in-src-block-p))) + ;; make delimiter auto-closing a little more conservative (sp-with-modes 'org-mode - (sp-local-pair "*" "*" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-point-at-bol-p)) - (sp-local-pair "_" "_" :unless '(:add sp-point-before-word-p sp-in-math-p)) - (sp-local-pair "/" "/" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-point-in-checkbox-p)) - (sp-local-pair "~" "~" :unless '(:add sp-point-before-word-p)) - (sp-local-pair "=" "=" :unless '(:add sp-point-before-word-p sp-in-math-p))))) + (sp-local-pair "*" "*" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-point-at-bol-p +org-sp-in-src-block-p)) + (sp-local-pair "_" "_" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-in-src-block-p)) + (sp-local-pair "/" "/" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-point-in-checkbox-p +org-sp-in-src-block-p)) + (sp-local-pair "~" "~" :unless '(:add sp-point-before-word-p +org-sp-in-src-block-p)) + (sp-local-pair "=" "=" :unless '(:add sp-point-before-word-p sp-in-math-p +org-sp-in-src-block-p))))) ;; @@ -794,7 +806,7 @@ compelling reason, so..." :preface ;; Change org defaults (should be set before org loads) (defvar org-directory "~/org/") - (defvar org-attach-directory ".attach/") + (defvar org-attach-id-dir ".attach/") (setq org-clock-persist-file (concat doom-etc-dir "org-clock-save.el") org-publish-timestamp-directory (concat doom-cache-dir "org-timestamps/") @@ -815,7 +827,6 @@ compelling reason, so..." (add-hook! 'org-mode-hook #'org-bullets-mode ; "prettier" bullets - #'org-indent-mode ; margin-based indentation #'toc-org-enable ; auto-table of contents #'auto-fill-mode ; hard line wrapping ;; `show-paren-mode' causes flickering with indentation margins made by @@ -850,7 +861,8 @@ compelling reason, so..." ;; In case the user has eagerly loaded org from their configs (when (and (featurep 'org) - (not doom-reloading-p)) + (not doom-reloading-p) + (not byte-compile-current-file)) (message "`org' was already loaded by the time lang/org loaded, this may cause issues") (run-hooks 'org-load-hook)) diff --git a/modules/lang/org/contrib/journal.el b/modules/lang/org/contrib/journal.el new file mode 100644 index 000000000..ee9be68b3 --- /dev/null +++ b/modules/lang/org/contrib/journal.el @@ -0,0 +1,14 @@ +;;; lang/org/contrib/journal.el -*- lexical-binding: t; -*- +;;;###if (featurep! +journal) + +(use-package! org-journal + :defer t + :config + (map! :map org-journal-search-mode-map + :localleader + "n" #'org-journal-search-next + "p" #'org-journal-search-prev) + (map! :map org-journal-mode-map + :localleader + "n" #'org-journal-open-next-entry + "p" #'org-journal-open-previous-entry)) diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el index b085c68b7..db46d566e 100644 --- a/modules/lang/org/packages.el +++ b/modules/lang/org/packages.el @@ -49,3 +49,6 @@ (package! centered-window :recipe (:host github :repo "anler/centered-window-mode")) (package! org-tree-slide) (package! ox-reveal)) + +(when (featurep! +journal) + (package! org-journal)) diff --git a/modules/lang/org/test/test-org.el b/modules/lang/org/test/test-org.el index 4ef30c231..cbe36f62d 100644 --- a/modules/lang/org/test/test-org.el +++ b/modules/lang/org/test/test-org.el @@ -19,33 +19,33 @@ (describe "headlines" (it "appends first-level headlines with an extra newline" - (insert! "* {0}Header") + (insert!! "* {0}Header") (+org/insert-item-below 1) (expect (eobp)) (expect (buffer-substring-no-properties (point-min) (point-max)) :to-equal "* Header\n\n* ")) (it "prepends first-level headlines with an extra newline" - (insert! "* {0}Header") + (insert!! "* {0}Header") (+org/insert-item-above 1) (expect (eolp)) (expect (buffer-substring-no-properties (point-min) (point-max)) :to-equal "* \n\n* Header")) (it "appends second-level headlines with an no extra newline" - (insert! "** {0}Header") + (insert!! "** {0}Header") (+org/insert-item-below 1) (expect (eobp)) (expect (buffer-substring-no-properties (point-min) (point-max)) :to-equal "** Header\n** ")) (it "prepends second-level headlines with an no extra newline" - (insert! "** {0}Header") + (insert!! "** {0}Header") (+org/insert-item-above 1) (expect (eolp)) (expect (buffer-substring-no-properties (point-min) (point-max)) :to-equal "** \n** Header")) (it "appends headlines, skipping subtrees" - (insert! "** {0}First\n" + (insert!! "** {0}First\n" "*** sub 1\n" "*** sub 2\n" "**** subsub 1\n" @@ -63,7 +63,7 @@ "** Header") "\n"))) (it "prepends headlines, skipping subtrees" - (insert! "** First\n" + (insert!! "** First\n" "*** sub 1\n" "*** sub 2\n" "**** {0}subsub 1\n" @@ -83,18 +83,18 @@ (describe "plain lists" (it "appends items" - (insert! "+ {0}List item") + (insert!! "+ {0}List item") (+org/insert-item-below 1) (expect (buffer-substring-no-properties (point-min) (point-max)) :to-equal "+ List item\n+ ")) (it "prepends items" - (insert! "+ {0}List item") + (insert!! "+ {0}List item") (+org/insert-item-above 1) (expect (buffer-substring-no-properties (point-min) (point-max)) :to-equal "+ \n+ List item")) (it "appends items, but skips over child items" - (insert! "+ {0}List item\n" + (insert!! "+ {0}List item\n" " + Sub item\n" "+ List item") (+org/insert-item-below 1) @@ -106,7 +106,7 @@ "+ List item") "\n"))) (it "prepends items, but skips over child items" - (insert! "+ List item\n" + (insert!! "+ List item\n" " + Sub item\n" "+ {0}List item") (+org/insert-item-above 1) @@ -120,7 +120,7 @@ (describe "numbered lists" (it "appends items and updates numbers" - (insert! "1. {0}List item\n" + (insert!! "1. {0}List item\n" "2. Sub item\n" "3. List item") (+org/insert-item-below 1) @@ -132,7 +132,7 @@ "4. List item") "\n"))) (it "prepends items and updates numbers" - (insert! "1. List item\n" + (insert!! "1. List item\n" "2. Sub item\n" "3. {0}List item") (+org/insert-item-above 1) diff --git a/modules/lang/php/README.org b/modules/lang/php/README.org index 9c2c87462..458f85c32 100644 --- a/modules/lang/php/README.org +++ b/modules/lang/php/README.org @@ -40,6 +40,11 @@ brew install composer sudo pacman --needed --noconfirm -S php composer # or php53, php54, php55 #+END_SRC +*** openSUSE +#+BEGIN_SRC sh :dir /sudo:: +sudo zypper install php-composer +#+END_SRC + ** Dependencies The features in this module optionally depend on the following php packages: diff --git a/modules/lang/php/autoload.el b/modules/lang/php/autoload.el index 0cd8a68a5..5da10009b 100644 --- a/modules/lang/php/autoload.el +++ b/modules/lang/php/autoload.el @@ -24,14 +24,3 @@ ignore the cache." (require 'json) (json-read-file package-file))) (puthash project-root data +php-composer-conf)))))) - -;;;###autoload -(defun +php|init-ac-php-core-eldoc () - "Initialize eldoc support for `php-mode' with `ac-php-core'. Fails gracefully -if phpctags isn't installed." - (require 'ac-php-core) - (cond ((not ac-php-ctags-executable)) - ((not (file-exists-p ac-php-ctags-executable)) - (message "Could not find phpctags executable, eldoc support is disabled") - (message "To disable these warnings, set ac-php-ctags-executable to nil")) - ((ac-php-core-eldoc-setup)))) diff --git a/modules/lang/plantuml/autoload.el b/modules/lang/plantuml/autoload.el index a66680d46..5ae3d4c6e 100644 --- a/modules/lang/plantuml/autoload.el +++ b/modules/lang/plantuml/autoload.el @@ -6,5 +6,5 @@ (interactive) (if (file-exists-p plantuml-jar-path) (user-error "plantuml.jar already installed") - (url-copy-file "https://datapacket.dl.sourceforge.net/project/plantuml/plantuml.jar" + (url-copy-file "https://downloads.sourceforge.net/project/plantuml/plantuml.jar" plantuml-jar-path))) diff --git a/modules/lang/plantuml/config.el b/modules/lang/plantuml/config.el index 593ecfead..1daf6eba3 100644 --- a/modules/lang/plantuml/config.el +++ b/modules/lang/plantuml/config.el @@ -13,3 +13,22 @@ :when (featurep! :tools flycheck) :after plantuml-mode :config (flycheck-plantuml-setup)) + + +(after! ob-plantuml + (defadvice! +plantuml--fix-atstart-in-org-src-blocks-a (args) + "Fix error when executing plantuml src blocks in org-mode for code that +begins with '@'. This character needs to be escaped with a backslash or comma +for the block to execute correctly, so we do it automatically." + :filter-args #'org-babel-execute:plantuml + (cl-destructuring-bind (body params) args + (let* ((origin-body body) + (fix-body + (replace-regexp-in-string + "^[[:blank:]\n]*\\(@start\\)" + "\\\\\\1" + origin-body))) + (list fix-body params)))) + + (add-to-list 'org-babel-default-header-args:plantuml + '(:cmdline . "-charset utf-8"))) diff --git a/modules/lang/python/README.org b/modules/lang/python/README.org new file mode 100644 index 000000000..91c011114 --- /dev/null +++ b/modules/lang/python/README.org @@ -0,0 +1,120 @@ +#+TITLE: lang/python +#+DATE: Oct, 2019 +#+SINCE: v2.0.9 +#+STARTUP: inlineimages + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] + - [[#language-server-protocol-support][Language Server Protocol Support]] +- [[#features][Features]] + - [[#keybindings][Keybindings]] +- [[#configuration][Configuration]] + +* Description +Adds Python support to Doom Emacs. + ++ Syntax checking (~flycheck~) ++ Snippets ++ Run tests (~nose~, ~pytest~) ++ Auto-format (~black~), requires ~:editor format~ + +** Module Flags ++ ~+lsp~ Language Server Protocol support ++ ~+pyenv~ Python virtual environment support via [[https://github.com/pyenv/pyenv][pyenv]] ++ ~+conda~ Python virtual environment support via [[https://conda.io/en/latest/][Conda]] + +** Plugins ++ [[https://github.com/pythonic-emacs/anaconda-mode][anaconda-mode]]* ++ [[https://github.com/Wilfred/pyimport][pyimport]]* ++ [[https://github.com/emacs-pe/pyimpsort.el][pyimpsort]]* ++ [[https://melpa.org/#/nose][nose]]* ++ [[https://github.com/wbolster/emacs-python-pytest][python-pytest]]* ++ [[https://github.com/Wilfred/pip-requirements.el][pip-requirements]]* ++ [[https://github.com/pwalsh/pipenv.el][pipenv]]* ++ ~:editor format~ + + [[https://github.com/lassik/emacs-format-all-the-code][format-all]]* ++ ~+conda~ + + [[https://github.com/necaris/conda.el][conda]]* ++ ~+pyenv~ + + [[https://github.com/pythonic-emacs/pyenv-mode][pyenv]]* ++ ~+lsp~ and ~:tools lsp~ + + [[https://github.com/emacs-lsp/lsp-mode][lsp]] + + [[https://github.com/emacs-lsp/lsp-python-ms][lsp-python-ms]]* + +* Prerequisites +This module has no direct prerequisites. Here are some of its soft dependencies. + ++ To run tests inside of Emacs: + + ~pip install pytest~ + + ~pip install nose~ + ++ The ~:editor format~ module uses [[https://github.com/psf/black][Black]] for python files + + ~pip install black~ + ++ ~pyimport~ requires Python's module ~pyimport~: + + ~pip install pyimport~ + ++ Python virtual environments install instructions at: + + [[https://github.com/pyenv/pyenv][pyenv]] + + [[https://conda.io/en/latest/][Conda]] + ++ ~pipenv~ requires [[https://pipenv.readthedocs.io/en/latest/][pipenv]] + +** Language Server Protocol Support +Requires the ~+lsp~ flag and ~:tools lsp~ module to be enabled. +By default LSP will use Microsoft's language server if installed. + +To install the language server: +~M-x lsp-python-ms-setup~ +To update the server: +~M-x lsp-python-ms-update-server~ + +Alternatively you can use the [[https://pypi.org/project/python-language-server/][Python Language Server]] instead. +~pip install 'python-language-server[all]'~ + +* Features +This module supports LSP. It requires installation of [[https://pypi.org/project/python-language-server/][Python Language +Server]] or [[https://github.com/Microsoft/python-language-server][Microsoft Lnaguaje Server]], see [[Language Server Protocol Support][LSP Support]]. + +To enable support for auto-formatting with black enable ~:editor format-all~ in +~init.el~ file. + +** Keybindings + +| Binding | Description | +|---------------------+----------------------------------| +| ~ i i~ | ~Insert mising imports~ | +| ~ i r~ | ~Remove unused imports~ | +| ~ i s~ | ~Sort imports~ | +| ~ i o~ | ~Optimize imports~ | +| ~ t r~ | ~nosetests-again~ | +| ~ t a~ | ~nosetests-all~ | +| ~ t s~ | ~nosetests-one~ | +| ~ t v~ | ~nosetests-module~ | +| ~ t A~ | ~nosetests-pdb-all~ | +| ~ t O~ | ~nosetests-pdb-one~ | +| ~ t V~ | ~nosetests-pdb-module~ | +| ~ t f~ | ~python-pytest-file~ | +| ~ t k~ | ~python-pytest-file-dwin~ | +| ~ t t~ | ~python-pytest-function~ | +| ~ t m~ | ~python-pytest-function-dwim~ | +| ~ t r~ | ~python-pytest-repeat~ | +| ~ t p~ | ~python-pytest-popup~ | +| ~ f d~ | ~anaconda-mode-find-definitions~ | +| ~ f h~ | ~anaconda-mode-show-doc~ | +| ~ f a~ | ~anaconda-mode-find-assignments~ | +| ~ f f~ | ~anaconda-mode-find-file~ | +| ~ f u~ | ~anaconda-mode-find-references~ | + +* Configuration +This module has the following variables to set extra arguments to [[https://ipython.org/][ipython]] and +[[https://jupyter.org/][jupyter]] shells: + +#+BEGIN_SRC elisp +;; ~/.doom.d/config.el +(setq +python-ipython-repl-args '("-i" "--simple-prompt" "--no-color-info")) +(setq +python-jupyter-repl-args '("--simple-prompt")) +#+END_SRC diff --git a/modules/lang/python/config.el b/modules/lang/python/config.el index 2abe9f80e..a049f639e 100644 --- a/modules/lang/python/config.el +++ b/modules/lang/python/config.el @@ -218,19 +218,12 @@ called.") :when (featurep! +conda) :after python :config - ;; The location of your anaconda home will be guessed from the following: + ;; The location of your anaconda home will be guessed from a list of common + ;; possibilities, starting with `conda-anaconda-home''s default value (which + ;; will consult a ANACONDA_HOME envvar, if it exists). ;; - ;; + `conda-anaconda-home's default value: - ;; + ANACONDA_HOME - ;; + ~/.anaconda3 - ;; + ~/.anaconda - ;; + ~/.miniconda - ;; + ~/usr/bin/anaconda3 - ;; + ~/usr/local/anaconda3 - ;; + ~/usr/local/miniconda3 - ;; - ;; If none of these work for you, you must set `conda-anaconda-home' - ;; explicitly. Once set, run M-x `conda-env-activate' to switch between + ;; If none of these work for you, `conda-anaconda-home' must be set + ;; explicitly. Afterwards, run M-x `conda-env-activate' to switch between ;; environments (unless (cl-loop for dir in (list conda-anaconda-home "~/.anaconda" @@ -259,6 +252,8 @@ called.") :when (featurep! +lsp) :after lsp-clients :init + (setq lsp-python-ms-dir (concat doom-etc-dir "mspyls/")) + ;; HACK lsp-python-ms shouldn't install itself if it isn't present. This ;; circumvents LSP falling back to pyls when lsp-python-ms is absent. ;; Installing the server should be a deliberate act; either 'M-x diff --git a/modules/lang/racket/config.el b/modules/lang/racket/config.el index 12d9994ad..891df7709 100644 --- a/modules/lang/racket/config.el +++ b/modules/lang/racket/config.el @@ -19,7 +19,6 @@ (add-hook! 'racket-mode-hook #'rainbow-delimiters-mode #'highlight-quoted-mode) - (set-lookup-handlers! 'racket-mode :definition #'racket-visit-definition) (map! :map (racket-mode-map racket-repl-mode-map) :i "[" #'racket-smart-open-bracket) diff --git a/modules/lang/rest/config.el b/modules/lang/rest/config.el index dfe9d053f..d655901e9 100644 --- a/modules/lang/rest/config.el +++ b/modules/lang/rest/config.el @@ -11,12 +11,12 @@ (setq-hook! 'restclient-mode-hook imenu-generic-expression '((nil "^[A-Z]+\s+.+" 0))) - ;; Forces underlying SSL verification to prompt for self-signed or invalid - ;; certs, rather than silently reject them. - (defun +rest*permit-self-signed-ssl (orig-fn &rest args) + (defadvice! +rest--permit-self-signed-ssl-a (orig-fn &rest args) + "Forces underlying SSL verification to prompt for self-signed or invalid +certs, rather than silently reject them." + :around #'restclient-http-do (let (gnutls-verify-error tls-checktrust) (apply orig-fn args))) - (advice-add #'restclient-http-do :around #'+rest*permit-self-signed-ssl) (map! :map restclient-mode-map :n [return] #'+rest/dwim-at-point diff --git a/modules/lang/ruby/config.el b/modules/lang/ruby/config.el index 1d3c6a38e..c603689ed 100644 --- a/modules/lang/ruby/config.el +++ b/modules/lang/ruby/config.el @@ -1,16 +1,16 @@ ;;; lang/ruby/config.el -*- lexical-binding: t; -*- ;; -;; Packages +;;; Packages (use-package! enh-ruby-mode - :mode ("\\.\\(?:pry\\|irb\\)rc\\'" . +ruby|init) - :mode ("\\.\\(?:rb\\|rake\\|rabl\\|ru\\|builder\\|gemspec\\|jbuilder\\|thor\\)\\'" . +ruby|init) - :mode ("/\\(?:Berks\\|Cap\\|Gem\\|Guard\\|Pod\\|Puppet\\|Rake\\|Thor\\|Vagrant\\)file\\'" . +ruby|init) + :mode ("\\.\\(?:pry\\|irb\\)rc\\'" . +ruby-init-h) + :mode ("\\.\\(?:rb\\|rake\\|rabl\\|ru\\|builder\\|gemspec\\|jbuilder\\|thor\\)\\'" . +ruby-init-h) + :mode ("/\\(?:Berks\\|Cap\\|Gem\\|Guard\\|Pod\\|Puppet\\|Rake\\|Thor\\|Vagrant\\)file\\'" . +ruby-init-h) :preface (after! ruby-mode (require 'enh-ruby-mode)) - (defun +ruby|init () + (defun +ruby-init-h () "Enable `enh-ruby-mode' if ruby is available, otherwise `ruby-mode'." (if (executable-find "ruby") (enh-ruby-mode) @@ -33,11 +33,11 @@ (use-package! robe :defer t :init - (defun +ruby|init-robe-mode-maybe () - "Start `robe-mode' if `lsp-mode' isn't active." - (unless (bound-and-true-p lsp-mode) - (robe-mode +1))) - (add-hook 'enh-ruby-mode-hook #'+ruby|init-robe-mode-maybe) + (add-hook! 'enh-ruby-mode-hook + (defun +ruby-init-robe-mode-maybe-h () + "Start `robe-mode' if `lsp-mode' isn't active." + (unless (bound-and-true-p lsp-mode) + (robe-mode +1)))) :config (set-repl-handler! 'enh-ruby-mode #'robe-start) (set-company-backend! 'enh-ruby-mode 'company-robe) @@ -76,7 +76,7 @@ ;; -;; Package & Ruby version management +;;; Package & Ruby version management (use-package! rake :defer t @@ -111,7 +111,7 @@ ;; -;; Testing frameworks +;;; Testing frameworks (use-package! rspec-mode :mode ("/\\.rspec\\'" . text-mode) diff --git a/modules/lang/rust/README.org b/modules/lang/rust/README.org index 4342cd1ed..2a93bd352 100644 --- a/modules/lang/rust/README.org +++ b/modules/lang/rust/README.org @@ -40,13 +40,14 @@ To get started with Rust, you can either use =rustup= and install rust with: Package manager is not recommended to install Nightly version of Rust what is required for ~racer~ from [[https://github.com/racer-rust/racer#installation][version 2.1]] (more info in [[#Troubleshooting][Troubleshooting]]) -Some commands require additional crates to be installed to work, e.g. ~cargo -add~. -#+BEGIN_SRC sh -rustup component add rustfmt -rustup component add cargo-check -rustup component add cargo-edit -#+END_SRC +Some features have additional dependencies: + ++ The ~:editor format~ module uses ~rustfmt~ for rust files: ~rustup component + add rustfmt-preview~ ++ The following commands require: + + ~cargo-process-check~: ~cargo install cargo-check~ + + ~cargo-process-clippy~: ~rustup component add clippy-preview~ + + ~cargo-process-{add,rm,upgrade}~: ~cargo install cargo-edit~ * Features This module also supports LSP, it requires installation of Rust Language Server diff --git a/modules/lang/rust/config.el b/modules/lang/rust/config.el index 60da2e9fa..92fd8fc15 100644 --- a/modules/lang/rust/config.el +++ b/modules/lang/rust/config.el @@ -4,6 +4,7 @@ :defer t :config (setq rust-indent-method-chain t) + (add-hook 'rust-mode-hook #'rainbow-delimiters-mode) ;; This is necessary because both plugins are fighting for supremacy in ;; `auto-mode-alist', so rustic-mode *must* load second. It only needs to @@ -51,12 +52,22 @@ ;; use :editor format instead rustic-format-on-save nil) - ;; `rustic-setup-rls' uses `package-installed-p' unnecessarily, which breaks - ;; because Doom lazy loads package.el. - (defadvice! +rust--disable-package-call-a (orig-fn &rest args) + (add-hook 'rustic-mode-hook #'rainbow-delimiters-mode) + + (defadvice! +rust--dont-install-packages-p (orig-fn &rest args) :around #'rustic-setup-rls - (cl-letf (((symbol-function 'package-installed-p) - (symbol-function 'ignore))) + (cl-letf (;; `rustic-setup-rls' uses `package-installed-p' to determine if + ;; lsp-mode/elgot are available. This breaks because Doom doesn't + ;; use package.el to begin with (and lazy loads it). + ((symbol-function #'package-installed-p) + (lambda (pkg) + (require pkg nil t))) + ;; If lsp/elgot isn't available, it attempts to install lsp-mode + ;; via package.el. Doom manages its own dependencies so we disable + ;; that behavior. + ((symbol-function #'rustic-install-rls-client-p) + (lambda (&rest _) + (message "No RLS server running.")))) (apply orig-fn args)))) diff --git a/modules/lang/scala/config.el b/modules/lang/scala/config.el index 702dc054f..0fdfb7c87 100644 --- a/modules/lang/scala/config.el +++ b/modules/lang/scala/config.el @@ -5,7 +5,8 @@ ;; indent block comments to first asterix, not second scala-indent:use-javadoc-style t) - (setq-hook! 'scala-mode-hook comment-line-break-function #'+scala-comment-indent-new-line) + (setq-hook! 'scala-mode-hook + comment-line-break-function #'+scala-comment-indent-new-line) (after! dtrt-indent (add-to-list 'dtrt-indent-hook-mapping-list '(scala-mode c/c++/java scala-indent:step))) @@ -14,23 +15,6 @@ (add-hook 'scala-mode-local-vars-hook #'lsp!))) -(use-package! ensime - :unless (featurep! +lsp) - :defer t - :config - (setq ensime-startup-snapshot-notification nil - ensime-startup-notification nil - ensime-eldoc-hints 'all - ;; let DOOM handle company setup - ensime-completion-style nil) - - (set-company-backend! 'scala-mode '(ensime-company company-yasnippet)) - - ;; Fix void-variable imenu-auto-rescan error caused by `ensime--setup-imenu' - ;; trying to make imenu variables buffer local before imenu is loaded. - (require 'imenu)) - - (use-package! sbt-mode :after scala-mode :config (set-repl-handler! 'scala-mode #'+scala/open-repl)) diff --git a/modules/lang/scala/doctor.el b/modules/lang/scala/doctor.el new file mode 100644 index 000000000..06a62720f --- /dev/null +++ b/modules/lang/scala/doctor.el @@ -0,0 +1,9 @@ +;;; lang/scala/doctor.el -*- lexical-binding: t; -*- + +(assert! (or (not (featurep! +lsp)) + (featurep! :tools lsp)) + "This module requires (:tools lsp)") + +(if (and (featurep! +lsp) + (not (executable-find "metals-emacs"))) + (warn! "metals-emacs isn't installed")) diff --git a/modules/lang/scala/packages.el b/modules/lang/scala/packages.el index 93c1f879e..8a331c698 100644 --- a/modules/lang/scala/packages.el +++ b/modules/lang/scala/packages.el @@ -3,9 +3,3 @@ (package! sbt-mode) (package! scala-mode) - -(unless (featurep! +lsp) - ;; Fix #1697: Ensime doesn't have a master branch and its MELPA recipe doesn't - ;; specify a branch. Straight can't handle packages with non-standard primary - ;; branches (at the time of writing), so we must specify it manually: - (package! ensime :recipe (:branch "2.0"))) diff --git a/modules/lang/scheme/config.el b/modules/lang/scheme/config.el index db9d7c96a..7641c8387 100644 --- a/modules/lang/scheme/config.el +++ b/modules/lang/scheme/config.el @@ -1,39 +1,42 @@ ;;; lang/scheme/config.el -*- lexical-binding: t; -*- +;;;###package scheme (add-hook 'scheme-mode-hook #'rainbow-delimiters-mode) -(def-package! geiser - :mode ("\\.scm\\'" . scheme-mode) - :mode ("\\.ss\\'" . scheme-mode) - :commands (geiser) + +(use-package! geiser + :defer t :init (setq geiser-active-implementations '(guile chicken mit chibi chez)) - (set-repl-handler! 'scheme-mode '+scheme/repl) - (set-eval-handler! 'scheme-mode #'geiser-eval-region) - (set-lookup-handlers! 'scheme-mode - :definition #'geiser-edit-symbol-at-point - :documentation #'geiser-doc-symbol-at-point) + (unless (featurep! :lang racket) + (push 'racket geiser-active-implementations)) + (after! scheme ; built-in + (set-repl-handler! 'scheme-mode '+scheme/repl) + (set-eval-handler! 'scheme-mode #'geiser-eval-region) + (set-lookup-handlers! 'scheme-mode + :definition #'geiser-edit-symbol-at-point + :documentation #'geiser-doc-symbol-at-point)) :config - (map! (:localleader - (:map scheme-mode-map - "'" #'geiser-mode-switch-to-repl - "s" #'geiser-set-scheme + (map! :localleader + :map scheme-mode-map + "'" #'geiser-mode-switch-to-repl + "s" #'geiser-set-scheme - (:prefix ("e" . "eval") - "b" #'geiser-eval-buffer - "B" #'geiser-eval-buffer-and-go - "e" #'geiser-eval-definition - "E" #'geiser-eval-definition-and-go - "r" #'geiser-eval-region - "R" #'geiser-eval-region-and-go) + (:prefix ("e" . "eval") + "b" #'geiser-eval-buffer + "B" #'geiser-eval-buffer-and-go + "e" #'geiser-eval-definition + "E" #'geiser-eval-definition-and-go + "r" #'geiser-eval-region + "R" #'geiser-eval-region-and-go) - (:prefix ("h" . "help") - "d" 'geiser-autodoc) - ;; TODO add more help keybindings + (:prefix ("h" . "help") + "d" 'geiser-autodoc) + ;; TODO add more help keybindings - (:prefix ("r" . "repl") - "b" #'geiser-switch-to-repl - "q" #'geiser-repl-exit - "r" #'geiser-restart-repl - "R" #'geiser-reload - "c" #'geiser-repl-clear-buffer))))) + (:prefix ("r" . "repl") + "b" #'geiser-switch-to-repl + "q" #'geiser-repl-exit + "r" #'geiser-restart-repl + "R" #'geiser-reload + "c" #'geiser-repl-clear-buffer))) diff --git a/modules/lang/sh/config.el b/modules/lang/sh/config.el index 34af5d4d0..03e9b3de1 100644 --- a/modules/lang/sh/config.el +++ b/modules/lang/sh/config.el @@ -1,14 +1,14 @@ ;;; lang/sh/config.el -*- lexical-binding: t; -*- (defvar +sh-builtin-keywords - '("cat" "cat" "cd" "chmod" "chown" "cp" "curl" "date" "echo" "find" "git" - "grep" "kill" "less" "ls" "make" "mkdir" "mv" "pgrep" "pkill" "pwd" - "rm" "sleep" "sudo" "touch") + '("cat" "cd" "chmod" "chown" "cp" "curl" "date" "echo" "find" "git" "grep" + "kill" "less" "ln" "ls" "make" "mkdir" "mv" "pgrep" "pkill" "pwd" "rm" + "sleep" "sudo" "touch") "A list of common shell commands to be fontified especially in `sh-mode'.") ;; -;; Packages +;;; Packages (use-package! sh-script ; built-in :mode ("\\.zunit\\'" . sh-mode) @@ -43,7 +43,7 @@ (2 'font-lock-variable-name-face prepend)) (+sh--match-command-subst-in-quotes (1 'sh-quoted-exec prepend)) - (,(regexp-opt +sh-builtin-keywords 'words) + (,(regexp-opt +sh-builtin-keywords 'symbols) (0 'font-lock-type-face append)))))) ;; 4. Fontify delimiters by depth (add-hook 'sh-mode-hook #'rainbow-delimiters-mode) diff --git a/modules/lang/solidity/config.el b/modules/lang/solidity/config.el index f9502a67f..7921df560 100644 --- a/modules/lang/solidity/config.el +++ b/modules/lang/solidity/config.el @@ -3,7 +3,7 @@ ;; ;; Packages -;; `solidity-mode' +;;;###package solidity-mode (setq solidity-comment-style 'slash) @@ -22,5 +22,5 @@ :when (featurep! :completion company) :after solidity-mode :config - (setq company-backends (delq 'company-solidity company-backends)) + (delq! 'company-solidity company-backends) (set-company-backend! 'solidity-mode 'company-solidity)) diff --git a/modules/lang/swift/config.el b/modules/lang/swift/config.el index f0a822830..83f21b647 100644 --- a/modules/lang/swift/config.el +++ b/modules/lang/swift/config.el @@ -5,15 +5,15 @@ (use-package! flycheck-swift - :when (and (featurep! :tools flycheck) - (not (featurep! +lsp))) + :when (featurep! :tools flycheck) + :unless (featurep! +lsp) :after swift-mode :config (flycheck-swift-setup)) (use-package! company-sourcekit - :when (and (featurep! :completion company) - (not (featurep! +lsp))) + :when (featurep! :completion company) + :unless (featurep! +lsp) :after swift-mode :config (set-company-backend! 'swift-mode '(company-sourcekit company-yasnippet))) diff --git a/modules/lang/terra/config.el b/modules/lang/terra/config.el index 7cf7db023..f6c448ce3 100644 --- a/modules/lang/terra/config.el +++ b/modules/lang/terra/config.el @@ -1,12 +1,11 @@ ;;; lang/lua/config.el -*- lexical-binding: t; -*- ;; -;; Major modes +;;; Major modes -(use-package! terra-mode - :defer t - :config - (set-lookup-handlers! 'terra-mode :documentation 'terra-search-documentation) +(after! terra-mode + (set-lookup-handlers! 'terra-mode + :documentation #'terra-search-documentation) (set-electric! 'terra-mode :words '("else" "end")) (set-repl-handler! 'terra-mode #'+terra/open-repl) (set-company-backend! 'terra-mode '(company-lua company-yasnippet))) diff --git a/modules/lang/web/+css.el b/modules/lang/web/+css.el index dc68ec39c..0dc02f0a6 100644 --- a/modules/lang/web/+css.el +++ b/modules/lang/web/+css.el @@ -1,7 +1,8 @@ ;;; lang/web/+css.el -*- lexical-binding: t; -*- ;; An improved newline+continue comment function -(setq-hook! css-mode comment-indent-function #'+css/comment-indent-new-line) +(setq-hook! css-mode + comment-indent-function #'+css/comment-indent-new-line) (map! :map (css-mode-map scss-mode-map less-css-mode-map) :localleader diff --git a/modules/lang/web/+html.el b/modules/lang/web/+html.el index 7bac671ab..59c94c21b 100644 --- a/modules/lang/web/+html.el +++ b/modules/lang/web/+html.el @@ -1,19 +1,20 @@ ;;; lang/web/+html.el -*- lexical-binding: t; -*- (use-package! web-mode - :mode "\\.p?html?$" - :mode "\\.\\(?:tpl\\|blade\\)\\(\\.php\\)?$" - :mode "\\.erb$" - :mode "\\.jsp$" - :mode "\\.as[cp]x$" - :mode "\\.hbs$" - :mode "\\.mustache$" - :mode "\\.tsx$" - :mode "\\.vue$" - :mode "\\.twig$" - :mode "\\.jinja$" - :mode "wp-content/themes/.+/.+\\.php$" - :mode "templates/.+\\.php$" + :mode "\\.p?html?\\'" + :mode "\\.\\(?:tpl\\|blade\\)\\(?:\\.php\\)?\\'" + :mode "\\.erb\\'" + :mode "\\.jsp\\'" + :mode "\\.as[cp]x\\'" + :mode "\\.hbs\\'" + :mode "\\.mustache\\'" + :mode "\\.svelte\\'" + :mode "\\.tsx\\'" + :mode "\\.vue\\'" + :mode "\\.twig\\'" + :mode "\\.jinja\\'" + :mode "wp-content/themes/.+/.+\\.php\\'" + :mode "templates/.+\\.php\\'" :config (set-docsets! 'web-mode "HTML" "CSS" "Twig" "WordPress") diff --git a/modules/lang/web/config.el b/modules/lang/web/config.el index 50a293cc5..1f872ec4c 100644 --- a/modules/lang/web/config.el +++ b/modules/lang/web/config.el @@ -19,7 +19,7 @@ ;; -;; Framework-based minor-modes +;;; Framework-based minor-modes (def-project-mode! +web-jekyll-mode :modes '(web-mode js-mode coffee-mode css-mode haml-mode pug-mode) diff --git a/modules/lang/web/packages.el b/modules/lang/web/packages.el index 0665b6250..2efb16915 100644 --- a/modules/lang/web/packages.el +++ b/modules/lang/web/packages.el @@ -16,6 +16,7 @@ (package! sass-mode) (package! stylus-mode) +(package! sws-mode) (package! rainbow-mode) (when (featurep! :completion ivy) (package! counsel-css)) diff --git a/modules/lang/web/test/test-web.el b/modules/lang/web/test/test-web.el index 72fbc04cf..ac97ad281 100644 --- a/modules/lang/web/test/test-web.el +++ b/modules/lang/web/test/test-web.el @@ -51,20 +51,20 @@ (describe "css-mode" (it "converts inline statements into multiline blocks" - (insert! "body { color: red{0}; font-size: 2em; }")) + (insert!! "body { color: red{0}; font-size: 2em; }")) (it "works when cursor is on closing brace" - (insert! "body { color: red; font-size: 2em; {0}}")) + (insert!! "body { color: red; font-size: 2em; {0}}")) (it "works when cursor is on opening brace" - (insert! "body {{0} color: red; font-size: 2em; }")) + (insert!! "body {{0} color: red; font-size: 2em; }")) (it "works when cursor is on same line" - (insert! "{0}body { color: red; font-size: 2em; }")))) + (insert!! "{0}body { color: red; font-size: 2em; }")))) (describe "comment-indent-new-line" (before-each (delay-mode-hooks (scss-mode))) (it "continues commented lines on newline" - (insert! "// test{0}\n" + (insert!! "// test{0}\n" "body { color: red; font-size: 2em; }") (+css/comment-indent-new-line) (expect (string-trim (buffer-string)) :to-equal @@ -76,7 +76,7 @@ (expect (eolp)) (expect (line-number-at-pos) :to-be 2)) (it "preserves indentation within continued comments" - (insert! "// test{0}\n" + (insert!! "// test{0}\n" "body { color: red; font-size: 2em; }") (+css/comment-indent-new-line) (expect (string-trim (buffer-string)) :to-equal diff --git a/modules/term/eshell/config.el b/modules/term/eshell/config.el index d2b9b5c76..c2bbf31f6 100644 --- a/modules/term/eshell/config.el +++ b/modules/term/eshell/config.el @@ -132,10 +132,12 @@ You should use `set-eshell-alias!' to change this.") :n "C" #'+eshell/evil-change-line :n "d" #'+eshell/evil-delete :n "D" #'+eshell/evil-delete-line - :i "TAB" #'+eshell/pcomplete - :i [tab] #'+eshell/pcomplete - :i "C-d" #'+eshell/quit-or-delete-char + :ig "TAB" #'+eshell/pcomplete + :ig [tab] #'+eshell/pcomplete + :ig "C-d" #'+eshell/quit-or-delete-char "C-s" #'+eshell/search-history + ;; Emacs bindings + "C-e" #'end-of-line ;; Tmux-esque prefix keybinds "C-c s" #'+eshell/split-below "C-c v" #'+eshell/split-right diff --git a/modules/tools/direnv/README.org b/modules/tools/direnv/README.org index 40ab0f851..20530ae92 100644 --- a/modules/tools/direnv/README.org +++ b/modules/tools/direnv/README.org @@ -79,10 +79,10 @@ nixpkgs.overlays = [ let src = (super.fetchFromGitHub { owner = "target"; repo = "lorri"; - rev = "e943fa403234f1a5e403b6fdc112e79abc1e29ba"; - sha256 = "1ar7clza117qdzldld9qzg4q0wr3g20zxrnd1s51cg6gxwlpg7fa"; + rev = "38eae3d487526ece9d1b8c9bb0d27fb45cf60816"; + sha256 = "11k9lxg9cv6dlxj4haydvw4dhcfyszwvx7jx9p24jadqsy9jmbj4"; }); - in super.callPackage src { inherit src; }; + in import src { inherit src; }; }) ]; diff --git a/modules/tools/flyspell/config.el b/modules/tools/flyspell/config.el index 3f1601fd1..0503755d6 100644 --- a/modules/tools/flyspell/config.el +++ b/modules/tools/flyspell/config.el @@ -1,13 +1,5 @@ ;;; tools/flyspell/config.el -*- lexical-binding: t; -*- -(defvar-local +flyspell-immediately t - "If non-nil, spellcheck the current buffer upon starting `flyspell-mode'. - -Since spellchecking can be slow in some buffers, this can be disabled with: - - (setq-hook! 'TeX-mode-hook +flyspell-immediately nil)") - - ;; ;;; Packages @@ -52,7 +44,10 @@ Since spellchecking can be slow in some buffers, this can be disabled with: ;;;###package flyspell (progn ; built-in - (setq flyspell-issue-welcome-flag nil) + (setq flyspell-issue-welcome-flag nil + ;; Significantly speeds up flyspell, which would otherwise print + ;; messages for every word when checking the entire buffer + flyspell-issue-message-flag nil) (when (featurep! +prog) (add-hook 'prog-mode-hook #'flyspell-prog-mode)) @@ -66,12 +61,6 @@ e.g. proselint and langtool." (featurep 'langtool)) (setq-local flyspell-mark-duplications-flag nil)))) - (add-hook! 'flyspell-mode-hook - (defun +flyspell-immediately-h () - "Spellcheck the buffer when `flyspell-mode' is enabled." - (when (and flyspell-mode +flyspell-immediately) - (flyspell-buffer)))) - ;; Ensure mode-local predicates declared with `set-flyspell-predicate!' are ;; used in their respective major modes. (add-hook 'flyspell-mode-hook #'+flyspell-init-predicate-h)) diff --git a/modules/tools/lsp/autoload.el b/modules/tools/lsp/autoload.el index 7b6baffd6..b84f6bf9d 100644 --- a/modules/tools/lsp/autoload.el +++ b/modules/tools/lsp/autoload.el @@ -10,8 +10,9 @@ been moved out to their respective modules, or these hooks: + `+lsp-init-company-h' (on `lsp-mode-hook') + `+lsp-init-ui-flycheck-or-flymake-h' (on `lsp-ui-mode-hook')" - (require 'lsp-mode) - (unless lsp-mode + (if (bound-and-true-p lsp-mode) + t + (require 'lsp-mode) (when lsp-auto-configure (require 'lsp-clients)) (when (and (buffer-file-name) @@ -21,10 +22,10 @@ been moved out to their respective modules, or these hooks: (lsp--try-project-root-workspaces (equal arg '(4)) (and arg (not (equal arg 1))))))) - (lsp-mode 1) - (lsp--info - "Connected to %s." - (apply - #'concat (mapcar - (lambda (it) (format "[%s]" (lsp--workspace-print it))) - lsp--buffer-workspaces)))))) + (prog1 (lsp-mode 1) + (lsp--info + "Connected to %s." + (apply + #'concat (mapcar + (lambda (it) (format "[%s]" (lsp--workspace-print it))) + lsp--buffer-workspaces))))))) diff --git a/modules/tools/lsp/config.el b/modules/tools/lsp/config.el index 371633abb..b6beca4fa 100644 --- a/modules/tools/lsp/config.el +++ b/modules/tools/lsp/config.el @@ -35,12 +35,13 @@ This can be a single company backend or a list thereof. It can be anything (add-hook! 'lsp-ui-mode-hook (defun +lsp-init-ui-flycheck-or-flymake-h () "Sets up flymake-mode or flycheck-mode, depending on `lsp-prefer-flymake'." - (unless (eq :none lsp-prefer-flymake) - (if (and (not (version< emacs-version "26.1")) - lsp-prefer-flymake) - (lsp--flymake-setup)) - (require 'lsp-ui-flycheck) - (lsp-ui-flycheck-enable t)))) + (cond ((eq :none lsp-prefer-flymake)) + ((and (not (version< emacs-version "26.1")) + lsp-prefer-flymake) + (lsp--flymake-setup)) + ((require 'flycheck nil t) + (require 'lsp-ui-flycheck) + (lsp-ui-flycheck-enable t))))) :config (setq lsp-prefer-flymake nil lsp-ui-doc-max-height 8 @@ -52,10 +53,7 @@ This can be a single company backend or a list thereof. It can be anything ;; Don't show symbol definitions in the sideline. They are pretty noisy, ;; and there is a bug preventing Flycheck errors from being shown (the ;; errors flash briefly and then disappear). - lsp-ui-sideline-show-hover nil - ;; `lsp-ui' offers its own eldoc integration, which is redundant with - ;; lsp-mode's. - lsp-eldoc-enable-hover nil) + lsp-ui-sideline-show-hover nil) (set-lookup-handlers! 'lsp-ui-mode :async t :definition 'lsp-ui-peek-find-definitions diff --git a/modules/tools/macos/autoload.el b/modules/tools/macos/autoload.el index 9ffbd5b57..0aa9f8b53 100644 --- a/modules/tools/macos/autoload.el +++ b/modules/tools/macos/autoload.el @@ -1,5 +1,8 @@ ;;; tools/macos/autoload.el -*- lexical-binding: t; -*- +;;;###autoload +(setq locate-command "mdfind") + ;;;###autoload (defun +macos-open-with (&optional app-name path) "Send PATH to APP-NAME on OSX." diff --git a/modules/tools/magit/autoload.el b/modules/tools/magit/autoload.el index bd167c8a5..745820836 100644 --- a/modules/tools/magit/autoload.el +++ b/modules/tools/magit/autoload.el @@ -3,7 +3,7 @@ ;; 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. -;;;###autoload (advice-add #'magit-version :around #'ignore) +;;;###autoload (advice-add #'magit-version :override #'ignore) ;;;###autoload (defun +magit-display-buffer-fn (buffer) @@ -55,13 +55,13 @@ (defvar-local +magit--vc-is-stale-p nil) ;;;###autoload -(defun +magit|refresh-vc-state-maybe () +(defun +magit-refresh-vc-state-maybe-h () "Update `vc' and `git-gutter' if out of date." (when +magit--vc-is-stale-p (+magit--refresh-vc-in-buffer (current-buffer)))) ;;;###autoload -(add-hook 'doom-switch-buffer-hook #'+magit|refresh-vc-state-maybe) +(add-hook 'doom-switch-buffer-hook #'+magit-refresh-vc-state-maybe-h) ;;;###autoload (defun +magit/quit (&optional _kill-buffer) diff --git a/modules/tools/magit/config.el b/modules/tools/magit/config.el index 28d7bd0b6..ae27d0952 100644 --- a/modules/tools/magit/config.el +++ b/modules/tools/magit/config.el @@ -95,7 +95,19 @@ disabled that in `+magit--forge-get-repository-lazily-a', we must manually ensure it is built when we actually use Forge." :before #'forge-dispatch (unless (file-executable-p emacsql-sqlite-executable) - (emacsql-sqlite-compile 2)))) + (emacsql-sqlite-compile 2) + (if (not (file-executable-p emacsql-sqlite-executable)) + (message (concat "Failed to build emacsql; forge may not work correctly.\n" + "See *Compile-Log* buffer for details")) + ;; HACK Due to changes upstream, forge doesn't initialize completely if + ;; it doesn't find `emacsql-sqlite-executable', so we have to do it + ;; manually after installing it. + (setq forge--sqlite-available-p t) + (magit-add-section-hook 'magit-status-sections-hook 'forge-insert-pullreqs nil t) + (magit-add-section-hook 'magit-status-sections-hook 'forge-insert-issues nil t) + (after! forge-topic + (dolist (hook forge-bug-reference-hooks) + (add-hook hook #'forge-bug-reference-setup))))))) (use-package! magit-todos diff --git a/modules/tools/pdf/autoload/pdf.el b/modules/tools/pdf/autoload/pdf.el index 31890d982..29818bc36 100644 --- a/modules/tools/pdf/autoload/pdf.el +++ b/modules/tools/pdf/autoload/pdf.el @@ -1,111 +1,30 @@ ;;; tools/pdf/autoload/pdf.el -*- lexical-binding: t; -*- -;;;###autoload -(defun *pdf-pdf-view-use-scaling-p () - "Return t if scaling should be used." - (and (or (and (eq (framep-on-display) 'ns) (string-equal emacs-version "27.0.50")) - (memq (pdf-view-image-type) - '(imagemagick image-io))) - pdf-view-use-scaling)) -;;;###autoload -(defun *pdf-pdf-annot-show-annotation (a &optional highlight-p window) - "Make annotation A visible. -Turn to A's page in WINDOW, and scroll it if necessary. - -If HIGHLIGHT-P is non-nil, visually distinguish annotation A from -other annotations." - - (save-selected-window - (when window (select-window window)) - (pdf-util-assert-pdf-window) - (let* ((page (pdf-annot-get a 'page)) - (size (pdf-view-image-size)) - (width (car size))) - (unless (= page (pdf-view-current-page)) - (pdf-view-goto-page page)) - (let ((edges (pdf-annot-get-display-edges a))) - (when highlight-p - (pdf-view-display-image - (pdf-view-create-image - (pdf-cache-renderpage-highlight - page width - `("white" "steel blue" 0.35 ,@edges)) - :map (pdf-view-apply-hotspot-functions - window page size) - :width width))) - (pdf-util-scroll-to-edges - (pdf-util-scale-relative-to-pixel (car edges))))))) ;;;###autoload -(defun *pdf-pdf-isearch-hl-matches (current matches &optional occur-hack-p) - "Highlighting edges CURRENT and MATCHES." - (cl-check-type current pdf-isearch-match) - (cl-check-type matches (list-of pdf-isearch-match)) - (cl-destructuring-bind (fg1 bg1 fg2 bg2) - (pdf-isearch-current-colors) - (let* ((width (car (pdf-view-image-size))) - (page (pdf-view-current-page)) - (window (selected-window)) - (buffer (current-buffer)) - (tick (cl-incf pdf-isearch--hl-matches-tick)) - (pdf-info-asynchronous - (lambda (status data) - (when (and (null status) - (eq tick pdf-isearch--hl-matches-tick) - (buffer-live-p buffer) - (window-live-p window) - (eq (window-buffer window) - buffer)) - (with-selected-window window - (when (and (derived-mode-p 'pdf-view-mode) - (or isearch-mode - occur-hack-p) - (eq page (pdf-view-current-page))) - (pdf-view-display-image - (pdf-view-create-image data - :width width)))))))) - (pdf-info-renderpage-text-regions - page width t nil - `(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative - current)) - `(,fg2 ,bg2 ,@(pdf-util-scale-pixel-to-relative - (apply 'append - (remove current matches)))))))) +(defun +pdf--supply-width-to-create-image-calls-a (orig-fn &rest args) + (cl-letf* ((old-create-image (symbol-function #'create-image)) + ((symbol-function #'create-image) + (lambda (file-or-data &optional type data-p &rest props) + (apply old-create-image file-or-data type data-p + :width (car (pdf-view-image-size)) + props)))) + (apply orig-fn args))) + ;;;###autoload -(defun *pdf-pdf-util-frame-scale-factor () - "Return the frame scale factor depending on the image type used for display. -When `pdf-view-use-scaling' is non-nil and imagemagick or -image-io are used as the image type for display, return the -backing-scale-factor of the frame if available. If a -backing-scale-factor attribute isn't available, return 2 if the -frame's PPI is larger than 180. Otherwise, return 1." +(defun +pdf--util-frame-scale-factor-a (orig-fn) (if (and pdf-view-use-scaling (memq (pdf-view-image-type) '(imagemagick image-io)) (fboundp 'frame-monitor-attributes)) - (or (cdr (assq 'backing-scale-factor (frame-monitor-attributes))) - (if (>= (pdf-util-frame-ppi) 180) - 2 - 1)) - (if (and (eq (framep-on-display) 'ns) (string-equal emacs-version "27.0.50")) + (funcall orig-fn) + ;; Add special support for retina displays on MacOS + (if (and (eq (framep-on-display) 'ns) + EMACS27+) 2 1))) + ;;;###autoload -(defun *pdf-pdf-view-display-region (&optional region rectangle-p) - ;; TODO: write documentation! - (unless region - (pdf-view-assert-active-region) - (setq region pdf-view-active-region)) - (let ((colors (pdf-util-face-colors - (if rectangle-p 'pdf-view-rectangle 'pdf-view-region) - (bound-and-true-p pdf-view-dark-minor-mode))) - (page (pdf-view-current-page)) - (width (car (pdf-view-image-size)))) - (pdf-view-display-image - (pdf-view-create-image - (if rectangle-p - (pdf-info-renderpage-highlight - page width nil - `(,(car colors) ,(cdr colors) 0.35 ,@region)) - (pdf-info-renderpage-text-regions - page width nil nil - `(,(car colors) ,(cdr colors) ,@region))) - :width width)))) +(defun +pdf--view-use-scaling-p-a () + "Returns t if on ns window-system on Emacs 27+." + (and (eq (framep-on-display) 'ns) + EMACS27+ + pdf-view-use-scaling)) diff --git a/modules/tools/pdf/config.el b/modules/tools/pdf/config.el index f57211695..b04753da2 100644 --- a/modules/tools/pdf/config.el +++ b/modules/tools/pdf/config.el @@ -1,40 +1,74 @@ ;;; tools/pdf/config.el -*- lexical-binding: t; -*- (use-package! pdf-tools - :mode ("\\.pdf\\'" . pdf-view-mode) + :mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode) + :magic ("%PDF" . pdf-view-mode) :config - (unless noninteractive - (pdf-tools-install)) - (map! :map pdf-view-mode-map :gn "q" #'kill-current-buffer) - (defun +pdf-cleanup-windows-h () - "Kill left-over annotation buffers when the document is killed." - (when (buffer-live-p pdf-annot-list-document-buffer) - (pdf-info-close pdf-annot-list-document-buffer)) - (when (buffer-live-p pdf-annot-list-buffer) - (kill-buffer pdf-annot-list-buffer)) - (let ((contents-buffer (get-buffer "*Contents*"))) - (when (and contents-buffer (buffer-live-p contents-buffer)) - (kill-buffer contents-buffer)))) - (add-hook! 'pdf-view-mode-hook - (add-hook 'kill-buffer-hook #'+pdf-cleanup-windows-h nil t)) + (after! pdf-annot + (defun +pdf-cleanup-windows-h () + "Kill left-over annotation buffers when the document is killed." + (when (buffer-live-p pdf-annot-list-document-buffer) + (pdf-info-close pdf-annot-list-document-buffer)) + (when (buffer-live-p pdf-annot-list-buffer) + (kill-buffer pdf-annot-list-buffer)) + (let ((contents-buffer (get-buffer "*Contents*"))) + (when (and contents-buffer (buffer-live-p contents-buffer)) + (kill-buffer contents-buffer)))) + (add-hook! 'pdf-view-mode-hook + (add-hook 'kill-buffer-hook #'+pdf-cleanup-windows-h nil t))) (setq-default pdf-view-display-size 'fit-page pdf-view-use-scaling t pdf-view-use-imagemagick nil) - (advice-add 'pdf-annot-show-annotation :override #'*pdf-pdf-annot-show-annotation) - (advice-add 'pdf-isearch-hl-matches :override #'*pdf-pdf-isearch-hl-matches) - (advice-add 'pdf-util-frame-scale-factor :override #'*pdf-pdf-util-frame-scale-factor) - (advice-add 'pdf-view-display-region :override #'*pdf-pdf-view-display-region) - (advice-add 'pdf-view-use-scaling-p :override #'*pdf-pdf-view-use-scaling-p) + ;; Add retina support for MacOS users + (when IS-MAC + (advice-add #'pdf-util-frame-scale-factor :around #'+pdf--util-frame-scale-factor-a) + (advice-add #'pdf-view-use-scaling-p :before-until #'+pdf--view-use-scaling-p-a) + (defadvice! +pdf--supply-width-to-create-image-calls-a (orig-fn &rest args) + :around '(pdf-annot-show-annotation + pdf-isearch-hl-matches + pdf-view-display-region) + (cl-letf* ((old-create-image (symbol-function #'create-image)) + ((symbol-function #'create-image) + (lambda (file-or-data &optional type data-p &rest props) + (apply old-create-image file-or-data type data-p + :width (car (pdf-view-image-size)) + props)))) + (apply orig-fn args)))) + ;; Turn off cua so copy works (add-hook! 'pdf-view-mode-hook (cua-mode 0)) ;; Handle PDF-tools related popups better (set-popup-rule! "^\\*Outline*" :side 'right :size 40 :select nil) - ;; The next rules are not needed, they are defined in modules/ui/popups/+hacks.el - ;; (set-popup-rule! "\\*Contents\\*" :side 'right :size 40) - ;; (set-popup-rule! "* annots\\*$" :side 'left :size 40 :select nil) + (set-popup-rule! "\\(?:^\\*Contents\\|'s annots\\*$\\)" :ignore t) + (add-hook 'pdf-annot-list-mode-hook #'hide-mode-line-mode) ;; Fix #1107: flickering pdfs when evil-mode is enabled - (setq-hook! 'pdf-view-mode-hook evil-normal-state-cursor (list nil))) + (setq-hook! 'pdf-view-mode-hook evil-normal-state-cursor (list nil)) + + ;; Install epdfinfo binary if needed, blocking until it is finished + (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 + (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)) diff --git a/modules/tools/upload/config.el b/modules/tools/upload/config.el index cb7631413..cec95bf2c 100644 --- a/modules/tools/upload/config.el +++ b/modules/tools/upload/config.el @@ -33,19 +33,19 @@ (put (car sym) 'safe-local-variable (cdr sym))) ;; Maybe auto-upload on save - (defun +upload|init-after-save () - (when (and (bound-and-true-p ssh-deploy-root-remote) - ssh-deploy-on-explicit-save) - (ssh-deploy-upload-handler))) - (add-hook 'after-save-hook #'+upload|init-after-save) + (add-hook! 'after-save-hook + (defun +upload-init-after-save-h () + (when (and (bound-and-true-p ssh-deploy-root-remote) + ssh-deploy-on-explicit-save) + (ssh-deploy-upload-handler)))) ;; Enable ssh-deploy if variables are set, and check for changes on open file ;; (if possible) - (defun +upload|init-find-file () - (when (bound-and-true-p ssh-deploy-root-remote) - (require 'ssh-deploy) - (unless ssh-deploy-root-local - (setq ssh-deploy-root-local (doom-project-root))) - (when ssh-deploy-automatically-detect-remote-changes - (ssh-deploy-remote-changes-handler)))) - (add-hook 'find-file-hook #'+upload|init-find-file)) + (add-hook! 'find-file-hook + (defun +upload-init-find-file-h () + (when (bound-and-true-p ssh-deploy-root-remote) + (require 'ssh-deploy) + (unless ssh-deploy-root-local + (setq ssh-deploy-root-local (doom-project-root))) + (when ssh-deploy-automatically-detect-remote-changes + (ssh-deploy-remote-changes-handler)))))) diff --git a/modules/ui/doom/config.el b/modules/ui/doom/config.el index e2b9c5642..48d4e0d3a 100644 --- a/modules/ui/doom/config.el +++ b/modules/ui/doom/config.el @@ -10,6 +10,7 @@ (doom-one . t) (doom-one-light . t) (doom-opera . t) + (doom-solarized-dark . t) (doom-solarized-light) (doom-spacegrey) (doom-vibrant) @@ -45,7 +46,7 @@ :init (add-hook! 'doom-load-theme-hook :append (defun +doom-solaire-mode-swap-bg-maybe-h () - (pcase-let ((`(,theme . ,swap) (assq doom-theme +doom-solaire-themes))) + (pcase-let ((`(,_theme . ,swap) (assq doom-theme +doom-solaire-themes))) (require 'solaire-mode) (if swap (solaire-mode-swap-bg))))) :config diff --git a/modules/ui/hydra/config.el b/modules/ui/hydra/config.el index 4d8bdb3fb..b8bfe3f79 100644 --- a/modules/ui/hydra/config.el +++ b/modules/ui/hydra/config.el @@ -1,4 +1,10 @@ ;;; ui/hydra/config.el -*- lexical-binding: t; -*- +(use-package! hydra-examples + :commands (hydra-move-splitter-up + hydra-move-splitter-down + hydra-move-splitter-right + hydra-move-splitter-left)) + ;;;###package hydra -(setq lv-use-seperator t) +(setq lv-use-separator t) diff --git a/modules/ui/modeline/config.el b/modules/ui/modeline/config.el index 43d35bcdf..61cc8add0 100644 --- a/modules/ui/modeline/config.el +++ b/modules/ui/modeline/config.el @@ -29,8 +29,8 @@ (defvar mouse-wheel-down-event nil) (defvar mouse-wheel-up-event nil) - (add-hook 'doom-modeline-mode-hook #'size-indication-mode) ; filesize in modeline - (add-hook 'doom-modeline-mode-hook #'column-number-mode) ; cursor column in modeline + (size-indication-mode +1) ; filesize in modeline + (column-number-mode +1) ; cursor column in modeline (add-hook 'doom-change-font-size-hook #'+modeline-resize-for-font-h) (add-hook 'doom-load-theme-hook #'doom-modeline-refresh-bars) @@ -72,4 +72,4 @@ (use-package! evil-anzu :when (featurep! :editor evil) - :after-call (evil-ex-start-search evil-ex-start-word-search evil-ex-search-activate-highlight)) + :after-call evil-ex-start-search evil-ex-start-word-search evil-ex-search-activate-highlight) diff --git a/modules/ui/neotree/config.el b/modules/ui/neotree/config.el index f9a729427..e0a0ddf42 100644 --- a/modules/ui/neotree/config.el +++ b/modules/ui/neotree/config.el @@ -48,7 +48,7 @@ (add-hook! 'neo-enter-hook (defun +neotree-fix-cursor-h (&rest _) (with-current-buffer neo-global--buffer - (+neotree*indent-cursor)))) + (+neotree--indent-cursor-a)))) (defadvice! +neotree--indent-cursor-a (&rest _) :after '(neotree-next-line neotree-previous-line) (beginning-of-line) diff --git a/modules/ui/popup/+hacks.el b/modules/ui/popup/+hacks.el index a2851f43f..ae046ae96 100644 --- a/modules/ui/popup/+hacks.el +++ b/modules/ui/popup/+hacks.el @@ -21,7 +21,7 @@ ;; ;; Hacks should be kept in alphabetical order, named after the feature they ;; modify, and should follow a ;;;## package-name header line (if not using -;; `after!' or `def-package!'). +;; `after!' or `use-package!'). ;; ;;; Core functions @@ -109,7 +109,7 @@ the command buffer." ;;;###package help-mode (after! help-mode - (defun doom--switch-from-popup (location) + (defun +popup--switch-from-popup (location) (let (origin enable-local-variables) (save-popups! (switch-to-buffer (car location) nil t) @@ -131,7 +131,7 @@ the command buffer." (require 'find-func) (when (eq file 'C-source) (setq file (help-C-file-name (indirect-function fun) 'fun))) - (doom--switch-from-popup (find-function-search-for-symbol fun nil file)))) + (+popup--switch-from-popup (find-function-search-for-symbol fun nil file)))) (define-button-type 'help-variable-def :supertype 'help-xref @@ -139,14 +139,14 @@ the command buffer." (lambda (var &optional file) (when (eq file 'C-source) (setq file (help-C-file-name var 'var))) - (doom--switch-from-popup (find-variable-noselect var file)))) + (+popup--switch-from-popup (find-variable-noselect var file)))) (define-button-type 'help-face-def :supertype 'help-xref 'help-function (lambda (fun file) (require 'find-func) - (doom--switch-from-popup (find-function-search-for-symbol fun 'defface file))))) + (+popup--switch-from-popup (find-function-search-for-symbol fun 'defface file))))) ;;;###package helpful @@ -188,7 +188,7 @@ the command buffer." ;; But it must exist for org to clean up later. (get-buffer-create "*Org Links*")) (apply old-org-completing-read args)))) - (apply orig-fn args))) + (apply #'funcall-interactively orig-fn args))) ;; Fix left-over popup window when closing persistent help for `helm-M-x' (defadvice! +popup--helm-elisp--persistent-help-a (candidate _fun &optional _name) @@ -207,10 +207,6 @@ the command buffer." (current-buffer))))) -;;;###package ibuffer -(setq ibuffer-use-other-window t) - - ;;;###package Info (defadvice! +popup--switch-to-info-window-a (&rest _) :after #'info-lookup-symbol @@ -239,71 +235,41 @@ the command buffer." org-capture-place-template org-export--dispatch-ui org-agenda-get-restriction-and-command - org-fast-tag-selection) + org-fast-tag-selection + org-fast-todo-selection) (if +popup-mode - (cl-letf (((symbol-function 'delete-other-windows) - (symbol-function 'ignore))) + (cl-letf (((symbol-function #'delete-other-windows) + (symbol-function #'ignore))) (apply orig-fn args)) (apply orig-fn args))) - (defadvice! +popup--org-fix-tags-window-a (orig-fn &rest args) + (defadvice! +popup--org-fix-popup-window-shrinking-a (orig-fn &rest args) "Hides the mode-line in *Org tags* buffer so you can actually see its content and displays it in a side window without deleting all other windows. Ugh, such an ugly hack." - :around #'org-fast-tag-selection + :around '(org-fast-tag-selection + org-fast-todo-selection) (if +popup-mode - (cl-letf* ((old-fit-buffer-fn (symbol-function 'org-fit-window-to-buffer)) - ((symbol-function 'org-fit-window-to-buffer) + (cl-letf* ((old-fit-buffer-fn (symbol-function #'org-fit-window-to-buffer)) + ((symbol-function #'org-fit-window-to-buffer) (lambda (&optional window max-height min-height shrink-only) (when-let (buf (window-buffer window)) (delete-window window) - (setq window (display-buffer-in-side-window buf nil)) - (select-window window) + (select-window + (setq window (display-buffer-at-bottom buf nil))) (with-current-buffer buf (setq mode-line-format nil))) (funcall old-fit-buffer-fn window max-height min-height shrink-only)))) (apply orig-fn args)) (apply orig-fn args))) - (defadvice! +popup--org-src-pop-to-buffer-a (orig-fn buffer context) - "Hand off the src-block window to the popup system by using `display-buffer' -instead of switch-to-buffer-*." - :around #'org-src-switch-to-buffer - (if (and (eq org-src-window-setup 'popup-window) - +popup-mode) - (pop-to-buffer buffer) - (funcall orig-fn buffer context))) - (setq org-src-window-setup 'popup-window) - ;; Ensure todo, agenda, and other minor popups are delegated to the popup system. (defadvice! +popup--org-pop-to-buffer-a (orig-fn buf &optional norecord) "Use `pop-to-buffer' instead of `switch-to-buffer' to open buffer.'" :around #'org-switch-to-buffer-other-window (if +popup-mode (pop-to-buffer buf nil norecord) - (funcall orig-fn buf norecord))) - - ;; `org-agenda' - (setq org-agenda-window-setup 'popup-window - org-agenda-restore-windows-after-quit nil) - ;; Don't monopolize the frame! - (defadvice! +popup--org-agenda-suppress-delete-other-windows-a (orig-fn &rest args) - :around #'org-agenda-prepare-window - (cond ((not +popup-mode) - (apply orig-fn args)) - ((eq org-agenda-window-setup 'popup-window) - (let ((org-agenda-window-setup 'other-window) - org-agenda-restore-windows-after-quit) - (cl-letf (((symbol-function 'delete-other-windows) - (symbol-function 'ignore))) - (apply orig-fn args)))) - ((memq org-agenda-window-setup '(current-window other-window)) - (with-popup-rules! nil - (cl-letf (((symbol-function 'delete-other-windows) - (symbol-function 'ignore))) - (apply orig-fn args)))) - ((with-popup-rules! nil - (apply orig-fn args)))))) + (funcall orig-fn buf norecord)))) ;;;###package persp-mode @@ -327,10 +293,7 @@ instead of switch-to-buffer-*." '((+popup-display-buffer-stacked-side-window-fn) (side . left) (slot . 3) - (inhibit-same-window . t))) - - (add-hook 'pdf-annot-list-mode-hook #'hide-mode-line-mode) - (set-popup-rule! "\\(^\\*Contents\\|'s annots\\*$\\)" :ignore t)) + (inhibit-same-window . t)))) ;;;###package profiler diff --git a/modules/ui/popup/autoload/popup.el b/modules/ui/popup/autoload/popup.el index cab480ebd..30dc9ea0c 100644 --- a/modules/ui/popup/autoload/popup.el +++ b/modules/ui/popup/autoload/popup.el @@ -27,7 +27,7 @@ the buffer is visible, then set another timer and try again later." (let (confirm-kill-processes) (when-let (process (get-buffer-process buffer)) (kill-process process)) - (let (kill-buffer-hook kill-buffer-query-functions) + (let (kill-buffer-query-functions) (kill-buffer buffer)))))))))) (defun +popup--delete-window (window) @@ -412,9 +412,11 @@ the message buffer in a popup window." t) ;;;###autoload -(defun +popup/raise (window) - "Raise the current popup window into a regular window." - (interactive (list (selected-window))) +(defun +popup/raise (window &optional arg) + "Raise the current popup window into a regular window. +If prefix ARG, raise the current popup into a new window." + (interactive + (list (selected-window) current-prefix-arg)) (cl-check-type window window) (unless (+popup-window-p window) (user-error "Cannot raise a non-popup window")) @@ -422,7 +424,9 @@ the message buffer in a popup window." (+popup--inhibit-transient t) +popup--remember-last) (+popup/close window 'force) - (display-buffer-pop-up-window buffer nil))) + (if arg + (pop-to-buffer buffer) + (switch-to-buffer buffer)))) ;;;###autoload (defun +popup/diagnose () diff --git a/modules/ui/popup/config.el b/modules/ui/popup/config.el index 2d605ebb0..391a266d8 100644 --- a/modules/ui/popup/config.el +++ b/modules/ui/popup/config.el @@ -151,7 +151,7 @@ prevent the popup(s) from messing up the UI (or vice versa)." :slot 2 :side left :size 20 :select t :quit t) ;; `help-mode', `helpful-mode' ("^\\*[Hh]elp" - :slot 2 :vslot -2 :size 0.35 :select t) + :slot 2 :vslot -8 :size 0.35 :select t) ("^\\*eww\\*" ; `eww' (and used by dash docsets) :vslot -11 :size 0.35 :select t) ("^\\*info\\*$" ; `Info-mode' diff --git a/modules/ui/pretty-code/+fira.el b/modules/ui/pretty-code/+fira.el index 24b2982af..c5f86067f 100644 --- a/modules/ui/pretty-code/+fira.el +++ b/modules/ui/pretty-code/+fira.el @@ -112,11 +112,11 @@ ("~~>" . #Xe169) ("%%" . #Xe16a))) -(defun +pretty-code|setup-fira-ligatures () +(defun +pretty-code-setup-fira-ligatures-h () (set-fontset-font t '(#Xe100 . #Xe16f) +pretty-code-fira-code-font-name) (setq-default prettify-symbols-alist (append prettify-symbols-alist (mapcar #'+pretty-code--correct-symbol-bounds +pretty-code-fira-code-font-ligatures)))) -(add-hook 'doom-init-ui-hook #'+pretty-code|setup-fira-ligatures) +(add-hook 'doom-init-ui-hook #'+pretty-code-setup-fira-ligatures-h) diff --git a/modules/ui/pretty-code/+hasklig.el b/modules/ui/pretty-code/+hasklig.el index ad4dd0580..3d54bf982 100644 --- a/modules/ui/pretty-code/+hasklig.el +++ b/modules/ui/pretty-code/+hasklig.el @@ -48,11 +48,11 @@ ("<->" . #Xe129))) -(defun +pretty-code|setup-hasklig-ligatures () +(defun +pretty-code-setup-hasklig-ligatures-h () (set-fontset-font t '(#Xe100 . #Xe129) +pretty-code-hasklig-font-name) (setq-default prettify-symbols-alist (append prettify-symbols-alist (mapcar #'+pretty-code--correct-symbol-bounds +pretty-code-hasklig-font-ligatures)))) -(add-hook 'doom-init-ui-hook #'+pretty-code|setup-hasklig-ligatures) +(add-hook 'doom-init-ui-hook #'+pretty-code-setup-hasklig-ligatures-h) diff --git a/modules/ui/pretty-code/+iosevka.el b/modules/ui/pretty-code/+iosevka.el index e076b34ce..315dbd754 100644 --- a/modules/ui/pretty-code/+iosevka.el +++ b/modules/ui/pretty-code/+iosevka.el @@ -223,11 +223,10 @@ ("+>" . #Xe1cc)) "Defines the character mappings for ligatures for Iosevka.") -(defun +pretty-code|setup-iosevka-ligatures () +(defun +pretty-code-setup-iosevka-ligatures-h () (set-fontset-font t '(#Xe100 . #Xe1cc) +pretty-code-iosevka-font-name) (setq-default prettify-symbols-alist (append prettify-symbols-alist +pretty-code-iosevka-font-ligatures))) -(add-hook 'doom-init-ui-hook #'+pretty-code|setup-iosevka-ligatures) - +(add-hook 'doom-init-ui-hook #'+pretty-code-setup-iosevka-ligatures-h) diff --git a/modules/ui/pretty-code/+pragmata-pro.el b/modules/ui/pretty-code/+pragmata-pro.el index eafa4cedd..c22ca2f74 100644 --- a/modules/ui/pretty-code/+pragmata-pro.el +++ b/modules/ui/pretty-code/+pragmata-pro.el @@ -198,10 +198,10 @@ ("\">" . #XEA90)) "Defines the character mappings for ligatures for Pragmata Pro.") -(defun +pretty-code|setup-pragmata-pro-ligatures () +(defun +pretty-code-setup-pragmata-pro-ligatures-h () (setq-default prettify-symbols-alist (append prettify-symbols-alist (mapcar #'+pretty-code--correct-symbol-bounds +pretty-code-pragmata-pro-font-ligatures)))) -(add-hook 'doom-init-ui-hook #'+pretty-code|setup-pragmata-pro-ligatures) +(add-hook 'doom-init-ui-hook #'+pretty-code-setup-pragmata-pro-ligatures-h) diff --git a/modules/ui/pretty-code/config.el b/modules/ui/pretty-code/config.el index da9b5480f..dcea1b671 100644 --- a/modules/ui/pretty-code/config.el +++ b/modules/ui/pretty-code/config.el @@ -17,7 +17,7 @@ besides what is listed.") ;; When you get to the right edge, it goes back to how it normally prints (setq prettify-symbols-unprettify-at-point 'right-edge) -(defun +pretty-code|init-pretty-symbols () +(defun +pretty-code-init-pretty-symbols-h () "Enable `prettify-symbols-mode'. If in fundamental-mode, or a mode derived from special, comint, eshell or term @@ -39,4 +39,4 @@ Otherwise it builds `prettify-code-symbols-alist' according to (prettify-symbols-mode -1)) (prettify-symbols-mode +1)))) -(add-hook 'after-change-major-mode-hook #'+pretty-code|init-pretty-symbols) +(add-hook 'after-change-major-mode-hook #'+pretty-code-init-pretty-symbols-h) diff --git a/modules/ui/tabs/autoload.el b/modules/ui/tabs/autoload.el index 26a131ca0..be3e62cfa 100644 --- a/modules/ui/tabs/autoload.el +++ b/modules/ui/tabs/autoload.el @@ -6,33 +6,6 @@ (or (memq buffer (window-parameter nil 'tab-buffers)) (eq buffer (doom-fallback-buffer)))) -;;;###autoload -(defun +tabs-window-tab-list () - (+tabs-window-buffer-list-fn)) - -;;;###autoload -(defun +tabs-window-buffer-list-fn () - (cl-delete-if-not #'buffer-live-p (window-parameter nil 'tab-buffers))) - -;;;###autoload -(defun +tabs-buffer-groups-fn () - (list - (cond ((or (string-equal "*" (substring (buffer-name) 0 1)) - (memq major-mode '(magit-process-mode - magit-status-mode - magit-diff-mode - magit-log-mode - magit-file-mode - magit-blob-mode - magit-blame-mode - ))) - "Emacs") - ((derived-mode-p 'eshell-mode) - "EShell") - ((derived-mode-p 'dired-mode) - "Dired") - ((centaur-tabs-get-group-name (current-buffer)))))) - ;; ;;; Commands @@ -54,7 +27,7 @@ ;;;###autoload (defun +tabs-kill-current-buffer-a (&rest _) - (+tabs|remove-buffer)) + (+tabs-remove-buffer-h)) ;;;###autoload (defun +tabs-bury-buffer-a (orig-fn &rest args) @@ -63,7 +36,7 @@ (apply orig-fn args) (unless (eq b (current-buffer)) (with-current-buffer b - (+tabs|remove-buffer)))) + (+tabs-remove-buffer-h)))) (apply orig-fn args))) ;;;###autoload @@ -86,11 +59,11 @@ (let* ((this-buf (current-buffer)) (buffers (window-parameter nil 'tab-buffers))) (cl-pushnew this-buf buffers) - (add-hook 'kill-buffer-hook #'+tabs|remove-buffer nil t) + (add-hook 'kill-buffer-hook #'+tabs-remove-buffer-h nil t) (set-window-parameter nil 'tab-buffers buffers)))) ;;;###autoload -(defun +tabs|remove-buffer () +(defun +tabs-remove-buffer-h () (when centaur-tabs-mode (set-window-parameter nil diff --git a/modules/ui/tabs/config.el b/modules/ui/tabs/config.el index c9e753c6e..8009b220c 100644 --- a/modules/ui/tabs/config.el +++ b/modules/ui/tabs/config.el @@ -1,7 +1,7 @@ ;;; ui/tabs/config.el -*- lexical-binding: t; -*- (use-package! centaur-tabs - :after-call (after-find-file dired-initial-position-hook) + :after-call after-find-file dired-initial-position-hook :init (setq centaur-tabs-height 28 centaur-tabs-set-bar 'left @@ -18,6 +18,37 @@ (add-to-list 'window-persistent-parameters '(tab-buffers . writable)) + (defun +tabs-window-buffer-list-fn () + (centaur-tabs-filter-out + 'centaur-tabs-hide-tab-cached + (delq nil + (cl-mapcar #'(lambda (b) + (cond + ;; Always include the current buffer. + ((eq (current-buffer) b) b) + ((buffer-file-name b) b) + ((char-equal ?\ (aref (buffer-name b) 0)) nil) + ((buffer-live-p b) b))) + (window-parameter nil 'tab-buffers))))) + + (defun +tabs-buffer-groups-fn () + (list + (cond ((or (string-equal "*" (substring (buffer-name) 0 1)) + (memq major-mode '(magit-process-mode + magit-status-mode + magit-diff-mode + magit-log-mode + magit-file-mode + magit-blob-mode + magit-blame-mode + ))) + "Emacs") + ((derived-mode-p 'eshell-mode) + "EShell") + ((derived-mode-p 'dired-mode) + "Dired") + ((centaur-tabs-get-group-name (current-buffer)))))) + (setq centaur-tabs-buffer-list-function #'+tabs-window-buffer-list-fn centaur-tabs-buffer-groups-function #'+tabs-buffer-groups-fn) diff --git a/modules/ui/treemacs/config.el b/modules/ui/treemacs/config.el index 2fd6167b1..1a1a1b322 100644 --- a/modules/ui/treemacs/config.el +++ b/modules/ui/treemacs/config.el @@ -7,14 +7,6 @@ treemacs-last-error-persist-file (concat doom-cache-dir "treemacs-last-error-persist")) -(after! treemacs-persistence - ;; This variable is defined with defconst, so we must wait to change it until - ;; it has loaded. - (setq treemacs--last-error-persist-file - (concat doom-cache-dir - "treemacs-persist-at-last-error"))) - - (after! treemacs (set-popup-rule! "^ \\*Treemacs" :side treemacs-position diff --git a/modules/ui/vc-gutter/config.el b/modules/ui/vc-gutter/config.el index d65a4b262..0f6ede1f6 100644 --- a/modules/ui/vc-gutter/config.el +++ b/modules/ui/vc-gutter/config.el @@ -77,7 +77,18 @@ is deferred until the file is saved. Respects `git-gutter:disabled-modes'." (ignore (git-gutter))))) ;; update git-gutter when using magit commands (advice-add #'magit-stage-file :after #'+vc-gutter-update-h) - (advice-add #'magit-unstage-file :after #'+vc-gutter-update-h)) + (advice-add #'magit-unstage-file :after #'+vc-gutter-update-h) + + (defadvice! +vc-gutter--fix-linearity-of-hunks-a (diffinfos is-reverse) + "Fixes `git-gutter:next-hunk' and `git-gutter:previous-hunk' sometimes + jumping to random hunks." + :override #'git-gutter:search-near-diff-index + (cl-position-if (let ((lineno (line-number-at-pos))) + (lambda (line) + (funcall (if is-reverse #'> #'<) lineno line))) + diffinfos + :key #'git-gutter-hunk-start-line + :from-end is-reverse))) ;; subtle diff indicators in the fringe diff --git a/modules/ui/workspaces/autoload/workspaces.el b/modules/ui/workspaces/autoload/workspaces.el index 1f91854fd..39fc7a786 100644 --- a/modules/ui/workspaces/autoload/workspaces.el +++ b/modules/ui/workspaces/autoload/workspaces.el @@ -240,14 +240,17 @@ workspace to delete." (if current-prefix-arg (completing-read (format "Delete workspace (default: %s): " current-name) (+workspace-list-names) - nil nil current-name) + nil nil nil nil current-name) current-name)))) (condition-case-unless-debug ex + ;; REVIEW refactor me (let ((workspaces (+workspace-list-names))) (if (not (member name workspaces)) (+workspace-message (format "'%s' workspace doesn't exist" name) 'warn) (cond ((delq (selected-frame) (persp-frames-with-persp (get-frame-persp))) (user-error "Can't close workspace, it's visible in another frame")) + ((not (equal (+workspace-current-name) name)) + (+workspace-delete name)) ((> (length workspaces) 1) (+workspace-delete name) (+workspace-switch @@ -328,8 +331,8 @@ end of the workspace list." ;;;###autoload (dotimes (i 9) - (fset (intern (format "+workspace/switch-to-%d" i)) - (lambda () (interactive) (+workspace/switch-to i)))) + (defalias (intern (format "+workspace/switch-to-%d" i)) + (lambda () (interactive) (+workspace/switch-to i)))) ;;;###autoload (defun +workspace/switch-to-final () @@ -491,10 +494,9 @@ This be hooked to `projectile-after-switch-project-hook'." (if (and (not (null +workspaces-on-switch-project-behavior)) (or (eq +workspaces-on-switch-project-behavior t) (+workspace-buffer-list))) - (let* (persp-p - (persp + (let* ((persp (let ((project-name (doom-project-name +workspaces--project-dir))) - (or (setq persp-p (+workspace-get project-name t)) + (or (+workspace-get project-name t) (+workspace-new project-name)))) (new-name (persp-name persp))) (+workspace-switch new-name) diff --git a/modules/ui/workspaces/config.el b/modules/ui/workspaces/config.el index 9dd214f64..33235cbd7 100644 --- a/modules/ui/workspaces/config.el +++ b/modules/ui/workspaces/config.el @@ -109,7 +109,7 @@ stored in `persp-save-dir'.") (setq persp-add-buffer-on-find-file nil persp-add-buffer-on-after-change-major-mode nil) - (add-hook! 'doom-switch-buffer-hook + (add-hook! '(doom-switch-buffer-hook server-visit-hook) (defun +workspaces-add-current-buffer-h () "Add current buffer to focused perspective." (and persp-mode diff --git a/modules/ui/workspaces/test/test-workspaces.el b/modules/ui/workspaces/test/test-workspaces.el index f38173683..cc0e76c8c 100644 --- a/modules/ui/workspaces/test/test-workspaces.el +++ b/modules/ui/workspaces/test/test-workspaces.el @@ -11,10 +11,11 @@ persp1 persp1-name persp2 persp2-name wconf) + (require! :ui workspaces) + (require 'persp-mode) + (before-all - (delete-other-windows) - (require! :ui workspaces) - (require 'persp-mode)) + (delete-other-windows)) (before-each (switch-to-buffer "*scratch*")