vertico-buffer.el (9797B)
1 ;;; vertico-buffer.el --- Display Vertico like a regular buffer -*- lexical-binding: t -*- 2 3 ;; Copyright (C) 2021-2024 Free Software Foundation, Inc. 4 5 ;; Author: Daniel Mendler <mail@daniel-mendler.de> 6 ;; Maintainer: Daniel Mendler <mail@daniel-mendler.de> 7 ;; Created: 2021 8 ;; Version: 1.8 9 ;; Package-Requires: ((emacs "27.1") (compat "29.1.4.4") (vertico "1.8")) 10 ;; Homepage: https://github.com/minad/vertico 11 12 ;; This file is part of GNU Emacs. 13 14 ;; This program is free software: you can redistribute it and/or modify 15 ;; it under the terms of the GNU General Public License as published by 16 ;; the Free Software Foundation, either version 3 of the License, or 17 ;; (at your option) any later version. 18 19 ;; This program is distributed in the hope that it will be useful, 20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 ;; GNU General Public License for more details. 23 24 ;; You should have received a copy of the GNU General Public License 25 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 26 27 ;;; Commentary: 28 29 ;; This package is a Vertico extension, which displays Vertico like a 30 ;; regular buffer in a large window instead of the miniwindow. The 31 ;; buffer display can be enabled by the `vertico-buffer-mode'. 32 33 ;; The mode can be enabled globally or via `vertico-multiform-mode' 34 ;; per command or completion category. Alternatively the buffer 35 ;; display can be toggled temporarily with M-B if 36 ;; `vertico-multiform-mode' is enabled. 37 38 ;;; Code: 39 40 (require 'vertico) 41 42 (defcustom vertico-buffer-hide-prompt t 43 "Hide prompt in the minibuffer." 44 :group 'vertico 45 :type 'boolean) 46 47 (defcustom vertico-buffer-display-action 48 '(display-buffer-reuse-window) 49 "Display action for the Vertico buffer." 50 :group 'vertico 51 :type `(choice 52 (const :tag "Reuse some window" 53 (display-buffer-reuse-window)) 54 (const :tag "Least recently used window" 55 (,'display-buffer-use-least-recent-window)) 56 (const :tag "Left of current window" 57 (display-buffer-in-direction 58 (direction . left) 59 (window-width . 0.3))) 60 (const :tag "Right of current window" 61 (display-buffer-in-direction 62 (direction . right) 63 (window-height . 0.3))) 64 (const :tag "Above current window" 65 (display-buffer-in-direction 66 (direction . above) 67 (window-height . ,(+ 3 vertico-count)))) 68 (const :tag "Below current window" 69 (display-buffer-in-direction 70 (direction . below) 71 (window-height . ,(+ 3 vertico-count)))) 72 (const :tag "Bottom of frame" 73 (display-buffer-at-bottom 74 (window-height . ,(+ 3 vertico-count)))) 75 (const :tag "Side window on the right" 76 (display-buffer-in-side-window 77 (side . right) 78 (window-width . 0.3))) 79 (const :tag "Side window on the left" 80 (display-buffer-in-side-window 81 (side . left) 82 (window-width . 0.3))) 83 (const :tag "Side window at the top" 84 (display-buffer-in-side-window 85 (window-height . ,(+ 3 vertico-count)) 86 (side . top))) 87 (const :tag "Side window at the bottom" 88 (display-buffer-in-side-window 89 (window-height . ,(+ 3 vertico-count)) 90 (side . bottom))) 91 (sexp :tag "Other"))) 92 93 (defvar-local vertico-buffer--restore nil) 94 95 (defun vertico-buffer--redisplay (win) 96 "Redisplay window WIN." 97 (when-let ((mbwin (active-minibuffer-window)) 98 ((eq (window-buffer mbwin) (current-buffer)))) 99 (unless (eq win mbwin) 100 (setq-local truncate-lines (< (window-point win) 101 (* 0.8 (window-width win)))) 102 (set-window-point win (point)) 103 (set-window-hscroll win 0)) 104 (when vertico-buffer-hide-prompt 105 (window-resize mbwin (- (window-pixel-height mbwin)) nil nil 'pixelwise) 106 (set-window-vscroll mbwin 3)) 107 (when transient-mark-mode 108 (with-silent-modifications 109 (vertico--remove-face (point-min) (point-max) 'region) 110 (when (use-region-p) 111 (add-face-text-property 112 (max (minibuffer-prompt-end) (region-beginning)) 113 (region-end) 'region)))) 114 (let ((old cursor-in-non-selected-windows) 115 (new (and (eq (selected-window) mbwin) 116 (if (memq cursor-type '(nil t)) 'box cursor-type)))) 117 (unless (eq new old) 118 (setq-local cursor-in-non-selected-windows new) 119 (force-mode-line-update t))))) 120 121 (defun vertico-buffer--setup () 122 "Setup buffer display." 123 (let* ((action vertico-buffer-display-action) 124 (old-wins (mapcar (lambda (w) (cons w (window-buffer w))) (window-list))) 125 win old-buf tmp-buf 126 (_ (unwind-protect 127 (progn 128 (with-current-buffer 129 (setq tmp-buf (generate-new-buffer "*vertico-buffer*")) 130 ;; Set a fake major mode such that 131 ;; `display-buffer-reuse-mode-window' does not take over! 132 (setq major-mode 'vertico-buffer-mode)) 133 ;; Temporarily select the original window such that 134 ;; `display-buffer-same-window' works. 135 (setq win (with-minibuffer-selected-window 136 (display-buffer tmp-buf action)) 137 old-buf (alist-get win old-wins)) 138 (set-window-buffer win (current-buffer))) 139 (kill-buffer tmp-buf))) 140 (old-no-other (window-parameter win 'no-other-window)) 141 (old-no-delete (window-parameter win 'no-delete-other-windows)) 142 (old-state (buffer-local-set-state 143 cursor-in-non-selected-windows cursor-in-non-selected-windows 144 show-trailing-whitespace nil 145 truncate-lines t 146 face-remapping-alist (copy-tree `((mode-line-inactive mode-line) 147 ,@face-remapping-alist)) 148 mode-line-format 149 (list (format #(" %s%s " 1 3 (face mode-line-buffer-id)) 150 (replace-regexp-in-string ":? *\\'" "" 151 (minibuffer-prompt)) 152 (let ((depth (recursion-depth))) 153 (if (< depth 2) "" (format " [%s]" depth))))) 154 vertico-count (- (/ (window-pixel-height win) 155 (default-line-height)) 2)))) 156 (set-window-parameter win 'no-other-window t) 157 (set-window-parameter win 'no-delete-other-windows t) 158 (set-window-dedicated-p win t) 159 (overlay-put vertico--candidates-ov 'window win) 160 (when (and vertico-buffer-hide-prompt vertico--count-ov) 161 (overlay-put vertico--count-ov 'window win)) 162 (setq-local vertico-buffer--restore (make-symbol "vertico-buffer--restore")) 163 (fset vertico-buffer--restore 164 (lambda () 165 (with-selected-window (active-minibuffer-window) 166 (when vertico-buffer--restore 167 (when transient-mark-mode 168 (with-silent-modifications 169 (vertico--remove-face (point-min) (point-max) 'region))) 170 (remove-hook 'pre-redisplay-functions #'vertico-buffer--redisplay 'local) 171 (remove-hook 'minibuffer-exit-hook vertico-buffer--restore) 172 (fset vertico-buffer--restore nil) 173 (kill-local-variable 'vertico-buffer--restore) 174 (buffer-local-restore-state old-state) 175 (overlay-put vertico--candidates-ov 'window nil) 176 (when vertico--count-ov (overlay-put vertico--count-ov 'window nil)) 177 (cond 178 ((and (window-live-p win) (buffer-live-p old-buf)) 179 (set-window-parameter win 'no-other-window old-no-other) 180 (set-window-parameter win 'no-delete-other-windows old-no-delete) 181 (set-window-dedicated-p win nil) 182 (set-window-buffer win old-buf)) 183 ((window-live-p win) 184 (delete-window win))) 185 (when vertico-buffer-hide-prompt 186 (set-window-vscroll nil 0)))))) 187 ;; We cannot use a buffer-local minibuffer-exit-hook here. The hook will 188 ;; not be called when abnormally exiting the minibuffer from another buffer 189 ;; via `keyboard-escape-quit'. 190 (add-hook 'minibuffer-exit-hook vertico-buffer--restore) 191 (add-hook 'pre-redisplay-functions #'vertico-buffer--redisplay nil 'local))) 192 193 ;;;###autoload 194 (define-minor-mode vertico-buffer-mode 195 "Display Vertico like a regular buffer in a large window." 196 :global t :group 'vertico 197 ;; Shrink current minibuffer window 198 (when-let ((win (active-minibuffer-window))) 199 (unless (frame-root-window-p win) 200 (window-resize win (- (window-pixel-height win)) nil nil 'pixelwise)) 201 (with-selected-window win 202 (cond 203 ((and vertico-buffer-mode vertico--input (not vertico-buffer--restore)) 204 (vertico-buffer--setup)) 205 ((and (not vertico-buffer-mode) vertico-buffer--restore) 206 (funcall vertico-buffer--restore)))))) 207 208 (cl-defmethod vertico--resize-window (_height &context (vertico-buffer-mode (eql t)))) 209 210 (cl-defmethod vertico--setup :after (&context (vertico-buffer-mode (eql t))) 211 (vertico-buffer--setup)) 212 213 (provide 'vertico-buffer) 214 ;;; vertico-buffer.el ends here