diff --git a/bin/org-capture b/bin/org-capture index 507b02faa..ac70e247a 100755 --- a/bin/org-capture +++ b/bin/org-capture @@ -10,34 +10,33 @@ set -e cleanup() { - emacsclient --eval '(kill-emacs)' + emacsclient --eval '(let (kill-emacs-hook) (kill-emacs))' } # If emacs isn't running, we start a temporary daemon, solely for this window. -daemon= -if ! pgrep emacs >/dev/null; then - emacs --daemon - trap cleanup EXIT INT TERM - daemon=1 +if ! emacsclient -e nil; then + emacs --daemon + trap cleanup EXIT INT TERM + daemon=1 fi # org-capture key mapped to argument flags keys=$(emacsclient -e "(+org-capture-available-keys)" | cut -d '"' -f2) while getopts $keys opt; do - key="\"$opt\"" - break + key="\"$opt\"" + break done shift $((OPTIND-1)) [ -t 0 ] && str="$*" || str=$(cat) if [[ $daemon ]]; then - emacsclient -a "" \ - -c -F '((name . "org-capture") (width . 70) (height . 25) (transient . t))' \ - -e "(+org-capture/open-frame \"$str\" ${key:-nil})" + emacsclient -a "" \ + -c -F '((name . "org-capture") (width . 70) (height . 25) (transient . t))' \ + -e "(+org-capture/open-frame \"$str\" ${key:-nil} t)" else - # Non-daemon servers flicker a lot if frames are created from terminal, so - # we do it internally instead. - emacsclient -a "" \ - -e "(+org-capture/open-frame \"$str\" ${key:-nil})" + # Non-daemon servers flicker a lot if frames are created from terminal, so we + # do it internally instead. + emacsclient -a "" \ + -e "(+org-capture/open-frame \"$str\" ${key:-nil})" fi diff --git a/core/autoload/cache.el b/core/autoload/cache.el index 1093bf9ce..bf1c46bd5 100644 --- a/core/autoload/cache.el +++ b/core/autoload/cache.el @@ -12,7 +12,7 @@ ;; Like persistent-soft, caches assume a 2-tier structure, where all caches are ;; namespaced by location. -(defvar doom-cache-alists () +(defvar doom-cache-alists '(t) "An alist of alists, containing lists of variables for the doom cache library to persist across Emacs sessions.") @@ -24,7 +24,7 @@ name under `pcache-directory' (by default a subdirectory under (defun doom|save-persistent-cache () "Hook to run when an Emacs session is killed. Saves all persisted variables listed in `doom-cache-alists' to files." - (dolist (alist doom-cache-alists) + (dolist (alist (butlast doom-cache-alists 1)) (cl-loop with key = (car alist) for var in (cdr alist) if (symbol-value var) @@ -54,8 +54,8 @@ Warning: this is incompatible with buffer-local variables." (dolist (var variables) (when (doom-cache-exists var location) (set var (doom-cache-get var location)))) - (map-put doom-cache-alists location - (append variables (cdr (assq location doom-cache-alists))))) + (setf (alist-get location doom-cache-alists) + (append variables (cdr (assq location doom-cache-alists))))) ;;;###autoload (defun doom-cache-desist (location &optional variables) @@ -63,10 +63,11 @@ Warning: this is incompatible with buffer-local variables." `doom-cache-alists', thus preventing them from being saved between sessions. Does not affect the actual variables themselves or their values." (if variables - (map-put doom-cache-alists location - (cl-set-difference (cdr (assq location doom-cache-alists)) - variables)) - (map-delete doom-cache-alists location))) + (setf (alist-get location doom-cache-alists) + (cl-set-difference (cdr (assq location doom-cache-alists)) + variables)) + (delq (assq location doom-cache-alists) + doom-cache-alists))) ;;;###autoload (defun doom-cache-get (key &optional location) diff --git a/core/autoload/editor.el b/core/autoload/editor.el index 2457981e0..e6f2fd041 100644 --- a/core/autoload/editor.el +++ b/core/autoload/editor.el @@ -186,8 +186,7 @@ possible, or just one char if that's not possible." (save-excursion (insert-char ?\s (- ocol (current-column)) nil)))) ;; - ((and (= n 1) - (bound-and-true-p smartparens-mode)) + ((and (= n 1) (bound-and-true-p smartparens-mode)) (cond ((and (memq (char-before) (list ?\ ?\t)) (save-excursion (and (> (- (skip-chars-backward " \t" (line-beginning-position))) 0) @@ -213,7 +212,7 @@ possible, or just one char if that's not possible." ((run-hook-with-args-until-success 'doom-delete-backward-functions)) ((doom/backward-delete-whitespace-to-column))))))) ;; Otherwise, do simple deletion. - (t (delete-char (- n) killflag)))) + ((delete-char (- n) killflag)))) ;;;###autoload (defun doom/retab (arg &optional beg end) diff --git a/core/autoload/hydras.el b/core/autoload/hydras.el new file mode 100644 index 000000000..450d5a95b --- /dev/null +++ b/core/autoload/hydras.el @@ -0,0 +1,47 @@ +;;; core/autoload/hydras.el -*- lexical-binding: t; -*- + +;;;###autoload (autoload 'doom-text-zoom-hydra/body "core/autoload/hydras" nil nil) +(defhydra doom-text-zoom-hydra (:hint t :color red) + " + Text zoom: _j_:zoom in, _k_:zoom out, _0_:reset +" + ("j" text-scale-increase "in") + ("k" text-scale-decrease "out") + ("0" (text-scale-set 0) "reset")) + +;;;###autoload (autoload 'doom-window-nav-hydra/body "core/autoload/hydras" nil nil) +(defhydra doom-window-nav-hydra (:hint nil) + " + Split: _v_ert _s_:horz + Delete: _c_lose _o_nly + Switch Window: _h_:left _j_:down _k_:up _l_:right + Buffers: _p_revious _n_ext _b_:select _f_ind-file + Resize: _H_:splitter left _J_:splitter down _K_:splitter up _L_:splitter right + Move: _a_:up _z_:down _i_menu +" + ("z" scroll-up-line) + ("a" scroll-down-line) + ("i" idomenu) + + ("h" windmove-left) + ("j" windmove-down) + ("k" windmove-up) + ("l" windmove-right) + + ("p" previous-buffer) + ("n" next-buffer) + ("b" switch-to-buffer) + ("f" find-file) + + ("s" split-window-below) + ("v" split-window-right) + + ("c" delete-window) + ("o" delete-other-windows) + + ("H" hydra-move-splitter-left) + ("J" hydra-move-splitter-down) + ("K" hydra-move-splitter-up) + ("L" hydra-move-splitter-right) + + ("q" nil)) diff --git a/core/autoload/memoize.el b/core/autoload/memoize.el deleted file mode 100644 index 8d82c3d77..000000000 --- a/core/autoload/memoize.el +++ /dev/null @@ -1,31 +0,0 @@ -;;; core/autoload/memoize.el -*- lexical-binding: t; -*- - -;;;###autoload -(defvar doom-memoized-table (make-hash-table :test 'equal :size 10) - "A lookup table containing memoized functions. The keys are argument lists, -and the value is the function's return value.") - -;;;###autoload -(defun doom-memoize (name) - "Memoizes an existing function. NAME is a symbol." - (let ((func (symbol-function name))) - (put name 'function-documentation - (concat (documentation func) " (memoized)")) - (fset name - `(lambda (&rest args) - (let ((key (cons ',name args))) - (or (gethash key doom-memoized-table) - (puthash key (apply ',func args) - doom-memoized-table))))))) - -;;;###autoload -(defmacro def-memoized! (name arglist &rest body) - "Create a memoize'd function. NAME, ARGLIST, DOCSTRING and BODY -have the same meaning as in `defun'." - (declare (indent defun) (doc-string 3)) - `(,(if (bound-and-true-p byte-compile-current-file) - 'with-no-warnings - 'progn) - (defun ,name ,arglist ,@body) - (doom-memoize ',name))) - diff --git a/core/autoload/packages.el b/core/autoload/packages.el index 21c0f8269..13c03d121 100644 --- a/core/autoload/packages.el +++ b/core/autoload/packages.el @@ -14,15 +14,17 @@ table)))) (defmacro doom--condition-case! (&rest body) - `(condition-case-unless-debug ex + `(condition-case-unless-debug e (progn ,@body) ('user-error - (print! (bold (red " NOTICE: %s" ex)))) + (print! (bold (red " NOTICE: %s" e)))) ('file-error - (print! (bold (red " FILE ERROR: %s" (error-message-string ex)))) + (print! (bold (red " FILE ERROR: %s" (error-message-string e)))) (print! " Trying again...") (quiet! (doom-refresh-packages-maybe t)) - ,@body))) + ,@body) + ('error + (print! (bold (red " FATAL ERROR: %s\n Run again with the -d flag for details" e)))))) (defun doom--refresh-pkg-cache () "Clear the cache for `doom-refresh-packages-maybe'." @@ -88,11 +90,12 @@ list of the package." (list name old-version new-version))))) ;;;###autoload -(defun doom-package-prop (name prop) +(defun doom-package-prop (name prop &optional eval) "Return PROPerty in NAME's plist." (cl-check-type name symbol) (cl-check-type prop keyword) - (plist-get (cdr (assq name doom-packages)) prop)) + (let ((value (plist-get (cdr (assq name doom-packages)) prop))) + (if eval (eval value) value))) ;;;###autoload (defun doom-package-different-backend-p (name) @@ -164,9 +167,9 @@ files." if (and (or (not backend) (eq (doom-package-backend sym t) backend)) (or (eq ignored 'any) - (if ignored - (plist-get plist :ignore) - (not (plist-get plist :ignore)))) + (let* ((form (plist-get plist :ignore)) + (value (eval form))) + (if ignored value (not value)))) (or (eq disabled 'any) (if disabled (plist-get plist :disable) @@ -236,9 +239,9 @@ Used by `doom-packages-update'." (let (quelpa-pkgs elpa-pkgs) ;; Separate quelpa from elpa packages (dolist (pkg (mapcar #'car package-alist)) - (when (and (or (not (doom-package-prop pkg :freeze)) + (when (and (or (not (doom-package-prop pkg :freeze 'eval)) include-frozen-p) - (not (doom-package-prop pkg :ignore)) + (not (doom-package-prop pkg :ignore 'eval)) (not (doom-package-different-backend-p pkg))) (push pkg (if (eq (doom-package-backend pkg) 'quelpa) @@ -345,7 +348,7 @@ example; the package name can be omitted)." (package-install name)) (if (not (package-installed-p name)) (doom--delete-package-files name) - (map-put doom-packages name plist) + (setf (alist-get name doom-packages) plist) name))) ;;;###autoload @@ -388,9 +391,10 @@ package.el as appropriate." (unless (package-installed-p name) (user-error "%s isn't installed" name)) (let ((inhibit-message (not doom-debug-mode)) + (spec (assq name quelpa-cache)) quelpa-p) - (when (assq name quelpa-cache) - (setq quelpa-cache (map-delete quelpa-cache name)) + (when spec + (setq quelpa-cache (delq spec quelpa-cache)) (quelpa-save-cache) (setq quelpa-p t)) (package-delete (cadr (assq name package-alist)) force-p) @@ -439,11 +443,11 @@ calls." "Update `quelpa-cache' upon a successful `package-delete'." (doom-initialize-packages) (let ((name (package-desc-name desc))) - (when (and (not (package-installed-p name)) - (assq name quelpa-cache)) - (setq quelpa-cache (map-delete quelpa-cache name)) - (quelpa-save-cache) - (doom--delete-package-files name)))) + (unless (package-installed-p name) + (when-let* ((spec (assq name quelpa-cache))) + (setq quelpa-cache (delq spec quelpa-cache)) + (quelpa-save-cache) + (doom--delete-package-files name))))) ;; diff --git a/core/core-cli.el b/core/core-cli.el index c57944a37..92059f919 100644 --- a/core/core-cli.el +++ b/core/core-cli.el @@ -78,10 +78,10 @@ BODY will be run when this dispatcher is called." (append (when aliases `((dolist (alias ',aliases) - (map-put doom--dispatch-alias-alist alias ',cmd)))) - `((map-put doom--dispatch-command-alist ',cmd - (list :desc ,docstring - :body (lambda (args) ,form)))))))) + (setf (alist-get alias doom--dispatch-alias-alist) ',cmd)))) + `((setf (alist-get ',cmd doom--dispatch-command-alist) + (list :desc ,docstring + :body (lambda (args) ,form)))))))) ;; @@ -749,6 +749,7 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files." (message "Couldn't find any valid targets") (message "No targets to %scompile" (if recompile-p "re" ""))) (cl-return-from 'byte-compile)) + (require 'use-package) (condition-case e (let ((use-package-defaults use-package-defaults) (use-package-expand-minimally t)) @@ -796,7 +797,7 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files." (if recompile-p "Recompiled" "Compiled") total-ok (- (length target-files) total-noop) total-noop)))) - (error + ((debug error) (print! (red "\n%s\n\n%%s" "There were breaking errors.") "Reverting changes...") (signal 'doom-error (list 'byte-compile e)))))))) diff --git a/core/core-editor.el b/core/core-editor.el index 2a2c5d569..abd3a6e81 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -148,22 +148,27 @@ fundamental-mode) for performance sake." sp-show-pair-from-inside t sp-cancel-autoskip-on-backward-movement nil sp-show-pair-delay 0.1 - sp-max-pair-length 3) + sp-max-pair-length 4 + sp-max-prefix-length 50) - ;; smartparens conflicts with 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) + ;; Slim down on smartparens' opinionated behavior + (defun doom|disable-smartparens-navigate-skip-match () + (setq sp-navigate-skip-match nil + sp-navigate-consider-sgml-tags nil)) + (add-hook 'after-change-major-mode-hook #'doom|disable-smartparens-navigate-skip-match) + ;; autopairing in `eval-expression' and `evil-ex' (defun doom|init-smartparens-in-eval-expression () "Enable `smartparens-mode' in the minibuffer, during `eval-expression' or `evil-ex'." (when (memq this-command '(eval-expression evil-ex)) (smartparens-mode))) (add-hook 'minibuffer-setup-hook #'doom|init-smartparens-in-eval-expression) - (sp-local-pair 'minibuffer-inactive-mode "'" nil :actions nil) - (sp-local-pair '(xml-mode nxml-mode php-mode) "" - :post-handlers '(("| " "SPC"))) + + ;; smartparenss conflicts with 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) (smartparens-global-mode +1)) diff --git a/core/core-keybinds.el b/core/core-keybinds.el index 578c9f4a7..1c4aaef25 100644 --- a/core/core-keybinds.el +++ b/core/core-keybinds.el @@ -46,68 +46,25 @@ If any hook returns non-nil, all hooks after it are ignored.") ;; (def-package! which-key - :config + :defer 1 + :after-call pre-command-hook + :init (setq which-key-sort-order #'which-key-prefix-then-key-order which-key-sort-uppercase-first nil which-key-add-column-padding 1 which-key-max-display-columns nil which-key-min-display-lines 6 which-key-side-window-slot -10) + :config ;; embolden local bindings (set-face-attribute 'which-key-local-map-description-face nil :weight 'bold) (which-key-setup-side-window-bottom) (setq-hook! 'which-key-init-buffer-hook line-spacing 3) - (add-hook 'doom-post-init-hook #'which-key-mode)) + (which-key-mode +1)) -(def-package! hydra - :defer t - :config - (setq lv-use-seperator t) - - (defhydra doom@text-zoom (:hint t :color red) - " - Text zoom: _j_:zoom in, _k_:zoom out, _0_:reset -" - ("j" text-scale-increase "in") - ("k" text-scale-decrease "out") - ("0" (text-scale-set 0) "reset")) - - (defhydra doom@window-nav (:hint nil) - " - Split: _v_ert _s_:horz - Delete: _c_lose _o_nly - Switch Window: _h_:left _j_:down _k_:up _l_:right - Buffers: _p_revious _n_ext _b_:select _f_ind-file - Resize: _H_:splitter left _J_:splitter down _K_:splitter up _L_:splitter right - Move: _a_:up _z_:down _i_menu -" - ("z" scroll-up-line) - ("a" scroll-down-line) - ("i" idomenu) - - ("h" windmove-left) - ("j" windmove-down) - ("k" windmove-up) - ("l" windmove-right) - - ("p" previous-buffer) - ("n" next-buffer) - ("b" switch-to-buffer) - ("f" find-file) - - ("s" split-window-below) - ("v" split-window-right) - - ("c" delete-window) - ("o" delete-other-windows) - - ("H" hydra-move-splitter-left) - ("J" hydra-move-splitter-down) - ("K" hydra-move-splitter-up) - ("L" hydra-move-splitter-right) - - ("q" nil))) +;; `hydra' +(setq lv-use-seperator t) ;; @@ -117,10 +74,11 @@ If any hook returns non-nil, all hooks after it are ignored.") KEYS should be a string in kbd format. DESC should be a string describing what KEY does. MODES should be a list of major mode symbols." - (if modes - (dolist (mode modes) - (which-key-add-major-mode-key-based-replacements mode key desc)) - (which-key-add-key-based-replacements key desc))) + (after! which-key + (if modes + (dolist (mode modes) + (which-key-add-major-mode-key-based-replacements mode key desc)) + (which-key-add-key-based-replacements key desc)))) (defun doom--keyword-to-states (keyword) diff --git a/core/core-lib.el b/core/core-lib.el index 3ea39d001..792aa8932 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -3,7 +3,6 @@ ;; Built in packages we use a lot of (require 'subr-x) (require 'cl-lib) -(require 'map) (eval-and-compile (unless EMACS26+ @@ -11,7 +10,23 @@ ;; if-let and when-let are deprecated in Emacs 26+ in favor of their ;; if-let* variants, so we alias them for 25 users. (defalias 'if-let* #'if-let) - (defalias 'when-let* #'when-let)))) + (defalias 'when-let* #'when-let) + + ;; `alist-get' doesn't have its 5th argument before Emacs 26 + (defun doom*alist-get (key alist &optional default remove testfn) + "Return the value associated with KEY in ALIST. +If KEY is not found in ALIST, return DEFAULT. +Use TESTFN to lookup in the alist if non-nil. Otherwise, use `assq'. + +This is a generalized variable suitable for use with `setf'. +When using it to set a value, optional argument REMOVE non-nil +means to remove KEY from ALIST if the new value is `eql' to DEFAULT." + (ignore remove) ;;Silence byte-compiler. + (let ((x (if (not testfn) + (assq key alist) + (assoc key alist testfn)))) + (if x (cdr x) default))) + (advice-add #'alist-get :override #'doom*alist-get)))) ;; @@ -36,6 +51,7 @@ Returns (file-exists-p \"/an/absolute/path\")))) This is used by `associate!', `file-exists-p!' and `project-file-exists-p!'." + (declare (pure t) (side-effect-free t)) (cond ((stringp spec) `(file-exists-p ,(if (file-name-absolute-p spec) @@ -57,6 +73,7 @@ This is used by `associate!', `file-exists-p!' and `project-file-exists-p!'." (t spec))) (defun doom--resolve-hook-forms (hooks) + (declare (pure t) (side-effect-free t)) (cl-loop with quoted-p = (eq (car-safe hooks) 'quote) for hook in (doom-enlist (doom-unquote hooks)) if (eq (car-safe hook) 'quote) @@ -82,24 +99,26 @@ This is used by `associate!', `file-exists-p!' and `project-file-exists-p!'." (defun doom-unquote (exp) "Return EXP unquoted." + (declare (pure t) (side-effect-free t)) (while (memq (car-safe exp) '(quote function)) (setq exp (cadr exp))) exp) (defun doom-enlist (exp) "Return EXP wrapped in a list, or as-is if already a list." + (declare (pure t) (side-effect-free t)) (if (listp exp) exp (list exp))) (defun doom-keyword-intern (str) "Converts STR (a string) into a keyword (`keywordp')." - (or (stringp str) - (signal 'wrong-type-argument (list 'stringp str))) + (declare (pure t) (side-effect-free t)) + (cl-check-type str string) (intern (concat ":" str))) (defun doom-keyword-name (keyword) "Returns the string name of KEYWORD (`keywordp') minus the leading colon." - (or (keywordp keyword) - (signal 'wrong-type-argument (list 'keywordp keyword))) + (declare (pure t) (side-effect-free t)) + (cl-check-type :test keyword) (substring (symbol-name keyword) 1)) (cl-defun doom-files-in @@ -184,17 +203,17 @@ MATCH is a string regexp. Only entries that match it will be included." ;; Macros ;; -(defmacro FILE! () +(defun FILE! () "Return the emacs lisp file this macro is called from." - `(cond ((bound-and-true-p byte-compile-current-file)) - ((stringp (car-safe current-load-list)) (car current-load-list)) - (load-file-name) - (buffer-file-name))) + (cond ((bound-and-true-p byte-compile-current-file)) + (load-file-name) + (buffer-file-name) + ((stringp (car-safe current-load-list)) (car current-load-list)))) -(defmacro DIR! () +(defun DIR! () "Returns the directory of the emacs lisp file this macro is called from." - `(let ((file (FILE!))) - (and file (file-name-directory file)))) + (let ((file (FILE!))) + (and file (file-name-directory file)))) (defmacro λ! (&rest body) "A shortcut for inline interactive lambdas." @@ -402,7 +421,7 @@ The available conditions are: collect `(add-hook ',hook #',hook-name)) `((add-hook 'after-change-major-mode-hook #',hook-name)))))) (match - `(map-put doom-auto-minor-mode-alist ,match ',mode)) + `(add-to-list 'doom-auto-minor-mode-alist '(,match . ,mode))) ((user-error "Invalid `associate!' rules for mode [%s] (:modes %s :match %s :files %s :when %s)" mode modes match files when))))) diff --git a/core/core-modules.el b/core/core-modules.el index 7a9f2e0ec..14d388334 100644 --- a/core/core-modules.el +++ b/core/core-modules.el @@ -7,7 +7,8 @@ "A hash table of enabled modules. Set by `doom-initialize-modules'.") (defvar doom-modules-dirs - (list (expand-file-name "modules/" doom-private-dir) doom-modules-dir) + (list (expand-file-name "modules/" doom-private-dir) + doom-modules-dir) "A list of module root directories. Order determines priority.") (defconst doom-obsolete-modules @@ -35,8 +36,6 @@ A warning will be put out if these deprecated modules are used.") session of Dooming. Will noop if used more than once, unless FORCE-P is non-nil." (when (or force-p (not doom-init-modules-p)) - ;; Set `doom-init-modules-p' early, so `doom-pre-init-hook' won't infinitely - ;; recurse by accident if any of them need `doom-initialize-modules'. (setq doom-init-modules-p t) (when doom-private-dir (condition-case e @@ -52,12 +51,14 @@ non-nil." (defun doom-module-p (category module) "Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')." + (declare (pure t) (side-effect-free t)) (and (hash-table-p doom-modules) (gethash (cons category module) doom-modules) t)) (defun doom-module-get (category module &optional property) "Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set." + (declare (pure t) (side-effect-free t)) (when-let* ((plist (gethash (cons category module) doom-modules))) (if property (plist-get plist property) @@ -103,7 +104,8 @@ MODULE (symbol). If the category isn't enabled this will always return nil. For finding disabled modules use `doom-module-locate-path'." - (let ((path (doom-module-get category module :path))) + (let ((path (doom-module-get category module :path)) + file-name-handler-alist) (if file (expand-file-name file path) path))) @@ -120,7 +122,8 @@ This doesn't require modules to be enabled. For enabled modules us (setq category (substring (symbol-name category) 1))) (when (and module (symbolp module)) (setq module (symbol-name module))) - (cl-loop for default-directory in doom-modules-dirs + (cl-loop with file-name-handler-alist = nil + for default-directory in doom-modules-dirs for path = (concat category "/" module "/" file) if (file-exists-p path) return (expand-file-name path))) @@ -140,6 +143,7 @@ This doesn't require modules to be enabled. For enabled modules us (defun doom-module-load-path (&optional all-p) "Return a list of absolute file paths to activated modules. If ALL-P is non-nil, return paths of possible modules, activated or otherwise." + (declare (pure t) (side-effect-free t)) (append (if all-p (doom-files-in doom-modules-dirs :type 'dirs @@ -184,7 +188,7 @@ non-nil, return paths of possible modules, activated or otherwise." ;; ;; This will load X on the first invokation of `find-file-hook' (then it will ;; remove itself from the hook/function). -(defvar doom--deferred-packages-alist ()) +(defvar doom--deferred-packages-alist '(t)) (after! use-package-core (add-to-list 'use-package-deferring-keywords :after-call nil #'eq) (setq use-package-keywords @@ -192,10 +196,9 @@ non-nil, return paths of possible modules, activated or otherwise." (defalias 'use-package-normalize/:after-call 'use-package-normalize-symlist) (defun use-package-handler/:after-call (name _keyword hooks rest state) - (let ((fn (intern (format "doom|transient-hook--load-%s" name))) - (hooks (delete-dups hooks))) - (if (plist-get state :demand) - (use-package-process-keywords name rest state) + (if (plist-get state :demand) + (use-package-process-keywords name rest state) + (let ((fn (intern (format "doom|transient-hook--load-%s" name)))) (use-package-concat `((fset ',fn (lambda (&rest _) @@ -208,15 +211,19 @@ non-nil, return paths of possible modules, activated or otherwise." (if (functionp hook) (advice-remove hook #',fn) (remove-hook hook #',fn))) - (map-delete doom--deferred-packages-alist ',name) + (delq (assq ',name doom--deferred-packages-alist) + doom--deferred-packages-alist) (fmakunbound ',fn)))) - (cl-loop for hook in hooks - collect (if (functionp hook) - `(advice-add #',hook :before #',fn) - `(add-hook ',hook #',fn))) - `((map-put doom--deferred-packages-alist - ',name - '(,@hooks ,@(cdr (assq name doom--deferred-packages-alist))))) + (let (forms) + (dolist (hook hooks forms) + (push (if (functionp hook) + `(advice-add #',hook :before #',fn) + `(add-hook ',hook #',fn)) + forms))) + `((unless (assq ',name doom--deferred-packages-alist) + (push '(,name) doom--deferred-packages-alist)) + (nconc (assq ',name doom--deferred-packages-alist) + '(,@hooks))) (use-package-process-keywords name rest state)))))) @@ -276,7 +283,7 @@ to least)." (throw 'doom-modules t)))) (let ((path (doom-module-locate-path category module))) (if (not path) - (message "Couldn't find the %s %s module" category module) + (message "Warning: couldn't find the %s %s module" category module) (let ((key (cons category module))) (doom-module-set category module :flags flags :path path) (push `(let ((doom--current-module ',key)) @@ -330,23 +337,21 @@ to have them return non-nil (or exploit that to overwrite Doom's config)." (defmacro require! (category module &rest plist) "Loads the module specified by CATEGORY (a keyword) and MODULE (a symbol)." - (let ((doom-modules (copy-hash-table doom-modules))) - (apply #'doom-module-set category module - (mapcar #'eval plist)) - (let ((module-path (doom-module-locate-path category module))) - (if (directory-name-p module-path) - `(condition-case-unless-debug ex - (let ((doom--current-module ',(cons category module))) - (load! "init" ,module-path :noerror) - (let ((doom--stage 'config)) - (load! "config" ,module-path :noerror))) - ('error - (lwarn 'doom-modules :error - "%s in '%s %s' -> %s" - (car ex) ,category ',module - (error-message-string ex)))) - (warn 'doom-modules :warning "Couldn't find module '%s %s'" - category module))))) + `(let ((module-path (doom-module-locate-path ,category ',module))) + (doom-module-set ,category ',module ,@plist) + (if (directory-name-p module-path) + (condition-case-unless-debug ex + (let ((doom--current-module ',(cons category module))) + (load! "init" module-path :noerror) + (let ((doom--stage 'config)) + (load! "config" module-path :noerror))) + ('error + (lwarn 'doom-modules :error + "%s in '%s %s' -> %s" + (car ex) ,category ',module + (error-message-string ex)))) + (warn 'doom-modules :warning "Couldn't find module '%s %s'" + ,category ',module)))) (defmacro featurep! (module &optional submodule flag) "Returns t if MODULE SUBMODULE is enabled. If FLAG is provided, returns t if @@ -370,8 +375,8 @@ omitted. eg. (featurep! +flag1)" module (car module-pair) submodule (cdr module-pair)))) (if flag - (and (memq flag (doom-module-get module submodule :flags)) t) - (doom-module-p module submodule))) + `(and (memq ',flag (doom-module-get ,module ',submodule :flags)) t) + `(doom-module-p ,module ',submodule))) ;; diff --git a/core/core-packages.el b/core/core-packages.el index 459f7e308..762277b02 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -171,7 +171,7 @@ them." ;; Module package macros ;; -(defmacro package! (name &rest plist) +(cl-defmacro package! (name &rest plist &key recipe pin disable _ignore _freeze) "Declares a package and how to install it (if applicable). This macro is declarative and does not load nor install packages. It is used to @@ -200,31 +200,27 @@ Returns t if package is successfully registered, and nil if it was disabled elsewhere." (declare (indent defun)) (doom--assert-stage-p 'packages #'package!) - (let* ((old-plist (cdr (assq name doom-packages))) - (pkg-recipe (or (plist-get plist :recipe) - (and old-plist (plist-get old-plist :recipe)))) - (pkg-pin (or (plist-get plist :pin) - (and old-plist (plist-get old-plist :pin)))) - (pkg-disable (or (plist-get plist :disable) - (and old-plist (plist-get old-plist :disable))))) - (when pkg-disable - (add-to-list 'doom-disabled-packages name nil #'eq)) - (when pkg-recipe - (when (= 0 (% (length pkg-recipe) 2)) - (setq plist (plist-put plist :recipe (cons name pkg-recipe)))) - (when pkg-pin - (setq plist (plist-put plist :pin nil)))) - (dolist (prop '(:ignore :freeze)) - (when-let* ((val (plist-get plist prop))) - (setq plist (plist-put plist prop (eval val))))) - (when (file-in-directory-p (or (bound-and-true-p byte-compile-current-file) - load-file-name) - doom-private-dir) + (let ((plist (append plist (cdr (assq name doom-packages))))) + (when recipe + (when (cl-evenp (length recipe)) + (setq plist (plist-put plist :recipe (cons name recipe)))) + (setq pin nil + plist (plist-put plist :pin nil))) + (when (file-in-directory-p (FILE!) doom-private-dir) (setq plist (plist-put plist :private t))) - `(progn - ,(if pkg-pin `(map-put package-pinned-packages ',name ,pkg-pin)) - (map-put doom-packages ',name ',plist) - (not (memq ',name doom-disabled-packages))))) + (let (newplist) + (while plist + (unless (null (cadr plist)) + (push (cadr plist) newplist) + (push (car plist) newplist)) + (pop plist) + (pop plist)) + (setq plist newplist)) + (macroexp-progn + (append (if disable `((add-to-list 'doom-disabled-packages ',name nil #'eq))) + (if pin `((setf (alist-get ',name package-pinned-packages) ,pin))) + `((setf (alist-get ',name doom-packages) ',plist) + (not (memq ',name doom-disabled-packages))))))) (defmacro packages! (&rest packages) "A convenience macro like `package!', but allows you to declare multiple diff --git a/core/core-ui.el b/core/core-ui.el index 2d8d8821d..63c280a9d 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -67,7 +67,9 @@ Also see `doom-before-switch-buffer-hook'.") enable-recursive-minibuffers nil frame-inhibit-implied-resize t ;; remove continuation arrow on right fringe - fringe-indicator-alist (map-delete fringe-indicator-alist 'continuation) + fringe-indicator-alist + (delq (assq 'continuation fringe-indicator-alist) + fringe-indicator-alist) highlight-nonselected-windows nil image-animate-loop t indicate-buffer-boundaries nil @@ -126,13 +128,13 @@ Also see `doom-before-switch-buffer-hook'.") (format "%s modeline segment" name)))) (cond ((and (symbolp (car body)) (not (cdr body))) - (map-put doom--modeline-var-alist name (car body)) - `(map-put doom--modeline-var-alist ',name ',(car body))) + (add-to-list 'doom--modeline-var-alist (cons name (car body))) + `(add-to-list 'doom--modeline-var-alist (cons ',name ',(car body)))) (t - (map-put doom--modeline-fn-alist name sym) + (add-to-list 'doom--modeline-fn-alist (cons name sym)) `(progn (fset ',sym (lambda () ,docstring ,@body)) - (map-put doom--modeline-fn-alist ',name ',sym) + (add-to-list 'doom--modeline-fn-alist (cons ',name ',sym)) ,(unless (bound-and-true-p byte-compile-current-file) `(let (byte-compile-warnings) (byte-compile #',sym)))))))) @@ -308,7 +310,7 @@ DEFAULT is non-nil, set the default mode-line for all buffers." ;; highlight matching delimiters (def-package! paren - :after-call post-command-hook + :after-call (after-find-file doom-before-switch-buffer-hook) :config (setq show-paren-delay 0.1 show-paren-highlight-openparen t @@ -549,7 +551,7 @@ frame's window-system, the theme will be reloaded.") (custom-set-faces (when (fontp doom-font) (let ((xlfd (font-xlfd-name doom-font))) - (map-put default-frame-alist 'font xlfd) + (add-to-list 'default-frame-alist (cons 'font xlfd)) `(fixed-pitch ((t (:font ,xlfd)))))) (when (fontp doom-variable-pitch-font) `(variable-pitch ((t (:font ,(font-xlfd-name doom-variable-pitch-font)))))) @@ -712,7 +714,7 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original (defun doom|init-ui () "Initialize Doom's user interface by applying all its advice and hooks." ;; Make `next-buffer', `other-buffer', etc. ignore unreal buffers. - (map-put default-frame-alist 'buffer-predicate #'doom-buffer-frame-predicate) + (add-to-list 'default-frame-alist (cons 'buffer-predicate #'doom-buffer-frame-predicate)) ;; Switch to `doom-fallback-buffer' if on last real buffer (advice-add #'kill-this-buffer :around #'doom*switch-to-fallback-buffer-maybe) ;; Don't kill the fallback buffer diff --git a/init.test.el b/init.test.el index 83dfa6ac2..1d150befc 100644 --- a/init.test.el +++ b/init.test.el @@ -7,6 +7,7 @@ company :ui doom-dashboard + popup :tools password-store :lang diff --git a/modules/app/email/+gmail.el b/modules/app/email/+gmail.el index 9d647b206..3f44d8cd7 100644 --- a/modules/app/email/+gmail.el +++ b/modules/app/email/+gmail.el @@ -12,17 +12,18 @@ (defun +email--mark-seen (docid msg target) (mu4e~proc-move docid (mu4e~mark-check-target target) "+S-u-N")) - (map-delete mu4e-marks 'delete) - (map-put mu4e-marks 'trash - (list :char '("d" . "▼") - :prompt "dtrash" - :dyn-target (lambda (_target msg) (mu4e-get-trash-folder msg)) - :action #'+email--mark-seen)) - ;; Refile will be my "archive" function. - (map-put mu4e-marks 'refile - (list :char '("r" . "▶") :prompt "refile" - :show-target (lambda (_target) "archive") - :action #'+email--mark-seen)) + (delq (assq 'delete mu4e-marks) mu4e-marks) + (setf (alist-get 'trash mu4e-marks) + (list :char '("d" . "▼") + :prompt "dtrash" + :dyn-target (lambda (_target msg) (mu4e-get-trash-folder msg)) + :action #'+email--mark-seen) + ;; Refile will be my "archive" function. + (alist-get 'refile mu4e-marks) + (list :char '("d" . "▼") + :prompt "dtrash" + :dyn-target (lambda (_target msg) (mu4e-get-trash-folder msg)) + :action #'+email--mark-seen)) ;; This hook correctly modifies gmail flags on emails when they are marked. ;; Without it, refiling (archiving), trashing, and flagging (starring) email diff --git a/modules/app/email/autoload/email.el b/modules/app/email/autoload/email.el index 232adfdad..7b2f017bc 100644 --- a/modules/app/email/autoload/email.el +++ b/modules/app/email/autoload/email.el @@ -40,6 +40,7 @@ default/fallback account." (setq-default mu4e-context-current context)) context))) +;; FIXME obsolete :email ;;;###autoload (def-setting! :email (label letvars &optional default-p) :obsolete set-email-account! diff --git a/modules/app/irc/README.org b/modules/app/irc/README.org index caf043a6d..865b11c31 100644 --- a/modules/app/irc/README.org +++ b/modules/app/irc/README.org @@ -3,10 +3,12 @@ This module turns adds an IRC client to Emacs ([[https://github.com/jorgenschaefer/circe][~circe~)]] with native notifications ([[https://github.com/eqyiel/circe-notifications][circe-notifications]]). * Table of Contents :TOC: -- [[#dependencies][Dependencies]] -- [[#configure][Configure]] - - [[#pass-the-unix-password-manager][Pass: the unix password manager]] - - [[#emacs-auth-source-api][Emacs' auth-source API]] +- [[Dependencies][Dependencies]] +- [[Configure][Configure]] + - [[Pass: the unix password manager][Pass: the unix password manager]] + - [[Emacs' auth-source API][Emacs' auth-source API]] +- [[Appendix][Appendix]] + - [[Commands][Commands]] * Dependencies This module has no dependencies, besides =gnutls-cli= or =openssl= for secure connections. @@ -52,6 +54,15 @@ But wait, there's more! This stores your password in a public variable which cou And you're good to go! +Note that =+pass-get-user= tries to find your username by looking for the fields +listed in =+pass-user-fields= (by default =login=, =user==, =username== and =email=)=). +An example configuration looks like + +#+BEGIN_SRC txt :tangle no +mysecretpassword +username: myusername +#+END_SRC + ** Emacs' auth-source API ~auth-source~ is built into Emacs. As suggested [[https://github.com/jorgenschaefer/circe/wiki/Configuration#safer-password-management][in the circe wiki]], you can store (and retrieve) encrypted passwords with it. @@ -78,3 +89,24 @@ And you're good to go! :channels ("#emacs"))) #+END_SRC +* Appendix +** Commands + +To connect to IRC you can invoke the ~=irc~ function using =M-x= or your own +custom keybinding. + +| command | description | +|---------+-------------------------------------------| +| ~=irc~ | Connect to IRC and all configured servers | + +When in a circe buffer these keybindings will be available. + +| command | key | description | +|------------------------+-----------+----------------------------------------------| +| ~tracking-next-buffer~ | =SPC m a= | Switch to the next active buffer | +| ~circe-command-JOIN~ | =SPC m j= | Join a channel | +| ~+irc/send-message~ | =SPC m m= | Send a private message | +| ~circe-command-NAMES~ | =SPC m n= | List the names of the current channel | +| ~circe-command-PART~ | =SPC m p= | Part the current channel | +| ~+irc/quit~ | =SPC m Q= | Kill the current circe session and workgroup | +| ~circe-reconnect~ | =SPC m R= | Reconnect the current server | diff --git a/modules/app/irc/autoload/irc.el b/modules/app/irc/autoload/irc.el index 1e1f01210..a630fb61d 100644 --- a/modules/app/irc/autoload/irc.el +++ b/modules/app/irc/autoload/irc.el @@ -36,6 +36,12 @@ workspace for it." (and (+irc-setup-wconf inhibit-workspace) (call-interactively #'circe))) +;;;###autoload +(defun +irc/send-message (who what) + "Send WHO a message containing WHAT." + (interactive "sWho: \nsWhat: ") + (circe-command-MSG who what)) + ;;;###autoload (defun +irc/quit () "Kill current circe session and workgroup." diff --git a/modules/app/irc/autoload/settings.el b/modules/app/irc/autoload/settings.el new file mode 100644 index 000000000..424a731cd --- /dev/null +++ b/modules/app/irc/autoload/settings.el @@ -0,0 +1,16 @@ +;;; app/irc/autoload/settings.el -*- lexical-binding: t; -*- + +;;;###autodef +(defun set-irc-server! (server letvars) + "Registers an irc SERVER for circe. + +See `circe-network-options' for details." + (after! circe + (push (cons server letvars) circe-network-options))) + +;; FIXME obsolete :irc +;;;###autoload +(def-setting! :irc (server letvars) + :obsolete set-irc-server! + `(after! circe + (push (cons ,server ,letvars) circe-network-options))) diff --git a/modules/app/irc/config.el b/modules/app/irc/config.el index a32ecf355..3adb8557f 100644 --- a/modules/app/irc/config.el +++ b/modules/app/irc/config.el @@ -36,11 +36,6 @@ See `circe-notifications-watch-strings'.") Useful for ZNC users who want to avoid the deluge of notifications during buffer playback.") -(def-setting! :irc (server letvars) - "Registers an irc server for circe." - `(after! circe - (push (cons ,server ,letvars) circe-network-options))) - (defvar +irc--defer-timer nil) (defsubst +irc--pad (left right) @@ -125,7 +120,23 @@ playback.") (after! solaire-mode ;; distinguish chat/channel buffers from server buffers. - (add-hook 'circe-chat-mode-hook #'solaire-mode))) + (add-hook 'circe-chat-mode-hook #'solaire-mode)) + + (map! + (:localleader + (:map circe-mode-map + :desc "Next active buffer" :n "a" #'tracking-next-buffer + :desc "Join channel" :n "j" #'circe-command-JOIN + :desc "Send private message" :n "m" #'+irc/send-message + :desc "Part current channel" :n "p" #'circe-command-PART + :desc "Quit irc" :n "Q" #'+irc/quit + :desc "Reconnect" :n "R" #'circe-reconnect + + (:when (featurep! :completion ivy) + :desc "Jump to channel" :n "c" #'+irc/ivy-jump-to-channel)) + + (:map circe-channel-mode-map + :desc "Show names" :n "n" #'circe-command-NAMES)))) (def-package! circe-color-nicks diff --git a/modules/completion/helm/config.el b/modules/completion/helm/config.el index 19dd7b65f..27e17e721 100644 --- a/modules/completion/helm/config.el +++ b/modules/completion/helm/config.el @@ -28,7 +28,7 @@ :config (helm-mode +1) ;; helm is too heavy for find-file-at-point - (map-put helm-completing-read-handlers-alist 'find-file-at-point nil)) + (add-to-list 'helm-completing-read-handlers-alist (cons #'find-file-at-point nil))) (def-package! helm @@ -179,11 +179,11 @@ (map! (:after helm :map helm-map - :ni "M-[" #'helm-previous-source - :ni "M-]" #'helm-next-source - :ni "M-l" #'helm-execute-persistent-action - :ni "M-j" #'helm-next-line - :ni "M-k" #'helm-previous-line + :ni "C-S-p" #'helm-previous-source + :ni "C-S-n" #'helm-next-source + :ni "C-l" #'helm-execute-persistent-action + :ni "C-j" #'helm-next-line + :ni "C-k" #'helm-previous-line :ni "C-f" #'helm-next-page :ni "C-b" #'helm-previous-page :n [tab] #'helm-select-action ; TODO: Ivy has "ga". @@ -206,7 +206,7 @@ :map (helm-find-files-map helm-read-file-map) :n "go" #'helm-ff-run-switch-other-window :n "/" #'helm-ff-run-find-sh-command - :ni "S-" #'helm-ff-run-switch-other-window + :ni "M-" #'helm-ff-run-switch-other-window :ni "M-h" #'helm-find-files-up-one-level :n "=" #'helm-ff-run-ediff-file :n "%" #'helm-ff-run-query-replace-regexp @@ -219,16 +219,16 @@ :map helm-buffer-map :n "go" #'helm-buffer-switch-other-window :n "gO" #'display-buffer - :ni "S-" #'helm-buffer-switch-other-window - :ni "M-" #'display-buffer + :ni "M-" #'helm-buffer-switch-other-window + :ni "" #'display-buffer :n "=" #'helm-buffer-run-ediff :n "%" #'helm-buffer-run-query-replace-regexp :n "D" #'helm-buffer-run-kill-persistent) ; Ivy has "D". (:after helm-regexp :map helm-moccur-map :n "go" #'helm-moccur-run-goto-line-ow - :ni "S-" #'helm-moccur-run-goto-line-ow) + :ni "M-" #'helm-moccur-run-goto-line-ow) (:after helm-grep :map helm-grep-map :n "go" #'helm-grep-run-other-window-action - :ni "S-" #'helm-grep-run-other-window-action))) + :ni "M-" #'helm-grep-run-other-window-action))) diff --git a/modules/completion/ivy/autoload/hydras.el b/modules/completion/ivy/autoload/hydras.el new file mode 100644 index 000000000..36c1ff9c9 --- /dev/null +++ b/modules/completion/ivy/autoload/hydras.el @@ -0,0 +1,43 @@ +;;; completion/ivy/autoload/hydras.el -*- lexical-binding: t; -*- + +;;;###autoload (autoload '+ivy-coo-hydra/body "completion/ivy/autoload/hydras" nil nil) +(defhydra +ivy-coo-hydra (:hint nil :color pink) + " + Move ^^^^^^^^^^ | Call ^^^^ | Cancel^^ | Options^^ | Action _w_/_s_/_a_: %s(ivy-action-name) +----------^^^^^^^^^^-+--------------^^^^-+-------^^-+--------^^-+--------------------------------- + _g_ ^ ^ _k_ ^ ^ _u_ | _f_orward _o_ccur | _i_nsert | _c_alling: %-7s(if ivy-calling \"on\" \"off\") _C_ase-fold: %-10`ivy-case-fold-search + ^↨^ _h_ ^+^ _l_ ^↕^ | _RET_ done ^^ | _q_uit | _m_atcher: %-7s(ivy--matcher-desc) _t_runcate: %-11`truncate-lines + _G_ ^ ^ _j_ ^ ^ _d_ | _TAB_ alt-done ^^ | ^ ^ | _<_/_>_: shrink/grow +" + ;; arrows + ("j" ivy-next-line) + ("k" ivy-previous-line) + ("l" ivy-alt-done) + ("h" ivy-backward-delete-char) + ("g" ivy-beginning-of-buffer) + ("G" ivy-end-of-buffer) + ("d" ivy-scroll-up-command) + ("u" ivy-scroll-down-command) + ("e" ivy-scroll-down-command) + ;; actions + ("q" keyboard-escape-quit :exit t) + ("C-g" keyboard-escape-quit :exit t) + ("" keyboard-escape-quit :exit t) + ("C-o" nil) + ("i" nil) + ("TAB" ivy-alt-done :exit nil) + ("C-j" ivy-alt-done :exit nil) + ("RET" ivy-done :exit t) + ("C-m" ivy-done :exit t) + ("C-SPC" ivy-call-and-recenter :exit nil) + ("f" ivy-call) + ("c" ivy-toggle-calling) + ("m" ivy-toggle-fuzzy) + (">" ivy-minibuffer-grow) + ("<" ivy-minibuffer-shrink) + ("w" ivy-prev-action) + ("s" ivy-next-action) + ("a" ivy-read-action) + ("t" (setq truncate-lines (not truncate-lines))) + ("C" ivy-toggle-case-fold) + ("o" ivy-occur :exit t)) diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el index 36b7a550a..e6f51fba9 100644 --- a/modules/completion/ivy/config.el +++ b/modules/completion/ivy/config.el @@ -126,49 +126,8 @@ immediately runs it on the current candidate (ending the ivy session)." :init (after! ivy (define-key! ivy-minibuffer-map - "\C-o" #'+ivy@coo/body - (kbd "M-o") #'ivy-dispatching-done-hydra)) - :config - (defhydra +ivy@coo (:hint nil :color pink) - " - Move ^^^^^^^^^^ | Call ^^^^ | Cancel^^ | Options^^ | Action _w_/_s_/_a_: %s(ivy-action-name) -----------^^^^^^^^^^-+--------------^^^^-+-------^^-+--------^^-+--------------------------------- - _g_ ^ ^ _k_ ^ ^ _u_ | _f_orward _o_ccur | _i_nsert | _c_alling: %-7s(if ivy-calling \"on\" \"off\") _C_ase-fold: %-10`ivy-case-fold-search - ^↨^ _h_ ^+^ _l_ ^↕^ | _RET_ done ^^ | _q_uit | _m_atcher: %-7s(ivy--matcher-desc) _t_runcate: %-11`truncate-lines - _G_ ^ ^ _j_ ^ ^ _d_ | _TAB_ alt-done ^^ | ^ ^ | _<_/_>_: shrink/grow -" - ;; arrows - ("j" ivy-next-line) - ("k" ivy-previous-line) - ("l" ivy-alt-done) - ("h" ivy-backward-delete-char) - ("g" ivy-beginning-of-buffer) - ("G" ivy-end-of-buffer) - ("d" ivy-scroll-up-command) - ("u" ivy-scroll-down-command) - ("e" ivy-scroll-down-command) - ;; actions - ("q" keyboard-escape-quit :exit t) - ("C-g" keyboard-escape-quit :exit t) - ("" keyboard-escape-quit :exit t) - ("C-o" nil) - ("i" nil) - ("TAB" ivy-alt-done :exit nil) - ("C-j" ivy-alt-done :exit nil) - ("RET" ivy-done :exit t) - ("C-m" ivy-done :exit t) - ("C-SPC" ivy-call-and-recenter :exit nil) - ("f" ivy-call) - ("c" ivy-toggle-calling) - ("m" ivy-toggle-fuzzy) - (">" ivy-minibuffer-grow) - ("<" ivy-minibuffer-shrink) - ("w" ivy-prev-action) - ("s" ivy-next-action) - ("a" ivy-read-action) - ("t" (setq truncate-lines (not truncate-lines))) - ("C" ivy-toggle-case-fold) - ("o" ivy-occur :exit t))) + "\C-o" #'+ivy-coo-hydra/body + (kbd "M-o") #'ivy-dispatching-done-hydra))) (def-package! wgrep @@ -186,26 +145,28 @@ immediately runs it on the current candidate (ending the ivy session)." (advice-add #'ivy-posframe-setup :override #'ignore) :config (setq ivy-fixed-height-minibuffer nil - ivy-posframe-parameters `((min-width . 90) - (min-height . ,ivy-height) - (internal-border-width . 10))) + ivy-posframe-parameters + `((min-width . 90) + (min-height . ,ivy-height) + (internal-border-width . 10))) - ;; ... let's do it manually - (dolist (fn (list 'ivy-posframe-display-at-frame-bottom-left - 'ivy-posframe-display-at-frame-center - 'ivy-posframe-display-at-point - 'ivy-posframe-display-at-frame-bottom-window-center - 'ivy-posframe-display - 'ivy-posframe-display-at-window-bottom-left - 'ivy-posframe-display-at-window-center - '+ivy-display-at-frame-center-near-bottom)) - (map-put ivy-display-functions-props fn '(:cleanup ivy-posframe-cleanup))) - - (map-put ivy-display-functions-alist 't '+ivy-display-at-frame-center-near-bottom) + ;; ... let's do it manually instead + (unless (assq 'ivy-posframe-display-at-frame-bottom-left ivy-display-functions-props) + (dolist (fn (list 'ivy-posframe-display-at-frame-bottom-left + 'ivy-posframe-display-at-frame-center + 'ivy-posframe-display-at-point + 'ivy-posframe-display-at-frame-bottom-window-center + 'ivy-posframe-display + 'ivy-posframe-display-at-window-bottom-left + 'ivy-posframe-display-at-window-center + '+ivy-display-at-frame-center-near-bottom)) + (push (cons fn '(:cleanup ivy-posframe-cleanup)) ivy-display-functions-props))) + ;; default to posframe display function + (setf (alist-get t ivy-display-functions-alist) #'+ivy-display-at-frame-center-near-bottom) ;; posframe doesn't work well with async sources (dolist (fn '(swiper counsel-rg counsel-ag counsel-pt counsel-grep counsel-git-grep)) - (map-put ivy-display-functions-alist fn nil))) + (setf (alist-get fn ivy-display-functions-alist) nil))) (def-package! flx @@ -216,7 +177,8 @@ immediately runs it on the current candidate (ending the ivy session)." '((counsel-ag . ivy--regex-plus) (counsel-rg . ivy--regex-plus) (counsel-pt . ivy--regex-plus) - (counsel-grep-or-swiper . ivy--regex-plus) + (counsel-grep . ivy--regex-plus) + (swiper . ivy--regex-plus) (t . ivy--regex-fuzzy)) ivy-initial-inputs-alist nil)) diff --git a/modules/config/default/+bindings.el b/modules/config/default/+bindings.el index 0f97bca81..901696b74 100644 --- a/modules/config/default/+bindings.el +++ b/modules/config/default/+bindings.el @@ -162,7 +162,7 @@ "M-RET" (+ivy-do-action! #'+ivy-git-grep-other-window-action)))) ;; easymotion - :m "gs" #'+default/easymotion ; lazy-load `evil-easymotion' + :m "gs" #'+evil/easymotion ; lazy-load `evil-easymotion' (:after evil-easymotion :map evilem-map "a" (evilem-create #'evil-forward-arg) diff --git a/modules/config/default/autoload/evil.el b/modules/config/default/autoload/evil.el index 6b119127e..a813659a1 100644 --- a/modules/config/default/autoload/evil.el +++ b/modules/config/default/autoload/evil.el @@ -35,18 +35,3 @@ buffers." (interactive "") (doom/kill-matching-buffers pattern bang)) - -;;;###autoload -(defun +default/easymotion () - "Invoke and lazy-load `evil-easymotion' without compromising which-key -integration." - (interactive) - (let ((prefix (this-command-keys))) - (evil-define-key* 'motion 'global prefix nil) - (evilem-default-keybindings prefix) - (which-key-reload-key-sequence - (vconcat (where-is-internal evil-this-operator - evil-normal-state-map - t) - prefix)))) - diff --git a/modules/config/default/config.el b/modules/config/default/config.el index 272ad49d7..d6822a1fc 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -44,10 +44,6 @@ (sp-local-pair 'ruby-mode "{" "}" :pre-handlers '(:rem sp-ruby-prehandler) :post-handlers '(:rem sp-ruby-posthandler)) - ;; sp's default rules for these modes are obnoxious, so disable them - (provide 'smartparens-elixir) - (provide 'smartparens-latex) - (provide 'smartparens-lua) ;; Expand {|} => { | } ;; Expand {|} => { @@ -63,6 +59,18 @@ (sp-local-pair '(emacs-lisp-mode org-mode markdown-mode gfm-mode) "[" nil :post-handlers '(:rem ("| " "SPC"))) + ;; Reasonable default pairs for comments + (sp-local-pair (append sp--html-modes '(markdown-mode gfm-mode)) + "" :actions '(insert) :post-handlers '(("| " "SPC"))) + + (sp-local-pair + '(js2-mode typescript-mode rjsx-mode rust-mode + c-mode c++-mode objc-mode java-mode php-mode + css-mode scss-mode less-css-mode stylus-mode) + "/*" "*/" + :actions '(insert) + :post-handlers '(("| " "SPC") ("|\n*/[i][d-2]" "RET") ("\n* ||\n*/[i][d-2]" "*"))) + ;; Highjacks backspace to: ;; a) balance spaces inside brackets/parentheses ( | ) -> (|) ;; b) delete space-indented `tab-width' steps at a time diff --git a/modules/emacs/eshell/autoload/settings.el b/modules/emacs/eshell/autoload/settings.el index 1bec69ce6..39a838517 100644 --- a/modules/emacs/eshell/autoload/settings.el +++ b/modules/emacs/eshell/autoload/settings.el @@ -7,7 +7,8 @@ (signal 'wrong-number-of-arguments (list 'even (length aliases)))) (after! eshell (while aliases - (map-put +eshell-aliases (pop aliases) (list (pop aliases)))) + (setf (alist-get (pop aliases) +eshell-aliases nil nil #'equal) + (list (pop aliases)))) (when (boundp 'eshell-command-aliases-list) (if +eshell--default-aliases (setq eshell-command-aliases-list diff --git a/modules/emacs/vc/autoload.el b/modules/emacs/vc/autoload.el index 65e007f1d..f9e50568f 100644 --- a/modules/emacs/vc/autoload.el +++ b/modules/emacs/vc/autoload.el @@ -50,3 +50,42 @@ info in the `header-line-format' is a good indication." (propertize author 'face 'git-timemachine-minibuffer-author-face) (propertize sha-or-subject 'face 'git-timemachine-minibuffer-detail-face) date-full date-relative)))) + +;;;###autoload (autoload '+hydra-smerge/body "emacs/vc/autoload" nil nil) +(defhydra +hydra-smerge (:hint nil + :pre (if (not smerge-mode) (smerge-mode 1)) + ;; Disable `smerge-mode' when quitting hydra if + ;; no merge conflicts remain. + :post (smerge-auto-leave)) + " + [smerge] + Movement Keep Diff Other + ╭─────────────────────────────────────────────────────────╯ + ^_g_^ [_b_] base [_<_] upper/base [_C_] Combine + ^_C-k_^ [_u_] upper [_=_] upper/lower [_r_] resolve + ^_k_ ↑^ [_l_] lower [_>_] base/lower [_R_] remove + ^_j_ ↓^ [_a_] all [_H_] hightlight + ^_C-j_^ [_RET_] current [_E_] ediff ╭────────── + ^_G_^ │ [_q_] quit +" + ("g" (progn (goto-char (point-min)) (smerge-next))) + ("G" (progn (goto-char (point-max)) (smerge-prev))) + ("C-j" smerge-next) + ("C-k" smerge-prev) + ("j" next-line) + ("k" previous-line) + ("b" smerge-keep-base) + ("u" smerge-keep-upper) + ("l" smerge-keep-lower) + ("a" smerge-keep-all) + ("RET" smerge-keep-current) + ("\C-m" smerge-keep-current) + ("<" smerge-diff-base-upper) + ("=" smerge-diff-upper-lower) + (">" smerge-diff-base-lower) + ("H" smerge-refine) + ("E" smerge-ediff) + ("C" smerge-combine-with-next) + ("r" smerge-resolve) + ("R" smerge-kill-current) + ("q" nil :color blue)) diff --git a/modules/emacs/vc/config.el b/modules/emacs/vc/config.el index 7558f4be4..77325ce17 100644 --- a/modules/emacs/vc/config.el +++ b/modules/emacs/vc/config.el @@ -22,22 +22,24 @@ ;; `git-commit-mode' -;; see https://chris.beams.io/posts/git-commit/ -(setq git-commit-fill-column 72 - git-commit-summary-max-length 50 - git-commit-style-convention-checks '(overlong-summary-line non-empty-second-line)) +(defun +vc|enforce-git-commit-conventions () + "See https://chris.beams.io/posts/git-commit/" + (setq fill-column 72 + git-commit-summary-max-length 50 + git-commit-style-convention-checks '(overlong-summary-line non-empty-second-line))) +(add-hook 'git-commit-mode-hook #'+vc|enforce-git-commit-conventions) (when (featurep! :feature evil) (add-hook 'git-commit-mode-hook #'evil-insert-state)) ;; -;; `vc' +;; `vc' (built-in) ;; ;; `vc-hooks' (setq vc-make-backup-files nil) -;; `vc-annotate' +;; `vc-annotate' (built-in) (after! vc-annotate (set-popup-rules! '(("^\\vc-d" :select nil) ; *vc-diff* @@ -46,18 +48,8 @@ '(vc-annotate-mode vc-git-log-view-mode) 'normal)) -;; `smerge-mode' -(defun +vcs|enable-smerge-mode-maybe () - "Auto-enable `smerge-mode' when merge conflict is detected." - (save-excursion - (goto-char (point-min)) - (when (re-search-forward "^<<<<<<< " nil :noerror) - (smerge-mode 1) - (when (and (featurep 'hydra) +vc-auto-hydra-smerge) - (+hydra-smerge/body))))) -(add-hook 'find-file-hook #'+vcs|enable-smerge-mode-maybe) - -(after! smerge-mode ; built-in +;; `smerge-mode' (built-in) +(after! smerge-mode (unless EMACS26+ (with-no-warnings (defalias #'smerge-keep-upper #'smerge-keep-mine) @@ -66,40 +58,5 @@ (defalias #'smerge-diff-upper-lower #'smerge-diff-mine-other) (defalias #'smerge-diff-base-lower #'smerge-diff-base-other))) - (defhydra +hydra-smerge (:hint nil - :pre (smerge-mode 1) - ;; Disable `smerge-mode' when quitting hydra if - ;; no merge conflicts remain. - :post (smerge-auto-leave)) - " - ╭────────┐ - Movement Keep Diff Other │ smerge │ - ╭─────────────────────────────────────────────────┴────────╯ - ^_g_^ [_b_] base [_<_] upper/base [_C_] Combine - ^_C-k_^ [_u_] upper [_=_] upper/lower [_r_] resolve - ^_k_ ↑^ [_l_] lower [_>_] base/lower [_R_] remove - ^_j_ ↓^ [_a_] all [_H_] hightlight - ^_C-j_^ [_RET_] current [_E_] ediff ╭────────── - ^_G_^ │ [_q_] quit" - ("g" (progn (goto-char (point-min)) (smerge-next))) - ("G" (progn (goto-char (point-max)) (smerge-prev))) - ("C-j" smerge-next) - ("C-k" smerge-prev) - ("j" next-line) - ("k" previous-line) - ("b" smerge-keep-base) - ("u" smerge-keep-upper) - ("l" smerge-keep-lower) - ("a" smerge-keep-all) - ("RET" smerge-keep-current) - ("\C-m" smerge-keep-current) - ("<" smerge-diff-base-upper) - ("=" smerge-diff-upper-lower) - (">" smerge-diff-base-lower) - ("H" smerge-refine) - ("E" smerge-ediff) - ("C" smerge-combine-with-next) - ("r" smerge-resolve) - ("R" smerge-kill-current) - ("q" nil :color blue))) + (add-hook 'smerge-mode-hook #'+hydra-smerge/body)) diff --git a/modules/feature/eval/autoload/settings.el b/modules/feature/eval/autoload/settings.el index 2849e5fab..443678f7d 100644 --- a/modules/feature/eval/autoload/settings.el +++ b/modules/feature/eval/autoload/settings.el @@ -18,7 +18,7 @@ function that creates and returns the REPL buffer." ;;;###autoload (def-setting! :repl (mode command) :obsolete set-repl-handler! - `(push (cons ,mode ,command) +eval-repls)) + `(set-repl-handler! ,mode ,command)) ;; diff --git a/modules/feature/evil/autoload/evil.el b/modules/feature/evil/autoload/evil.el index c6ab35535..27c89e0f2 100644 --- a/modules/feature/evil/autoload/evil.el +++ b/modules/feature/evil/autoload/evil.el @@ -370,3 +370,18 @@ more information on modifiers." "Call `doom/escape' if `evil-force-normal-state' is called interactively." (when (called-interactively-p 'any) (call-interactively #'doom/escape))) + +;;;###autoload +(defun +evil/easymotion () + "Invoke and lazy-load `evil-easymotion' without compromising which-key +integration." + (interactive) + (let ((prefix (this-command-keys))) + (evil-define-key* 'motion 'global prefix nil) + (evilem-default-keybindings prefix) + (which-key-reload-key-sequence + (vconcat (when evil-this-operator + (where-is-internal evil-this-operator + evil-normal-state-map + t)) + prefix)))) diff --git a/modules/feature/evil/config.el b/modules/feature/evil/config.el index 9b187cd2b..1c1bfd00b 100644 --- a/modules/feature/evil/config.el +++ b/modules/feature/evil/config.el @@ -3,39 +3,6 @@ ;; I'm a vimmer at heart. Its modal philosophy suits me better, and this module ;; strives to make Emacs a much better vim than vim was. -(defvar +evil-collection-disabled-list - '(kotlin-mode ; doesn't do anything useful - simple - ;; we'll do these ourselves - anaconda-mode - company - dired - helm - ivy - minibuffer - ruby-mode - slime) - "A list of `evil-collection' modules to disable. See the definition of this -variable for an explanation of the defaults (in comments). See -`evil-collection-mode-list' for a list of available options.") - - -(def-package! evil-collection - :when (featurep! +everywhere) - :defer 1 - :after-call post-command-hook - :preface - ;; must be set before evil/evil-collection is loaded - (setq evil-want-integration nil - evil-collection-company-use-tng nil) - :config - (dolist (sym +evil-collection-disabled-list) - (setq evil-collection-mode-list - (funcall (if (symbolp sym) #'delq #'delete) - sym evil-collection-mode-list))) - (evil-collection-init)) - - (def-package! evil :init (setq evil-want-C-u-scroll t @@ -60,7 +27,9 @@ variable for an explanation of the defaults (in comments). See evil-normal-state-cursor 'box evil-emacs-state-cursor '(box +evil-emacs-cursor) evil-insert-state-cursor 'bar - evil-visual-state-cursor 'hollow) + evil-visual-state-cursor 'hollow + ;; must be set before evil/evil-collection is loaded + evil-want-integration (not (featurep! +everywhere))) :config (add-hook 'doom-post-init-hook #'evil-mode) @@ -169,7 +138,193 @@ variable for an explanation of the defaults (in comments). See (evil-set-command-properties '+evil:align :move-point t :ex-arg 'buffer-match :ex-bang t :evil-mc t :keep-visual t :suppress-operator t) (evil-set-command-properties - '+evil:mc :move-point nil :ex-arg 'global-match :ex-bang t :evil-mc t)) + '+evil:mc :move-point nil :ex-arg 'global-match :ex-bang t :evil-mc t) + + ;; `evil-collection' + ;; *Truly* lazy-load evil-collection's modules, and do ourselves, here, + ;; instead of lazy-loading evil-collection.el so we can ensure `after!' blocks + ;; in private configs happen after evil-collection has finished. + ;; + ;; Also so we can be very selective about what modules it loads. + (when (featurep! +everywhere) + (after! eldoc + (eldoc-add-command-completions "evil-window-")) + + (after! comint + (evil-define-key* 'normal comint-mode-map + (kbd "C-d") #'evil-scroll-down + (kbd "C-n") #'comint-next-input + (kbd "C-p") #'comint-previous-input + (kbd "gj") #'comint-next-input + (kbd "gk") #'comint-previous-input + (kbd "]") #'comint-next-input + (kbd "[") #'comint-previous-input) + (evil-define-key* 'insert comint-mode-map + (kbd "") #'comint-previous-input + (kbd "") #'comint-next-input)) + + (after! cus-edit + (evil-set-initial-state 'Custom-mode 'normal) + (evil-define-key* 'motion custom-mode-map + (kbd "") 'widget-forward + (kbd "S-") 'widget-backward + (kbd "") 'widget-backward + (kbd "]") 'widget-forward + (kbd "[") 'widget-backward + (kbd "C-n") 'widget-forward + (kbd "C-p") 'widget-backward + "gj" 'widget-forward + "gk" 'widget-backward) + (evil-define-key* 'normal custom-mode-map + (kbd "") 'Custom-newline + (kbd "C-o") 'Custom-goto-parent + "^" 'Custom-goto-parent + "<" 'Custom-goto-parent + ;; quit + "q" 'Custom-buffer-done + "ZQ" 'evil-quit + "ZZ" 'Custom-buffer-done)) + + (after! help + (evil-set-initial-state 'help-mode 'normal) + (evil-define-key* 'normal help-mode-map + ;; motion + (kbd "SPC") 'scroll-up-command + (kbd "S-SPC") 'scroll-down-command + (kbd "C-f") 'scroll-up-command + (kbd "C-b") 'scroll-down-command + (kbd "") 'forward-button + (kbd "") 'backward-button + (kbd "C-o") 'help-go-back + (kbd "C-i") 'help-go-forward + ;; TODO: Enable more help-go-* bindings? + ;; "gj" 'help-go-forward + ;; "gk" 'help-go-back + ;; "\C-j" 'help-go-forward + ;; "\C-k" 'help-go-back + ;; The following bindings don't do what they are supposed to. "go" should + ;; open in the same window and "gO" should open in a different one. + "go" 'push-button + "gO" 'push-button + "g?" 'describe-mode + "gr" 'revert-buffer + "<" 'help-go-back + ">" 'help-go-forward + "r" 'help-follow + ;; quit + "q" 'quit-window + "ZQ" 'evil-quit + "ZZ" 'quit-window)) + + (add-transient-hook! 'image-mode (evil-collection-init 'image)) + (add-transient-hook! 'emacs-lisp-mode (evil-collection-init 'elisp-mode)) + + (defvar evil-collection-mode-list + '(ace-jump-mode + ag + alchemist + ;; anaconda-mode + arc-mode + avy + bookmark + (buff-menu "buff-menu") + calc + calendar + cider + cmake-mode + ;; comint + ;; company + compile + ;; custom + cus-theme + daemons + debbugs + debug + diff-mode + ;; dired + doc-view + edebug + ediff + ;; eldoc + ;; elfeed + ;; elisp-mode + elisp-refs + emms + epa + ;; ert + eshell + eval-sexp-fu + etags-select + eww + flycheck + ;; free-keys + geiser + ggtags + git-timemachine + go-mode + ;; help + guix + ;; helm + ibuffer + ;; image + image+ + indium + info + ;; ivy + js2-mode + log-view + lsp-ui-imenu + lua-mode + ;; kotlin-mode + macrostep + man + magit + mu4e + mu4e-conversation + neotree + notmuch + nov + ;; occur is in replace.el which was built-in before Emacs 26. + (occur ,(if EMACS26+ 'replace "replace")) + outline + p4 + ;; (package-menu package) + paren + pass + (pdf pdf-view) + popup + proced + prodigy + profiler + python + quickrun + racer + realgud + reftex + rjsx-mode + robe + ;; ruby-mode + rtags + ;; simple + ;; slime + (term term ansi-term multi-term) + tide + transmission + typescript-mode + vc-annotate + vdiff + view + vlf + which-key + wdired + wgrep + woman + xref + (ztree ztree-diff))) + + (dolist (req evil-collection-mode-list) + (with-eval-after-load (car (doom-enlist req)) + (evil-collection-init (list req)))))) ;; @@ -231,12 +386,12 @@ variable for an explanation of the defaults (in comments). See (cons (format "(%s " (or (read-string "(") "")) ")")) ;; Add escaped-sequence support to embrace - (map-put (default-value 'embrace--pairs-list) - ?\\ (make-embrace-pair-struct - :key ?\\ - :read-function #'+evil--embrace-escaped - :left-regexp "\\[[{(]" - :right-regexp "\\[]})]"))) + (setf (alist-get ?\\ (default-value 'embrace--pairs-list)) + (make-embrace-pair-struct + :key ?\\ + :read-function #'+evil--embrace-escaped + :left-regexp "\\[[{(]" + :right-regexp "\\[]})]"))) (def-package! evil-escape @@ -249,12 +404,6 @@ variable for an explanation of the defaults (in comments). See (add-hook 'pre-command-hook #'evil-escape-pre-command-hook) (evil-define-key* '(insert replace visual operator) 'global "\C-g" #'evil-escape) :config - ;; TODO PR this upstream - (defun +evil*escape-func (ret) - (if (eq evil-state 'multiedit-insert) - #'evil-multiedit-state - ret)) - (advice-add #'evil-escape-func :filter-return #'+evil*escape-func) ;; no `evil-escape' in minibuffer (add-hook 'evil-escape-inhibit-functions #'minibufferp)) @@ -332,8 +481,7 @@ the new algorithm is confusing, like in python or ruby." ;; Add custom commands to whitelisted commands (dolist (fn '(doom/backward-to-bol-or-indent doom/forward-to-last-non-comment-or-eol doom/backward-kill-to-bol-and-indent)) - (map-put evil-mc-custom-known-commands - fn '((:default . evil-mc-execute-default-call)))) + (add-to-list 'evil-mc-custom-known-commands `(,fn (:default . evil-mc-execute-default-call)))) ;; Activate evil-mc cursors upon switching to insert mode (defun +evil-mc|resume-cursors () (setq evil-mc-frozen nil)) diff --git a/modules/feature/file-templates/autoload.el b/modules/feature/file-templates/autoload.el index be789d92d..731edb9bb 100644 --- a/modules/feature/file-templates/autoload.el +++ b/modules/feature/file-templates/autoload.el @@ -2,7 +2,9 @@ (defun +file-templates--set (pred plist) (if (null (car-safe plist)) - (setq +file-templates-alist (map-delete +file-templates-alist pred)) + (setq +file-templates-alist + (delq (assoc pred +file-templates-alist) + +file-templates-alist)) (push `(,pred ,@plist) +file-templates-alist))) ;;;###autodef @@ -56,7 +58,7 @@ these properties: ;; ;;;###autoload -(cl-defun +file-templates--expand (pred &key project mode trigger ignore) +(cl-defun +file-templates--expand (pred &key project mode trigger ignore _when) "Auto insert a yasnippet snippet into current file and enter insert mode (if evil is loaded and enabled)." (when (and pred (not ignore)) diff --git a/modules/feature/lookup/autoload/devdocs.el b/modules/feature/lookup/autoload/devdocs.el index 3558b4cbd..5baf61cf4 100644 --- a/modules/feature/lookup/autoload/devdocs.el +++ b/modules/feature/lookup/autoload/devdocs.el @@ -9,8 +9,9 @@ DOCSET (a string). See `devdocs-alist' for the defaults. " (after! (:when (boundp 'devdocs-alist)) (dolist (mode (doom-enlist modes)) - (map-put devdocs-alist mode docset)))) + (setf (alist-get mode devdocs-alist) docset)))) +;; FIXME obsolete :devdocs ;;;###autoload (def-setting! :devdocs (modes docset) "Map major MODES (one major-mode symbol or a list of them) to a devdocs diff --git a/modules/feature/lookup/autoload/docsets.el b/modules/feature/lookup/autoload/docsets.el index 2255e1662..6293b6f29 100644 --- a/modules/feature/lookup/autoload/docsets.el +++ b/modules/feature/lookup/autoload/docsets.el @@ -9,9 +9,13 @@ ;;;###autodef (defun set-docset! (modes &rest docsets) - "Registers a list of DOCSETS (strings) for MODES (either one major mode + "Registers a list of DOCSETS (strings) for MODES (either one major/minor mode symbol or a list of them). +If MODES is a minor mode, you can use :add or :remove as the first element of +DOCSETS, to instruct it to append (or remove) those from the docsets already set +by a major-mode, if any. + Used by `+lookup/in-docsets' and `+lookup/documentation'." (declare (indent defun)) (dolist (mode (doom-enlist modes)) @@ -30,16 +34,9 @@ Used by `+lookup/in-docsets' and `+lookup/documentation'." docsets))))) (add-hook hook fn)))))) +;; FIXME obsolete :docset ;;;###autoload (def-setting! :docset (modes &rest docsets) - "Registers a list of DOCSETS (strings) for MODES (either one major mode -symbol or a list of them). - -If MODES is a minor mode, you can use :add or :remove as the first element of -DOCSETS, to instruct it to append (or remove) those from the docsets already set -by a major-mode, if any. - -Used by `+lookup/in-docsets' and `+lookup/documentation'." :obsolete set-docset! `(set-docset! ,modes ,@docsets)) diff --git a/modules/feature/lookup/autoload/lookup.el b/modules/feature/lookup/autoload/lookup.el index de9216787..6cbcf3735 100644 --- a/modules/feature/lookup/autoload/lookup.el +++ b/modules/feature/lookup/autoload/lookup.el @@ -56,6 +56,7 @@ properties: (add-hook 'xref-backend-functions xref-backend nil t)))))) (add-hook hook fn)))))) +;; FIXME obsolete :lookup ;;;###autoload (def-setting! :lookup (modes &rest plist) :obsolete set-lookup-handlers! @@ -72,7 +73,7 @@ properties: "Search on: " (mapcar #'car +lookup-provider-url-alist) nil t))) - (map-put +lookup--last-provider key provider) + (setf (alist-get +lookup--last-provider key) provider) provider)))) (defun +lookup--symbol-or-region (&optional initial) @@ -245,7 +246,7 @@ Otherwise, falls back on `find-file-at-point'." ((not (and +lookup-file-functions (+lookup--jump-to :file path))) (let ((fullpath (expand-file-name path))) - (when (file-equal-p fullpath buffer-file-name) + (when (and buffer-file-name (file-equal-p fullpath buffer-file-name)) (user-error "Already here")) (let* ((insert-default-directory t) (project-root (doom-project-root 'nocache)) @@ -318,7 +319,9 @@ for the provider." (user-error "The search query is empty")) (funcall +lookup-open-url-fn (format url (url-encode-url search)))) (error - (map-delete +lookup--last-provider major-mode) + (setq +lookup--last-provider + (delq (assq major-mode +lookup--last-provider) + +lookup--last-provider)) (signal (car e) (cdr e))))) ;;;###autoload diff --git a/modules/feature/snippets/autoload/settings.el b/modules/feature/snippets/autoload/settings.el index b9769afa2..adb9f36da 100644 --- a/modules/feature/snippets/autoload/settings.el +++ b/modules/feature/snippets/autoload/settings.el @@ -9,6 +9,7 @@ can have its own snippets category, if the folder exists." (fset fn (lambda () (yas-activate-extra-mode mode))) (add-hook (intern (format "%s-hook" mode)) fn)))) +;; FIXME obsolete :yas-minor-mode ;;;###autoload (def-setting! :yas-minor-mode (mode) :obsolete set-yas-minor-mode! diff --git a/modules/feature/workspaces/autoload/workspaces.el b/modules/feature/workspaces/autoload/workspaces.el index e249f60e4..0b2adabaa 100644 --- a/modules/feature/workspaces/autoload/workspaces.el +++ b/modules/feature/workspaces/autoload/workspaces.el @@ -291,7 +291,9 @@ workspace to delete." current-name)))) (condition-case-unless-debug ex (let ((workspaces (+workspace-list-names))) - (cond ((> (length workspaces) 1) + (cond ((delq (selected-frame) (persp-frames-with-persp (get-frame-persp))) + (user-error "Can't close workspace, it's visible in another frame")) + ((> (length workspaces) 1) (+workspace-delete name) (+workspace-switch (if (+workspace-exists-p +workspace--last) @@ -330,18 +332,16 @@ workspace, otherwise the new workspace is blank." (interactive "iP") (unless name (setq name (format "#%s" (+workspace--generate-id)))) - (condition-case-unless-debug ex - (if (+workspace-exists-p name) - (error "%s already exists" name) - (+workspace-switch name t) - (if clone-p - (let ((persp (+workspace-get name))) - (dolist (window (window-list)) - (persp-add-buffer (window-buffer window) persp nil))) - (delete-other-windows-internal) - (switch-to-buffer (doom-fallback-buffer))) - (+workspace/display)) - ('error (+workspace-error (cadr ex) t)))) + (condition-case e + (cond ((+workspace-exists-p name) + (error "%s already exists" name)) + (clone-p (persp-copy name t)) + (t + (+workspace-switch name t) + (persp-delete-other-windows) + (switch-to-buffer (doom-fallback-buffer)) + (+workspace/display))) + ((debug error) (+workspace-error (cadr e) t)))) ;;;###autoload (defun +workspace/switch-to (index) diff --git a/modules/feature/workspaces/config.el b/modules/feature/workspaces/config.el index bc225158a..a168b57e0 100644 --- a/modules/feature/workspaces/config.el +++ b/modules/feature/workspaces/config.el @@ -29,8 +29,9 @@ stored in `persp-save-dir'.") ;; If emacs is passed --restore, restore the last session on startup. This is ;; particularly useful for the `+workspace/restart-emacs-then-restore' command. (defun +workspaces-restore-last-session (&rest _) - (add-hook 'emacs-startup-hook #'+workspace/load-session 'append)) -(map-put command-switch-alist "--restore" #'+workspaces-restore-last-session) + (add-hook 'emacs-startup-hook #'+workspace/load-session :append)) +(add-to-list 'command-switch-alist (cons "--restore" #'+workspaces-restore-last-session)) + ;; ;; Plugins @@ -41,7 +42,9 @@ stored in `persp-save-dir'.") :init (defun +workspaces|init () ;; Remove default buffer predicate so persp-mode can put in its own - (setq default-frame-alist (map-delete default-frame-alist 'buffer-predicate)) + (setq default-frame-alist + (delq (assq 'buffer-predicate default-frame-alist) + default-frame-alist)) (add-hook 'after-make-frame-functions #'+workspaces|init-frame) (require 'persp-mode) (unless (daemonp) @@ -160,8 +163,8 @@ Uses `+workspaces-main' to determine the name of the main workspace." (list tag (buffer-name buf) vars (buffer-name (buffer-base-buffer)))) :load-function (lambda (savelist &rest _rest) - (destructuring-bind - (buf-name vars base-buf-name &rest _rest) (cdr savelist) + (destructuring-bind (buf-name _vars base-buf-name &rest _) + (cdr savelist) (push (cons buf-name base-buf-name) +workspaces--indirect-buffers-to-restore) nil))) diff --git a/modules/feature/workspaces/test/test-workspaces.el b/modules/feature/workspaces/test/test-workspaces.el index 7d7d1f6a9..c04cc7a56 100644 --- a/modules/feature/workspaces/test/test-workspaces.el +++ b/modules/feature/workspaces/test/test-workspaces.el @@ -5,48 +5,49 @@ :var (persp-auto-resume-time persp-auto-save-opt persp-switch-to-added-buffer + persp-autokill-persp-when-removed-last-buffer + persp-autokill-buffer-on-remove in1 in2 out1 out2 persp1 persp1-name persp2 persp2-name - doom-before-switch-buffer-hook - doom-after-switch-buffer-hook) + wconf) (before-all + (delete-other-windows) (require! :feature workspaces) (require 'persp-mode)) - (after-all - (unload-feature 'persp-mode t)) (before-each - (setq persp-auto-resume-time -1 + (switch-to-buffer "*scratch*") + (setq wconf (current-window-configuration) + persp-auto-resume-time -1 persp-auto-save-opt 0 persp-switch-to-added-buffer nil - in1 (get-buffer-create "a") - in2 (get-buffer-create "b") - out1 (get-buffer-create "c") - out2 (get-buffer-create "d")) + persp-autokill-persp-when-removed-last-buffer nil + persp-autokill-buffer-on-remove nil + in1 (get-buffer-create "in1") + in2 (get-buffer-create "in2") + out1 (get-buffer-create "out1") + out2 (get-buffer-create "out2")) (doom-set-buffer-real in1 t) (doom-set-buffer-real out1 t) (let (noninteractive) - (persp-mode +1)) - (let (persp-before-switch-functions persp-activated-functions) - (setq persp1-name +workspaces-main - persp1 (persp-add-new persp1-name) - persp2-name "test" - persp2 (persp-add-new persp2-name)) - (persp-frame-switch +workspaces-main)) - (delete-other-windows) - (switch-to-buffer in1) - (persp-add-buffer (list in1 in2)) - (spy-on 'persp-add-buffer :and-call-through) - (doom|init-custom-hooks)) + (persp-mode +1) + (let (persp-before-switch-functions persp-activated-functions) + (setq persp1-name +workspaces-main + persp1 (persp-add-new persp1-name) + persp2-name "test" + persp2 (persp-add-new persp2-name)) + (persp-switch persp1-name) + (persp-add-buffer (list in1 in2) persp1)))) (after-each - (doom|init-custom-hooks 'disable) (let (kill-buffer-query-functions kill-buffer-hook) - (mapc #'kill-buffer (list in1 in2 out1 out2))) - (let (noninteractive) - (mapc #'persp-kill (cdr (persp-names))) - (persp-mode -1))) + (let (noninteractive ignore-window-parameters) + (dolist (persp (persp-names)) + (ignore-errors (persp-kill persp))) + (persp-mode -1)) + (set-window-configuration wconf) + (mapc #'kill-buffer (list in1 in2 out1 out2)))) ;; (describe "switch" @@ -80,7 +81,7 @@ (expect (+workspace-contains-buffer-p out1) :to-be nil)) (it "automatically adds interactively opened buffers" (expect (+workspace-contains-buffer-p out1) :to-be nil) - (switch-to-buffer out1) + (with-current-buffer out1 (+workspaces|auto-add-buffer)) (expect (+workspace-contains-buffer-p out1))) (xit "returns a list of orphaned buffers" (expect (+workspace-orphaned-buffer-list) :to-contain out2))) @@ -107,20 +108,6 @@ (expect (+workspace-list-names) :not :to-contain persp2-name))) (describe "command" - (describe "new" - (it "creates a new, blank workspace" - (quiet! (+workspace/new "X")) - (expect (one-window-p)) - (expect (current-buffer) :to-be (doom-fallback-buffer))) - (it "clones a workspace" - (quiet! (+workspace/new "X" t)) - (expect (current-buffer) :to-be in1))) - - (describe "rename" - (it "renames the current workspace" - (quiet! (+workspace/rename "X")) - (expect (+workspace-current-name) :to-equal "X"))) - (describe "close-window-or-workspace" (before-each (+workspace-switch persp2-name) @@ -132,4 +119,9 @@ (it "kills workspace on last window" (quiet! (+workspace/close-window-or-workspace) (+workspace/close-window-or-workspace)) - (expect (+workspace-current-name) :to-equal persp1-name))))) + (expect (+workspace-current-name) :to-equal persp1-name))) + + (describe "rename" + (it "renames the current workspace" + (quiet! (+workspace/rename "X")) + (expect (+workspace-current-name) :to-equal "X"))))) diff --git a/modules/lang/assembly/autoload.el b/modules/lang/assembly/autoload.el index 38b582a66..29e8ded24 100644 --- a/modules/lang/assembly/autoload.el +++ b/modules/lang/assembly/autoload.el @@ -1,4 +1,4 @@ ;;; lang/assembly/autoload.el -*- lexical-binding: t; -*- ;;;###autoload -(map-put auto-mode-alist "\\.hax\\'" 'haxor-mode) +(add-to-list 'auto-mode-alist '("\\.hax\\'" . haxor-mode)) diff --git a/modules/lang/cc/config.el b/modules/lang/cc/config.el index e05acbec6..9f7479434 100644 --- a/modules/lang/cc/config.el +++ b/modules/lang/cc/config.el @@ -83,35 +83,37 @@ compilation database is present in the project.") +cc|fontify-constants)) ;; Custom style, based off of linux - (map-put c-style-alist "doom" - `((c-basic-offset . ,tab-width) - (c-comment-only-line-offset . 0) - (c-hanging-braces-alist (brace-list-open) - (brace-entry-open) - (substatement-open after) - (block-close . c-snug-do-while) - (arglist-cont-nonempty)) - (c-cleanup-list brace-else-brace) - (c-offsets-alist - (statement-block-intro . +) - (knr-argdecl-intro . 0) - (substatement-open . 0) - (substatement-label . 0) - (statement-cont . +) - (case-label . +) - ;; align args with open brace OR don't indent at all (if open - ;; brace is at eolp and close brace is after arg with no trailing - ;; comma) - (arglist-intro . +) - (arglist-close +cc-lineup-arglist-close 0) - ;; don't over-indent lambda blocks - (inline-open . 0) - (inlambda . 0) - ;; indent access keywords +1 level, and properties beneath them - ;; another level - (access-label . -) - (inclass +cc-c++-lineup-inclass +) - (label . 0)))) + (unless (assoc "doom" c-style-alist) + (push '("doom" + (c-basic-offset . ,tab-width) + (c-comment-only-line-offset . 0) + (c-hanging-braces-alist (brace-list-open) + (brace-entry-open) + (substatement-open after) + (block-close . c-snug-do-while) + (arglist-cont-nonempty)) + (c-cleanup-list brace-else-brace) + (c-offsets-alist + (statement-block-intro . +) + (knr-argdecl-intro . 0) + (substatement-open . 0) + (substatement-label . 0) + (statement-cont . +) + (case-label . +) + ;; align args with open brace OR don't indent at all (if open + ;; brace is at eolp and close brace is after arg with no trailing + ;; comma) + (arglist-intro . +) + (arglist-close +cc-lineup-arglist-close 0) + ;; don't over-indent lambda blocks + (inline-open . 0) + (inlambda . 0) + ;; indent access keywords +1 level, and properties beneath them + ;; another level + (access-label . -) + (inclass +cc-c++-lineup-inclass +) + (label . 0))) + c-style-alist)) ;;; Keybindings ;; Disable electric keys because it interferes with smartparens and custom @@ -133,9 +135,6 @@ compilation database is present in the project.") :when '(+cc-sp-point-is-template-p +cc-sp-point-after-include-p) :post-handlers '(("| " "SPC")))) (sp-with-modes '(c-mode c++-mode objc-mode java-mode) - (sp-local-pair "/*" "*/" :post-handlers '(("||\n[i]" "RET") ("| " "SPC"))) - ;; Doxygen blocks - (sp-local-pair "/**" "*/" :post-handlers '(("||\n[i]" "RET") ("||\n[i]" "SPC"))) (sp-local-pair "/*!" "*/" :post-handlers '(("||\n[i]" "RET") ("[d-1]< | " "SPC"))))) diff --git a/modules/lang/clojure/config.el b/modules/lang/clojure/config.el index e0c3b3767..5f33f3f96 100644 --- a/modules/lang/clojure/config.el +++ b/modules/lang/clojure/config.el @@ -5,14 +5,7 @@ (def-package! clj-refactor - :after clojure-mode - :config - ;; setup some extra namespace auto completion for great awesome - (dolist (ns '(("re-frame" . "re-frame.core") - ("reagent" . "reagent.core") - ("str" . "clojure.string"))) - (map-put cljr-magic-require-namespaces (car ns) (cdr ns)))) - + :after clojure-mode) (def-package! cider ;; NOTE: if you don't have an org directory set (the dir doesn't exist), cider @@ -23,13 +16,7 @@ (setq nrepl-hide-special-buffers t cider-stacktrace-default-filters '(tooling dup) cider-prompt-save-file-on-load nil - cider-repl-use-clojure-font-lock t - ;; Setup cider for clojurescript / figwheel dev. - cider-cljs-lein-repl - "(do (require 'figwheel-sidecar.repl-api) - (figwheel-sidecar.repl-api/start-figwheel!) - (figwheel-sidecar.repl-api/cljs-repl))") - + cider-repl-use-clojure-font-lock t) (set-popup-rule! "^\\*cider-repl" :quit nil :select nil) (set-repl-handler! 'clojure-mode #'+clojure/repl) (set-eval-handler! 'clojure-mode #'cider-eval-region) diff --git a/modules/lang/crystal/config.el b/modules/lang/crystal/config.el index c740414f6..c1e219f45 100644 --- a/modules/lang/crystal/config.el +++ b/modules/lang/crystal/config.el @@ -7,9 +7,9 @@ :definition #'crystal-def-jump :references #'crystal-tool-imp) (set-eval-handler! 'crystal-mode - '((:command . "crystal") - (:exec . "%c %s") - (:description . "Run Crystal script")))) + '((:command . "crystal") + (:exec . "%c %s") + (:description . "Run Crystal script")))) (def-package! flycheck-crystal diff --git a/modules/lang/csharp/config.el b/modules/lang/csharp/config.el index 760db1493..f8927b350 100644 --- a/modules/lang/csharp/config.el +++ b/modules/lang/csharp/config.el @@ -1,6 +1,7 @@ ;;; lang/csharp/config.el -*- lexical-binding: t; -*- -(map-put auto-mode-alist '"\\.shader$" 'dshader-mode) ; unity shaders +;; unity shaders +(add-to-list 'auto-mode-alist '("\\.shader$" . shader-mode)) (def-package! omnisharp diff --git a/modules/lang/data/config.el b/modules/lang/data/config.el index 9786197f2..cc3e79889 100644 --- a/modules/lang/data/config.el +++ b/modules/lang/data/config.el @@ -1,10 +1,10 @@ ;;; lang/data/config.el -*- lexical-binding: t; -*- ;; Built in plugins -(dolist (spec '(("/sxhkdrc\\'" . conf-mode) - ("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode) - ("\\.plist\\'" . nxml-mode))) - (map-put auto-mode-alist (car spec) (cdr spec))) +(unless after-init-time + (push '("/sxhkdrc\\'" . conf-mode) auto-mode-alist) + (push '("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode) auto-mode-alist) + (push '("\\.plist\\'" . nxml-mode) auto-mode-alist)) (after! nxml-mode (set-company-backend! 'nxml-mode '(company-nxml company-yasnippet))) diff --git a/modules/lang/elixir/config.el b/modules/lang/elixir/config.el index eb0f7e857..8f0fae3e3 100644 --- a/modules/lang/elixir/config.el +++ b/modules/lang/elixir/config.el @@ -2,6 +2,9 @@ (def-package! elixir-mode :defer t + :init + ;; disable default smartparens config + (provide 'smartparens-elixir) :config ;; ...and only complete the basics (after! smartparens @@ -9,7 +12,6 @@ (sp-local-pair "do" "end" :when '(("RET" "")) :unless '(sp-in-comment-p sp-in-string-p) - :skip-match 'sp-elixir-skip-def-p :post-handlers '("||\n[i]")) (sp-local-pair "do " " end" :unless '(sp-in-comment-p sp-in-string-p)) (sp-local-pair "fn " " end" :unless '(sp-in-comment-p sp-in-string-p)))) diff --git a/modules/lang/emacs-lisp/config.el b/modules/lang/emacs-lisp/config.el index 8863099c0..a39f2da66 100644 --- a/modules/lang/emacs-lisp/config.el +++ b/modules/lang/emacs-lisp/config.el @@ -1,8 +1,8 @@ ;;; lang/emacs-lisp/config.el -*- lexical-binding: t; -*- -(def-package! elisp-mode ; built-in - :mode ("/Cask$" . emacs-lisp-mode) - :config +(add-to-list 'auto-mode-alist '("\\.Cask\\'" . emacs-lisp-mode)) + +(defun +emacs-lisp|init () (set-repl-handler! 'emacs-lisp-mode #'+emacs-lisp/repl) (set-eval-handler! 'emacs-lisp-mode #'+emacs-lisp-eval) (set-lookup-handlers! 'emacs-lisp-mode :documentation 'info-lookup-symbol) @@ -61,6 +61,8 @@ return t)) (flycheck-mode -1)))) +(add-transient-hook! 'emacs-lisp-mode (+emacs-lisp|init)) + ;; ;; Plugins @@ -74,21 +76,21 @@ ;; `macrostep' (map! :after macrostep :map macrostep-keymap - :n "RET" #'macrostep-expand - :n "e" #'macrostep-expand - :n "u" #'macrostep-collapse - :n "c" #'macrostep-collapse + :n "RET" #'macrostep-expand + :n "e" #'macrostep-expand + :n "u" #'macrostep-collapse + :n "c" #'macrostep-collapse - :n "TAB" #'macrostep-next-macro - :n "n" #'macrostep-next-macro - :n "J" #'macrostep-next-macro + :n [tab] #'macrostep-next-macro + :n "C-n" #'macrostep-next-macro + :n "J" #'macrostep-next-macro - :n "S-TAB" #'macrostep-prev-macro - :n "K" #'macrostep-prev-macro - :n "p" #'macrostep-prev-macro + :n [backtab] #'macrostep-prev-macro + :n "K" #'macrostep-prev-macro + :n "C-p" #'macrostep-prev-macro - :n "q" #'macrostep-collapse-all - :n "C" #'macrostep-collapse-all) + :n "q" #'macrostep-collapse-all + :n "C" #'macrostep-collapse-all) (after! evil ;; `evil-normalize-keymaps' seems to be required for macrostep or it won't diff --git a/modules/lang/erlang/config.el b/modules/lang/erlang/config.el index 933c455f4..1f4a062b8 100644 --- a/modules/lang/erlang/config.el +++ b/modules/lang/erlang/config.el @@ -5,7 +5,7 @@ "/rebar\\.config\\(?:\\.script\\)?$" ;; erlang configs "/\\(?:app\\|sys\\)\\.config$")) - (map-put auto-mode-alist regexp 'erlang-mode)) + (add-to-list 'auto-mode-alist (cons regexp 'erlang-mode))) (def-package! flycheck-rebar3 diff --git a/modules/lang/ess/config.el b/modules/lang/ess/config.el index f3e78ccc1..f3ee2b564 100644 --- a/modules/lang/ess/config.el +++ b/modules/lang/ess/config.el @@ -31,7 +31,7 @@ ("\\.[Jj][Mm][Dd]\\'" . ess-jags-mode)) :init (unless (featurep! :lang julia) - (map-put auto-mode-alist "\\.jl\'" 'ess-julia-mode)) + (add-to-list 'auto-mode-alist '("\\.jl\\'" . ess-julia-mode))) :config (add-hook 'ess-mode-hook #'doom|enable-line-numbers) (setq ess-offset-continued 'straight diff --git a/modules/lang/haskell/+dante.el b/modules/lang/haskell/+dante.el index e3687fd2b..2eb5645f5 100644 --- a/modules/lang/haskell/+dante.el +++ b/modules/lang/haskell/+dante.el @@ -2,17 +2,8 @@ ;;;###if (featurep! +dante) (def-package! dante - :after haskell-mode :hook (haskell-mode . dante-mode) :config - (add-hook 'haskell-mode-hook #'interactive-haskell-mode)) - - -(def-package! company-ghc - :when (featurep! :completion company) - :after haskell-mode - :init - (add-hook 'haskell-mode-hook #'ghc-comp-init) - :config - (setq company-ghc-show-info 'oneline) - (set-company-backend! 'haskell-mode #'company-ghc)) + (when (featurep! :feature syntax-checker) + (add-hook! 'dante-mode-hook + (flycheck-add-next-checker 'haskell-dante '(warning . haskell-hlint))))) diff --git a/modules/lang/haskell/doctor.el b/modules/lang/haskell/doctor.el index f803ea3d2..b62d40aa1 100644 --- a/modules/lang/haskell/doctor.el +++ b/modules/lang/haskell/doctor.el @@ -4,9 +4,8 @@ (when (featurep! +dante) (unless (executable-find "cabal") (warn! "Couldn't find cabal, haskell-mode may have issues")) - - (unless (executable-find "ghc-mod") - (warn! "Couldn't find ghc-mod on PATH. Code completion will not work"))) + (unless (executable-find "hlint") + (warn! "Couldn't find hlint. Flycheck may have issues in haskell-mode/"))) (when (featurep! +intero) (unless (executable-find "stack") diff --git a/modules/lang/haskell/packages.el b/modules/lang/haskell/packages.el index 2b637c3f8..541ed0bcd 100644 --- a/modules/lang/haskell/packages.el +++ b/modules/lang/haskell/packages.el @@ -5,9 +5,7 @@ ;; (cond ((featurep! +dante) - (package! dante) - (when (featurep! :completion company) - (package! company-ghc))) + (package! dante)) (t (package! intero) (package! hindent))) diff --git a/modules/lang/javascript/config.el b/modules/lang/javascript/config.el index eda3a2268..0caa4a554 100644 --- a/modules/lang/javascript/config.el +++ b/modules/lang/javascript/config.el @@ -17,11 +17,6 @@ ;; Other :yield "import")) -(after! smartparens - (sp-with-modes '(js2-mode typescript-mode rjsx-mode) - (sp-local-pair "/**" "" :post-handlers '(("| " "SPC") ("|\n*/[i][d-2]" "RET"))) - (sp-local-pair "/*" "*/" :post-handlers '(("| " "SPC") ("|\n*/[i][d-2]" "RET"))))) - ;; ;; Major modes @@ -66,7 +61,7 @@ magic-mode-regexp-match-limit t) (progn (goto-char (match-beginning 1)) (not (sp-point-in-string-or-comment))))) - (map-put magic-mode-alist #'+javascript-jsx-file-p 'rjsx-mode) + (add-to-list 'magic-mode-alist '(+javascript-jsx-file-p . rjsx-mode)) :config (set-electric! 'rjsx-mode :chars '(?\} ?\) ?. ?>)) (add-hook! 'rjsx-mode-hook @@ -76,7 +71,7 @@ ;; `rjsx-electric-gt' relies on js2's parser to tell it when the cursor is in ;; a self-closing tag, so that it can insert a matching ending tag at point. ;; However, the parser doesn't run immediately, so a fast typist can outrun - ;; it, causing issues, so force it to parse. + ;; it, causing tags to stay unclosed, so force it to parse. (defun +javascript|reparse (n) ;; if n != 1, rjsx-electric-gt calls rjsx-maybe-reparse itself (if (= n 1) (rjsx-maybe-reparse))) @@ -134,23 +129,19 @@ :config (setq tide-completion-detailed t tide-always-show-documentation t) - ;; code completion (after! company ;; tide affects the global `company-backends', undo this so doom can handle ;; it buffer-locally (setq-default company-backends (delq 'company-tide (default-value 'company-backends)))) (set-company-backend! 'tide-mode 'company-tide) - ;; navigation (set-lookup-handlers! 'tide-mode :definition #'tide-jump-to-definition :references #'tide-references :documentation #'tide-documentation-at-point) - ;; resolve to `doom-project-root' if `tide-project-root' fails (advice-add #'tide-project-root :override #'+javascript*tide-project-root) - ;; cleanup tsserver when no tide buffers are left (add-hook! 'tide-mode-hook (add-hook 'kill-buffer-hook #'+javascript|cleanup-tide-processes nil t)) diff --git a/modules/lang/latex/config.el b/modules/lang/latex/config.el index 9be62a148..52bba4314 100644 --- a/modules/lang/latex/config.el +++ b/modules/lang/latex/config.el @@ -14,6 +14,9 @@ ;; Plugins ;; +;; sp's default rules are obnoxious, so disable them +(provide 'smartparens-latex) + (after! tex ;; Set some varibles to fontify common LaTeX commands. (load! "+fontification") @@ -75,33 +78,30 @@ (setcar (cdr (assoc "Check" TeX-command-list)) "chktex -v6 %s") ;; Set a custom item indentation (dolist (env '("itemize" "enumerate" "description")) - (map-put LaTeX-indent-environment-list - env '(+latex/LaTeX-indent-item))) + (add-to-list 'LaTeX-indent-environment-list `(,env +latex/LaTeX-indent-item))) ;; ;; Use Okular if the user says so. (when (featurep! +okular) ;; Configure Okular as viewer. Including a bug fix ;; (https://bugs.kde.org/show_bug.cgi?id=373855) - (map-put TeX-view-program-list - "Okular" '(("okular --unique file:%o" (mode-io-correlate "#src:%n%a")))) - (map-put TeX-view-program-list 'output-pdf '("Okular"))) + (add-to-list 'TeX-view-program-list '("Okular" ("okular --unique file:%o" (mode-io-correlate "#src:%n%a")))) + (add-to-list 'TeX-view-program-selection '(output-pdf "Okular"))) ;; Or Skim (when (featurep! +skim) - (map-put TeX-view-program-list - "Skim" '("/Applications/Skim.app/Contents/SharedSupport/displayline -b -g %n %o %b")) - (map-put TeX-view-program-selection 'output-pdf '("Skim"))) + (add-to-list 'TeX-view-program-list '("Skim" "/Applications/Skim.app/Contents/SharedSupport/displayline -b -g %n %o %b")) + (add-to-list 'TeX-view-program-selection 'output-pdf '("Skim"))) ;; Or Zathura (when (featurep! +zathura) - (map-put TeX-view-program-selection 'output-pdf '("Zathura"))) + (add-to-list 'TeX-view-program-selection '(output-pdf "Zathura"))) ;; Or PDF-tools, but only if the module is also loaded (when (and (featurep! :tools pdf) (featurep! +pdf-tools)) - (map-put TeX-view-program-list "PDF Tools" '("TeX-pdf-tools-sync-view")) - (map-put TeX-view-program-selection 'output-pdf '("PDF Tools")) + (add-to-list 'TeX-view-program-list ("PDF Tools" "TeX-pdf-tools-sync-view")) + (add-to-list 'TeX-view-program-selection '(output-pdf "PDF Tools")) ;; Enable auto reverting the PDF document with PDF Tools (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer))) @@ -124,8 +124,8 @@ :init (setq latex-preview-pane-multifile-mode 'auctex) :config - (map-put TeX-view-program-list "preview-pane" '(latex-preview-pane-mode)) - (map-put TeX-view-program-selection 'output-pdf '("preview-pane")) + (add-to-list 'TeX-view-program-list '("preview-pane" latex-preview-pane-mode)) + (add-to-list 'TeX-view-program-selection '(output-pdf "preview-pane")) (define-key! doc-view-mode-map (kbd "ESC") #'delete-window "q" #'delete-window diff --git a/modules/lang/lua/config.el b/modules/lang/lua/config.el index db48750f7..9774638b2 100644 --- a/modules/lang/lua/config.el +++ b/modules/lang/lua/config.el @@ -1,5 +1,8 @@ ;;; lang/lua/config.el -*- lexical-binding: t; -*- +;; sp's default rules are obnoxious, so disable them +(provide 'smartparens-lua) + (after! lua-mode (set-lookup-handlers! 'lua-mode :documentation 'lua-search-documentation) (set-electric! 'lua-mode :words '("else" "end")) diff --git a/modules/lang/markdown/config.el b/modules/lang/markdown/config.el index 74c8b5601..53b226708 100644 --- a/modules/lang/markdown/config.el +++ b/modules/lang/markdown/config.el @@ -3,6 +3,9 @@ (def-package! markdown-mode :mode ("/README\\(?:\\.\\(?:markdown\\|md\\)\\)?\\'" . gfm-mode) :init + (when (featurep! +pandoc) + (setq markdown-command "pandoc --from=markdown --to=html --standalone --mathjax --highlight-style=pygments")) + (setq markdown-enable-wiki-links t markdown-italic-underscore t markdown-asymmetric-header t @@ -47,3 +50,10 @@ :nv "t" #'markdown-toc-generate-toc :nv "i" #'markdown-insert-image :nv "l" #'markdown-insert-link))) + +(def-package! pandoc-mode + :when (featurep! +pandoc) + :commands + pandoc-mode + :hook + (markdown-mode . conditionally-turn-on-pandoc)) diff --git a/modules/lang/markdown/doctor.el b/modules/lang/markdown/doctor.el new file mode 100644 index 000000000..353453365 --- /dev/null +++ b/modules/lang/markdown/doctor.el @@ -0,0 +1,6 @@ +;; -*- lexical-binding: t; no-byte-compile: t; -*- +;;; lang/markdown/doctor.el + +(when (featurep! +pandoc) + (unless (executable-find "pandoc") + (warn! "Couldn't find pandoc, markdown-mode may have issues"))) diff --git a/modules/lang/markdown/packages.el b/modules/lang/markdown/packages.el index 72eee45f7..bccae4844 100644 --- a/modules/lang/markdown/packages.el +++ b/modules/lang/markdown/packages.el @@ -4,3 +4,7 @@ (package! markdown-mode) (package! markdown-toc) +(when (featurep! +pandoc) + (package! pandoc-mode)) + + diff --git a/modules/lang/org/+babel.el b/modules/lang/org/+babel.el index 0af4f6389..ab151c4bd 100644 --- a/modules/lang/org/+babel.el +++ b/modules/lang/org/+babel.el @@ -36,7 +36,7 @@ string). Stops at the first function to return non-nil.") (or (cdr (assq lang +org-babel-mode-alist)) lang))) nil t))) - (map-put org-babel-load-languages lang t)) + (add-to-list 'org-babel-load-languages (cons lang t))) t))) (advice-add #'org-babel-confirm-evaluate :around #'+org*babel-lazy-load-library) diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index 358728c5e..5089a24f9 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -25,17 +25,15 @@ :when (featurep! :feature evil +everywhere) :hook (org-mode . evil-org-mode) :init - (setq evil-org-key-theme '(navigation insert textobjects)) + (defvar evil-org-key-theme '(navigation insert textobjects)) (add-hook 'org-load-hook #'+org|setup-evil) (add-hook 'evil-org-mode-hook #'evil-normalize-keymaps) :config ;; in case it is called later - (advice-add #'evil-org-set-key-theme :after #'+org|setup-evil)) - -(def-package! evil-org-agenda - :when (featurep! :feature evil +everywhere) - :after org-agenda - :config (evil-org-agenda-set-keys)) + (advice-add #'evil-org-set-key-theme :after #'+org|setup-evil) + (def-package! evil-org-agenda + :after org-agenda + :config (evil-org-agenda-set-keys))) ;; @@ -266,8 +264,10 @@ between the two." [remap doom/backward-to-bol-or-indent] #'org-beginning-of-line [remap doom/forward-to-last-non-comment-or-eol] #'org-end-of-line)) -(defun +org|setup-evil (&rest _) - (require 'evil-org) +(defun +org|setup-evil (&rest args) + ;; In case this hook is used in an advice on `evil-org-set-key-theme', this + ;; prevents recursive requires. + (unless args (require 'evil-org)) ;; By default, TAB cycles the visibility of all children under the current ;; tree between three states. I want to toggle the tree between two states, ;; without affecting its children. @@ -341,7 +341,7 @@ between the two." (defun +org|setup-hacks () "Getting org to behave." ;; Don't open separate windows - (map-put org-link-frame-setup 'file #'find-file) + (setf (alist-get 'file org-link-frame-setup) #'find-file) ;; Let OS decide what to do with files when opened (setq org-file-apps `(("pdf" . default) diff --git a/modules/lang/sh/config.el b/modules/lang/sh/config.el index 9e1f09dd3..2c1d7e748 100644 --- a/modules/lang/sh/config.el +++ b/modules/lang/sh/config.el @@ -22,9 +22,9 @@ (setq sh-indent-after-continuation 'always) ;; recognize function names with dashes in them - (map-put sh-imenu-generic-expression - 'sh '((nil "^\\s-*function\\s-+\\([[:alpha:]_-][[:alnum:]_-]*\\)\\s-*\\(?:()\\)?" 1) - (nil "^\\s-*\\([[:alpha:]_-][[:alnum:]_-]*\\)\\s-*()" 1))) + (add-to-list 'sh-imenu-generic-expression + '(sh (nil "^\\s-*function\\s-+\\([[:alpha:]_-][[:alnum:]_-]*\\)\\s-*\\(?:()\\)?" 1) + (nil "^\\s-*\\([[:alpha:]_-][[:alnum:]_-]*\\)\\s-*()" 1))) ;; `sh-set-shell' is chatty about setting up indentation rules (advice-add #'sh-set-shell :around #'doom*shut-up) diff --git a/modules/lang/web/+css.el b/modules/lang/web/+css.el index 171775876..48f26a858 100644 --- a/modules/lang/web/+css.el +++ b/modules/lang/web/+css.el @@ -3,10 +3,6 @@ ;; An improved newline+continue comment function (setq-hook! css-mode comment-indent-function #'+css/comment-indent-new-line) -(after! smartparens - (sp-with-modes '(css-mode scss-mode less-css-mode stylus-mode) - (sp-local-pair "/*" "*/" :post-handlers '(("||\n[i]" "RET") ("| " "SPC"))))) - (map! :map* (css-mode-map scss-mode-map less-css-mode-map) :localleader :n "rb" #'+css/toggle-inline-or-block) diff --git a/modules/lang/web/+html.el b/modules/lang/web/+html.el index c25b2b4d6..5c8af811e 100644 --- a/modules/lang/web/+html.el +++ b/modules/lang/web/+html.el @@ -16,7 +16,25 @@ :mode "templates/.+\\.php$" :config (setq web-mode-enable-html-entities-fontification t - web-mode-enable-auto-quoting nil) + web-mode-auto-close-style 2) + + (after! smartparens + ;; let smartparens handle these + (setq web-mode-enable-auto-quoting nil + web-mode-enable-auto-pairing t) + ;; Remove web-mode auto pairs that end with >, because smartparens autopairs + ;; them, causing duplicates. Also remove truncated autopairs, like . + (dolist (alist web-mode-engines-auto-pairs) + (setcdr alist (delq nil + (mapcar (lambda (pair) + (unless (string-match-p "^[a-z-]" (cdr pair)) + (cons (car pair) + (if (equal (substring (cdr pair) -1) ">") + (substring (cdr pair) 0 -1) + (cdr pair))))) + (cdr alist))))) + (setf (alist-get nil web-mode-engines-auto-pairs) nil)) (map! :map web-mode-map (:localleader diff --git a/modules/tools/editorconfig/config.el b/modules/tools/editorconfig/config.el index 318ff7dc2..82d4966f0 100644 --- a/modules/tools/editorconfig/config.el +++ b/modules/tools/editorconfig/config.el @@ -21,11 +21,12 @@ :after-call (doom-before-switch-buffer after-find-file) :config ;; Register missing indent variables - (setq editorconfig-indentation-alist - (append '((mips-mode mips-tab-width) - (haxor-mode haxor-tab-width) - (nasm-mode nasm-basic-offset)) - editorconfig-indentation-alist)) + (unless (assq 'mips-mode editorconfig-indentation-alist) + (setq editorconfig-indentation-alist + (append '((mips-mode mips-tab-width) + (haxor-mode haxor-tab-width) + (nasm-mode nasm-basic-offset)) + editorconfig-indentation-alist))) (defun doom*editorconfig-smart-detection (orig-fn &rest args) "Retrieve the properties for the current file. If it doesn't have an @@ -51,7 +52,8 @@ extension, try to guess one." ;; editorconfig to ignore indentation there. I prefer dynamic indentation ;; support built into Emacs. (dolist (mode '(emacs-lisp-mode lisp-mode)) - (map-delete editorconfig-indentation-alist mode)) + (delq (assq mode editorconfig-indentation-alist) + editorconfig-indentation-alist)) ;; (editorconfig-mode +1)) diff --git a/modules/tools/ein/autoload.el b/modules/tools/ein/autoload.el index 303deb947..3796e2a22 100644 --- a/modules/tools/ein/autoload.el +++ b/modules/tools/ein/autoload.el @@ -1,5 +1,6 @@ ;;; tools/ein/autoload.el -*- lexical-binding: t; -*- +;; FIXME obsolete :ein-notebook-dir ;;;###autoload (def-setting! :ein-notebook-dir (dir) "Set the default directory from where to open Jupyter notebooks." @@ -33,4 +34,47 @@ (goto-char (1+ res)) (widget-button-press (point))))) - +;;;###autoload (autoload '+ein-hydra/body "tools/ein/autoload" nil nil) +(defhydra +ein-hydra (:hint t :color red) + " + Operations on Cells^^^^^^ Other + ----------------------------^^^^^^ ----------------------------------^^^^ + [_k_/_j_]^^ select prev/next [_t_]^^ toggle output + [_K_/_J_]^^ move up/down [_C-l_/_C-S-l_] clear/clear all output + [_C-k_/_C-j_]^^ merge above/below [_C-o_]^^ open console + [_O_/_o_]^^ insert above/below [_C-s_/_C-r_] save/rename notebook + [_y_/_p_/_d_] copy/paste [_x_]^^ close notebook + [_u_]^^^^ change type [_q_]^^ quit + [_RET_]^^^^ execute +" + ("q" nil :exit t) + ("h" ein:notebook-worksheet-open-prev-or-last) + ("j" ein:worksheet-goto-next-input) + ("k" ein:worksheet-goto-prev-input) + ("l" ein:notebook-worksheet-open-next-or-first) + ("H" ein:notebook-worksheet-move-prev) + ("J" ein:worksheet-move-cell-down) + ("K" ein:worksheet-move-cell-up) + ("L" ein:notebook-worksheet-move-next) + ("t" ein:worksheet-toggle-output) + ("d" ein:worksheet-kill-cell) + ("R" ein:worksheet-rename-sheet) + ("y" ein:worksheet-copy-cell) + ("p" ein:worksheet-yank-cell) + ("o" ein:worksheet-insert-cell-below) + ("O" ein:worksheet-insert-cell-above) + ("u" ein:worksheet-change-cell-type) + ("RET" ein:worksheet-execute-cell-and-goto-next) + ;; Output + ("C-l" ein:worksheet-clear-output) + ("C-S-l" ein:worksheet-clear-all-output) + ;;Console + ("C-o" ein:console-open :exit t) + ;; Merge and split cells + ("C-k" ein:worksheet-merge-cell) + ("C-j" spacemacs/ein:worksheet-merge-cell-next) + ("s" ein:worksheet-split-cell-at-point) + ;; Notebook + ("C-s" ein:notebook-save-notebook-command) + ("C-r" ein:notebook-rename-command) + ("x" ein:notebook-close :exit t)) diff --git a/modules/tools/ein/config.el b/modules/tools/ein/config.el index 0c92dcf59..97126600d 100644 --- a/modules/tools/ein/config.el +++ b/modules/tools/ein/config.el @@ -40,49 +40,4 @@ ;; Ace-link on notebook list buffers (after! ein-notebooklist - (define-key ein:notebooklist-mode-map "o" #'+ein/ace-link-ein)) - - ;; add hydra - (defhydra +ein/hydra (:hint t :color red) - " - Operations on Cells^^^^^^ Other - ----------------------------^^^^^^ ----------------------------------^^^^ - [_k_/_j_]^^ select prev/next [_t_]^^ toggle output - [_K_/_J_]^^ move up/down [_C-l_/_C-S-l_] clear/clear all output - [_C-k_/_C-j_]^^ merge above/below [_C-o_]^^ open console - [_O_/_o_]^^ insert above/below [_C-s_/_C-r_] save/rename notebook - [_y_/_p_/_d_] copy/paste [_x_]^^ close notebook - [_u_]^^^^ change type [_q_]^^ quit - [_RET_]^^^^ execute -" - ("q" nil :exit t) - ("h" ein:notebook-worksheet-open-prev-or-last) - ("j" ein:worksheet-goto-next-input) - ("k" ein:worksheet-goto-prev-input) - ("l" ein:notebook-worksheet-open-next-or-first) - ("H" ein:notebook-worksheet-move-prev) - ("J" ein:worksheet-move-cell-down) - ("K" ein:worksheet-move-cell-up) - ("L" ein:notebook-worksheet-move-next) - ("t" ein:worksheet-toggle-output) - ("d" ein:worksheet-kill-cell) - ("R" ein:worksheet-rename-sheet) - ("y" ein:worksheet-copy-cell) - ("p" ein:worksheet-yank-cell) - ("o" ein:worksheet-insert-cell-below) - ("O" ein:worksheet-insert-cell-above) - ("u" ein:worksheet-change-cell-type) - ("RET" ein:worksheet-execute-cell-and-goto-next) - ;; Output - ("C-l" ein:worksheet-clear-output) - ("C-S-l" ein:worksheet-clear-all-output) - ;;Console - ("C-o" ein:console-open :exit t) - ;; Merge and split cells - ("C-k" ein:worksheet-merge-cell) - ("C-j" spacemacs/ein:worksheet-merge-cell-next) - ("s" ein:worksheet-split-cell-at-point) - ;; Notebook - ("C-s" ein:notebook-save-notebook-command) - ("C-r" ein:notebook-rename-command) - ("x" ein:notebook-close :exit t))) + (define-key ein:notebooklist-mode-map "o" #'+ein/ace-link-ein))) diff --git a/modules/tools/password-store/autoload.el b/modules/tools/password-store/autoload.el index 14b644ee8..856b984ce 100644 --- a/modules/tools/password-store/autoload.el +++ b/modules/tools/password-store/autoload.el @@ -64,6 +64,7 @@ search of your username. May prompt for your gpg passphrase." ;; Commands ;; +;;;###autoload (autoload 'password-store-dir "password-store") ;;;###autoload (autoload 'password-store-list "password-store") ;;;###autoload (autoload 'password-store--completing-read "password-store") @@ -113,6 +114,7 @@ fields in `+pass-url-fields' is used to find the url field." ;; Ivy interface ;; +;;;###autoload (defun +pass/ivy (arg) "TODO" (interactive "P") diff --git a/modules/tools/password-store/config.el b/modules/tools/password-store/config.el index abc6db2c6..bf982a1d2 100644 --- a/modules/tools/password-store/config.el +++ b/modules/tools/password-store/config.el @@ -34,7 +34,7 @@ "k" #'pass-prev-entry "d" #'pass-kill "\C-j" #'pass-next-directory - "\C-k" #'pass-next-directory)) + "\C-k" #'pass-prev-directory)) ;; Is built into Emacs 26+ diff --git a/modules/tools/prodigy/autoload.el b/modules/tools/prodigy/autoload.el index 6f27d40df..01d255136 100644 --- a/modules/tools/prodigy/autoload.el +++ b/modules/tools/prodigy/autoload.el @@ -1,5 +1,6 @@ ;;; tools/prodigy/autoload.el -*- lexical-binding: t; -*- +;; FIXME obsolete :service ;;;###autoload (def-setting! :service (&rest plist) "TODO" diff --git a/modules/tools/rgb/autoload.el b/modules/tools/rgb/autoload.el new file mode 100644 index 000000000..96647de20 --- /dev/null +++ b/modules/tools/rgb/autoload.el @@ -0,0 +1,13 @@ +;;; tools/rgb/autoload.el -*- lexical-binding: t; -*- + +;;;###autoload (autoload '+rgb-kurecolor-hydra/body "tools/rgb/autoload" nil nil) +(defhydra +rgb-kurecolor-hydra (:color pink :hint nil) + " +Inc/Dec _w_/_W_ brightness _d_/_D_ saturation _e_/_E_ hue " + ("w" kurecolor-decrease-brightness-by-step) + ("W" kurecolor-increase-brightness-by-step) + ("d" kurecolor-decrease-saturation-by-step) + ("D" kurecolor-increase-saturation-by-step) + ("e" kurecolor-decrease-hue-by-step) + ("E" kurecolor-increase-hue-by-step) + ("q" nil "cancel" :color blue)) diff --git a/modules/tools/rgb/config.el b/modules/tools/rgb/config.el deleted file mode 100644 index 08a93d06a..000000000 --- a/modules/tools/rgb/config.el +++ /dev/null @@ -1,19 +0,0 @@ -;;; tools/rgb/config.el -*- lexical-binding: t; -*- - -;; -;; Plugins -;; - -(def-package! kurecolor - :after rainbow-mode - :config - (defhydra +rgb@kurecolor (:color pink :hint nil) - " -Inc/Dec _w_/_W_ brightness _d_/_D_ saturation _e_/_E_ hue " - ("w" kurecolor-decrease-brightness-by-step) - ("W" kurecolor-increase-brightness-by-step) - ("d" kurecolor-decrease-saturation-by-step) - ("D" kurecolor-increase-saturation-by-step) - ("e" kurecolor-decrease-hue-by-step) - ("E" kurecolor-increase-hue-by-step) - ("q" nil "cancel" :color blue))) diff --git a/modules/tools/wakatime/autoload.el b/modules/tools/wakatime/autoload.el index 3babe8bb5..269b8e259 100644 --- a/modules/tools/wakatime/autoload.el +++ b/modules/tools/wakatime/autoload.el @@ -1,17 +1,62 @@ ;;; tools/wakatime/autoload.el -*- lexical-binding: t; -*- -;;;###autoload -(add-hook 'doom-after-switch-buffer-hook #'+wakatime|autostart) +(defvar +wakatime-api-file (concat doom-cache-dir "wakatime.el") + "Where the wakatime api key is cached.") + +(defvar +wakatime-hide-filenames nil + "If non-nil, obfuscate files and only show what projects you're working on.") ;;;###autoload -(defalias '+wakatime/start '+wakatime|autostart) +(add-hook 'doom-post-init-hook #'+wakatime|delayed-autostart) ;;;###autoload -(defun +wakatime|autostart () +(defun +wakatime/setup () + "Setup Wakatime in Emacs and start `global-wakatime-mode'. + +This will prompt you for your api key. You only need to run this when your api +changes." + (interactive) + (when (y-or-n-p "No API key is registered. Open a browser on the wakatime api key page?") + (browse-url "https://wakatime.com/settings/api-key")) + (let ((api-key (read-string "Enter your wakatime API key: "))) + (unless api-key + (user-error "No api key was received.")) + (setq wakatime-api-key api-key) + (with-temp-file +wakatime-api-file + (prin1 `(setq wakatime-api-key ,wakatime-api-key) + (current-buffer))) + (require 'wakatime-mode) + (unless (or (and wakatime-cli-path (file-executable-p wakatime-cli-path)) + (not (equal (wakatime-find-binary "wakatime") "wakatime"))) + (user-error "Couldn't find wakatime executable (%s)" + (or wakatime-cli-path "wakatime"))) + (global-wakatime-mode +1) + (message "Wakatime enabled. You're good to go!"))) + +;;;###autoload +(defun +wakatime|autostart (&rest _) "Initialize wakatime (if `wakatime-api-key' is set, otherwise no-op with a warning)." (interactive) - (if (boundp 'wakatime-api-key) + (unless (bound-and-true-p wakatime-api-key) + (ignore-errors (load +wakatime-api-file t t))) + (if (bound-and-true-p wakatime-api-key) (global-wakatime-mode +1) - (message "No `wakatime-api-key' set! wakaktime-mode will stay disabled.")) - (remove-hook 'doom-after-switch-buffer-hook #'+wakatime-init)) + (message "wakatime-mode isn't set up. Run `M-x +wakatime/start' to do so.")) + ;; + (remove-hook 'doom-before-switch-buffer-hook #'+wakatime|autostart) + (advice-remove 'after-find-file #'+wakatime|autostart)) + +;;;###autoload +(defun +wakatime|delayed-autostart (&rest _) + "Lazily initialize `wakatime-mode' until the next time you switch buffers or +open a file." + (add-hook 'doom-before-switch-buffer-hook #'+wakatime|autostart) + ;; this is necessary in case the user opens emacs with file arguments + (advice-add 'after-find-file :before #'+wakatime|autostart)) + +(defun +wakatime*append-hide-filenames-option (ret) + "Enables filename obfuscation in wakatime if `+wakatime-hide-filenames' is +non-nil." + (concat ret (if +wakatime-hide-filenames " --hide-filenames"))) +(advice-add #'wakatime-client-command :filter-return #'+wakatime*append-hide-filenames-option ) diff --git a/modules/ui/popup/+hacks.el b/modules/ui/popup/+hacks.el index 1314d5f6d..b8c4fb0cc 100644 --- a/modules/ui/popup/+hacks.el +++ b/modules/ui/popup/+hacks.el @@ -234,10 +234,7 @@ instead of switch-to-buffer-*." (defun +popup*org-pop-to-buffer (orig-fn buf &optional norecord) "Use `pop-to-buffer' instead of `switch-to-buffer' to open buffer.'" (if +popup-mode - (pop-to-buffer - (cond ((stringp buf) (get-buffer-create buf)) - ((bufferp buf) buf) - (t (error "Invalid buffer %s" buf)))) + (pop-to-buffer buf nil norecord) (funcall orig-fn buf norecord))) (advice-add #'org-switch-to-buffer-other-window :around #'+popup*org-pop-to-buffer) diff --git a/modules/ui/popup/autoload/popup.el b/modules/ui/popup/autoload/popup.el index b3ad9fdf2..417a5cba2 100644 --- a/modules/ui/popup/autoload/popup.el +++ b/modules/ui/popup/autoload/popup.el @@ -33,6 +33,7 @@ the buffer is visible, then set another timer and try again later." default window parameters for popup windows, clears leftover transient timers and enables `+popup-buffer-mode'." (with-selected-window window + (setq alist (delq (assq 'actions alist) alist)) (when (and alist +popup--populate-wparams) ;; Emacs 26+ will automatically map the window-parameters alist entry to ;; the popup window, so we need this for Emacs 25.x users @@ -58,58 +59,63 @@ and enables `+popup-buffer-mode'." `transient' window parameter (see `+popup-window-parameters'). + And finally deletes the window!" (let ((buffer (window-buffer window)) - (inhibit-quit t) - ttl) - (when (and (buffer-file-name buffer) - (buffer-modified-p buffer) - (or (+popup-parameter-fn 'autosave window buffer) - (y-or-n-p "Popup buffer is modified. Save it?"))) - (with-current-buffer buffer (save-buffer))) - (set-buffer-modified-p nil) + (inhibit-quit t)) + (and (buffer-file-name buffer) + (buffer-modified-p buffer) + (let ((autosave (+popup-parameter 'autosave window))) + (cond ((eq autosave 't)) + ((null autosave) + (y-or-n-p "Popup buffer is modified. Save it?")) + ((functionp autosave) + (funcall autosave buffer)))) + (with-current-buffer buffer (save-buffer))) (let ((ignore-window-parameters t)) - (delete-window window)) + (if-let* ((wconf (window-parameter window 'saved-wconf))) + (set-window-configuration wconf) + (delete-window window))) (unless (window-live-p window) (with-current-buffer buffer + (set-buffer-modified-p nil) (+popup-buffer-mode -1) - ;; t = default - ;; integer = ttl - ;; nil = no timer (unless +popup--inhibit-transient - (setq ttl (+popup-parameter-fn 'ttl window buffer)) - (when ttl - (when (eq ttl t) - (setq ttl (or (plist-get +popup-defaults :ttl) - 0))) - (cl-assert (integerp ttl) t) - (if (= ttl 0) - (+popup--kill-buffer buffer 0) - (add-hook 'kill-buffer-hook #'+popup|kill-buffer-hook nil t) - (setq +popup--timer - (run-at-time ttl nil #'+popup--kill-buffer - buffer ttl))))))))) + (let ((ttl (+popup-parameter 'ttl window))) + (when (eq ttl 't) + (setq ttl (plist-get +popup-defaults :ttl))) + (cond ((null ttl)) + ((functionp ttl) + (funcall ttl buffer)) + ((not (integerp ttl)) + (signal 'wrong-type-argument (list 'integerp ttl))) + ((= ttl 0) + (+popup--kill-buffer buffer 0)) + ((add-hook 'kill-buffer-hook #'+popup|kill-buffer-hook nil t)) + ((setq +popup--timer + (run-at-time ttl nil #'+popup--kill-buffer + buffer ttl)))))))))) (defun +popup--normalize-alist (alist) "Merge `+popup-default-alist' and `+popup-default-parameters' with ALIST." - (let ((alist ; handle defaults - (cl-remove-duplicates - (append alist +popup-default-alist) - :key #'car :from-end t)) - (parameters - (cl-remove-duplicates - (append (cdr (assq 'window-parameters alist)) - +popup-default-parameters) - :key #'car :from-end t))) - ;; handle `size' - (when-let* ((size (cdr (assq 'size alist))) - (side (or (cdr (assq 'side alist)) 'bottom)) - (param (if (memq side '(left right)) - 'window-width - 'window-height))) - (setq alist (map-delete alist 'size)) - (map-put alist param size)) - (setcdr (assq 'window-parameters alist) - (cl-remove-if #'null parameters :key #'cdr)) - (cl-remove-if #'null alist :key #'cdr))) + (when alist + (let ((alist ; handle defaults + (cl-remove-duplicates + (append alist +popup-default-alist) + :key #'car :from-end t)) + (parameters + (cl-remove-duplicates + (append (cdr (assq 'window-parameters alist)) + +popup-default-parameters) + :key #'car :from-end t))) + ;; handle `size' + (when-let* ((size (cdr (assq 'size alist))) + (side (or (cdr (assq 'side alist)) 'bottom)) + (param (if (memq side '(left right)) + 'window-width + 'window-height))) + (setq list (assq-delete-all 'size alist)) + (setcdr (assq param alist) size)) + (setcdr (assq 'window-parameters alist) + parameters) + alist))) ;; @@ -120,23 +126,21 @@ and enables `+popup-buffer-mode'." (defun +popup-buffer-p (&optional buffer) "Return non-nil if BUFFER is a popup buffer. Defaults to the current buffer." (when +popup-mode - (unless buffer - (setq buffer (current-buffer))) - (cl-assert (bufferp buffer) t) - (and (buffer-live-p buffer) - (buffer-local-value '+popup-buffer-mode buffer) - buffer))) + (let ((buffer (or buffer (current-buffer)))) + (and (bufferp buffer) + (buffer-live-p buffer) + (buffer-local-value '+popup-buffer-mode buffer) + buffer)))) ;;;###autoload (defun +popup-window-p (&optional window) "Return non-nil if WINDOW is a popup window. Defaults to the current window." (when +popup-mode - (unless window - (setq window (selected-window))) - (cl-assert (windowp window) t) - (and (window-live-p window) - (window-parameter window 'popup) - window))) + (let ((window (or window (selected-window)))) + (and (windowp window) + (window-live-p window) + (window-parameter window 'popup) + window)))) ;;;###autoload (defun +popup-buffer (buffer &optional alist) @@ -216,11 +220,14 @@ restoring it if `+popup-buffer-mode' is disabled." + If a function, it takes the current buffer as its argument and must return one of the above values." (when (bound-and-true-p +popup-buffer-mode) - (let ((modeline (+popup-parameter-fn 'modeline nil (current-buffer)))) + (let ((modeline (+popup-parameter 'modeline))) (cond ((eq modeline 't)) ((or (eq modeline 'nil) (null modeline)) + ;; TODO use `mode-line-format' window parameter instead (emacs 26+) (hide-mode-line-mode +1)) + ((functionp modeline) + (funcall modeline)) ((symbolp modeline) (when-let* ((hide-mode-line-format (doom-modeline modeline))) (hide-mode-line-mode +1))))))) @@ -246,9 +253,9 @@ restoring it if `+popup-buffer-mode' is disabled." (defun +popup|cleanup-rules () "Cleans up any duplicate popup rules." (interactive) - (cl-delete-duplicates - +popup--display-buffer-alist - :key #'car :test #'equal :from-end t) + (setq +popup--display-buffer-alist + (cl-delete-duplicates +popup--display-buffer-alist + :key #'car :test #'equal :from-end t)) (when +popup-mode (setq display-buffer-alist +popup--display-buffer-alist))) @@ -291,16 +298,15 @@ This will do nothing if the popup's `quit' window parameter is either nil or (interactive (list (selected-window) current-prefix-arg)) - (unless window - (setq window (selected-window))) - (when (and (+popup-window-p window) - (or force-p - (memq (+popup-parameter-fn 'quit window window) - '(t current)))) - (when +popup--remember-last - (+popup--remember (list window))) - (delete-window window) - t)) + (let ((window (or window (selected-window)))) + (when (and (+popup-window-p window) + (or force-p + (memq (+popup-parameter-fn 'quit window window) + '(t current)))) + (when +popup--remember-last + (+popup--remember (list window))) + (delete-window window) + t))) ;;;###autoload (defun +popup/close-all (&optional force-p) @@ -372,6 +378,19 @@ the message buffer in a popup window." prevent the popup(s) from messing up the UI (or vice versa)." (save-popups! (apply orig-fn args))) +;;;###autoload +(defun +popup-display-buffer-fullframe (buffer alist) + "Displays the buffer fullscreen." + (let ((wconf (current-window-configuration))) + (when-let (window (or (display-buffer-reuse-window buffer alist) + (display-buffer-same-window buffer alist) + (display-buffer-pop-up-window buffer alist) + (display-buffer-use-some-window buffer alist))) + (set-window-parameter window 'saved-wconf wconf) + (add-to-list 'window-persistent-parameters '(saved-wconf . t)) + (delete-other-windows window) + window))) + ;;;###autoload (defun +popup-display-buffer-stacked-side-window (buffer alist) "A `display-buffer' action that serves as an alternative to @@ -426,7 +445,7 @@ Accepts the same arguments as `display-buffer-in-side-window'. You must set (lambda (_side) (frame-root-window (selected-frame))))) (when-let* ((window (window--make-major-side-window buffer side slot alist))) (set-window-parameter window 'window-vslot vslot) - (map-put window-persistent-parameters 'window-vslot 'writable) + (add-to-list 'window-persistent-parameters '(window-vslot . writable)) window))) (t ;; Scan windows on SIDE. @@ -570,11 +589,11 @@ and may be called only if no window on SIDE exists yet." ;; Initialize `window-side' parameter of new window to SIDE and ;; make that parameter persistent. (set-window-parameter window 'window-side side) - (map-put window-persistent-parameters 'window-side 'writable) + (add-to-list 'window-persistent-parameters '(window-side . writable)) ;; Install `window-slot' parameter of new window and make that ;; parameter persistent. (set-window-parameter window 'window-slot slot) - (map-put window-persistent-parameters 'window-slot 'writable) + (add-to-list 'window-persistent-parameters '(window-slot . writable)) ;; Auto-adjust height/width of new window unless a size has been ;; explicitly requested. (unless (if left-or-right diff --git a/modules/ui/popup/autoload/settings.el b/modules/ui/popup/autoload/settings.el index 0ff8d28b9..9f98b53c9 100644 --- a/modules/ui/popup/autoload/settings.el +++ b/modules/ui/popup/autoload/settings.el @@ -10,11 +10,11 @@ :quit t :select #'ignore :ttl 5) - "Default setup for `set-popup-rule!' ") + "Default properties for popup rules defined with `set-popup-rule!'.") ;;;###autoload (defun +popup--make (predicate plist) - (cond ((not (keywordp (car plist))) + (cond ((and plist (not (keywordp (car plist)))) ;; FIXME deprecated popup rule support (message "Warning: the old usage of `set-popup-rule!' is deprecated; update the rule for '%s'" predicate) @@ -51,14 +51,13 @@ (defun set-popup-rule! (predicate &rest plist) "Define a popup rule. -Buffers displayed by `pop-to-buffer' and `display-buffer' (or their siblings) -will be tested against PREDICATE, which is either a) a regexp string (which is -matched against the buffer's name) or b) a function that takes no arguments and -returns a boolean. +These rules affect buffers displayed with `pop-to-buffer' and `display-buffer' +(or their siblings). Buffers displayed with `switch-to-buffer' (and its +variants) will not be affected by these rules (as they are unaffected by +`display-buffer-alist', which powers the popup management system). -Buffers displayed with `switch-to-buffer' and its variants will not be affected -by these rules (as they are unaffected by `display-buffer-alist', which powers -the popup management system). +PREDICATE can be either a) a regexp string (matched against the buffer's name) +or b) a function that takes no arguments and returns a boolean. PLIST can be made up of any of the following properties: @@ -72,83 +71,93 @@ PLIST can be made up of any of the following properties: `+popup-display-buffer-stacked-side-window' or `display-buffer-in-side-window' is in :actions or `+popup-default-display-buffer-actions'. -:size/:width/:height FLOAT|INT - Determines the size of the popup. If opened at the top or bottom, the width is - irrelevant unless it is opened in an adjacent slot. Same deal with the left - and right side. +:size/:width/:height FLOAT|INT|FN + Determines the size of the popup. If more tha one of these size properties are + given :size always takes precedence, and is mapped with window-width or + window-height depending on what :side the popup is opened. Setting a height + for a popup that opens on the left or right is harmless, but comes into play + if two popups occupy the same :vslot. - If given a FLOAT (0 < x < 1), the number represents how much of the window - will be consumed by the popup (a percentage). - If given an INT, the number determines the size in lines (height) or units of + If a FLOAT (0 < x < 1), the number represents how much of the window will be + consumed by the popup (a percentage). + If an INT, the number determines the size in lines (height) or units of character width (width). + If a function, it takes one argument: the popup window, and can do whatever it + wants with it, typically resize it, like `+popup-shrink-to-fit'. :slot/:vslot INT - This only applies to popups with a :side. For popups opened at the top or - bottom, slot designates the horizontal positioning of a popup. If two popups - are assigned the same slot (and same vslot), the later popup will replace the - earlier one. If the later popup has a lower slot, it will open to the older - popup's left. A higher slot opens it to the old popup's right. + (This only applies to popups with a :side and only if :actions is blank or + contains the `+popup-display-buffer-stacked-side-window' action) These control + how multiple popups are laid out. INT can be any integer, positive and + negative. - On the other hand, vslot operates the same way, but controls how popups are - stacked. + :slot controls lateral positioning (e.g. the horizontal positioning for + top/bottom popups, or vertical positioning for left/right popups). + :vslot controls popup stacking (from the edge of the frame toward the center). - When a popup is opened on the left and right, slot determines vertical - position and vslot horizontal. + Let's assume popup A and B are opened with :side 'bottom, in that order. + If they possess the same :slot and :vslot, popup B will replace popup A. + If popup B has a higher :slot, it will open to the right of popup A. + If popup B has a lower :slot, it will open to the left of popup A. + If popup B has a higher :vslot, it will open above popup A. + If popup B has a lower :vslot, it will open below popup A. :ttl INT|BOOL|FN - Stands for time-to-live. CDR can be t, an integer, nil or a function that - returns one of these. It represents the number of seconds before the buffer - belonging to a closed popup window is killed. + Stands for time-to-live. It can be t, an integer, nil or a function. This + controls how (and if) the popup system will clean up after the popup. - If t, CDR will default to `+popup-ttl'. + If any non-zero integer, wait that many seconds before killing the buffer (and + any associated processes). If 0, the buffer is immediately killed. - If nil, the buffer won't be killed. - If a function, it must return one of the other possible values above. It takes - the popup buffer as its sole argument. + If nil, the buffer won't be killed and is left to its own devices. + If t, resort to the default :ttl in `+popup-defaults'. If none exists, this is + the same as nil. + If a function, it takes one argument: the target popup buffer. The popup + system does nothing else and ignores the function's return value. -:quit BOOL|FN - CDR can be t, 'other, 'current, nil, or a function that returns one of these. - This determines the behavior of the ESC/C-g keys in or outside of popup - windows. +:quit FN|BOOL|'other|'current + Can be t, 'other, 'current, nil, or a function. This determines the behavior + of the ESC/C-g keys in or outside of popup windows. - If t, close the popup if ESC/C-g is pressed inside or outside of popups. + If t, close the popup if ESC/C-g is pressed anywhere. If 'other, close this popup if ESC/C-g is pressed outside of any popup. This - is great for popups you just want to peek at and discard, but might also - want to poke around in, without the risk of closing it from the inside. + is great for popups you may press ESC/C-g a lot in. If 'current, close the current popup if ESC/C-g is pressed from inside of the - popup. - If nil, pressing ESC/C-g will never close this buffer. - If a function, it is checked each time ESC/C-g is pressed to determine the - fate of the popup window. This function takes one argument: the popup window - and must return one of the other possible values. + popup. This makes it harder to accidentally close a popup until you really + want to. + If nil, pressing ESC/C-g will never close this popup. + If a function, it takes one argument: the to-be-closed popup window, and is + run when ESC/C-g is pressed while that popup is open. It must return one of + the other values to determine the fate of the popup. :select BOOL|FN - CDR can be a boolean or function. The boolean determines whether to focus the + Can be a boolean or function. The boolean determines whether to focus the popup window after it opens (non-nil) or focus the origin window (nil). - If a function, it takes two arguments: the popup window and the source window - (where you were before the popup was opened). It does nothing else, and - ignores its return value. + If a function, it takes two arguments: the popup window and originating window + (where you were before the popup opened). The popup system does nothing else + and ignores the function's return value. :modeline BOOL|SYMBOL|FN - CDR can be t (show the default modeline), a symbol representing the name of a + Can be t (show the default modeline), a symbol representing the name of a modeline defined with `def-modeline!', nil (show no modeline) or a function - that returns one of these. The function takes one argument: the popup buffer. + that returns a modeline format. The function takes no arguments and is run in + the context of the popup buffer. :autosave BOOL|FN - This parameter determines what to do with modified buffers in closing popup - windows. CDR can be a t, 'ignore, a function or nil. + This parameter determines what to do with modified buffers when closing popup + windows. It accepts t, 'ignore, a function or nil. If t, no prompts. Just save them automatically (if they're file-visiting - buffers). - If 'ignore, no prompts, no saving. Just silently kill it. + buffers). Same as 'ignore for non-file-visiting buffers. If nil (the default), prompt the user what to do if the buffer is file-visiting and modified. - If a function, the return value must return one of the other values. It takes - two arguments: the popup window and buffer. + If 'ignore, no prompts, no saving. Just silently kill it. + If a function, it is run with one argument: the popup buffer, and must return + non-nil to save or nil to do nothing (but no prompts). :parameters ALIST - An alist of custom window parameters. See \(info window-parameters) + An alist of custom window parameters. See `(elisp)Window Parameters'. If any of these are omitted, defaults derived from `+popup-defaults' will be used." @@ -160,9 +169,11 @@ used." ;;;###autodef (defun set-popup-rules! (&rest rulesets) - "Like `set-popup-rules!', but defines multiple popup rules. Every entry in RULESETS -should be a list of lists (each sublist is a popup rule that could be passed to -`set-popup-rule!'). + "Defines multiple popup rules. + +Every entry in RULESETS should be a list of alists where the CAR is the +predicate and CDR is a plist. See `set-popup-rule!' for details on the predicate +and plist. Example: diff --git a/modules/ui/popup/config.el b/modules/ui/popup/config.el index b5b6bccd2..b07ebe2b6 100644 --- a/modules/ui/popup/config.el +++ b/modules/ui/popup/config.el @@ -70,8 +70,8 @@ adjustment.") window--sides-inhibit-check nil) (+popup|cleanup-rules) (dolist (prop +popup-window-parameters) - (setq window-persistent-parameters - (map-delete window-persistent-parameters prop)))))) + (delq (assq prop window-persistent-parameters) + window-persistent-parameters))))) (define-minor-mode +popup-buffer-mode "Minor mode for individual popup windows. @@ -103,7 +103,7 @@ should match the arguments of `+popup-define' or the :popup setting." (declare (indent defun)) `(let ((+popup--display-buffer-alist +popup--old-display-buffer-alist) display-buffer-alist) - (set-popup-rules! ,@rules) + (set-popup-rules! ,rules) (when (bound-and-true-p +popup-mode) (setq display-buffer-alist +popup--display-buffer-alist)) ,@body)) diff --git a/modules/ui/popup/test/test-popup.el b/modules/ui/popup/test/test-popup.el new file mode 100644 index 000000000..332a2fded --- /dev/null +++ b/modules/ui/popup/test/test-popup.el @@ -0,0 +1,212 @@ +;; -*- no-byte-compile: t; -*- +;;; ui/popup/test/test-popup.el + +(describe "ui/popup" + :var (display-buffer-alist + +popup-default-display-buffer-actions + +popup--display-buffer-alist + +popup-defaults + wconf) + + (before-all + (require! :ui popup) + (delete-other-windows) + (switch-to-buffer "*scratch*") + (setq wconf (current-window-configuration)) + (+popup-mode +1)) + (after-all + (+popup-mode -1)) + + (before-each + (setq display-buffer-alist nil + +popup--display-buffer-alist nil + +popup-default-display-buffer-actions '(+popup-display-buffer-stacked-side-window) + +popup-defaults '(:side bottom :select ignore :ttl nil :slot 1 :vslot 1))) + (after-each + (set-window-configuration wconf)) + + (describe "set-popup-rule!" + (it "sets popup rules" + (set-popup-rule! "does-not-exist" :size 10) + (let ((rule (cdr (assoc "does-not-exist" display-buffer-alist)))) + (expect rule :to-contain '(+popup-buffer)) + (expect rule :to-contain '(size . 10)))) + (it "shadows old rules" + (set-popup-rule! "a" :size 10) + (set-popup-rule! "a" :size 20) + (expect (cdr (assoc "a" display-buffer-alist)) + :to-contain '(size . 20))) + (it "resolves to defaults" + (let ((+popup-defaults '(:size 5))) + (set-popup-rule! "a") + (expect (cdr (assoc "a" display-buffer-alist)) + :to-contain '(size . 5))))) + + (describe "popup rules" + :var (origin a b c d e f g) + (before-all (setq origin (current-buffer))) + (before-each + (dolist (name '(a b c d e f g)) + (set name (get-buffer-create (symbol-name name))))) + (after-each + (let (kill-buffer-query-functions kill-buffer-hook) + (dolist (x (list a b c d e f g)) + (ignore-errors (delete-window (get-buffer-window x))) + (kill-buffer x)))) + + (describe "slot positioning" + (before-each + (set-popup-rules! + '(("a" :slot 1 :vslot 1) + ("b" :slot 2 :vslot 1) + ("c" :slot 1 :vslot 2) + ("d" :slot 2 :vslot 2) + ("e" :slot 1 :vslot 3) + ("f" :slot 1 :vslot 3) + ("g")))) + + (it "replaces popups with the same slots" + (mapc #'display-buffer (list e f)) + (expect (length (+popup-windows)) :to-be 1)) + + (it "replaces popups among multiple that have the same slots" + (let ((first (display-buffer a)) + (second (display-buffer b)) + (third (display-buffer e)) + (fourth (display-buffer f))) + (expect (+popup-windows) :to-have-same-items-as + (list first second fourth)))) + + (describe ":slot" + (it "opens left of others if lower" + (let ((first (display-buffer b)) + (second (display-buffer a))) + (expect (length (+popup-windows)) :to-be 2) + (expect (window-in-direction 'left first t) + :to-equal second))) + (it "opens right of others if higher" + (let ((first (display-buffer a)) + (second (display-buffer b))) + (expect (length (+popup-windows)) :to-be 2) + (expect (window-in-direction 'right first t) + :to-equal second))) + (it "obeys default :slot" + (let ((window (display-buffer g))) + (expect (window-parameter window 'window-slot) :to-be 1) + (expect (window-parameter window 'window-vslot) :to-be 1)))) + + (describe ":vslot" + ;; TODO Implement this, somehow + (xit "opens lower :vslot popups above others" + (let ((first (display-buffer c)) + (second (display-buffer a))) + (expect (length (+popup-windows)) :to-be 2) + (expect (window-in-direction 'above first t) + :to-equal second))) + (it "opens higher :vslot popups below others" + (let ((first (display-buffer c)) + (second (display-buffer e))) + (expect (length (+popup-windows)) :to-be 2) + (expect (window-in-direction 'below first t) + :to-equal second))))) + + (describe ":select" + (it "selects the popup if non-nil" + (set-popup-rule! "^a$" :select t) + (display-buffer a) + (expect (current-buffer) :to-equal a)) + (it "selects the originating window if nil" + (set-popup-rule! "^a$" :select nil) + (display-buffer a) + (expect (current-buffer) :to-equal origin)) + (it "fall back to base selection if passed #'ignore" + (spy-on 'ignore) + (set-popup-rule! "^a$" :select #'ignore) + (save-window-excursion + (display-buffer a) + (expect (current-buffer) :to-equal origin)) + (save-window-excursion + (pop-to-buffer a) + (expect (current-buffer) :to-equal a)) + (expect 'ignore :to-have-been-called-times 2))) + + (describe ":modeline" + (it "disables the mode-line if nil" + (set-popup-rule! "a" :modeline nil :select t) + (display-buffer a) + (expect mode-line-format :to-be nil)) + (it "uses the default mode-line if t" + (set-popup-rule! "a" :modeline t :select t) + (display-buffer a) + (expect mode-line-format :to-equal (default-value 'mode-line-format))) + (it "uses a predefined mode-line if passed a symbol" + (def-modeline! test-popup-modeline ("x") ()) + (set-popup-rule! "a" :modeline 'test-popup-modeline :select t) + (display-buffer a) + (expect mode-line-format :to-equal (doom-modeline 'test-popup-modeline))) + (it "runs the handler if passed a function" + (set-popup-rule! "a" :modeline (lambda () (setq mode-line-format '("x"))) :select t) + (display-buffer a) + (expect mode-line-format :to-equal '("x")))) + + ;; TODO + (xdescribe ":autosave") + + (describe ":quit" + (it "will close from anywhere if :quit = t" + (set-popup-rule! "a" :quit t) + (save-window-excursion + (display-buffer a) + (call-interactively #'+popup/close-all) + (expect (get-buffer-window a) :to-be nil)) + (save-window-excursion + (pop-to-buffer a) + (call-interactively #'+popup/close) + (expect (get-buffer-window a) :to-be nil))) + (it "will only close from outside if :quit = 'other" + (set-popup-rule! "a" :quit 'other) + (save-window-excursion + (display-buffer a) + (call-interactively #'+popup/close-all) + (expect (get-buffer-window a) :to-be nil)) + (save-window-excursion + (pop-to-buffer a) + (call-interactively #'+popup/close) + (expect (get-buffer-window a)))) + (it "will only close from inside if :quit = 'current" + (set-popup-rule! "a" :quit 'current) + (save-window-excursion + (display-buffer a) + (call-interactively #'+popup/close-all) + (expect (get-buffer-window a))) + (save-window-excursion + (pop-to-buffer a) + (call-interactively #'+popup/close) + (expect (get-buffer-window a) :to-be nil))) + (it "never close a if :quit = nil" + (set-popup-rule! "a" :quit nil) + (save-window-excursion + (display-buffer a) + (call-interactively #'+popup/close-all) + (expect (get-buffer-window a))) + (save-window-excursion + (pop-to-buffer a) + (call-interactively #'+popup/close) + (expect (get-buffer-window a))))) + + ;; TODO + (xdescribe ":ttl") + (xdescribe ":size") + (xdescribe ":width") + (xdescribe ":height") + (xdescribe ":side") + (xdescribe ":actions")) + + ;; TODO + (xdescribe "predicate functions" + (describe "buffer-p") + (describe "window-p")) + + ;; TODO + (xdescribe "save-popups!") + (xdescribe "with-popup-rules!")) diff --git a/modules/ui/pretty-code/autoload.el b/modules/ui/pretty-code/autoload.el index b11795f42..0ed207d93 100644 --- a/modules/ui/pretty-code/autoload.el +++ b/modules/ui/pretty-code/autoload.el @@ -28,6 +28,7 @@ besides what is listed.") :lambda "λ" :def "ƒ" :composition "∘" + :map "↦" ;; Types :null "∅" :true "𝕋" @@ -48,7 +49,8 @@ besides what is listed.") :yield "⟻" ;; Other :tuple "⨂" - :pipe "") + :pipe "" + :dot "•") "Options plist for `pretty-code-get-pairs'.") (defvar +pretty-code--iosevka-ligeratures-enabled nil) diff --git a/modules/ui/vc-gutter/autoload.el b/modules/ui/vc-gutter/autoload.el new file mode 100644 index 000000000..def6ed45b --- /dev/null +++ b/modules/ui/vc-gutter/autoload.el @@ -0,0 +1,26 @@ +;;; ui/vc-gutter/autoload.el -*- lexical-binding: t; -*- + +;;;###autoload (autoload '+vc-gutter-hydra/body "ui/vc-gutter/autoload" nil nil) +(defhydra +vc-gutter-hydra + (:body-pre (git-gutter-mode 1) :hint nil) + " + [git gutter] + Movement Hunk Actions Misc. +%-4s(car (git-gutter:statistic))/ -%-4s(cdr (git-gutter:statistic)) + ╭──────────────────────────────────┴────────────────╯ + ^_g_^ [_s_] stage [_R_] set start Rev + ^_k_^ [_r_] revert + ^↑ ^ [_m_] mark + ^↓ ^ [_p_] popup ╭───────────────────── + ^_j_^ │[_q_] quit + ^_G_^ │[_Q_] Quit and disable" + ("j" (progn (git-gutter:next-hunk 1) (recenter))) + ("k" (progn (git-gutter:previous-hunk 1) (recenter))) + ("g" (progn (goto-char (point-min)) (git-gutter:next-hunk 1))) + ("G" (progn (goto-char (point-min)) (git-gutter:previous-hunk 1))) + ("s" git-gutter:stage-hunk) + ("r" git-gutter:revert-hunk) + ("m" git-gutter:mark-hunk) + ("p" git-gutter:popup-hunk) + ("R" git-gutter:set-start-revision) + ("q" nil :color blue) + ("Q" (git-gutter-mode -1) :color blue)) diff --git a/modules/ui/vc-gutter/config.el b/modules/ui/vc-gutter/config.el index 5c8417ead..ee90b7a15 100644 --- a/modules/ui/vc-gutter/config.el +++ b/modules/ui/vc-gutter/config.el @@ -51,30 +51,6 @@ to the right fringe.") ;; update git-gutter when using these commands (add-hook 'magit-post-refresh-hook #'+version-control|update-git-gutter) - (defhydra +version-control@git-gutter - (:body-pre (git-gutter-mode 1) :hint nil) - " - ╭─────────────────┐ - Movement Hunk Actions Misc. │ gg: +%-4s(car (git-gutter:statistic))/ -%-3s(cdr (git-gutter:statistic)) │ - ╭──────────────────────────────────┴─────────────────╯ - ^_g_^ [_s_] stage [_R_] set start Rev - ^_k_^ [_r_] revert - ^↑ ^ [_m_] mark - ^↓ ^ [_p_] popup ╭────────────────────── - ^_j_^ │[_q_] quit - ^_G_^ │[_Q_] Quit and disable" - ("j" (progn (git-gutter:next-hunk 1) (recenter))) - ("k" (progn (git-gutter:previous-hunk 1) (recenter))) - ("g" (progn (goto-char (point-min)) (git-gutter:next-hunk 1))) - ("G" (progn (goto-char (point-min)) (git-gutter:previous-hunk 1))) - ("s" git-gutter:stage-hunk) - ("r" git-gutter:revert-hunk) - ("m" git-gutter:mark-hunk) - ("p" git-gutter:popup-hunk) - ("R" git-gutter:set-start-revision) - ("q" nil :color blue) - ("Q" (git-gutter-mode -1) :color blue)) - ;; subtle diff indicators in the fringe (when +vc-gutter-default-style ;; places the git gutter outside the margins.