config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

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