💥 Change set-popup-rule! usage
Now accepts a flat plist of all its former parameters, including new :parameters and :actions properties to increase your control over the fate of your windows. The old usage of set-popup-rule! is deprecated and may not work right! The :ui popup module has also seen a major refactor to improve efficiency and load times. Sorry! This is the last "big" change before 2.1!
This commit is contained in:
parent
4e5c8b6052
commit
6808c46b58
26 changed files with 356 additions and 351 deletions
|
@ -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
|
||||
;;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue