refactor: Move doom-popup into its own repo
This commit is contained in:
@@ -1,9 +0,0 @@
|
|||||||
#+title: Popups
|
|
||||||
|
|
||||||
This library is almost entirely poached from Doom Emacs. Taming various ill-behaved packages is not something I want to hack myself.
|
|
||||||
|
|
||||||
* My changes
|
|
||||||
|
|
||||||
- Remove uses of Doom's libraries, e.g. ~defadvice!~, ~after!~, ...
|
|
||||||
- Substitute ~+popup-«name»~ for ~doom-popup-«name»~.
|
|
||||||
- Prepend MIT license to each stolen file (as Doom is MIT-licensed).
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
;;; doom-popup-config.el -*- lexical-binding: t; -*-
|
|
||||||
|
|
||||||
;; The MIT License (MIT)
|
|
||||||
;;
|
|
||||||
;; Copyright (c) 2014-2024 Henrik Lissner.
|
|
||||||
;;
|
|
||||||
;; Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
;; a copy of this software and associated documentation files (the
|
|
||||||
;; "Software"), to deal in the Software without restriction, including
|
|
||||||
;; without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
;; distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
;; permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
;; the following conditions:
|
|
||||||
;;
|
|
||||||
;; The above copyright notice and this permission notice shall be
|
|
||||||
;; included in all copies or substantial portions of the Software.
|
|
||||||
;;
|
|
||||||
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
(defconst doom-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.")
|
|
||||||
|
|
||||||
(defvar doom-popup-default-display-buffer-actions
|
|
||||||
'(doom-popup-display-buffer-stacked-side-window-fn)
|
|
||||||
"The functions to use to display the popup buffer.")
|
|
||||||
|
|
||||||
(defvar doom-popup-default-alist
|
|
||||||
'((window-height . 0.16) ; remove later
|
|
||||||
(reusable-frames . visible))
|
|
||||||
"The default alist for `display-buffer-alist' rules.")
|
|
||||||
|
|
||||||
(defvar doom-popup-default-parameters
|
|
||||||
'((transient . t) ; remove later
|
|
||||||
(quit . t) ; remove later
|
|
||||||
(select . ignore) ; remove later
|
|
||||||
(no-other-window . t))
|
|
||||||
"The default window parameters.")
|
|
||||||
|
|
||||||
(defvar doom-popup-margin-width 1
|
|
||||||
"Size of the margins to give popup windows. Set this to nil to disable margin
|
|
||||||
adjustment.")
|
|
||||||
|
|
||||||
(defvar doom-popup--inhibit-transient nil)
|
|
||||||
(defvar doom-popup--inhibit-select nil)
|
|
||||||
(defvar doom-popup--old-display-buffer-alist nil)
|
|
||||||
(defvar doom-popup--remember-last t)
|
|
||||||
(defvar doom-popup--last nil)
|
|
||||||
(defvar-local doom-popup--timer nil)
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Global modes
|
|
||||||
|
|
||||||
(defvar doom-popup-mode-map (make-sparse-keymap)
|
|
||||||
"Active keymap in a session with the popup system enabled. See
|
|
||||||
`doom-popup-mode'.")
|
|
||||||
|
|
||||||
(defvar doom-popup-buffer-mode-map
|
|
||||||
(let ((map (make-sparse-keymap)))
|
|
||||||
(when (featurep 'evil)
|
|
||||||
;; For maximum escape coverage in emacs state buffers; this only works in
|
|
||||||
;; GUI Emacs, in tty Emacs use C-g instead
|
|
||||||
(define-key map [escape] #'syd/escape))
|
|
||||||
map)
|
|
||||||
"Active keymap in popup windows. See `doom-popup-buffer-mode'.")
|
|
||||||
|
|
||||||
(define-minor-mode doom-popup-mode
|
|
||||||
"Global minor mode representing Doom's popup management system."
|
|
||||||
:init-value nil
|
|
||||||
:global t
|
|
||||||
:keymap doom-popup-mode-map
|
|
||||||
(cond (doom-popup-mode
|
|
||||||
(add-hook 'doom-escape-hook #'doom-popup-close-on-escape-h 'append)
|
|
||||||
(setq doom-popup--old-display-buffer-alist display-buffer-alist
|
|
||||||
display-buffer-alist doom-popup--display-buffer-alist
|
|
||||||
window--sides-inhibit-check t)
|
|
||||||
(dolist (prop doom-popup-window-parameters)
|
|
||||||
(push (cons prop 'writable) window-persistent-parameters)))
|
|
||||||
(t
|
|
||||||
(remove-hook 'doom-escape-hook #'doom-popup-close-on-escape-h)
|
|
||||||
(setq display-buffer-alist doom-popup--old-display-buffer-alist
|
|
||||||
window--sides-inhibit-check nil)
|
|
||||||
(doom-popup-cleanup-rules-h)
|
|
||||||
(dolist (prop doom-popup-window-parameters)
|
|
||||||
(delq (assq prop window-persistent-parameters)
|
|
||||||
window-persistent-parameters)))))
|
|
||||||
|
|
||||||
(define-minor-mode doom-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 doom-popup-buffer-mode-map
|
|
||||||
(if (not doom-popup-buffer-mode)
|
|
||||||
(remove-hook 'after-change-major-mode-hook #'doom-popup-set-modeline-on-enable-h t)
|
|
||||||
(add-hook 'after-change-major-mode-hook #'doom-popup-set-modeline-on-enable-h
|
|
||||||
nil 'local)
|
|
||||||
(when (timerp doom-popup--timer)
|
|
||||||
(remove-hook 'kill-buffer-hook #'doom-popup-kill-buffer-hook-h t)
|
|
||||||
(cancel-timer doom-popup--timer)
|
|
||||||
(setq doom-popup--timer nil))))
|
|
||||||
|
|
||||||
(put 'doom-popup-buffer-mode 'permanent-local t)
|
|
||||||
(put 'doom-popup-buffer-mode 'permanent-local-hook t)
|
|
||||||
(put 'doom-popup-set-modeline-on-enable-h '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 `doom-popup-define' or the :popup setting."
|
|
||||||
(declare (indent defun))
|
|
||||||
`(let ((doom-popup--display-buffer-alist doom-popup--old-display-buffer-alist)
|
|
||||||
display-buffer-alist)
|
|
||||||
(set-popup-rules! ,rules)
|
|
||||||
(when (bound-and-true-p doom-popup-mode)
|
|
||||||
(setq display-buffer-alist doom-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 (doom-popup-buffer-p))
|
|
||||||
(popups (doom-popup-windows))
|
|
||||||
(doom-popup--inhibit-transient t)
|
|
||||||
buffer-list-update-hook
|
|
||||||
doom-popup--last)
|
|
||||||
(dolist (p popups)
|
|
||||||
(doom-popup/close p 'force))
|
|
||||||
(unwind-protect
|
|
||||||
(progn ,@body)
|
|
||||||
(when popups
|
|
||||||
(let ((origin (selected-window)))
|
|
||||||
(doom-popup/restore)
|
|
||||||
(unless in-popup-p
|
|
||||||
(select-window origin)))))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Default popup rules & bootstrap
|
|
||||||
|
|
||||||
(defvar doom-popup-use-aggressive-defaults nil)
|
|
||||||
|
|
||||||
(defvar doom-popup-use-defaults t)
|
|
||||||
|
|
||||||
(set-popup-rules!
|
|
||||||
(when doom-popup-use-aggressive-defaults
|
|
||||||
'(("^\\*" :slot 1 :vslot -1 :select t)
|
|
||||||
("^ \\*" :slot 1 :vslot -1 :size doom-popup-shrink-to-fit)))
|
|
||||||
(when doom-popup-use-defaults
|
|
||||||
'(("^\\*Completions" :ignore t)
|
|
||||||
("^\\*Local variables\\*$"
|
|
||||||
:vslot -1 :slot 1 :size doom-popup-shrink-to-fit)
|
|
||||||
("^\\*\\(?:[Cc]ompil\\(?:ation\\|e-Log\\)\\|Messages\\)"
|
|
||||||
:vslot -2 :size 0.3 :autosave t :quit t :ttl nil)
|
|
||||||
("^\\*\\(?:doom \\|Pp E\\)" ; transient buffers (no interaction required)
|
|
||||||
:vslot -3 :size doom-popup-shrink-to-fit :autosave t :select ignore :quit t :ttl 0)
|
|
||||||
("^\\*doom:" ; editing buffers (interaction required)
|
|
||||||
:vslot -4 :size 0.35 :autosave t :select t :modeline t :quit nil :ttl t)
|
|
||||||
("^\\*doom:\\(?:v?term\\|e?shell\\)-popup" ; editing buffers (interaction required)
|
|
||||||
:vslot -5 :size 0.35 :select t :modeline nil :quit nil :ttl nil)
|
|
||||||
("^\\*\\(?:Wo\\)?Man "
|
|
||||||
:vslot -6 :size 0.45 :select t :quit t :ttl 0)
|
|
||||||
("^\\*Calc"
|
|
||||||
:vslot -7 :side bottom :size 0.4 :select t :quit nil :ttl 0)
|
|
||||||
("^\\*Customize"
|
|
||||||
:slot 2 :side right :size 0.5 :select t :quit nil)
|
|
||||||
("^ \\*undo-tree\\*"
|
|
||||||
:slot 2 :side left :size 20 :select t :quit t)
|
|
||||||
;; `help-mode', `helpful-mode'
|
|
||||||
("^\\*\\([Hh]elp\\|Apropos\\)"
|
|
||||||
:slot 2 :vslot -8 :size 0.42 :select t)
|
|
||||||
("^\\*eww\\*" ; `eww' (and used by dash docsets)
|
|
||||||
:vslot -11 :size 0.35 :select t)
|
|
||||||
("^\\*xwidget"
|
|
||||||
:vslot -11 :size 0.35 :select nil)
|
|
||||||
("^\\*info\\*$" ; `Info-mode'
|
|
||||||
:slot 2 :vslot 2 :size 0.45 :select t)))
|
|
||||||
'(("^\\*Warnings" :vslot 99 :size 0.25)
|
|
||||||
("^\\*Backtrace" :vslot 99 :size 0.4 :quit nil)
|
|
||||||
("^\\*CPU-Profiler-Report " :side bottom :vslot 100 :slot 1 :height 0.4 :width 0.5 :quit nil)
|
|
||||||
("^\\*Memory-Profiler-Report " :side bottom :vslot 100 :slot 2 :height 0.4 :width 0.5 :quit nil)
|
|
||||||
("^\\*Process List\\*" :side bottom :vslot 101 :size 0.25 :select t :quit t)
|
|
||||||
("^\\*\\(?:Proced\\|timer-list\\|Abbrevs\\|Output\\|Occur\\|unsent mail.*?\\|message\\)\\*" :ignore t)))
|
|
||||||
|
|
||||||
(add-hook 'on-init-ui-hook #'doom-popup-mode 'append)
|
|
||||||
|
|
||||||
(dolist (hook-fn (list #'doom-popup-adjust-fringes-h
|
|
||||||
#'doom-popup-adjust-margins-h
|
|
||||||
#'doom-popup-set-modeline-on-enable-h
|
|
||||||
#'doom-popup-unset-modeline-on-disable-h))
|
|
||||||
(add-hook 'doom-popup-buffer-mode-hook hook-fn))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;;; Hacks
|
|
||||||
|
|
||||||
(require 'doom-popup-hacks)
|
|
||||||
|
|
||||||
(provide 'doom-popup-config)
|
|
||||||
@@ -1,447 +0,0 @@
|
|||||||
;;; ui/popup/+hacks.el -*- lexical-binding: t; -*-
|
|
||||||
|
|
||||||
;; The MIT License (MIT)
|
|
||||||
;;
|
|
||||||
;; Copyright (c) 2014-2024 Henrik Lissner.
|
|
||||||
;;
|
|
||||||
;; Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
;; a copy of this software and associated documentation files (the
|
|
||||||
;; "Software"), to deal in the Software without restriction, including
|
|
||||||
;; without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
;; distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
;; permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
;; the following conditions:
|
|
||||||
;;
|
|
||||||
;; The above copyright notice and this permission notice shall be
|
|
||||||
;; included in all copies or substantial portions of the Software.
|
|
||||||
;;
|
|
||||||
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
;; What follows are all the hacks needed to get various parts of Emacs and other
|
|
||||||
;; plugins to cooperate with the popup management system. Essentially, it comes
|
|
||||||
;; down to:
|
|
||||||
;;
|
|
||||||
;; 1. Making plugins that control their own window environment less greedy (e.g.
|
|
||||||
;; org agenda, which tries to reconfigure the entire frame by deleting all
|
|
||||||
;; other windows just to pop up one tiny window).
|
|
||||||
;; 2. Forcing plugins to use `display-buffer' and `pop-to-buffer' instead of
|
|
||||||
;; `switch-to-buffer' (which is unaffected by `display-buffer-alist', which
|
|
||||||
;; we must rely on, heavily).
|
|
||||||
;; 3. Closing popups (temporarily) before functions that are highly destructive
|
|
||||||
;; to the illusion of popup control get run (with the use of the
|
|
||||||
;; `save-popups!' macro).
|
|
||||||
;;
|
|
||||||
;; Keep in mind, all this black magic may break in future updates, and will need
|
|
||||||
;; to be watched carefully for corner cases. Also, once this file is loaded,
|
|
||||||
;; many of its changes are irreversible without restarting Emacs! I don't like
|
|
||||||
;; it either, but I will address this over time.
|
|
||||||
;;
|
|
||||||
;; Hacks should be kept in alphabetical order, named after the feature they
|
|
||||||
;; modify, and should follow a ;;;## package-name header line (if not using
|
|
||||||
;; `after!' or `use-package!').
|
|
||||||
|
|
||||||
;;
|
|
||||||
;;; Core functions
|
|
||||||
|
|
||||||
(defun doom-popup--make-case-sensitive-a (fn &rest args)
|
|
||||||
"Make regexps in `display-buffer-alist' case-sensitive.
|
|
||||||
|
|
||||||
To reduce fewer edge cases and improve performance when `display-buffer-alist'
|
|
||||||
grows larger."
|
|
||||||
(let (case-fold-search)
|
|
||||||
(apply fn args)))
|
|
||||||
|
|
||||||
(advice-add #'display-buffer-assq-regexp
|
|
||||||
:around #'doom-popup--make-case-sensitive-a)
|
|
||||||
|
|
||||||
;; Don't try to resize popup windows
|
|
||||||
(advice-add #'balance-windows :around #'doom-popup-save-a)
|
|
||||||
|
|
||||||
(defun doom-popup/quit-window (&optional arg)
|
|
||||||
"The regular `quit-window' sometimes kills the popup buffer and switches to a
|
|
||||||
buffer that shouldn't be in a popup. We prevent that by remapping `quit-window'
|
|
||||||
to this commmand."
|
|
||||||
(interactive "P")
|
|
||||||
(let ((orig-buffer (current-buffer)))
|
|
||||||
(quit-window arg)
|
|
||||||
(when (and (eq orig-buffer (current-buffer))
|
|
||||||
(doom-popup-buffer-p))
|
|
||||||
(doom-popup/close nil 'force))))
|
|
||||||
(define-key doom-popup-buffer-mode-map [remap quit-window] #'doom-popup/quit-window)
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;;; External functions
|
|
||||||
|
|
||||||
(with-eval-after-load 'buff-menu
|
|
||||||
(define-key Buffer-menu-mode-map (kbd "RET") #'Buffer-menu-other-window))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'company
|
|
||||||
(defun doom-popup--dont-select-me-a (fn &rest args)
|
|
||||||
(let ((doom-popup--inhibit-select t))
|
|
||||||
(apply fn args)))
|
|
||||||
|
|
||||||
(advice-add #'company-show-doc-buffer
|
|
||||||
:around #'doom-popup--dont-select-me-a))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'compile
|
|
||||||
(defun doom-popup--compilation-goto-locus-a (fn &rest args)
|
|
||||||
(cl-letf ((pop-to-buffer (symbol-function #'pop-to-buffer)))
|
|
||||||
(ignore pop-to-buffer)
|
|
||||||
(cl-letf (((symbol-function #'pop-to-buffer)
|
|
||||||
(lambda (buffer &optional action norecord)
|
|
||||||
(let ((pop-up-windows (not (doom-popup-buffer-p (current-buffer)))))
|
|
||||||
(funcall pop-to-buffer buffer action norecord)))))
|
|
||||||
(apply fn args))))
|
|
||||||
|
|
||||||
(advice-add #'compilation-goto-locus
|
|
||||||
:around #'doom-popup--compilation-goto-locus-a))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'eshell
|
|
||||||
(setq eshell-destroy-buffer-when-process-dies t)
|
|
||||||
|
|
||||||
;; When eshell runs a visual command (see `eshell-visual-commands'), it spawns
|
|
||||||
;; a term buffer to run it in, but where it spawns it is the problem...
|
|
||||||
(defun doom-popup--eshell-undedicate-popup (&rest _)
|
|
||||||
"Force spawned term buffer to share with the eshell popup (if necessary)."
|
|
||||||
(when (doom-popup-window-p)
|
|
||||||
(set-window-dedicated-p nil nil)
|
|
||||||
(add-transient-hook! (function eshell-query-kill-processes)
|
|
||||||
:after (set-window-dedicated-p nil t))))
|
|
||||||
|
|
||||||
(advice-add #'eshell-exec-visual
|
|
||||||
:around #'doom-popup--eshell-undedicate-popup))
|
|
||||||
|
|
||||||
(with-eval-after-load 'evil
|
|
||||||
(defun doom-popup--evil-command-window-execute-a nil
|
|
||||||
"Execute the command under the cursor in the appropriate buffer, rather than
|
|
||||||
the command buffer."
|
|
||||||
(interactive)
|
|
||||||
(let ((result (buffer-substring (line-beginning-position)
|
|
||||||
(line-end-position)))
|
|
||||||
(execute-fn evil-command-window-execute-fn)
|
|
||||||
(execute-window
|
|
||||||
(get-buffer-window evil-command-window-current-buffer))
|
|
||||||
(popup (selected-window)))
|
|
||||||
(if execute-window
|
|
||||||
(select-window execute-window)
|
|
||||||
(user-error "Originating buffer is no longer active"))
|
|
||||||
(delete-window popup)
|
|
||||||
(funcall execute-fn result)
|
|
||||||
(setq evil-command-window-current-buffer nil)))
|
|
||||||
|
|
||||||
(advice-add #'evil-command-window-execute
|
|
||||||
:around #'doom-popup--evil-command-window-execute-a)
|
|
||||||
|
|
||||||
;; Don't mess with popups
|
|
||||||
(advice-add #'+evil--window-swap :around #'doom-popup-save-a)
|
|
||||||
(advice-add #'evil-window-move-very-bottom :around #'doom-popup-save-a)
|
|
||||||
(advice-add #'evil-window-move-very-top :around #'doom-popup-save-a)
|
|
||||||
(advice-add #'evil-window-move-far-left :around #'doom-popup-save-a)
|
|
||||||
(advice-add #'evil-window-move-far-right :around #'doom-popup-save-a))
|
|
||||||
|
|
||||||
(with-eval-after-load 'help-mode
|
|
||||||
(defun doom-popup--switch-from-popup (location)
|
|
||||||
(let (origin enable-local-variables)
|
|
||||||
(save-popups!
|
|
||||||
(switch-to-buffer (car location) nil t)
|
|
||||||
(if (not (cdr location))
|
|
||||||
(message "Unable to find location in file")
|
|
||||||
(goto-char (cdr location))
|
|
||||||
(recenter)
|
|
||||||
(setq origin (selected-window))))
|
|
||||||
(select-window origin)))
|
|
||||||
|
|
||||||
;; Help buffers use `pop-to-window' to decide where to open followed links,
|
|
||||||
;; which can be unpredictable. It should *only* replace the original buffer we
|
|
||||||
;; opened the popup from. To fix this these three button types need to be
|
|
||||||
;; redefined to set aside the popup before following a link.
|
|
||||||
(define-button-type 'help-function-def
|
|
||||||
:supertype 'help-xref
|
|
||||||
'help-function
|
|
||||||
(lambda (fun file)
|
|
||||||
(require 'find-func)
|
|
||||||
(when (eq file 'C-source)
|
|
||||||
(setq file (help-C-file-name (indirect-function fun) 'fun)))
|
|
||||||
(doom-popup--switch-from-popup (find-function-search-for-symbol fun nil file))))
|
|
||||||
|
|
||||||
(define-button-type 'help-variable-def
|
|
||||||
:supertype 'help-xref
|
|
||||||
'help-function
|
|
||||||
(lambda (var &optional file)
|
|
||||||
(when (eq file 'C-source)
|
|
||||||
(setq file (help-C-file-name var 'var)))
|
|
||||||
(doom-popup--switch-from-popup (find-variable-noselect var file))))
|
|
||||||
|
|
||||||
(define-button-type 'help-face-def
|
|
||||||
:supertype 'help-xref
|
|
||||||
'help-function
|
|
||||||
(lambda (fun file)
|
|
||||||
(require 'find-func)
|
|
||||||
(doom-popup--switch-from-popup (find-function-search-for-symbol fun 'defface file)))))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'helpful
|
|
||||||
(defadvice! doom-popup--helpful-open-in-origin-window-a (button)
|
|
||||||
"Open links in non-popup, originating window rather than helpful's window."
|
|
||||||
:override #'helpful--navigate
|
|
||||||
(let ((path (substring-no-properties (button-get button 'path)))
|
|
||||||
enable-local-variables
|
|
||||||
origin)
|
|
||||||
(save-popups!
|
|
||||||
(find-file path)
|
|
||||||
(when-let (pos (get-text-property button 'position
|
|
||||||
(marker-buffer button)))
|
|
||||||
(goto-char pos))
|
|
||||||
(setq origin (selected-window))
|
|
||||||
(recenter))
|
|
||||||
(select-window origin))))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'helm
|
|
||||||
(setq helm-default-display-buffer-functions '(doom-popup-display-buffer-stacked-side-window-fn))
|
|
||||||
|
|
||||||
;; Fix #897: "cannot open side window" error when TAB-completing file links
|
|
||||||
(defadvice! doom-popup--helm-hide-org-links-popup-a (fn &rest args)
|
|
||||||
:around #'org-insert-link
|
|
||||||
(letf! ((defun org-completing-read (&rest args)
|
|
||||||
(when-let (win (get-buffer-window "*Org Links*"))
|
|
||||||
;; While helm is opened as a popup, it will mistaken the *Org
|
|
||||||
;; Links* popup for the "originated window", and will target it
|
|
||||||
;; for actions invoked by the user. However, since *Org Links*
|
|
||||||
;; is a popup too (they're dedicated side windows), Emacs
|
|
||||||
;; complains about being unable to split a side window. The
|
|
||||||
;; simple fix: get rid of *Org Links*!
|
|
||||||
(delete-window win)
|
|
||||||
;; ...but it must exist for org to clean up later.
|
|
||||||
(get-buffer-create "*Org Links*"))
|
|
||||||
(apply org-completing-read args)))
|
|
||||||
(apply #'funcall-interactively fn args)))
|
|
||||||
|
|
||||||
;; Fix left-over popup window when closing persistent help for `helm-M-x'
|
|
||||||
(defadvice! doom-popup--helm-elisp--persistent-help-a (candidate _fun &optional _name)
|
|
||||||
:before #'helm-elisp--persistent-help
|
|
||||||
(let (win)
|
|
||||||
(and (helm-attr 'help-running-p)
|
|
||||||
(string= candidate (helm-attr 'help-current-symbol))
|
|
||||||
(setq win (get-buffer-window (get-buffer (help-buffer))))
|
|
||||||
(delete-window win)))))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'Info
|
|
||||||
(defadvice! doom-popup--switch-to-info-window-a (&rest _)
|
|
||||||
:after #'info-lookup-symbol
|
|
||||||
(when-let (win (get-buffer-window "*info*"))
|
|
||||||
(when (doom-popup-window-p win)
|
|
||||||
(select-window win)))))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'latex
|
|
||||||
(defadvice! doom-popup--use-popup-window-for-reftex-citation-a (fn &rest args)
|
|
||||||
:around #'reftex-do-citation
|
|
||||||
(letf! ((#'switch-to-buffer-other-window #'pop-to-buffer))
|
|
||||||
(apply fn args))))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'org
|
|
||||||
(defun doom-popup--suppress-delete-other-windows-a (fn &rest args)
|
|
||||||
"Org has a scorched-earth window management policy I'm not fond of. i.e. it
|
|
||||||
kills all other windows just so it can monopolize the frame. No thanks. We can
|
|
||||||
do better."
|
|
||||||
(if doom-popup-mode
|
|
||||||
(cl-letf (((symbol-function #'delete-other-windows) #'ignore))
|
|
||||||
(cl-letf
|
|
||||||
(((symbol-function #'delete-window) #'ignore))
|
|
||||||
(apply fn args)))
|
|
||||||
(apply fn args)))
|
|
||||||
|
|
||||||
(dolist (target (list #'org-add-log-note
|
|
||||||
#'org-capture-place-template
|
|
||||||
#'org-export--dispatch-ui
|
|
||||||
#'org-agenda-get-restriction-and-command
|
|
||||||
#'org-goto-location
|
|
||||||
#'org-fast-tag-selection
|
|
||||||
#'org-fast-todo-selection))
|
|
||||||
(advice-add target
|
|
||||||
:around #'doom-popup--suppress-delete-other-windows-a))
|
|
||||||
|
|
||||||
(defun doom-popup--org-fix-goto-a (fn &rest args)
|
|
||||||
"`org-goto' uses `with-output-to-temp-buffer' to display its help buffer,
|
|
||||||
for some reason, which is very unconventional, and so requires these gymnastics
|
|
||||||
to tame (i.e. to get the popup manager to handle it)."
|
|
||||||
(if doom-popup-mode
|
|
||||||
(cl-letf ((internal-temp-output-buffer-show
|
|
||||||
(symbol-function #'internal-temp-output-buffer-show)))
|
|
||||||
(ignore internal-temp-output-buffer-show)
|
|
||||||
(cl-letf (((symbol-function #'internal-temp-output-buffer-show)
|
|
||||||
(lambda (buffer)
|
|
||||||
(let ((temp-buffer-show-function
|
|
||||||
(lambda (&rest args)
|
|
||||||
(apply
|
|
||||||
#'doom-popup-display-buffer-stacked-side-window-fn
|
|
||||||
nil args))))
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(doom-popup-buffer-mode 1))
|
|
||||||
(funcall internal-temp-output-buffer-show buffer)))))
|
|
||||||
(apply fn args)))
|
|
||||||
(apply fn args)))
|
|
||||||
(advice-add #'org-goto-location
|
|
||||||
:around #'doom-popup--org-fix-goto-a)
|
|
||||||
|
|
||||||
(defun doom-popup--org-fix-popup-window-shrinking-a (fn &rest args)
|
|
||||||
"Hides the mode-line in *Org tags* buffer so you can actually see its
|
|
||||||
content and displays it in a side window without deleting all other windows.
|
|
||||||
Ugh, such an ugly hack."
|
|
||||||
(if doom-popup-mode
|
|
||||||
(cl-letf ((read-char-exclusive
|
|
||||||
(symbol-function #'read-char-exclusive)))
|
|
||||||
(ignore read-char-exclusive)
|
|
||||||
(cl-letf (((symbol-function #'read-char-exclusive)
|
|
||||||
(lambda (&rest args) (message nil)
|
|
||||||
(apply read-char-exclusive args))))
|
|
||||||
(cl-letf ((split-window-vertically
|
|
||||||
(symbol-function #'split-window-vertically)))
|
|
||||||
(ignore split-window-vertically)
|
|
||||||
(cl-letf (((symbol-function #'split-window-vertically)
|
|
||||||
(lambda (&optional _size)
|
|
||||||
(funcall split-window-vertically
|
|
||||||
(- 0 window-min-height 1)))))
|
|
||||||
(cl-letf ((org-fit-window-to-buffer
|
|
||||||
(symbol-function #'org-fit-window-to-buffer)))
|
|
||||||
(ignore org-fit-window-to-buffer)
|
|
||||||
(cl-letf (((symbol-function #'org-fit-window-to-buffer)
|
|
||||||
(lambda (&optional window max-height
|
|
||||||
min-height shrink-only)
|
|
||||||
(when-let* ((buf (window-buffer window)))
|
|
||||||
(with-current-buffer buf
|
|
||||||
(doom-popup-buffer-mode)))
|
|
||||||
(when (> (window-buffer-height window)
|
|
||||||
(window-height window))
|
|
||||||
(fit-window-to-buffer
|
|
||||||
window
|
|
||||||
(window-buffer-height window))))))
|
|
||||||
(apply fn args)))))))
|
|
||||||
(apply fn args)))
|
|
||||||
(dolist (target (list #'org-fast-tag-selection
|
|
||||||
#'org-fast-todo-selection))
|
|
||||||
(advice-add target
|
|
||||||
:around #'doom-popup--org-fix-popup-window-shrinking-a))
|
|
||||||
|
|
||||||
(defun doom-popup--org-edit-src-exit-a (fn &rest args)
|
|
||||||
"If you switch workspaces or the src window is recreated..."
|
|
||||||
(let* ((window (selected-window))
|
|
||||||
(popup-p (doom-popup-window-p window)))
|
|
||||||
(prog1 (apply fn args)
|
|
||||||
(when (and popup-p (window-live-p window))
|
|
||||||
(delete-window window)))))
|
|
||||||
(advice-add #'org-edit-src-exit
|
|
||||||
:around #'doom-popup--org-edit-src-exit-a))
|
|
||||||
|
|
||||||
(with-eval-after-load 'org-journal
|
|
||||||
(defadvice! doom-popup--use-popup-window-a (fn &rest args)
|
|
||||||
:around #'org-journal--search-by-string
|
|
||||||
(letf! ((#'switch-to-buffer #'pop-to-buffer))
|
|
||||||
(apply fn args))))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'persp-mode
|
|
||||||
(defadvice! doom-popup--persp-mode-restore-popups-a (&rest _)
|
|
||||||
"Restore popup windows when loading a perspective from file."
|
|
||||||
:after #'persp-load-state-from-file
|
|
||||||
(dolist (window (window-list))
|
|
||||||
(when (doom-popup-parameter 'popup window)
|
|
||||||
(doom-popup--init window nil)))))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'pdf-tools
|
|
||||||
(setq tablist-context-window-display-action
|
|
||||||
'((doom-popup-display-buffer-stacked-side-window-fn)
|
|
||||||
(side . left)
|
|
||||||
(slot . 2)
|
|
||||||
(window-height . 0.3)
|
|
||||||
(inhibit-same-window . t))
|
|
||||||
pdf-annot-list-display-buffer-action
|
|
||||||
'((doom-popup-display-buffer-stacked-side-window-fn)
|
|
||||||
(side . left)
|
|
||||||
(slot . 3)
|
|
||||||
(inhibit-same-window . t))))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'profiler
|
|
||||||
(defadvice! doom-popup--profiler-report-find-entry-in-other-window-a (fn function)
|
|
||||||
:around #'profiler-report-find-entry
|
|
||||||
(letf! ((#'find-function #'find-function-other-window))
|
|
||||||
(funcall fn function))))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'undo-tree
|
|
||||||
(defadvice! doom-popup--use-popup-window-for-undo-tree-visualizer-a (fn &rest args)
|
|
||||||
"TODO"
|
|
||||||
:around #'undo-tree-visualize
|
|
||||||
(if undo-tree-visualizer-diff
|
|
||||||
(apply fn args)
|
|
||||||
(letf! ((#'switch-to-buffer-other-window #'pop-to-buffer))
|
|
||||||
(apply fn args)))))
|
|
||||||
|
|
||||||
(with-eval-after-load 'wdired
|
|
||||||
;; close the popup after you're done with a wdired buffer
|
|
||||||
(advice-add #'wdired-abort-changes :after #'doom-popup-close-a)
|
|
||||||
(advice-add #'wdired-finish-edit :after #'doom-popup-close-a))
|
|
||||||
|
|
||||||
(with-eval-after-load 'wgrep
|
|
||||||
;; close the popup after you're done with a wgrep buffer
|
|
||||||
(advice-add #'wgrep-abort-changes :after #'doom-popup-close-a)
|
|
||||||
(advice-add #'wgrep-finish-edit :after #'doom-popup-close-a))
|
|
||||||
|
|
||||||
|
|
||||||
(with-eval-after-load 'which-key
|
|
||||||
(when (eq which-key-popup-type 'side-window)
|
|
||||||
(setq which-key-popup-type 'custom
|
|
||||||
which-key-custom-popup-max-dimensions-function
|
|
||||||
(lambda (_) (which-key--side-window-max-dimensions))
|
|
||||||
which-key-custom-hide-popup-function #'which-key--hide-buffer-side-window
|
|
||||||
which-key-custom-show-popup-function
|
|
||||||
(lambda (act-popup-dim)
|
|
||||||
(letf! (defun display-buffer-in-side-window (buffer alist)
|
|
||||||
(doom-popup-display-buffer-stacked-side-window-fn
|
|
||||||
buffer (append '((vslot . -9999) (select . t)) alist)))
|
|
||||||
;; HACK Fix #2219 where the which-key popup would get cut off.
|
|
||||||
(setcar act-popup-dim (1+ (car act-popup-dim)))
|
|
||||||
(which-key--show-buffer-side-window act-popup-dim))))))
|
|
||||||
|
|
||||||
|
|
||||||
;; Users should be able to hop into popups easily, but Elisp shouldn't.
|
|
||||||
(with-eval-after-load 'windmove
|
|
||||||
(defun doom-popup--ignore-window-parameters-a (fn &rest args)
|
|
||||||
"Allow *interactive* window moving commands to traverse popups."
|
|
||||||
(cl-letf ((windmove-find-other-window
|
|
||||||
(symbol-function #'windmove-find-other-window)))
|
|
||||||
(ignore windmove-find-other-window)
|
|
||||||
(cl-letf (((symbol-function #'windmove-find-other-window)
|
|
||||||
(lambda (dir &optional arg window)
|
|
||||||
(window-in-direction (pcase dir
|
|
||||||
(`up 'above)
|
|
||||||
(`down 'below)
|
|
||||||
(_ dir))
|
|
||||||
window
|
|
||||||
(bound-and-true-p doom-popup-mode)
|
|
||||||
arg
|
|
||||||
windmove-wrap-around
|
|
||||||
t))))
|
|
||||||
(apply fn args))))
|
|
||||||
|
|
||||||
(dolist (target (list #'windmove-up #'windmove-down
|
|
||||||
#'windmove-left #'windmove-right))
|
|
||||||
(advice-add target
|
|
||||||
:around #'doom-popup--ignore-window-parameters-a)))
|
|
||||||
|
|
||||||
(provide 'doom-popup-hacks)
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
;;; settings.el -*- lexical-binding: t; -*-
|
|
||||||
|
|
||||||
;; The MIT License (MIT)
|
|
||||||
;;
|
|
||||||
;; Copyright (c) 2014-2024 Henrik Lissner.
|
|
||||||
;;
|
|
||||||
;; Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
;; a copy of this software and associated documentation files (the
|
|
||||||
;; "Software"), to deal in the Software without restriction, including
|
|
||||||
;; without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
;; distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
;; permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
;; the following conditions:
|
|
||||||
;;
|
|
||||||
;; The above copyright notice and this permission notice shall be
|
|
||||||
;; included in all copies or substantial portions of the Software.
|
|
||||||
;;
|
|
||||||
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defvar doom-popup--display-buffer-alist nil)
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defvar doom-popup-defaults
|
|
||||||
(list :side 'bottom
|
|
||||||
:height 0.16
|
|
||||||
:width 40
|
|
||||||
:quit t
|
|
||||||
:select #'ignore
|
|
||||||
:ttl 5)
|
|
||||||
"Default properties for popup rules defined with `set-popup-rule!'.")
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-make-rule (predicate plist)
|
|
||||||
(if (plist-get plist :ignore)
|
|
||||||
(list predicate nil)
|
|
||||||
(let* ((plist (append plist doom-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 (doom-popup-buffer)
|
|
||||||
,@alist
|
|
||||||
(window-parameters ,@params)))))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun set-popup-rule! (predicate &rest plist)
|
|
||||||
"Define a popup rule.
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
PREDICATE accepts anything that the CONDITION argument in `buffer-match-p' takes
|
|
||||||
(if you're on Emacs 29 or newer). On Emacs 28 or older, it can either be a) a
|
|
||||||
regexp string (matched against the buffer's name) or b) a function that takes
|
|
||||||
two arguments (a buffer name and the ACTION argument of `display-buffer') and
|
|
||||||
returns a boolean.
|
|
||||||
|
|
||||||
PLIST can be made up of any of the following properties:
|
|
||||||
|
|
||||||
:ignore BOOL
|
|
||||||
If BOOL is non-nil, popups matching PREDICATE will not be handled by the popup
|
|
||||||
system. Use this for buffers that have their own window management system like
|
|
||||||
magit or helm.
|
|
||||||
|
|
||||||
: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, `doom-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
|
|
||||||
`doom-popup-display-buffer-stacked-side-window-fn' or `display-buffer-in-side-window'
|
|
||||||
is in :actions or `doom-popup-default-display-buffer-actions'.
|
|
||||||
|
|
||||||
:size/:width/:height FLOAT|INT|FN
|
|
||||||
Determines the size of the popup. If more than 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 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 `doom-popup-shrink-to-fit'.
|
|
||||||
|
|
||||||
:slot/:vslot INT
|
|
||||||
(This only applies to popups with a :side and only if :actions is blank or
|
|
||||||
contains the `doom-popup-display-buffer-stacked-side-window-fn' action) These control
|
|
||||||
how multiple popups are laid out. INT can be any integer, positive and
|
|
||||||
negative.
|
|
||||||
|
|
||||||
: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).
|
|
||||||
|
|
||||||
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. 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 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 and is left to its own devices.
|
|
||||||
If t, resort to the default :ttl in `doom-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 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 anywhere.
|
|
||||||
If 'other, close this popup if ESC/C-g is pressed outside of any popup. This
|
|
||||||
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. 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
|
|
||||||
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 originating window
|
|
||||||
(where you were before the popup opened). The popup system does nothing else
|
|
||||||
and ignores the function's return value.
|
|
||||||
|
|
||||||
:modeline BOOL|FN|LIST
|
|
||||||
Can be t (show the default modeline), nil (show no modeline), a function that
|
|
||||||
returns a modeline format or a valid value for `mode-line-format' to be used
|
|
||||||
verbatim. 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 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). 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 '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 `(elisp)Window Parameters'.
|
|
||||||
|
|
||||||
If any of these are omitted, defaults derived from `doom-popup-defaults' will be
|
|
||||||
used.
|
|
||||||
|
|
||||||
\(fn PREDICATE &key IGNORE ACTIONS SIDE SIZE WIDTH HEIGHT SLOT VSLOT TTL QUIT SELECT MODELINE AUTOSAVE PARAMETERS)"
|
|
||||||
(declare (indent defun))
|
|
||||||
(push (doom-popup-make-rule predicate plist) doom-popup--display-buffer-alist)
|
|
||||||
;; TODO: Don't overwrite user entries in `display-buffer-alist'
|
|
||||||
(when (bound-and-true-p doom-popup-mode)
|
|
||||||
(setq display-buffer-alist doom-popup--display-buffer-alist))
|
|
||||||
doom-popup--display-buffer-alist)
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun set-popup-rules! (&rest rulesets)
|
|
||||||
"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:
|
|
||||||
|
|
||||||
(set-popup-rules!
|
|
||||||
'((\"^ \\*\" :slot 1 :vslot -1 :size #'doom-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 (doom-popup-make-rule (car rule) (cdr rule))
|
|
||||||
doom-popup--display-buffer-alist)))
|
|
||||||
(when (bound-and-true-p doom-popup-mode)
|
|
||||||
(setq display-buffer-alist doom-popup--display-buffer-alist))
|
|
||||||
doom-popup--display-buffer-alist)
|
|
||||||
|
|
||||||
(provide 'doom-popup-settings)
|
|
||||||
@@ -1,688 +0,0 @@
|
|||||||
;;; doom-popup.el -*- lexical-binding: t; -*-
|
|
||||||
|
|
||||||
;; The MIT License (MIT)
|
|
||||||
;;
|
|
||||||
;; Copyright (c) 2014-2024 Henrik Lissner.
|
|
||||||
;;
|
|
||||||
;; Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
;; a copy of this software and associated documentation files (the
|
|
||||||
;; "Software"), to deal in the Software without restriction, including
|
|
||||||
;; without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
;; distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
;; permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
;; the following conditions:
|
|
||||||
;;
|
|
||||||
;; The above copyright notice and this permission notice shall be
|
|
||||||
;; included in all copies or substantial portions of the Software.
|
|
||||||
;;
|
|
||||||
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
(require 'doom-popup-settings)
|
|
||||||
|
|
||||||
(defvar doom-popup--internal nil)
|
|
||||||
|
|
||||||
(defun doom-popup--remember (windows)
|
|
||||||
"Remember WINDOWS (a list of windows) for later restoration."
|
|
||||||
(cl-assert (cl-every #'windowp windows) t)
|
|
||||||
(setq doom-popup--last
|
|
||||||
(cl-loop for w in windows
|
|
||||||
collect (cons (window-buffer w)
|
|
||||||
(window-state-get w)))))
|
|
||||||
|
|
||||||
(defun doom-popup--kill-buffer (buffer ttl)
|
|
||||||
"Tries to kill BUFFER, as was requested by a transient timer. If it fails, eg.
|
|
||||||
the buffer is visible, then set another timer and try again later."
|
|
||||||
(let ((inhibit-quit t))
|
|
||||||
(cond ((not (buffer-live-p buffer)))
|
|
||||||
((not (get-buffer-window buffer t))
|
|
||||||
(with-demoted-errors "Error killing transient buffer: %s"
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(let ((kill-buffer-hook (remq 'doom-popup-kill-buffer-hook-h
|
|
||||||
kill-buffer-hook))
|
|
||||||
confirm-kill-processes)
|
|
||||||
(when-let (process (get-buffer-process buffer))
|
|
||||||
(when (eq (process-type process) 'real)
|
|
||||||
(kill-process process)))
|
|
||||||
(let (kill-buffer-query-functions)
|
|
||||||
;; HACK The debugger backtrace buffer, when killed, called
|
|
||||||
;; `top-level'. This causes jumpiness when the popup
|
|
||||||
;; manager tries to clean it up.
|
|
||||||
(cl-letf (((symbol-function #'top-level) #'ignore))
|
|
||||||
(kill-buffer buffer)))))))
|
|
||||||
((let ((ttl (if (= ttl 0)
|
|
||||||
(or (plist-get doom-popup-defaults :ttl) 3)
|
|
||||||
ttl)))
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(setq doom-popup--timer
|
|
||||||
(run-at-time
|
|
||||||
ttl nil #'doom-popup--kill-buffer buffer ttl))))))))
|
|
||||||
|
|
||||||
(defun doom-popup--delete-window (window)
|
|
||||||
"Do housekeeping before destroying a popup window.
|
|
||||||
|
|
||||||
+ Disables `doom-popup-buffer-mode' so that any hooks attached to it get a chance to
|
|
||||||
run and do cleanup of its own.
|
|
||||||
+ Either kills the buffer or sets a transient timer, if the window has a
|
|
||||||
`transient' window parameter (see `doom-popup-window-parameters').
|
|
||||||
+ And finally deletes the window!"
|
|
||||||
(let ((buffer (window-buffer window))
|
|
||||||
(inhibit-quit t))
|
|
||||||
(and (or (buffer-file-name buffer)
|
|
||||||
(if-let (base-buffer (buffer-base-buffer buffer))
|
|
||||||
(buffer-file-name base-buffer)))
|
|
||||||
(buffer-modified-p buffer)
|
|
||||||
(let ((autosave (doom-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))
|
|
||||||
(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)
|
|
||||||
(doom-popup-buffer-mode -1)
|
|
||||||
(unless doom-popup--inhibit-transient
|
|
||||||
(let ((ttl (doom-popup-parameter 'ttl window)))
|
|
||||||
(when (eq ttl 't)
|
|
||||||
(setq ttl (plist-get doom-popup-defaults :ttl)))
|
|
||||||
(cond ((null ttl))
|
|
||||||
((functionp ttl)
|
|
||||||
(funcall ttl buffer))
|
|
||||||
((not (integerp ttl))
|
|
||||||
(signal 'wrong-type-argument (list 'integerp ttl)))
|
|
||||||
((= ttl 0)
|
|
||||||
(doom-popup--kill-buffer buffer 0))
|
|
||||||
((add-hook 'kill-buffer-hook
|
|
||||||
#'doom-popup-kill-buffer-hook-h
|
|
||||||
nil
|
|
||||||
t)
|
|
||||||
(setq doom-popup--timer
|
|
||||||
(run-at-time ttl nil #'doom-popup--kill-buffer
|
|
||||||
buffer ttl))))))))))
|
|
||||||
|
|
||||||
(defun doom-popup--delete-other-windows (window)
|
|
||||||
"Fixes `delete-other-windows' when used from a popup window."
|
|
||||||
(when-let (window (ignore-errors (doom-popup/raise window)))
|
|
||||||
(let ((ignore-window-parameters t))
|
|
||||||
(delete-other-windows window)))
|
|
||||||
nil)
|
|
||||||
|
|
||||||
(defun doom-popup--normalize-alist (alist)
|
|
||||||
"Merge `doom-popup-default-alist' and `doom-popup-default-parameters' with ALIST."
|
|
||||||
(when alist
|
|
||||||
(let ((alist ; handle defaults
|
|
||||||
(cl-remove-duplicates
|
|
||||||
(append alist doom-popup-default-alist)
|
|
||||||
:key #'car-safe :from-end t))
|
|
||||||
(parameters
|
|
||||||
(cl-remove-duplicates
|
|
||||||
(append (cdr (assq 'window-parameters alist))
|
|
||||||
doom-popup-default-parameters)
|
|
||||||
:key #'car-safe :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 (assq-delete-all 'size alist))
|
|
||||||
(setf (alist-get param alist) size))
|
|
||||||
(setf (alist-get 'window-parameters alist)
|
|
||||||
parameters)
|
|
||||||
;; Fixes #1305: addresses an edge case where a popup with a :size, :width
|
|
||||||
;; or :height greater than the current frame's dimensions causes
|
|
||||||
;; hanging/freezing (a bug in Emacs' `display-buffer' API perhaps?)
|
|
||||||
(let ((width (cdr (assq 'window-width alist)))
|
|
||||||
(height (cdr (assq 'window-height alist))))
|
|
||||||
(setf (alist-get 'window-width alist)
|
|
||||||
(if (numberp width)
|
|
||||||
(min width (frame-width))
|
|
||||||
width))
|
|
||||||
(setf (alist-get 'window-height alist)
|
|
||||||
(if (numberp height)
|
|
||||||
(min height (frame-height))
|
|
||||||
height))
|
|
||||||
alist))))
|
|
||||||
|
|
||||||
(defun doom-popup--split-window (window size side)
|
|
||||||
"Ensure a non-dedicated/popup window is selected when splitting a window."
|
|
||||||
(unless doom-popup--internal
|
|
||||||
(cl-loop for win
|
|
||||||
in (cons (or window (selected-window))
|
|
||||||
(window-list nil 0 window))
|
|
||||||
unless (doom-popup-window-p win)
|
|
||||||
return (setq window win)))
|
|
||||||
(let ((ignore-window-parameters t))
|
|
||||||
(split-window window size side)))
|
|
||||||
|
|
||||||
(defun doom-popup--maybe-select-window (window origin)
|
|
||||||
"Select a window based on `doom-popup--inhibit-select' and this window's `select'
|
|
||||||
parameter."
|
|
||||||
(unless doom-popup--inhibit-select
|
|
||||||
;; REVIEW: Once our minimum version is bumped up to Emacs 30.x, replace this
|
|
||||||
;; with `post-command-select-window' window parameter.
|
|
||||||
(let ((select (doom-popup-parameter 'select window)))
|
|
||||||
(if (functionp select)
|
|
||||||
(funcall select window origin)
|
|
||||||
(select-window (if select window origin))))))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup--init (window &optional alist)
|
|
||||||
"Initializes a popup window. Run any time a popup is opened. It sets the
|
|
||||||
default window parameters for popup windows, clears leftover transient timers
|
|
||||||
and enables `doom-popup-buffer-mode'."
|
|
||||||
(with-selected-window window
|
|
||||||
(setq alist (delq (assq 'actions alist) alist))
|
|
||||||
(set-window-parameter window 'popup t)
|
|
||||||
(set-window-parameter window 'split-window #'doom-popup--split-window)
|
|
||||||
(set-window-parameter window 'delete-window #'doom-popup--delete-window)
|
|
||||||
(set-window-parameter window
|
|
||||||
'delete-other-windows
|
|
||||||
#'doom-popup--delete-other-windows)
|
|
||||||
(set-window-dedicated-p window 'popup)
|
|
||||||
(window-preserve-size
|
|
||||||
window (memq (window-parameter window 'window-side)
|
|
||||||
'(left right))
|
|
||||||
t)
|
|
||||||
(doom-popup-buffer-mode +1)
|
|
||||||
(run-hooks 'doom-popup-create-window-hook)))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Public library
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-buffer-p (&optional buffer)
|
|
||||||
"Return non-nil if BUFFER is a popup buffer. Defaults to the current buffer."
|
|
||||||
(when doom-popup-mode
|
|
||||||
(let ((buffer (or buffer (current-buffer))))
|
|
||||||
(and (bufferp buffer)
|
|
||||||
(buffer-live-p buffer)
|
|
||||||
(buffer-local-value 'doom-popup-buffer-mode buffer)
|
|
||||||
buffer))))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-window-p (&optional window)
|
|
||||||
"Return non-nil if WINDOW is a popup window. Defaults to the current window."
|
|
||||||
(when doom-popup-mode
|
|
||||||
(let ((window (or window (selected-window))))
|
|
||||||
(and (windowp window)
|
|
||||||
(window-live-p window)
|
|
||||||
(window-parameter window 'popup)
|
|
||||||
window))))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-buffer (buffer &optional alist)
|
|
||||||
"Open BUFFER in a popup window. ALIST describes its features."
|
|
||||||
(let* ((origin (selected-window))
|
|
||||||
(window-min-height 3)
|
|
||||||
(alist (doom-popup--normalize-alist alist))
|
|
||||||
(actions (or (cdr (assq 'actions alist))
|
|
||||||
doom-popup-default-display-buffer-actions)))
|
|
||||||
(or (let* ((alist (remove (assq 'window-width alist) alist))
|
|
||||||
(alist (remove (assq 'window-height alist) alist))
|
|
||||||
(window (display-buffer-reuse-window buffer alist)))
|
|
||||||
(when window
|
|
||||||
(doom-popup--maybe-select-window window origin)
|
|
||||||
window))
|
|
||||||
(when-let (popup (cl-loop for func in actions
|
|
||||||
if (funcall func buffer alist)
|
|
||||||
return it))
|
|
||||||
(doom-popup--init popup alist)
|
|
||||||
(doom-popup--maybe-select-window popup origin)
|
|
||||||
popup))))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-parameter (parameter &optional window)
|
|
||||||
"Fetch the window PARAMETER (symbol) of WINDOW"
|
|
||||||
(window-parameter (or window (selected-window)) parameter))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-parameter-fn (parameter &optional window &rest args)
|
|
||||||
"Fetch the window PARAMETER (symbol) of WINDOW. If it is a function, run it
|
|
||||||
with ARGS to get its return value."
|
|
||||||
(let ((val (doom-popup-parameter parameter window)))
|
|
||||||
(if (functionp val)
|
|
||||||
(apply val args)
|
|
||||||
val)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-windows ()
|
|
||||||
"Returns a list of all popup windows."
|
|
||||||
(cl-remove-if-not #'doom-popup-window-p (window-list)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-shrink-to-fit (&optional window)
|
|
||||||
"Shrinks WINDOW to fit the buffer contents, if the buffer isn't empty.
|
|
||||||
|
|
||||||
Uses `shrink-window-if-larger-than-buffer'."
|
|
||||||
(unless window
|
|
||||||
(setq window (selected-window)))
|
|
||||||
(unless (= (- (point-max) (point-min)) 0)
|
|
||||||
(shrink-window-if-larger-than-buffer window)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-alist-from-window-state (state)
|
|
||||||
"Convert window STATE (from `window-state-get') to a `display-buffer' alist."
|
|
||||||
(let* ((params (alist-get 'parameters state)))
|
|
||||||
`((side . ,(alist-get 'window-side params))
|
|
||||||
(window-width . ,(alist-get 'total-width state))
|
|
||||||
(window-height . ,(alist-get 'total-height state))
|
|
||||||
(window-parameters ,@params))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Hooks
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-adjust-fringes-h ()
|
|
||||||
"Hides the fringe in popup windows, restoring them if `doom-popup-buffer-mode' is
|
|
||||||
disabled."
|
|
||||||
(let ((f (if (bound-and-true-p doom-popup-buffer-mode) 0)))
|
|
||||||
(set-window-fringes nil f f fringes-outside-margins)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-adjust-margins-h ()
|
|
||||||
"Creates padding for the popup window determined by `doom-popup-margin-width',
|
|
||||||
restoring it if `doom-popup-buffer-mode' is disabled."
|
|
||||||
(when doom-popup-margin-width
|
|
||||||
(unless (memq (window-parameter nil 'window-side) '(left right))
|
|
||||||
(let ((m (if (bound-and-true-p doom-popup-buffer-mode) doom-popup-margin-width)))
|
|
||||||
(set-window-margins nil m m)))))
|
|
||||||
|
|
||||||
(defvar hide-mode-line-format)
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-set-modeline-on-enable-h ()
|
|
||||||
"Don't show modeline in popup windows without a `modeline' window-parameter.
|
|
||||||
Possible values for this parameter are:
|
|
||||||
|
|
||||||
t show the mode-line as normal
|
|
||||||
nil hide the modeline entirely (the default)
|
|
||||||
a function `mode-line-format' is set to its return value
|
|
||||||
|
|
||||||
Any non-nil value besides the above will be used as the raw value for
|
|
||||||
`mode-line-format'."
|
|
||||||
(when (and (bound-and-true-p doom-popup-buffer-mode)
|
|
||||||
(boundp 'hide-mode-line-mode))
|
|
||||||
(let ((modeline (doom-popup-parameter 'modeline)))
|
|
||||||
(cond ((eq modeline 't))
|
|
||||||
((null modeline)
|
|
||||||
;; TODO use `mode-line-format' window parameter instead (emacs 26+)
|
|
||||||
(hide-mode-line-mode +1))
|
|
||||||
((let ((hide-mode-line-format
|
|
||||||
(if (functionp modeline)
|
|
||||||
(funcall modeline)
|
|
||||||
modeline)))
|
|
||||||
(hide-mode-line-mode +1)))))))
|
|
||||||
(put 'doom-popup-set-modeline-on-enable-h 'permanent-local-hook t)
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-unset-modeline-on-disable-h ()
|
|
||||||
"Restore the modeline when `doom-popup-buffer-mode' is deactivated."
|
|
||||||
(when (and (not (bound-and-true-p doom-popup-buffer-mode))
|
|
||||||
(bound-and-true-p hide-mode-line-mode)
|
|
||||||
(not (bound-and-true-p global-hide-mode-line-mode)))
|
|
||||||
(hide-mode-line-mode -1)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-close-on-escape-h ()
|
|
||||||
"If called inside a popup, try to close that popup window (see
|
|
||||||
`doom-popup/close'). If called outside, try to close all popup windows (see
|
|
||||||
`doom-popup/close-all')."
|
|
||||||
(if (doom-popup-window-p)
|
|
||||||
(doom-popup/close)
|
|
||||||
(doom-popup/close-all)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-cleanup-rules-h ()
|
|
||||||
"Cleans up any duplicate popup rules."
|
|
||||||
(interactive)
|
|
||||||
(setq doom-popup--display-buffer-alist
|
|
||||||
(cl-delete-duplicates doom-popup--display-buffer-alist
|
|
||||||
:key #'car :test #'equal :from-end t))
|
|
||||||
(when doom-popup-mode
|
|
||||||
(setq display-buffer-alist doom-popup--display-buffer-alist)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-kill-buffer-hook-h ()
|
|
||||||
"TODO"
|
|
||||||
(when-let (window (get-buffer-window))
|
|
||||||
(when (doom-popup-window-p window)
|
|
||||||
(let ((doom-popup--inhibit-transient t))
|
|
||||||
(doom-popup--delete-window window)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;
|
|
||||||
;; Commands
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defalias 'other-popup #'doom-popup/other)
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup/buffer ()
|
|
||||||
"Open this buffer in a popup window."
|
|
||||||
(interactive)
|
|
||||||
(let ((doom-popup-default-display-buffer-actions
|
|
||||||
'(doom-popup-display-buffer-stacked-side-window-fn))
|
|
||||||
(display-buffer-alist doom-popup--display-buffer-alist)
|
|
||||||
(buffer (current-buffer)))
|
|
||||||
(push (doom-popup-make-rule "." doom-popup-defaults) display-buffer-alist)
|
|
||||||
(bury-buffer)
|
|
||||||
(pop-to-buffer buffer)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup/other ()
|
|
||||||
"Cycle through popup windows, like `other-window'. Ignores regular windows."
|
|
||||||
(interactive)
|
|
||||||
(if-let (popups (cl-remove-if-not
|
|
||||||
(lambda (w) (or (doom-popup-window-p w)
|
|
||||||
;; This command should be able to hop between
|
|
||||||
;; windows with a `no-other-window'
|
|
||||||
;; parameter, since `other-window' won't.
|
|
||||||
(window-parameter w 'no-other-window)))
|
|
||||||
(window-list)))
|
|
||||||
(select-window (if (or (doom-popup-window-p)
|
|
||||||
(window-parameter nil 'no-other-window))
|
|
||||||
(let ((window (selected-window)))
|
|
||||||
(or (car-safe (cdr (memq window popups)))
|
|
||||||
(car (delq window popups))
|
|
||||||
(car popups)))
|
|
||||||
(car popups)))
|
|
||||||
(user-error "No popups are open")))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup/close (&optional window force-p)
|
|
||||||
"Close WINDOW, if it's a popup window.
|
|
||||||
|
|
||||||
This will do nothing if the popup's `quit' window parameter is either nil or
|
|
||||||
'other. This window parameter is ignored if FORCE-P is non-nil."
|
|
||||||
(interactive
|
|
||||||
(list (selected-window)
|
|
||||||
current-prefix-arg))
|
|
||||||
(let ((window (or window (selected-window))))
|
|
||||||
(when (and (doom-popup-window-p window)
|
|
||||||
(or force-p
|
|
||||||
(memq (doom-popup-parameter-fn 'quit window window)
|
|
||||||
'(t current))))
|
|
||||||
(when doom-popup--remember-last
|
|
||||||
(doom-popup--remember (list window)))
|
|
||||||
(delete-window window)
|
|
||||||
t)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup/close-all (&optional force-p)
|
|
||||||
"Close all open popup windows.
|
|
||||||
|
|
||||||
This will ignore popups with an `quit' parameter that is either nil or 'current.
|
|
||||||
This window parameter is ignored if FORCE-P is non-nil."
|
|
||||||
(interactive "P")
|
|
||||||
(let (targets doom-popup--remember-last)
|
|
||||||
(dolist (window (doom-popup-windows))
|
|
||||||
(when (or force-p
|
|
||||||
(memq (doom-popup-parameter-fn 'quit window window)
|
|
||||||
'(t other)))
|
|
||||||
(push window targets)))
|
|
||||||
(when targets
|
|
||||||
(doom-popup--remember targets)
|
|
||||||
(mapc #'delete-window targets)
|
|
||||||
t)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup/toggle ()
|
|
||||||
"Toggle any visible popups.
|
|
||||||
If no popups are available, display the *Messages* buffer in a popup window."
|
|
||||||
(interactive)
|
|
||||||
(let ((doom-popup--inhibit-transient t))
|
|
||||||
(cond ((doom-popup-windows) (doom-popup/close-all t))
|
|
||||||
((ignore-errors (doom-popup/restore)))
|
|
||||||
((display-buffer (get-buffer "*Messages*"))))))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup/restore ()
|
|
||||||
"Restore the last popups that were closed, if any."
|
|
||||||
(interactive)
|
|
||||||
(unless doom-popup--last
|
|
||||||
(error "No popups to restore"))
|
|
||||||
(cl-loop for (buffer . state) in doom-popup--last
|
|
||||||
if (buffer-live-p buffer)
|
|
||||||
do (doom-popup-buffer buffer (doom-popup-alist-from-window-state state)))
|
|
||||||
(setq doom-popup--last nil)
|
|
||||||
t)
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup/raise (window &optional arg)
|
|
||||||
"Raise the current popup window into a regular window and
|
|
||||||
return it. If prefix ARG, raise the current popup into a new
|
|
||||||
window and return that window."
|
|
||||||
(interactive
|
|
||||||
(list (selected-window) current-prefix-arg))
|
|
||||||
(cl-check-type window window)
|
|
||||||
(unless (doom-popup-window-p window)
|
|
||||||
(user-error "Cannot raise a non-popup window"))
|
|
||||||
(let ((buffer (current-buffer))
|
|
||||||
(doom-popup--inhibit-transient t)
|
|
||||||
doom-popup--remember-last)
|
|
||||||
(doom-popup/close window 'force)
|
|
||||||
(let (display-buffer-alist)
|
|
||||||
(if arg
|
|
||||||
(pop-to-buffer buffer)
|
|
||||||
(switch-to-buffer buffer)))
|
|
||||||
(selected-window)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup/diagnose ()
|
|
||||||
"Reveal what popup rule will be used for the current buffer."
|
|
||||||
(interactive)
|
|
||||||
(if-let (rule (cl-loop with bname = (buffer-name)
|
|
||||||
for (pred . action) in display-buffer-alist
|
|
||||||
if (and (functionp pred) (funcall pred bname action))
|
|
||||||
return (cons pred action)
|
|
||||||
else if (and (stringp pred) (string-match-p pred bname))
|
|
||||||
return (cons pred action)))
|
|
||||||
(message "Rule matches: %s" rule)
|
|
||||||
(message "No popup rule for this buffer")))
|
|
||||||
|
|
||||||
|
|
||||||
;; Advice
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-close-a (&rest _)
|
|
||||||
"TODO"
|
|
||||||
(doom-popup/close nil t))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-save-a (fn &rest args)
|
|
||||||
"Sets aside all popups before executing the original function, usually to
|
|
||||||
prevent the popup(s) from messing up the UI (or vice versa)."
|
|
||||||
(save-popups! (apply fn args)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun doom-popup-display-buffer-fullframe-fn (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 doom-popup-display-buffer-stacked-side-window-fn (buffer alist)
|
|
||||||
"A `display-buffer' action that serves as an alternative to
|
|
||||||
`display-buffer-in-side-window', but allows for stacking popups with the `vslot'
|
|
||||||
alist entry.
|
|
||||||
|
|
||||||
Accepts the same arguments as `display-buffer-in-side-window'. You must set
|
|
||||||
`window--sides-inhibit-check' to non-nil for this work properly."
|
|
||||||
(let* ((side (or (cdr (assq 'side alist)) 'bottom))
|
|
||||||
(slot (or (cdr (assq 'slot alist)) 0))
|
|
||||||
(vslot (or (cdr (assq 'vslot alist)) 0))
|
|
||||||
(left-or-right (memq side '(left right)))
|
|
||||||
(display-buffer-mark-dedicated (or display-buffer-mark-dedicated
|
|
||||||
'popup)))
|
|
||||||
|
|
||||||
(cond ((not (memq side '(top bottom left right)))
|
|
||||||
(error "Invalid side %s specified" side))
|
|
||||||
((not (numberp slot))
|
|
||||||
(error "Invalid slot %s specified" slot))
|
|
||||||
((not (numberp vslot))
|
|
||||||
(error "Invalid vslot %s specified" vslot)))
|
|
||||||
|
|
||||||
(let* ((live (get-window-with-predicate
|
|
||||||
(lambda (window)
|
|
||||||
(and (eq (window-parameter window 'window-side) side)
|
|
||||||
(eq (window-parameter window 'window-vslot) vslot)))
|
|
||||||
nil))
|
|
||||||
;; As opposed to the `window-side' property, our `window-vslot'
|
|
||||||
;; parameter is set only on a single live window and never on
|
|
||||||
;; internal windows. Moreover, as opposed to `window-with-parameter'
|
|
||||||
;; (as used by the original `display-buffer-in-side-window'),
|
|
||||||
;; `get-window-with-predicate' only returns live windows anyway. In
|
|
||||||
;; any case, we will have missed the major side window and got a
|
|
||||||
;; child instead if the major side window happens to be an internal
|
|
||||||
;; window with multiple children. In that case, all childen should
|
|
||||||
;; have the same `window-vslot' parameter, and the major side window
|
|
||||||
;; is the parent of the live window.
|
|
||||||
(prev (and live (window-prev-sibling live)))
|
|
||||||
(next (and live (window-next-sibling live)))
|
|
||||||
(prev-vslot (and prev (window-parameter prev 'window-vslot)))
|
|
||||||
(next-vslot (and next (window-parameter next 'window-vslot)))
|
|
||||||
(major (and live
|
|
||||||
(if (or (eq prev-vslot vslot) (eq next-vslot vslot))
|
|
||||||
(window-parent live)
|
|
||||||
live)))
|
|
||||||
(reversed (window--sides-reverse-on-frame-p (selected-frame)))
|
|
||||||
(windows
|
|
||||||
(cond ((window-live-p major)
|
|
||||||
(list major))
|
|
||||||
((window-valid-p major)
|
|
||||||
(let* ((first (window-child major))
|
|
||||||
(next (window-next-sibling first))
|
|
||||||
(windows (list next first)))
|
|
||||||
(setq reversed (> (window-parameter first 'window-slot)
|
|
||||||
(window-parameter next 'window-slot)))
|
|
||||||
(while (setq next (window-next-sibling next))
|
|
||||||
(setq windows (cons next windows)))
|
|
||||||
(if reversed windows (nreverse windows))))))
|
|
||||||
(slots (if major (max 1 (window-child-count major))))
|
|
||||||
(max-slots
|
|
||||||
(nth (plist-get '(left 0 top 1 right 2 bottom 3) side)
|
|
||||||
window-sides-slots))
|
|
||||||
(window--sides-inhibit-check t)
|
|
||||||
window this-window this-slot prev-window next-window
|
|
||||||
best-window best-slot abs-slot)
|
|
||||||
|
|
||||||
(cond ((and (numberp max-slots) (<= max-slots 0))
|
|
||||||
nil)
|
|
||||||
((not windows)
|
|
||||||
(cl-letf (((symbol-function
|
|
||||||
'window--make-major-side-window-next-to)
|
|
||||||
(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)
|
|
||||||
(add-to-list 'window-persistent-parameters
|
|
||||||
'(window-vslot . writable))
|
|
||||||
window)))
|
|
||||||
(t
|
|
||||||
;; Scan windows on SIDE.
|
|
||||||
(catch 'found
|
|
||||||
(dolist (window windows)
|
|
||||||
(setq this-slot (window-parameter window 'window-slot))
|
|
||||||
(cond ((not (numberp this-slot)))
|
|
||||||
((= this-slot slot) ; A window with a matching slot found
|
|
||||||
(setq this-window window)
|
|
||||||
(throw 'found t))
|
|
||||||
(t
|
|
||||||
;; Check if this window has a better slot value wrt the
|
|
||||||
;; slot of the window we want.
|
|
||||||
(setq abs-slot
|
|
||||||
(if (or (and (> this-slot 0) (> slot 0))
|
|
||||||
(and (< this-slot 0) (< slot 0)))
|
|
||||||
(abs (- slot this-slot))
|
|
||||||
(+ (abs slot) (abs this-slot))))
|
|
||||||
(unless (and best-slot (<= best-slot abs-slot))
|
|
||||||
(setq best-window window)
|
|
||||||
(setq best-slot abs-slot))
|
|
||||||
(if reversed
|
|
||||||
(cond
|
|
||||||
((<= this-slot slot)
|
|
||||||
(setq next-window window))
|
|
||||||
((not prev-window)
|
|
||||||
(setq prev-window window)))
|
|
||||||
(cond
|
|
||||||
((<= this-slot slot)
|
|
||||||
(setq prev-window window))
|
|
||||||
((not next-window)
|
|
||||||
(setq next-window window))))))))
|
|
||||||
|
|
||||||
;; `this-window' is the first window with the same SLOT.
|
|
||||||
;; `prev-window' is the window with the largest slot < SLOT. A new
|
|
||||||
;; window will be created after it.
|
|
||||||
;; `next-window' is the window with the smallest slot > SLOT. A new
|
|
||||||
;; window will be created before it.
|
|
||||||
;; `best-window' is the window with the smallest absolute
|
|
||||||
;; difference of its slot and SLOT.
|
|
||||||
(or (and this-window
|
|
||||||
;; Reuse `this-window'.
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(setq window--sides-shown t))
|
|
||||||
(window--display-buffer
|
|
||||||
buffer this-window 'reuse alist))
|
|
||||||
(and (or (not max-slots) (< slots max-slots))
|
|
||||||
(or (and next-window
|
|
||||||
;; Make new window before `next-window'.
|
|
||||||
(let ((next-side (if left-or-right 'above 'left))
|
|
||||||
(doom-popup--internal t)
|
|
||||||
(window-combination-resize 'side))
|
|
||||||
(setq window
|
|
||||||
(ignore-errors
|
|
||||||
(split-window next-window
|
|
||||||
nil
|
|
||||||
next-side)))))
|
|
||||||
(and prev-window
|
|
||||||
;; Make new window after `prev-window'.
|
|
||||||
(let ((prev-side (if left-or-right
|
|
||||||
'below
|
|
||||||
'right))
|
|
||||||
(doom-popup--internal t)
|
|
||||||
(window-combination-resize 'side))
|
|
||||||
(setq window
|
|
||||||
(ignore-errors
|
|
||||||
(split-window prev-window
|
|
||||||
nil
|
|
||||||
prev-side))))))
|
|
||||||
(set-window-parameter window 'window-slot slot)
|
|
||||||
(set-window-parameter window 'window-vslot vslot)
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(setq window--sides-shown t))
|
|
||||||
(window--display-buffer
|
|
||||||
buffer window 'window alist))
|
|
||||||
(and best-window
|
|
||||||
;; Reuse `best-window'.
|
|
||||||
(progn
|
|
||||||
;; Give best-window the new slot value.
|
|
||||||
(set-window-parameter best-window 'window-slot slot)
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(setq window--sides-shown t))
|
|
||||||
(window--display-buffer
|
|
||||||
buffer best-window 'reuse alist)))))))))
|
|
||||||
|
|
||||||
(require 'doom-popup-config)
|
|
||||||
|
|
||||||
(provide 'doom-popup)
|
|
||||||
@@ -1,24 +1,23 @@
|
|||||||
;; syd-popups.el -*- lexical-binding: t; -*-
|
;; syd-popups.el -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
(use-package popper
|
(use-package popper
|
||||||
:disabled
|
|
||||||
:init
|
:init
|
||||||
(setq popper-display-control nil
|
(setq popper-display-control nil
|
||||||
popper-reference-buffers
|
popper-reference-buffers
|
||||||
(list (lambda (buf)
|
(list (lambda (buf)
|
||||||
(with-current-buffer buf
|
(with-current-buffer buf
|
||||||
(bound-and-true-p doom-popup-mode)))))
|
(bound-and-true-p doom-popup-buffer-mode)))))
|
||||||
:config
|
:config
|
||||||
(popper-mode 1))
|
(popper-mode 1))
|
||||||
|
|
||||||
(use-package doom-popup
|
(use-package doom-popup
|
||||||
;; :after popper
|
;; :after popper
|
||||||
:load-path "/persist/dots/users/crumb/programs/emacs/lib/doom-popup"
|
;; :load-path "/persist/dots/users/crumb/programs/emacs/lib/doom-popup"
|
||||||
:straight nil
|
;; :straight nil
|
||||||
;; :straight
|
:straight
|
||||||
;; (:type nil
|
(:type git
|
||||||
;; :local-repo "/persist/dots/users/crumb/programs/emacs/modules/doom-popup")
|
:host gitlab
|
||||||
)
|
:repo "crumbtoo/doom-popup"))
|
||||||
|
|
||||||
(provide 'syd-popups)
|
(provide 'syd-popups)
|
||||||
;;; syd-popups.el ends here
|
;;; syd-popups.el ends here
|
||||||
|
|||||||
Reference in New Issue
Block a user