doomemacs/modules/feature/popup/config.el

197 lines
7 KiB
EmacsLisp
Raw Normal View History

;;; config.el -*- lexical-binding: t; -*-
(defconst +popup-window-parameters
'(transient escape-quit select modeline alist)
"A list of custom parameters to be added to `window-persistent-parameters'.
Modifying this has no effect, unless done before feature/popup loads.
(transient . CDR)
CDR can be t, an integer or nil. 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.
(escape-quit . CDR)
CDR can be t, 'other, 'current or nil. This determines the behavior of the
escape key in or outside of popup windows.
If t, close the popup if escape is pressed inside or outside of popups.
If 'other, close this popup if escape is pressed outside of any popup. This is
great for popups you just want to peek at and discard.
If 'current, close the current popup if escape is pressed from inside of
the popup.
If nil, pressing escape will never close this buffer.
(select . BOOl)
CDR is a boolean that determines whether to focus the popup window after it
opens.
(modeline . CDR)
CDR can be t (show the default modeline), a symbol representing the name of a
modeline defined with `def-modeline!', or nil (show no modeline).
(alist . CDR)
This is an internal parameter and should not be set or modified.
Since I can't find this information anywhere but the Emacs manual, I'll include
a brief description of some native window parameters that Emacs uses:
(delete-window . CDR)
(delete-other-window . CDR)
(split-window . CDR)
(other-window . CDR)
This applies to all four of the above: CDR can be t or a function. If t, using
those functions on this window will ignore all window parameters.
If CDR is a function, it will replace the native function when used on this
window. e.g. if CDR is #'ignore (delete-window popup) will run (ignore popup)
instead of deleting the window!
(no-other-window . BOOL)
If CDR is non-nil, this window becomes invisible to `other-window' and
`pop-to-buffer'. Doom popups sets this. The default is nil.")
(defvar +popup-default-alist
'((slot . 1)
(window-height . 0.14)
(window-width . 26)
(reusable-frames . visible))
"The default alist for `display-buffer-alist' rules.")
(defvar +popup-default-parameters
'((transient . t)
(escape-quit . t))
"The default window parameters to add alists fed to `display-buffer-alist'.")
(defvar +popup-ttl 10
"The default time-to-live for transient buffers whose popup buffers have been
deleted.")
(defvar +popup-mode-map (make-sparse-keymap)
"Active keymap in a session with the popup system enabled. See
`+popup-mode'.")
(defvar +popup-buffer-mode-map (make-sparse-keymap)
"Active keymap in popup windows. See `+popup-buffer-mode'.")
(defvar +popup--display-buffer-alist nil)
(defvar +popup--old-display-buffer-alist nil)
(defvar +popup--remember-last t)
(defvar +popup--last nil)
(defvar-local +popup--timer nil)
;;
(def-setting! :popup (condition &optional alist parameters)
"Register a popup rule.
CONDITION can be a regexp string or a function. See `display-buffer' for a list
of possible entries for ALIST, which tells the display system how to initialize
the popup window. PARAMETERS is an alist of window parameters. See
`+popup-window-parameters' for a list of custom parameters provided by the popup
module."
`(let ((alist ,alist)
(parameters ,parameters))
,(when alist
'(when-let* ((size (cdr (assq 'size alist))))
(setq alist (assq-delete-all 'size alist))
(push (cons (pcase (cdr (or (assq 'side alist)
(assq 'side +popup-default-alist)))
((or `left `right) 'window-width)
(_ 'window-height))
size)
alist)))
(prog1 (push (append (list ,condition '(+popup-buffer))
alist
(list (cons 'window-parameters parameters)))
+popup--display-buffer-alist)
(when (bound-and-true-p +popup-mode)
(setq display-buffer-alist +popup--display-buffer-alist)))))
;;
;; Default popup rules & bootstrap
;;
(eval-when-compile
(set! :popup "^ \\*")
(set! :popup "^\\*" nil '((select . t)))
(set! :popup "^\\*\\(?:scratch\\|Messages\\)" nil '((transient)))
(set! :popup "^\\*Help"
'((window-height . 0.2))
'((select . t)))
(set! :+popup "^\\*doom:"
'((window-height . 0.35))
'((select . t) (escape-quit) (transient))))
(setq +popup--display-buffer-alist (eval-when-compile +popup--display-buffer-alist))
(add-hook 'doom-init-ui-hook #'+popup-mode)
(add-hook '+popup-buffer-mode-hook #'+popup|adjust-fringes)
(add-hook '+popup-buffer-mode-hook #'+popup|set-modeline)
;;
;; Hacks
;;
(defun doom*ignore-window-parameters (orig-fn &rest args)
"Allow *interactive* window moving commands to traverse popups."
(cl-letf (((symbol-function #'windmove-find-other-window)
(lambda (direction &optional window ignore sign wrap mini)
(window-in-direction
(pcase dir (`up 'above) (`down 'below) (_ dir))
window (bound-and-true-p +popup-mode) arg windmove-wrap-around t))))
(apply orig-fn args)))
(advice-add #'windmove-up :around #'doom*ignore-window-parameters)
(advice-add #'windmove-down :around #'doom*ignore-window-parameters)
(advice-add #'windmove-left :around #'doom*ignore-window-parameters)
(advice-add #'windmove-right :around #'doom*ignore-window-parameters)
(after! help-mode
(defun doom--switch-from-popup (location)
(let (origin)
(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))))
(+popup/close)
(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--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--switch-from-popup (find-variable-noselect var file))))
(define-button-type 'help-face-def
:supertype 'help-xref
'help-function
(lambda (fun file)
(require 'find-func)
(doom--switch-from-popup (find-function-search-for-symbol fun 'defface file)))))
(provide 'config)
;;; config.el ends here