config

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

vertico-buffer.el (10010B)


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