diff --git a/modules/app/regex/config.el b/modules/app/regex/config.el index 539297e46..f48488d01 100644 --- a/modules/app/regex/config.el +++ b/modules/app/regex/config.el @@ -47,10 +47,6 @@ https://mediatemple.net" "TODO") (set-popup-rules! - '(("^\\*doom-regex\\*$" - ((size . 4)) - ((quit))) - ("^\\*doom-regex-groups" - ((side . left) (size . 28)) - ((select) (quit))))) + '(("^\\*doom-regex\\*$" :size 4 :quit nil) + ("^\\*doom-regex-groups" :side 'left :size 28 :select nil :quit nil))) diff --git a/modules/app/rss/config.el b/modules/app/rss/config.el index 4710079e5..abe04731e 100644 --- a/modules/app/rss/config.el +++ b/modules/app/rss/config.el @@ -27,8 +27,8 @@ absolute paths.") shr-max-image-proportion 0.6) (set-popup-rule! "^\\*elfeed-entry" - '((size . 0.75) (side . bottom)) - '((select . t) (quit) (transient . t))) + :size 0.75 :side 'bottom + :select t :quit nil :ttl t) (make-directory elfeed-db-directory t) diff --git a/modules/app/twitter/config.el b/modules/app/twitter/config.el index ed7075592..c3a869468 100644 --- a/modules/app/twitter/config.el +++ b/modules/app/twitter/config.el @@ -25,9 +25,7 @@ twittering-initial-timeline-spec-string '(":home" ":mentions" ":direct_messages")) - (set-popup-rule! "^\\*twittering-edit" - '((size . 15)) - '((transient) (quit) (select . t))) + (set-popup-rule! "^\\*twittering-edit" :size 15 :ttl nil :quit nil :select t) (defface twitter-divider '((((background dark)) (:underline (:color "#141519"))) diff --git a/modules/completion/helm/config.el b/modules/completion/helm/config.el index a4a8a4537..19dd7b65f 100644 --- a/modules/completion/helm/config.el +++ b/modules/completion/helm/config.el @@ -94,9 +94,7 @@ ;; `helm-ag' (after! helm-ag (define-key helm-ag-edit-map [remap quit-window] #'helm-ag--edit-abort) - (set-popup-rule! "^\\*helm-ag-edit" - '((size . 0.35)) - '((transient . 0) (quit)))) + (set-popup-rule! "^\\*helm-ag-edit" :size 0.35 :ttl 0 :quit nil)) ;; `helm-bookmark' diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el index b2d697f49..3661f8f60 100644 --- a/modules/completion/ivy/config.el +++ b/modules/completion/ivy/config.el @@ -86,7 +86,7 @@ immediately runs it on the current candidate (ending the ivy session)." [remap swiper] #'counsel-grep-or-swiper) :config - (set-popup-rule! "^\\*ivy-occur" '((size . 0.35)) '((transient . 0) (quit))) + (set-popup-rule! "^\\*ivy-occur" :size 0.35 :ttl 0 :quit nil) (setq counsel-find-file-ignore-regexp "\\(?:^[#.]\\)\\|\\(?:[#~]$\\)\\|\\(?:^Icon?\\)" ;; Add smart-casing and compressed archive searching (-zS) to default diff --git a/modules/emacs/imenu/config.el b/modules/emacs/imenu/config.el index 37cd79361..de82a401e 100644 --- a/modules/emacs/imenu/config.el +++ b/modules/emacs/imenu/config.el @@ -8,8 +8,7 @@ (setq imenu-list-idle-update-delay 0.5) (set-popup-rule! "^\\*Ilist" - '((side . right) (size . 35)) - '((quit . current) (select) (transient . 0))) + :side 'right :size 35 :quit 'current :select nil :ttl 0) (defun +imenu|cleanup-on-popup-close () "Clean up after `imenu-list-minor-mode' when killing the list window." diff --git a/modules/feature/debugger/config.el b/modules/feature/debugger/config.el index 77d0858e2..49d3e3856 100644 --- a/modules/feature/debugger/config.el +++ b/modules/feature/debugger/config.el @@ -3,8 +3,7 @@ (def-package! realgud :commands (realgud:gdb realgud:trepanjs realgud:bashdb realgud:zshdb) :config - (set-popup-rule! "^\\*\\(?trepanjs:\\(?:g\\|zsh\\|bash\\)db\\)" - '((size . 20))) + (set-popup-rule! "^\\*\\(?trepanjs:\\(?:g\\|zsh\\|bash\\)db\\)" :size 20) ;; TODO Temporary Ex commands for the debugger ;; (def-tmp-excmd! doom:def-debug-on doom:def-debug-off diff --git a/modules/feature/eval/config.el b/modules/feature/eval/config.el index 53545e4f0..f17bc335d 100644 --- a/modules/feature/eval/config.el +++ b/modules/feature/eval/config.el @@ -17,7 +17,7 @@ :config (setq quickrun-focus-p nil) - (set-popup-rule! "^\\*quickrun" '((size . 0.3)) '((transient . 0))) + (set-popup-rule! "^\\*quickrun" :size 0.3 :ttl 0) (defun +eval*quickrun-auto-close (&rest _) "Allows us to silently re-run quickrun from within the quickrun buffer." diff --git a/modules/feature/evil/config.el b/modules/feature/evil/config.el index 2cf3fa314..3435a4fb0 100644 --- a/modules/feature/evil/config.el +++ b/modules/feature/evil/config.el @@ -68,8 +68,8 @@ variable for an explanation of the defaults (in comments). See (put 'evil-define-key* 'lisp-indent-function 'defun) (set-popup-rules! - '(("^\\*evil-registers" ((size . 0.3))) - ("^\\*Command Line" ((size . 8))))) + '(("^\\*evil-registers" :size 0.3) + ("^\\*Command Line" :size 8))) ;; Change the cursor color in emacs mode (defvar +evil--default-cursor-color "#ffffff") diff --git a/modules/feature/version-control/+git.el b/modules/feature/version-control/+git.el index 534649369..5828a2157 100644 --- a/modules/feature/version-control/+git.el +++ b/modules/feature/version-control/+git.el @@ -20,7 +20,7 @@ (git-gutter-mode +1))) (add-hook! (text-mode prog-mode conf-mode) #'+version-control|git-gutter-maybe) :config - (set-popup-rule! "^\\*git-gutter" nil '((select))) + (set-popup-rule! "^\\*git-gutter" :select nil) ;; Update git-gutter on focus (in case I was using git externally) (add-hook 'focus-in-hook #'git-gutter:update-all-windows) diff --git a/modules/feature/version-control/config.el b/modules/feature/version-control/config.el index 353daedbd..d3d83f234 100644 --- a/modules/feature/version-control/config.el +++ b/modules/feature/version-control/config.el @@ -12,8 +12,8 @@ (after! vc-annotate (set-popup-rules! - '(("^\\vc-d" nil ((select))) ; *vc-diff* - ("^\\vc-c" nil ((select . t))))) ; *vc-change-log* + '(("^\\vc-d" :select) ; *vc-diff* + ("^\\vc-c" :select t))) ; *vc-change-log* (set-evil-initial-state! '(vc-annotate-mode vc-git-log-view-mode) 'normal)) diff --git a/modules/lang/clojure/config.el b/modules/lang/clojure/config.el index f61172558..e0c3b3767 100644 --- a/modules/lang/clojure/config.el +++ b/modules/lang/clojure/config.el @@ -30,7 +30,7 @@ (figwheel-sidecar.repl-api/start-figwheel!) (figwheel-sidecar.repl-api/cljs-repl))") - (set-popup-rule! "^\\*cider-repl" nil '((quit) (select))) + (set-popup-rule! "^\\*cider-repl" :quit nil :select nil) (set-repl-handler! 'clojure-mode #'+clojure/repl) (set-eval-handler! 'clojure-mode #'cider-eval-region) (set-lookup-handlers! 'clojure-mode diff --git a/modules/lang/latex/config.el b/modules/lang/latex/config.el index d7e127d2a..f90a2200a 100644 --- a/modules/lang/latex/config.el +++ b/modules/lang/latex/config.el @@ -33,7 +33,7 @@ font-latex-fontify-sectioning 1.15) (setq-default TeX-master nil) ;; Display the output of the latex commands in a popup. - (set-popup-rule! " output\\*$" '((size . 15))) + (set-popup-rule! " output\\*$" :size 15) ;; TeX Font Styling ;; (def-package! tex-style :defer t) diff --git a/modules/lang/org/+babel.el b/modules/lang/org/+babel.el index 39dd2222a..315fc1b9d 100644 --- a/modules/lang/org/+babel.el +++ b/modules/lang/org/+babel.el @@ -73,17 +73,17 @@ string). Stops at the first function to return non-nil.") :config (set-popup-rules! '(("^\\*Org Src" - ((size . 100) (side . right) (slot . -1) (window-height . 0.6)) - ((quit) (select . t) (modeline))) + :side 'right :size 100 :height 0.6 :slot -1 + :quit nil :select t) ("^\\*Python" - ((slot . 0) (side . right) (size . 100)) - ((select) (quit) (transient))) + :slot 0 :side 'right :size 100 + :select nil :quit nil :ttl nil) ("\\*ob-ipython.*" - ((slot . 2) (side . right) (size . 100) (window-height . 0.2)) - ((select) (quit) (transient))) + :slot 2 :side 'right :size 100 :height 0.2 + :select nil :quit nil :transient nil) ("\\*Python:.*" - ((slot . 0) (side . right) (size . 100)) - ((select) (quit) (transient))))) + :slot 0 :side 'right :size 100 + :select nil :quit nil :transient nil))) ;; TODO Add more popup styles ;; advices for remote kernel and org-src-edit diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index 0c006e6fc..bce7415a9 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -140,17 +140,10 @@ unfold to point on startup." "Defines popup rules for org-mode (does nothing if :ui popup is disabled)." (set-popup-rules! '(("^\\*\\(?:Agenda Com\\|Calendar\\|Org \\(?:Links\\|Export Dispatcher\\|Select\\)\\)" - ((slot . -1) (vslot . -1) (size . +popup-shrink-to-fit)) - ((transient . 0))) - ("^\\*Org Agenda" - ((size . 0.35)) - ((select . t) (transient))) - ("^\\*Org Src" - ((size . 0.3)) - ((quit) (select . t))) - ("^CAPTURE.*\\.org$" - ((size . 0.2)) - ((quit) (select . t)))))) + :slot -1 :vslot -1 :size #'+popup-shrink-to-fit :ttl 0) + ("^\\*Org Agenda" :size 0.35 :select t :ttl nil) + ("^\\*Org Src" :size 0.3 :quit nil :select t) + ("^CAPTURE.*\\.org$" :size 0.2 :quit nil :select t)))) (defun +org|setup-pretty-code () "Setup the default pretty symbols for" diff --git a/modules/lang/plantuml/config.el b/modules/lang/plantuml/config.el index a798870ad..04001c496 100644 --- a/modules/lang/plantuml/config.el +++ b/modules/lang/plantuml/config.el @@ -6,7 +6,7 @@ (setq plantuml-jar-path (concat doom-etc-dir "plantuml.jar") org-plantuml-jar-path plantuml-jar-path) :config - (set-popup-rule! "^\\*PLANTUML" '((size . 0.4)) '((select) (transient . 0)))) + (set-popup-rule! "^\\*PLANTUML" :size 0.4 :select nil :ttl 0)) (def-package! flycheck-plantuml diff --git a/modules/lang/python/config.el b/modules/lang/python/config.el index 48ba4f077..2ae760085 100644 --- a/modules/lang/python/config.el +++ b/modules/lang/python/config.el @@ -98,7 +98,7 @@ environment variables." (add-hook 'python-mode-hook #'anaconda-mode) (add-hook 'anaconda-mode-hook #'anaconda-eldoc-mode) (set-company-backend! 'python-mode '(company-anaconda)) - (set-popup-rule! "^\\*anaconda-mode" nil '((select))) + (set-popup-rule! "^\\*anaconda-mode" :select nil) (set-lookup-handlers! 'python-mode :definition #'anaconda-mode-find-definitions :references #'anaconda-mode-find-references @@ -130,7 +130,7 @@ environment variables." :init (associate! nose-mode :match "/test_.+\\.py$" :modes (python-mode)) :config - (set-popup-rule! "^\\*nosetests" '((size . 0.4)) '((select))) + (set-popup-rule! "^\\*nosetests" :size 0.4 :select nil) (set-yas-minor-mode! 'nose-mode) (map! :map nose-mode-map :localleader diff --git a/modules/lang/rest/config.el b/modules/lang/rest/config.el index 2114e2015..9b54b78c7 100644 --- a/modules/lang/rest/config.el +++ b/modules/lang/rest/config.el @@ -3,7 +3,7 @@ (def-package! restclient :mode ("\\.http\\'" . restclient-mode) :config - (set-popup-rule! "^\\*HTTP Response" '((size . 0.4)) '((quit . other))) + (set-popup-rule! "^\\*HTTP Response" :size 0.4 :quit 'other) (map! :mode restclient-mode :n [M-return] 'restclient-http-send-current :localleader diff --git a/modules/tools/ein/config.el b/modules/tools/ein/config.el index dda5acd71..0c92dcf59 100644 --- a/modules/tools/ein/config.el +++ b/modules/tools/ein/config.el @@ -18,12 +18,8 @@ (set-popup-rules! '(("\\*ein: .*" :ignore t) - ("\\*ein:tb .*" - ((side . bottom) (size . 0.3)) - ((quit . t) (transient) (select))) - ("\\*ein:notebooklist *" - ((side . left) (size . 50)) - ((select))))) + ("\\*ein:tb .*" :side 'bottom :size 0.3 :quit t :ttl nil :select nil) + ("\\*ein:notebooklist *" :side 'left :size 50 :select nil))) (when (featurep! :completion company) ;; Code completion with company diff --git a/modules/tools/magit/config.el b/modules/tools/magit/config.el index a65ed7ee0..739a346c6 100644 --- a/modules/tools/magit/config.el +++ b/modules/tools/magit/config.el @@ -26,7 +26,7 @@ load everything.") magit-display-buffer-function #'+magit-display-buffer-fullscreen magit-popup-display-buffer-action '((display-buffer-in-side-window))) - (set-popup-rule! "^\\(?:\\*magit\\|magit:\\)" :ignore) + (set-popup-rule! "^\\(?:\\*magit\\|magit:\\)" :ignore t) ;; Consider magit buffers real (so they can switched to) (add-hook 'magit-mode-hook #'doom|mark-buffer-as-real) ;; no mode-line in magit popups diff --git a/modules/tools/password-store/config.el b/modules/tools/password-store/config.el index 27e7ca61e..abc6db2c6 100644 --- a/modules/tools/password-store/config.el +++ b/modules/tools/password-store/config.el @@ -28,9 +28,7 @@ (after! pass (set-env! "PASSWORD_STORE_DIR") (set-evil-initial-state! 'pass-mode 'emacs) - (set-popup-rule! "^\\*Password-Store" - '((side . left) (size . 0.25)) - '((quit))) + (set-popup-rule! "^\\*Password-Store" :side 'left :size 0.25 :quit nil) (define-key! pass-mode-map "j" #'pass-next-entry "k" #'pass-prev-entry diff --git a/modules/tools/pdf/config.el b/modules/tools/pdf/config.el index 2d917ce2b..5b2952b64 100644 --- a/modules/tools/pdf/config.el +++ b/modules/tools/pdf/config.el @@ -34,9 +34,9 @@ (load! "+modeline") (add-hook! 'pdf-tools-enabled-hook (doom-set-modeline 'pdf-tools-modeline))) ;; Handle PDF-tools related popups better - (set-popup-rule! "^\\*Outline*" '((side . right) (size . 40)) '((select))) + (set-popup-rule! "^\\*Outline*" :side 'right :size 40 :select nil) ;; TODO: Add additional important windows that should be handled differently ;; TODO: These two next rules don't work (they should), investigate - ;; (set-popup-rule! "\\*Contents\\*" '((side . right) (size . 40)) nil) - ;; (set-popup-rule! "* annots\\*$" '((side . left) (size . 40)) '((select))) + ;; (set-popup-rule! "\\*Contents\\*" :side 'right :size 40) + ;; (set-popup-rule! "* annots\\*$" :side 'left :size 40 :select nil) ) diff --git a/modules/ui/neotree/config.el b/modules/ui/neotree/config.el index dca65e7c4..b308ee247 100644 --- a/modules/ui/neotree/config.el +++ b/modules/ui/neotree/config.el @@ -36,8 +36,8 @@ "^#.*#$")) (set-popup-rule! "^ ?\\*NeoTree" - `((side . ,neo-window-position) (size . ,neo-window-width)) - '((quit . current) (select . t))) + :side neo-window-position :size neo-window-width + :quit 'current :select t) (after! winner (cl-pushnew neo-buffer-name winner-boring-buffers)) diff --git a/modules/ui/popup/autoload/popup.el b/modules/ui/popup/autoload/popup.el index ab988a67a..32a1f8457 100644 --- a/modules/ui/popup/autoload/popup.el +++ b/modules/ui/popup/autoload/popup.el @@ -83,10 +83,11 @@ and enables `+popup-buffer-mode'." ;; integer = ttl ;; nil = no timer (unless +popup--inhibit-transient - (setq ttl (+popup-parameter-fn 'transient window buffer)) + (setq ttl (+popup-parameter-fn 'ttl window buffer)) (when ttl (when (eq ttl t) - (setq ttl +popup-ttl)) + (setq ttl (or (plist-get +popup-defaults :ttl) + 0))) (cl-assert (integerp ttl) t) (if (= ttl 0) (+popup--kill-buffer buffer 0) @@ -97,23 +98,26 @@ and enables `+popup-buffer-mode'." (defun +popup--normalize-alist (alist) "Merge `+popup-default-alist' and `+popup-default-parameters' with ALIST." - (if (not alist) - (setq alist +popup-default-alist) - (let* ((alist (map-merge 'list +popup-default-alist alist)) - (params (map-merge 'list - +popup-default-parameters - (cdr (assq 'window-parameters alist))))) - ;; translate side => window-(width|height) - (when-let* ((size (cdr (assq 'size alist))) - (side (or (cdr (assq 'side alist)) 'bottom))) - (map-delete alist 'size) - (map-put alist (if (memq side '(left right)) + (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) - size)) - ;; - (map-put alist 'window-parameters params) - (nreverse alist)))) + '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))) ;; @@ -145,18 +149,21 @@ and enables `+popup-buffer-mode'." ;;;###autoload (defun +popup-buffer (buffer &optional alist) "Open BUFFER in a popup window. ALIST describes its features." - (let ((old-window (selected-window)) - (alist (+popup--normalize-alist alist)) - (window-min-height 3)) - (when-let* ((new-window (run-hook-with-args-until-success - '+popup-display-buffer-actions buffer alist))) - (+popup--init new-window alist) + (let* ((origin (selected-window)) + (window-min-height 3) + (alist (+popup--normalize-alist alist)) + (actions (or (cdr (assq 'actions alist)) + +popup-default-display-buffer-actions))) + (when-let* ((popup (cl-loop for func in actions + if (funcall func buffer alist) + return it))) + (+popup--init popup alist) (unless +popup--inhibit-select - (let ((select (+popup-parameter 'select new-window))) + (let ((select (+popup-parameter 'select popup))) (if (functionp select) - (funcall select new-window old-window) - (select-window (if select new-window old-window))))) - new-window))) + (funcall select popup origin) + (select-window (if select popup origin))))) + popup))) ;;;###autoload (defun +popup-parameter (parameter &optional window) @@ -188,70 +195,6 @@ Uses `shrink-window-if-larger-than-buffer'." (shrink-window-if-larger-than-buffer window))) -;; -;; Minor mode -;; - -;;;###autoload -(defvar +popup-mode-map (make-sparse-keymap) - "Active keymap in a session with the popup system enabled. See -`+popup-mode'.") - -;;;###autoload -(defvar +popup-buffer-mode-map - (let ((map (make-sparse-keymap))) - (when (featurep! :feature evil) - ;; for maximum escape coverage in emacs state buffers - (define-key map [escape] #'doom/escape) - (define-key map (kbd "ESC") #'doom/escape)) - map) - "Active keymap in popup windows. See `+popup-buffer-mode'.") - -;;;###autoload -(define-minor-mode +popup-mode - "Global minor mode representing Doom's popup management system." - :init-value nil - :global t - :keymap +popup-mode-map - (cond (+popup-mode - (add-hook 'doom-unreal-buffer-functions #'+popup-buffer-p) - (add-hook 'doom-escape-hook #'+popup|close-on-escape t) - (add-hook 'doom-cleanup-hook #'+popup|cleanup-rules) - (add-hook 'after-change-major-mode-hook #'+popup|set-modeline-on-enable) - (setq +popup--old-display-buffer-alist display-buffer-alist - display-buffer-alist +popup--display-buffer-alist - window--sides-inhibit-check t) - (dolist (prop +popup-window-parameters) - (push (cons prop 'writable) window-persistent-parameters))) - (t - (remove-hook 'doom-unreal-buffer-functions #'+popup-buffer-p) - (remove-hook 'doom-escape-hook #'+popup|close-on-escape) - (remove-hook 'doom-cleanup-hook #'+popup|cleanup-rules) - (remove-hook 'after-change-major-mode-hook #'+popup|set-modeline-on-enable) - (setq display-buffer-alist +popup--old-display-buffer-alist - window--sides-inhibit-check nil) - (+popup|cleanup-rules) - (dolist (prop +popup-window-parameters) - (setq window-persistent-parameters - (map-delete window-persistent-parameters prop)))))) - -;;;###autoload -(define-minor-mode +popup-buffer-mode - "Minor mode for individual popup windows. - -It is enabled when a buffer is displayed in a popup window and disabled when -that window has been changed or closed." - :init-value nil - :keymap +popup-buffer-mode-map - (when (and +popup-buffer-mode (timerp +popup--timer)) - (remove-hook 'kill-buffer-hook #'+popup|kill-buffer-hook t) - (cancel-timer +popup--timer) - (setq +popup--timer nil))) - -(put '+popup-buffer-mode 'permanent-local t) -(put '+popup-buffer-mode 'permanent-local-hook t) - - ;; ;; Hooks ;; @@ -289,6 +232,7 @@ restoring it if `+popup-buffer-mode' is disabled." ((symbolp modeline) (when-let* ((hide-mode-line-format (doom-modeline modeline))) (hide-mode-line-mode +1))))))) +(put '+popup|set-modeline-on-enable 'permanent-local-hook t) ;;;###autoload (defun +popup|unset-modeline-on-disable () @@ -416,53 +360,11 @@ the message buffer in a popup window." (let ((window (selected-window)) (buffer (current-buffer)) +popup--remember-last) - (set-window-parameter window 'transient nil) + (set-window-parameter window 'ttl nil) (+popup/close window 'force) (display-buffer-pop-up-window buffer nil))) -;; -;; Macros -;; - -;;;###autoload -(defmacro with-popup-rules! (rules &rest body) - "Evaluate BODY with popup RULES. RULES is a list of popup rules. Each rule -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) - ,@(cl-loop for rule in rules collect `(set-popup-rule! ,@rule)) - (when (bound-and-true-p +popup-mode) - (setq display-buffer-alist +popup--display-buffer-alist)) - ,@body)) - -;;;###autoload -(defmacro without-popups! (&rest body) - "Run BODY with a default `display-buffer-alist', ignoring the popup rules set -with the :popup setting." - `(let ((display-buffer-alist +popup--old-display-buffer-alist)) - ,@body)) - -;;;###autoload -(defmacro save-popups! (&rest body) - "Sets aside all popups before executing the original function, usually to -prevent the popup(s) from messing up the UI (or vice versa)." - `(let* ((in-popup-p (+popup-buffer-p)) - (popups (+popup-windows)) - (+popup--inhibit-transient t) - +popup--last) - (dolist (p popups) - (+popup/close p 'force)) - (unwind-protect - (progn ,@body) - (when popups - (let ((origin (selected-window))) - (+popup/restore) - (unless in-popup-p - (select-window origin))))))) - - ;; ;; Advice ;; diff --git a/modules/ui/popup/autoload/settings.el b/modules/ui/popup/autoload/settings.el index ab5f4cecd..0ff8d28b9 100644 --- a/modules/ui/popup/autoload/settings.el +++ b/modules/ui/popup/autoload/settings.el @@ -2,69 +2,187 @@ (defvar +popup--display-buffer-alist nil) -(defsubst +popup--rule (args) - (cl-destructuring-bind (condition &optional alist parameters) args - (if (eq alist :ignore) - (list condition nil) - `(,condition (+popup-buffer) - ,@alist - (window-parameters ,@parameters))))) +;;;###autoload +(defvar +popup-defaults + (list :side 'bottom + :height 0.16 + :width 40 + :quit t + :select #'ignore + :ttl 5) + "Default setup for `set-popup-rule!' ") -(defun +popup--define (condition &optional alist parameters) - (when after-init-time - (setq +popup--display-buffer-alist - (map-delete +popup--display-buffer-alist condition))) - (push (+popup--rule (list condition alist parameters)) - +popup--display-buffer-alist)) +;;;###autoload +(defun +popup--make (predicate plist) + (cond ((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) + (cl-destructuring-bind (condition &optional alist parameters) + (list predicate (car plist) (cadr plist)) + (if (eq alist :ignore) + (list condition nil) + `(,condition (+popup-buffer) + ,@alist + (window-parameters ,@parameters))))) + ((plist-get plist :ignore) + (list predicate nil)) + ((let* ((plist (append plist +popup-defaults)) + (alist + `((actions . ,(plist-get plist :actions)) + (side . ,(plist-get plist :side)) + (size . ,(plist-get plist :size)) + (window-width . ,(plist-get plist :width)) + (window-height . ,(plist-get plist :height)) + (slot . ,(plist-get plist :slot)) + (vslot . ,(plist-get plist :vslot)))) + (params + `((ttl . ,(plist-get plist :ttl)) + (quit . ,(plist-get plist :quit)) + (select . ,(plist-get plist :select)) + (modeline . ,(plist-get plist :modeline)) + (autosave . ,(plist-get plist :autosave)) + ,@(plist-get plist :parameters)))) + `(,predicate (+popup-buffer) + ,@alist + (window-parameters ,@params)))))) ;;;###autodef -(defun set-popup-rule! (condition &optional alist parameters) +(defun set-popup-rule! (predicate &rest plist) "Define a popup rule. -CONDITION can be a regexp string or a function. - -For ALIST, see `display-buffer' and `display-buffer-alist' for a list of -possible entries, which instruct the display system how to initialize the popup -window. - -ALIST also supports the `size' parameter, which will be translated to -`window-width' or `window-height' depending on `side'. - -PARAMETERS is an alist of window parameters. See `+popup-window-parameters' for -a list of custom parameters provided by the popup module. If certain -attributes/parameters are omitted, the ones from `+popup-default-alist' and -`+popup-default-parameters' will be used. - -The buffers of new windows displayed by `pop-to-buffer' and `display-buffer' -will be tested against CONDITION, which is either a) a regexp string (which is +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. -See `def-popups!' for defining multiple rules in bulk." +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). + +PLIST can be made up of any of the following properties: + +:actions ACTIONS + ACTIONS is a list of functions or an alist containing (FUNCTION . ALIST). See + `display-buffer''s second argument for more information on its format and what + it accepts. If omitted, `+popup-default-display-buffer-actions' is used. + +:side 'bottom|'top|'left|'right + Which side of the frame to open the popup on. This is only respected if + `+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. + + 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 + character width (width). + +: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. + + On the other hand, vslot operates the same way, but controls how popups are + stacked. + + When a popup is opened on the left and right, slot determines vertical + position and vslot horizontal. + +: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. + + If t, CDR will default to `+popup-ttl'. + 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. + +: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. + + If t, close the popup if ESC/C-g is pressed inside or outside of popups. + 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. + 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. + +:select BOOL|FN + CDR 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. + +:modeline BOOL|SYMBOL|FN + CDR 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. + +: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. + + If t, no prompts. Just save them automatically (if they're file-visiting + buffers). + If 'ignore, no prompts, no saving. Just silently kill it. + 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. + +:parameters ALIST + An alist of custom window parameters. See \(info window-parameters) + +If any of these are omitted, defaults derived from `+popup-defaults' will be +used." (declare (indent defun)) - (+popup--define condition alist parameters) + (push (+popup--make predicate plist) +popup--display-buffer-alist) (when (bound-and-true-p +popup-mode) (setq display-buffer-alist +popup--display-buffer-alist)) +popup--display-buffer-alist) ;;;###autodef (defun set-popup-rules! (&rest rulesets) - "Define multiple popup rules. See `def-popup!' for the specifications of each -individual rule. + "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!'). - (set-popup-rules! - '((\"^ \\*\" ((slot . 1) (vslot . -1) (size . +popup-shrink-to-fit))) - (\"^\\*\" ((slot . 1) (vslot . -1)) ((select . t)))))" - (dolist (ruleset rulesets) - (dolist (rule ruleset) - (apply #'+popup--define rule))) +Example: + + (set-popup-rules! + '((\"^ \\*\" :slot 1 :vslot -1 :size #'+popup-shrink-to-fit) + (\"^\\*\" :slot 1 :vslot -1 :select t)) + '((\"^\\*Completions\" :slot -1 :vslot -2 :ttl 0) + (\"^\\*Compil\\(?:ation\\|e-Log\\)\" :size 0.3 :ttl 0 :quit t)))" + (declare (indent 0)) + (dolist (rules rulesets) + (dolist (rule rules) + (push (+popup--make (car rule) (cdr rule)) + +popup--display-buffer-alist))) (when (bound-and-true-p +popup-mode) (setq display-buffer-alist +popup--display-buffer-alist)) +popup--display-buffer-alist) ;; -;; Obsolete +;; Obsolete settings ;; ;; FIXME obsolete :popup diff --git a/modules/ui/popup/config.el b/modules/ui/popup/config.el index 3e020c3a9..7e89c04df 100644 --- a/modules/ui/popup/config.el +++ b/modules/ui/popup/config.el @@ -1,151 +1,160 @@ ;;; ui/popup/config.el -*- lexical-binding: t; -*- -(defconst +popup-window-parameters - '(transient quit select modeline popup) +(defconst +popup-window-parameters '(ttl quit select modeline popup) "A list of custom parameters to be added to `window-persistent-parameters'. -Modifying this has no effect, unless done before ui/popup loads. +Modifying this has no effect, unless done before ui/popup loads.") -(transient . CDR) - 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. - - If t, CDR will default to `+popup-ttl'. - 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. - -(quit . CDR) - 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. - - If t, close the popup if ESC/C-g is pressed inside or outside of popups. - 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. - 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. - -(select . CDR) - CDR 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. - -(modeline . CDR) - CDR 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. - -(autosave . CDR) - This parameter determines what to do with modified buffers in closing popup - windows. CDR can be a 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. - 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. - -(popup . t) - This is for internal use, do not change this. It simply marks a window as a - popup window. - -Since I can't find this information anywhere but the Emacs manual, I'll include -a brief description of some native window parameters that Emacs uses: - -(delete-window . CDR) -(delete-other-window . CDR) -(split-window . CDR) -(other-window . CDR) - This applies to all four of the above: CDR can be t or a function. If t, using - those functions on this window will ignore all window parameters. - - If CDR is a function, it will replace the native function when used on this - window. e.g. if CDR is #'ignore (delete-window popup) will run (ignore popup) - instead of deleting the window! -(no-other-window . BOOL) - If CDR is non-nil, this window becomes invisible to `other-window' and - `pop-to-buffer'. Doom popups sets this. The default is nil.") - -(defvar +popup-display-buffer-actions +(defvar +popup-default-display-buffer-actions '(display-buffer-reuse-window +popup-display-buffer-stacked-side-window) "The functions to use to display the popup buffer.") (defvar +popup-default-alist - '((window-height . 0.16) + '((window-height . 0.16) ; remove later (reusable-frames . visible)) "The default alist for `display-buffer-alist' rules.") (defvar +popup-default-parameters - '((transient . t) - (quit . t) - (select . ignore) + '((transient . t) ; remove later + (quit . t) ; remove later + (select . ignore) ; remove later (no-other-window . t)) "The default window parameters.") -(defvar +popup-ttl 5 - "The default time-to-live for transient buffers whose popup buffers have been -deleted.") - (defvar +popup-margin-width 1 "Size of the margins to give popup windows. Set this to nil to disable margin adjustment.") +;; +;; Global modes +;; + +(defvar +popup-mode-map (make-sparse-keymap) + "Active keymap in a session with the popup system enabled. See +`+popup-mode'.") + +(defvar +popup-buffer-mode-map + (let ((map (make-sparse-keymap))) + (when (featurep! :feature evil) + ;; for maximum escape coverage in emacs state buffers + (define-key map [escape] #'doom/escape) + (define-key map (kbd "ESC") #'doom/escape)) + map) + "Active keymap in popup windows. See `+popup-buffer-mode'.") + +(define-minor-mode +popup-mode + "Global minor mode representing Doom's popup management system." + :init-value nil + :global t + :keymap +popup-mode-map + (cond (+popup-mode + (add-hook 'doom-escape-hook #'+popup|close-on-escape t) + (add-hook 'doom-cleanup-hook #'+popup|cleanup-rules) + (setq +popup--old-display-buffer-alist display-buffer-alist + display-buffer-alist +popup--display-buffer-alist + window--sides-inhibit-check t) + (dolist (prop +popup-window-parameters) + (push (cons prop 'writable) window-persistent-parameters))) + (t + (remove-hook 'doom-escape-hook #'+popup|close-on-escape) + (remove-hook 'doom-cleanup-hook #'+popup|cleanup-rules) + (setq display-buffer-alist +popup--old-display-buffer-alist + window--sides-inhibit-check nil) + (+popup|cleanup-rules) + (dolist (prop +popup-window-parameters) + (setq window-persistent-parameters + (map-delete window-persistent-parameters prop)))))) + +(define-minor-mode +popup-buffer-mode + "Minor mode for individual popup windows. + +It is enabled when a buffer is displayed in a popup window and disabled when +that window has been changed or closed." + :init-value nil + :keymap +popup-buffer-mode-map + (if (not +popup-buffer-mode) + (remove-hook 'after-change-major-mode-hook #'+popup|set-modeline-on-enable t) + (add-hook 'after-change-major-mode-hook #'+popup|set-modeline-on-enable nil t) + (when (timerp +popup--timer) + (remove-hook 'kill-buffer-hook #'+popup|kill-buffer-hook t) + (cancel-timer +popup--timer) + (setq +popup--timer nil)))) + +(put '+popup-buffer-mode 'permanent-local t) +(put '+popup-buffer-mode 'permanent-local-hook t) +(put '+popup|set-modeline-on-enable 'permanent-local-hook t) + + +;; +;; Macros +;; + +(defmacro with-popup-rules! (rules &rest body) + "Evaluate BODY with popup RULES. RULES is a list of popup rules. Each rule +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) + (when (bound-and-true-p +popup-mode) + (setq display-buffer-alist +popup--display-buffer-alist)) + ,@body)) + +(defmacro save-popups! (&rest body) + "Sets aside all popups before executing the original function, usually to +prevent the popup(s) from messing up the UI (or vice versa)." + `(let* ((in-popup-p (+popup-buffer-p)) + (popups (+popup-windows)) + (+popup--inhibit-transient t) + +popup--last) + (dolist (p popups) + (+popup/close p 'force)) + (unwind-protect + (progn ,@body) + (when popups + (let ((origin (selected-window))) + (+popup/restore) + (unless in-popup-p + (select-window origin))))))) + + ;; ;; Default popup rules & bootstrap ;; (set-popup-rules! (when (featurep! +all) - '(("^\\*" ((slot . 1) (vslot . -1)) ((select . t))) - ("^ \\*" ((slot . 1) (vslot . -1) (size . +popup-shrink-to-fit))))) + '(("^\\*" :slot 1 :vslot -1 :select t) + ("^ \\*" :slot 1 :vslot -1 :size +popup-shrink-to-fit))) (when (featurep! +defaults) '(("^\\*Completions" - ((slot . -1) (vslot . -2)) - ((transient . 0))) + :slot -1 :vslot -2 :ttl 0) ("^\\*Compil\\(?:ation\\|e-Log\\)" - ((size . 0.3)) - ((transient . 0) (quit . t))) + :size 0.3 :ttl 0 :quit t) ("^\\*\\(?:scratch\\|Messages\\)" - nil - ((autosave . t) (transient))) + :autosave t :ttl nil) + ("^\\*Man " + :size 0.45 :vslot -6 :ttl 0 :quit t :select t) ("^\\*doom \\(?:term\\|eshell\\)" - ((size . 0.25) (vslot . -10)) - ((select . t) (quit) (transient . 0))) + :size 0.25 :vslot -10 :select t :quit nil :ttl 0) ("^\\*doom:" - ((size . 0.35) (side . bottom)) - ((autosave . t) (select . t) (modeline . t) (quit) (transient . t))) + :size 0.35 :size bottom :autosave t :select t :modeline t :quit nil) ("^\\*\\(?:\\(?:Pp E\\|doom e\\)val\\)" - ((size . +popup-shrink-to-fit)) - ((transient . 0) (select . ignore))) + :size +popup-shrink-to-fit :ttl 0 :select ignore) ("^\\*Customize" - ((slot . 2) (side . right)) - ((modeline . nil) (select . t) (quit . t))) + :slot 2 :side right :select t :quit t) ("^ \\*undo-tree\\*" - ((slot . 2) (side . left) (size . 20)) - ((modeline . nil) (select . t) (quit . t))) + :slot 2 :side left :size 20 :select t :quit t) ;; `help-mode', `helpful-mode' ("^\\*[Hh]elp" - ((slot . 2) (vslot . 2) (size . 0.25)) - ((select . t))) + :slot 2 :vslot 2 :size 0.35 :select t) ;; `Info-mode' ("^\\*info\\*$" - ((slot . 2) (vslot . 2) (size . 0.45)) - ((select . t)))))) + :slot 2 :vslot 2 :size 0.45 :select t))) + '(("^\\*Backtrace" :ignore t))) + +(add-hook 'doom-init-ui-hook #'+popup-mode :append) -(add-hook 'doom-init-ui-hook #'+popup-mode) (add-hook! '+popup-buffer-mode-hook #'(+popup|adjust-fringes +popup|adjust-margins @@ -157,5 +166,4 @@ adjustment.") ;; Hacks ;; -(when (featurep! +defaults) - (load! "+hacks")) +(load! "+hacks")