config

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

corfu.el (61833B)


      1 ;;; corfu.el --- COmpletion in Region FUnction -*- 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.5
      9 ;; Package-Requires: ((emacs "28.1") (compat "30"))
     10 ;; Homepage: https://github.com/minad/corfu
     11 ;; Keywords: abbrev, convenience, matching, completion, text
     12 
     13 ;; This file is part of GNU Emacs.
     14 
     15 ;; This program is free software: you can redistribute it and/or modify
     16 ;; it under the terms of the GNU General Public License as published by
     17 ;; the Free Software Foundation, either version 3 of the License, or
     18 ;; (at your option) any later version.
     19 
     20 ;; This program is distributed in the hope that it will be useful,
     21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     23 ;; GNU General Public License for more details.
     24 
     25 ;; You should have received a copy of the GNU General Public License
     26 ;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
     27 
     28 ;;; Commentary:
     29 
     30 ;; Corfu enhances in-buffer completion with a small completion popup.
     31 ;; The current candidates are shown in a popup below or above the
     32 ;; point.  The candidates can be selected by moving up and down.
     33 ;; Corfu is the minimalistic in-buffer completion counterpart of the
     34 ;; Vertico minibuffer UI.
     35 
     36 ;;; Code:
     37 
     38 (require 'compat)
     39 (eval-when-compile
     40   (require 'cl-lib)
     41   (require 'subr-x))
     42 
     43 (defgroup corfu nil
     44   "COmpletion in Region FUnction."
     45   :link '(info-link :tag "Info Manual" "(corfu)")
     46   :link '(url-link :tag "Homepage" "https://github.com/minad/corfu")
     47   :link '(emacs-library-link :tag "Library Source" "corfu.el")
     48   :group 'convenience
     49   :group 'tools
     50   :group 'matching
     51   :prefix "corfu-")
     52 
     53 (defcustom corfu-count 10
     54   "Maximal number of candidates to show."
     55   :type 'natnum)
     56 
     57 (defcustom corfu-scroll-margin 2
     58   "Number of lines at the top and bottom when scrolling.
     59 The value should lie between 0 and corfu-count/2."
     60   :type 'natnum)
     61 
     62 (defcustom corfu-min-width 15
     63   "Popup minimum width in characters."
     64   :type 'natnum)
     65 
     66 (defcustom corfu-max-width 100
     67   "Popup maximum width in characters."
     68   :type 'natnum)
     69 
     70 (defcustom corfu-cycle nil
     71   "Enable cycling for `corfu-next' and `corfu-previous'."
     72   :type 'boolean)
     73 
     74 (defcustom corfu-on-exact-match 'insert
     75   "Configure how a single exact match should be handled.
     76 - nil: No special handling, continue completion.
     77 - insert: Insert candidate, quit and call the `:exit-function'.
     78 - quit: Quit completion without further action.
     79 - show: Initiate completion even for a single match only."
     80   :type '(choice (const insert) (const show) (const quit) (const nil)))
     81 
     82 (defcustom corfu-continue-commands
     83   ;; nil is undefined command
     84   '(nil ignore universal-argument universal-argument-more digit-argument
     85     "\\`corfu-" "\\`scroll-other-window")
     86   "Continue Corfu completion after executing these commands.
     87 The list can container either command symbols or regular expressions."
     88   :type '(repeat (choice regexp symbol)))
     89 
     90 (defcustom corfu-preview-current 'insert
     91   "Preview currently selected candidate.
     92 If the variable has the value `insert', the candidate is automatically
     93 inserted on further input."
     94   :type '(choice boolean (const insert)))
     95 
     96 (defcustom corfu-preselect 'valid
     97   "Configure if the prompt or first candidate is preselected.
     98 - prompt: Always select the prompt.
     99 - first: Always select the first candidate.
    100 - valid: Only select the prompt if valid and not equal to the first candidate.
    101 - directory: Like first, but select the prompt if it is a directory."
    102   :type '(choice (const prompt) (const valid) (const first) (const directory)))
    103 
    104 (defcustom corfu-separator ?\s
    105   "Component separator character.
    106 The character used for separating components in the input.  The presence
    107 of this separator character will inhibit quitting at completion
    108 boundaries, so that any further characters can be entered.  To enter the
    109 first separator character, call `corfu-insert-separator' (bound to M-SPC
    110 by default).  Useful for multi-component completion styles such as
    111 Orderless."
    112   :type 'character)
    113 
    114 (defcustom corfu-quit-at-boundary 'separator
    115   "Automatically quit at completion boundary.
    116 nil: Never quit at completion boundary.
    117 t: Always quit at completion boundary.
    118 separator: Quit at boundary if no `corfu-separator' has been inserted."
    119   :type '(choice boolean (const separator)))
    120 
    121 (defcustom corfu-quit-no-match 'separator
    122   "Automatically quit if no matching candidate is found.
    123 When staying alive even if there is no match a warning message is
    124 shown in the popup.
    125 nil: Stay alive even if there is no match.
    126 t: Quit if there is no match.
    127 separator: Only stay alive if there is no match and
    128 `corfu-separator' has been inserted."
    129   :type '(choice boolean (const separator)))
    130 
    131 (defcustom corfu-left-margin-width 0.5
    132   "Width of the left margin in units of the character width."
    133   :type 'float)
    134 
    135 (defcustom corfu-right-margin-width 0.5
    136   "Width of the right margin in units of the character width."
    137   :type 'float)
    138 
    139 (defcustom corfu-bar-width 0.2
    140   "Width of the bar in units of the character width."
    141   :type 'float)
    142 
    143 (defcustom corfu-margin-formatters nil
    144   "Registry for margin formatter functions.
    145 Each function of the list is called with the completion metadata as
    146 argument until an appropriate formatter is found.  The function should
    147 return a formatter function, which takes the candidate string and must
    148 return a string, possibly an icon."
    149   :type 'hook)
    150 
    151 (defcustom corfu-sort-function #'corfu-sort-length-alpha
    152   "Default sorting function.
    153 This function is used if the completion table does not specify a
    154 `display-sort-function'."
    155   :type `(choice
    156           (const :tag "No sorting" nil)
    157           (const :tag "By length and alpha" ,#'corfu-sort-length-alpha)
    158           (function :tag "Custom function")))
    159 
    160 (defcustom corfu-sort-override-function nil
    161   "Override sort function which overrides the `display-sort-function'.
    162 This function is used even if a completion table specifies its
    163 own sort function."
    164   :type '(choice (const nil) function))
    165 
    166 (defcustom corfu-auto-prefix 3
    167   "Minimum length of prefix for auto completion.
    168 The completion backend can override this with
    169 :company-prefix-length.  It is *not recommended* to use a small
    170 prefix length (below 2), since this will create high load for
    171 Emacs.  See also `corfu-auto-delay'."
    172   :type 'natnum)
    173 
    174 (defcustom corfu-auto-delay 0.2
    175   "Delay for auto completion.
    176 It is *not recommended* to use a short delay or even 0, since
    177 this will create high load for Emacs, in particular if executing
    178 the completion backend is costly."
    179   :type 'float)
    180 
    181 (defcustom corfu-auto-commands
    182   '("self-insert-command\\'" "delete-backward-char\\'" "\\`backward-delete-char"
    183     c-electric-colon c-electric-lt-gt c-electric-slash c-scope-operator)
    184   "Commands which initiate auto completion.
    185 The list can container either command symbols or regular expressions."
    186   :type '(repeat (choice regexp symbol)))
    187 
    188 (defcustom corfu-auto nil
    189   "Enable auto completion.
    190 See also the settings `corfu-auto-delay', `corfu-auto-prefix' and
    191 `corfu-auto-commands'."
    192   :type 'boolean)
    193 
    194 (defgroup corfu-faces nil
    195   "Faces used by Corfu."
    196   :group 'corfu
    197   :group 'faces)
    198 
    199 (defface corfu-default
    200   '((((class color) (min-colors 88) (background dark)) :background "#191a1b")
    201     (((class color) (min-colors 88) (background light)) :background "#f0f0f0")
    202     (t :background "gray"))
    203   "Default face, foreground and background colors used for the popup.")
    204 
    205 (defface corfu-current
    206   '((((class color) (min-colors 88) (background dark))
    207      :background "#00415e" :foreground "white")
    208     (((class color) (min-colors 88) (background light))
    209      :background "#c0efff" :foreground "black")
    210     (t :background "blue" :foreground "white"))
    211   "Face used to highlight the currently selected candidate.")
    212 
    213 (defface corfu-bar
    214   '((((class color) (min-colors 88) (background dark)) :background "#a8a8a8")
    215     (((class color) (min-colors 88) (background light)) :background "#505050")
    216     (t :background "gray"))
    217   "The background color is used for the scrollbar indicator.")
    218 
    219 (defface corfu-border
    220   '((((class color) (min-colors 88) (background dark)) :background "#323232")
    221     (((class color) (min-colors 88) (background light)) :background "#d7d7d7")
    222     (t :background "gray"))
    223   "The background color used for the thin border.")
    224 
    225 (defface corfu-annotations
    226   '((t :inherit completions-annotations))
    227   "Face used for annotations.")
    228 
    229 (defface corfu-deprecated
    230   '((t :inherit shadow :strike-through t))
    231   "Face used for deprecated candidates.")
    232 
    233 (defvar-keymap corfu-mode-map
    234   :doc "Keymap used when `corfu-mode' is active.")
    235 
    236 (defvar-keymap corfu-map
    237   :doc "Keymap used when popup is shown."
    238   "<remap> <move-beginning-of-line>" #'corfu-prompt-beginning
    239   "<remap> <move-end-of-line>" #'corfu-prompt-end
    240   "<remap> <beginning-of-buffer>" #'corfu-first
    241   "<remap> <end-of-buffer>" #'corfu-last
    242   "<remap> <scroll-down-command>" #'corfu-scroll-down
    243   "<remap> <scroll-up-command>" #'corfu-scroll-up
    244   "<remap> <next-line>" #'corfu-next
    245   "<remap> <previous-line>" #'corfu-previous
    246   "<remap> <completion-at-point>" #'corfu-complete
    247   "<remap> <keyboard-escape-quit>" #'corfu-reset
    248   "<down>" #'corfu-next
    249   "<up>" #'corfu-previous
    250   "M-n" #'corfu-next
    251   "M-p" #'corfu-previous
    252   "C-g" #'corfu-quit
    253   "RET" #'corfu-insert
    254   "TAB" #'corfu-complete
    255   "M-TAB" #'corfu-expand
    256   "M-g" 'corfu-info-location
    257   "M-h" 'corfu-info-documentation
    258   "M-SPC" #'corfu-insert-separator)
    259 
    260 (defvar corfu--auto-timer (timer-create)
    261   "Auto completion timer.")
    262 
    263 (defvar corfu--candidates nil
    264   "List of candidates.")
    265 
    266 (defvar corfu--metadata nil
    267   "Completion metadata.")
    268 
    269 (defvar corfu--base ""
    270   "Base string, which is concatenated with the candidate.")
    271 
    272 (defvar corfu--total 0
    273   "Length of the candidate list `corfu--candidates'.")
    274 
    275 (defvar corfu--hilit #'identity
    276   "Lazy candidate highlighting function.")
    277 
    278 (defvar corfu--index -1
    279   "Index of current candidate or negative for prompt selection.")
    280 
    281 (defvar corfu--preselect -1
    282   "Index of preselected candidate, negative for prompt selection.")
    283 
    284 (defvar corfu--scroll 0
    285   "Scroll position.")
    286 
    287 (defvar corfu--input nil
    288   "Cons of last prompt contents and point.")
    289 
    290 (defvar corfu--preview-ov nil
    291   "Current candidate overlay.")
    292 
    293 (defvar corfu--change-group nil
    294   "Undo change group.")
    295 
    296 (defvar corfu--frame nil
    297   "Popup frame.")
    298 
    299 (defconst corfu--initial-state
    300   (mapcar
    301    (lambda (k) (cons k (symbol-value k)))
    302    '(corfu--base
    303      corfu--candidates
    304      corfu--hilit
    305      corfu--index
    306      corfu--preselect
    307      corfu--scroll
    308      corfu--input
    309      corfu--total
    310      corfu--preview-ov
    311      corfu--change-group
    312      corfu--metadata))
    313   "Initial Corfu state.")
    314 
    315 (defvar corfu--frame-parameters
    316   '((no-accept-focus . t)
    317     (no-focus-on-map . t)
    318     (min-width . t)
    319     (min-height . t)
    320     (border-width . 0)
    321     (outer-border-width . 0)
    322     (internal-border-width . 1)
    323     (child-frame-border-width . 1)
    324     (left-fringe . 0)
    325     (right-fringe . 0)
    326     (vertical-scroll-bars . nil)
    327     (horizontal-scroll-bars . nil)
    328     (menu-bar-lines . 0)
    329     (tool-bar-lines . 0)
    330     (tab-bar-lines . 0)
    331     (no-other-frame . t)
    332     (unsplittable . t)
    333     (undecorated . t)
    334     (cursor-type . nil)
    335     (no-special-glyphs . t)
    336     (desktop-dont-save . t))
    337   "Default child frame parameters.")
    338 
    339 (defvar corfu--buffer-parameters
    340   '((mode-line-format . nil)
    341     (header-line-format . nil)
    342     (tab-line-format . nil)
    343     (tab-bar-format . nil)
    344     (frame-title-format . "")
    345     (truncate-lines . t)
    346     (cursor-in-non-selected-windows . nil)
    347     (cursor-type . nil)
    348     (show-trailing-whitespace . nil)
    349     (display-line-numbers . nil)
    350     (left-fringe-width . nil)
    351     (right-fringe-width . nil)
    352     (left-margin-width . 0)
    353     (right-margin-width . 0)
    354     (fringes-outside-margins . 0)
    355     (fringe-indicator-alist . nil)
    356     (indicate-empty-lines . nil)
    357     (indicate-buffer-boundaries . nil)
    358     (buffer-read-only . t))
    359   "Default child frame buffer parameters.")
    360 
    361 (defvar corfu--mouse-ignore-map
    362   (let ((map (make-sparse-keymap)))
    363     (dotimes (i 7)
    364       (dolist (k '(mouse down-mouse drag-mouse double-mouse triple-mouse))
    365         (keymap-set map (format "<%s-%s>" k (1+ i)) #'ignore)))
    366     map)
    367   "Ignore all mouse clicks.")
    368 
    369 (defun corfu--replace (beg end str)
    370   "Replace range between BEG and END with STR."
    371   (unless (equal str (buffer-substring-no-properties beg end))
    372     ;; bug#55205: completion--replace removed properties as an unwanted
    373     ;; side-effect.  We also don't want to leave text properties.
    374     (completion--replace beg end (substring-no-properties str))))
    375 
    376 (defun corfu--capf-wrapper (fun &optional prefix)
    377   "Wrapper for `completion-at-point' FUN.
    378 The wrapper determines if the Capf is applicable at the current
    379 position and performs sanity checking on the returned result.
    380 For non-exclusive Capfs wrapper additionally checks if the
    381 current input can be completed successfully.  PREFIX is a prefix
    382 length override, set to t for manual completion."
    383   (pcase (funcall fun)
    384     ((and res `(,beg ,end ,table . ,plist))
    385      (and (integer-or-marker-p beg) ;; Valid Capf result
    386           (<= beg (point) end)      ;; Sanity checking
    387           ;; When auto completing, check the prefix length!
    388           (let ((len (or prefix
    389                          (plist-get plist :company-prefix-length)
    390                          (- (point) beg))))
    391             (or (eq len t) (>= len corfu-auto-prefix)))
    392           ;; For non-exclusive Capfs, check for valid completion.
    393           (or (not (eq 'no (plist-get plist :exclusive)))
    394               (let* ((str (buffer-substring-no-properties beg end))
    395                      (pt (- (point) beg))
    396                      (pred (plist-get plist :predicate))
    397                      (md (completion-metadata (substring str 0 pt) table pred)))
    398                 ;; We use `completion-try-completion' to check if there are
    399                 ;; completions. The upstream `completion--capf-wrapper' uses
    400                 ;; `try-completion' which is incorrect since it only checks for
    401                 ;; prefix completions.
    402                 (completion-try-completion str table pred pt md)))
    403           (cons fun res)))))
    404 
    405 (defun corfu--make-buffer (name)
    406   "Create buffer with NAME."
    407   (let ((fr face-remapping-alist)
    408         (ls line-spacing)
    409         (buffer (get-buffer-create name)))
    410     (with-current-buffer buffer
    411       ;;; XXX HACK install mouse ignore map
    412       (use-local-map corfu--mouse-ignore-map)
    413       (dolist (var corfu--buffer-parameters)
    414         (set (make-local-variable (car var)) (cdr var)))
    415       (setq-local face-remapping-alist (copy-tree fr)
    416                   line-spacing ls)
    417       (cl-pushnew 'corfu-default (alist-get 'default face-remapping-alist))
    418       buffer)))
    419 
    420 (defvar x-gtk-resize-child-frames) ;; Not present on non-gtk builds
    421 (defvar corfu--gtk-resize-child-frames
    422   (let ((case-fold-search t))
    423      ;; XXX HACK to fix resizing on gtk3/gnome taken from posframe.el
    424      ;; More information:
    425      ;; * https://github.com/minad/corfu/issues/17
    426      ;; * https://gitlab.gnome.org/GNOME/mutter/-/issues/840
    427      ;; * https://lists.gnu.org/archive/html/emacs-devel/2020-02/msg00001.html
    428     (and (string-match-p "gtk3" system-configuration-features)
    429          (string-match-p "gnome\\|cinnamon"
    430                          (or (getenv "XDG_CURRENT_DESKTOP")
    431                              (getenv "DESKTOP_SESSION") ""))
    432          'resize-mode)))
    433 
    434 ;; Function adapted from posframe.el by tumashu
    435 (defun corfu--make-frame (frame x y width height buffer)
    436   "Show BUFFER in child frame at X/Y with WIDTH/HEIGHT.
    437 FRAME is the existing frame."
    438   (when-let (((frame-live-p frame))
    439              (timer (frame-parameter frame 'corfu--hide-timer)))
    440     (cancel-timer timer)
    441     (set-frame-parameter frame 'corfu--hide-timer nil))
    442   (let* ((window-min-height 1)
    443          (window-min-width 1)
    444          (inhibit-redisplay t)
    445          (x-gtk-resize-child-frames corfu--gtk-resize-child-frames)
    446          (before-make-frame-hook)
    447          (after-make-frame-functions)
    448          (parent (window-frame)))
    449     (unless (and (frame-live-p frame)
    450                  (eq (frame-parent frame)
    451                      (and (not (bound-and-true-p exwm--connection)) parent))
    452                  ;; If there is more than one window, `frame-root-window' may
    453                  ;; return nil.  Recreate the frame in this case.
    454                  (window-live-p (frame-root-window frame)))
    455       (when frame (delete-frame frame))
    456       (setq frame (make-frame
    457                    `((parent-frame . ,parent)
    458                      (minibuffer . ,(minibuffer-window parent))
    459                      (width . 0) (height . 0) (visibility . nil)
    460                      ,@corfu--frame-parameters))))
    461     ;; XXX HACK Setting the same frame-parameter/face-background is not a nop.
    462     ;; Check before applying the setting. Without the check, the frame flickers
    463     ;; on Mac. We have to apply the face background before adjusting the frame
    464     ;; parameter, otherwise the border is not updated.
    465     (let ((new (face-attribute 'corfu-border :background nil 'default)))
    466       (unless (equal (face-attribute 'internal-border :background frame 'default) new)
    467         (set-face-background 'internal-border new frame))
    468       ;; XXX The Emacs Mac Port does not support `internal-border', we also have
    469       ;; to set `child-frame-border'.
    470       (unless (or (not (facep 'child-frame-border))
    471                   (equal (face-attribute 'child-frame-border :background frame 'default) new))
    472         (set-face-background 'child-frame-border new frame)))
    473     ;; Reset frame parameters if they changed.  For example `tool-bar-mode'
    474     ;; overrides the parameter `tool-bar-lines' for every frame, including child
    475     ;; frames.  The child frame API is a pleasure to work with.  It is full of
    476     ;; lovely surprises.
    477     (let* ((is (frame-parameters frame))
    478            (should `((background-color
    479                       . ,(face-attribute 'corfu-default :background nil 'default))
    480                      (font . ,(frame-parameter parent 'font))
    481                      ,@corfu--frame-parameters))
    482            (diff (cl-loop for p in should for (k . v) = p
    483                           unless (equal (alist-get k is) v) collect p)))
    484       (when diff (modify-frame-parameters frame diff)))
    485     (let ((win (frame-root-window frame)))
    486       (unless (eq (window-buffer win) buffer)
    487         (set-window-buffer win buffer))
    488       ;; Disallow selection of root window (gh:minad/corfu#63)
    489       (set-window-parameter win 'no-delete-other-windows t)
    490       (set-window-parameter win 'no-other-window t)
    491       ;; Mark window as dedicated to prevent frame reuse (gh:minad/corfu#60)
    492       (set-window-dedicated-p win t))
    493     (redirect-frame-focus frame parent)
    494     (set-frame-size frame width height t)
    495     (pcase-let ((`(,px . ,py) (frame-position frame)))
    496       (unless (and (= x px) (= y py))
    497         (set-frame-position frame x y))))
    498   (make-frame-visible frame)
    499   ;; Unparent child frame if EXWM is used, otherwise EXWM buffers are drawn on
    500   ;; top of the Corfu child frame.
    501   (when (and (bound-and-true-p exwm--connection) (frame-parent frame))
    502     (set-frame-parameter frame 'parent-frame nil))
    503   frame)
    504 
    505 (defun corfu--hide-frame-deferred (frame)
    506   "Deferred hiding of child FRAME."
    507   (when (and (frame-live-p frame) (frame-visible-p frame))
    508     (set-frame-parameter frame 'corfu--hide-timer nil)
    509     (make-frame-invisible frame)
    510     (with-current-buffer (window-buffer (frame-root-window frame))
    511       (with-silent-modifications
    512         (erase-buffer)))))
    513 
    514 (defun corfu--hide-frame (frame)
    515   "Hide child FRAME."
    516   (when (and (frame-live-p frame) (frame-visible-p frame)
    517              (not (frame-parameter frame 'corfu--hide-timer)))
    518     (set-frame-parameter frame 'corfu--hide-timer
    519                          (run-at-time 0 nil #'corfu--hide-frame-deferred frame))))
    520 
    521 (defun corfu--move-to-front (elem list)
    522   "Move ELEM to front of LIST."
    523   ;; In contrast to Vertico, this function handles duplicates. See also the
    524   ;; special deduplication function `corfu--delete-dups' based on
    525   ;; `equal-including-properties'
    526   (nconc (cl-loop for x in list if (equal x elem) collect x)
    527          (delete elem list)))
    528 
    529 (defun corfu--filter-completions (&rest args)
    530   "Compute all completions for ARGS with lazy highlighting."
    531   (dlet ((completion-lazy-hilit t) (completion-lazy-hilit-fn nil))
    532     (static-if (>= emacs-major-version 30)
    533         (cons (apply #'completion-all-completions args) completion-lazy-hilit-fn)
    534       (cl-letf* ((orig-pcm (symbol-function #'completion-pcm--hilit-commonality))
    535                  (orig-flex (symbol-function #'completion-flex-all-completions))
    536                  ((symbol-function #'completion-flex-all-completions)
    537                   (lambda (&rest args)
    538                     ;; Unfortunately for flex we have to undo the lazy highlighting, since flex uses
    539                     ;; the completion-score for sorting, which is applied during highlighting.
    540                     (cl-letf (((symbol-function #'completion-pcm--hilit-commonality) orig-pcm))
    541                       (apply orig-flex args))))
    542                  ((symbol-function #'completion-pcm--hilit-commonality)
    543                   (lambda (pattern cands)
    544                     (setq completion-lazy-hilit-fn
    545                           (lambda (x)
    546                             ;; `completion-pcm--hilit-commonality' sometimes throws an internal error
    547                             ;; for example when entering "/sudo:://u".
    548                             (condition-case nil
    549                                 (car (completion-pcm--hilit-commonality pattern (list x)))
    550                               (t x))))
    551                     cands))
    552                  ((symbol-function #'completion-hilit-commonality)
    553                   (lambda (cands prefix &optional base)
    554                     (setq completion-lazy-hilit-fn
    555                           (lambda (x) (car (completion-hilit-commonality (list x) prefix base))))
    556                     (and cands (nconc cands base)))))
    557         (cons (apply #'completion-all-completions args) completion-lazy-hilit-fn)))))
    558 
    559 (defsubst corfu--length-string< (x y)
    560   "Sorting predicate which compares X and Y first by length then by `string<'."
    561   (or (< (length x) (length y)) (and (= (length x) (length y)) (string< x y))))
    562 
    563 (defmacro corfu--partition! (list form)
    564   "Evaluate FORM for every element and partition LIST."
    565   (cl-with-gensyms (head1 head2 tail1 tail2)
    566     `(let* ((,head1 (cons nil nil))
    567             (,head2 (cons nil nil))
    568             (,tail1 ,head1)
    569             (,tail2 ,head2))
    570        (while ,list
    571          (if (let ((it (car ,list))) ,form)
    572              (progn
    573                (setcdr ,tail1 ,list)
    574                (pop ,tail1))
    575            (setcdr ,tail2 ,list)
    576            (pop ,tail2))
    577          (pop ,list))
    578        (setcdr ,tail1 (cdr ,head2))
    579        (setcdr ,tail2 nil)
    580        (setq ,list (cdr ,head1)))))
    581 
    582 (defun corfu--move-prefix-candidates-to-front (field cands)
    583   "Move CANDS which match prefix of FIELD to the beginning."
    584   (let* ((word (substring field 0
    585                           (seq-position field corfu-separator)))
    586          (len (length word)))
    587     (corfu--partition!
    588      cands
    589      (and (>= (length it) len)
    590           (eq t (compare-strings word 0 len it 0 len
    591                                  completion-ignore-case))))))
    592 
    593 ;; bug#6581: `equal-including-properties' uses `eq' for properties until 29.1.
    594 ;; Approximate by comparing `text-properties-at' position 0.
    595 (defalias 'corfu--equal-including-properties
    596   (static-if (< emacs-major-version 29)
    597       (lambda (x y)
    598         (and (equal x y)
    599              (equal (text-properties-at 0 x) (text-properties-at 0 y))))
    600     #'equal-including-properties))
    601 
    602 (defun corfu--delete-dups (list)
    603   "Delete `equal-including-properties' consecutive duplicates from LIST."
    604   (let ((beg list))
    605     (while (cdr beg)
    606       (let ((end (cdr beg)))
    607         (while (equal (car beg) (car end)) (pop end))
    608         ;; The deduplication is quadratic in the number of duplicates.  We can
    609         ;; avoid the quadratic complexity with a hash table which takes
    610         ;; properties into account (available since Emacs 28).
    611         (while (not (eq beg end))
    612           (let ((dup beg))
    613             (while (not (eq (cdr dup) end))
    614               (if (corfu--equal-including-properties (car beg) (cadr dup))
    615                   (setcdr dup (cddr dup))
    616                 (pop dup))))
    617           (pop beg)))))
    618   list)
    619 
    620 (defun corfu--sort-function ()
    621   "Return the sorting function."
    622   (or corfu-sort-override-function
    623       (corfu--metadata-get 'display-sort-function)
    624       corfu-sort-function))
    625 
    626 (defun corfu--recompute (str pt table pred)
    627   "Recompute state from STR, PT, TABLE and PRED."
    628   (pcase-let* ((before (substring str 0 pt))
    629                (after (substring str pt))
    630                (corfu--metadata (completion-metadata before table pred))
    631                ;; bug#47678: `completion-boundaries' fails for `partial-completion'
    632                ;; if the cursor is moved before the slashes of "~//".
    633                ;; See also vertico.el which has the same issue.
    634                (bounds (condition-case nil
    635                            (completion-boundaries before table pred after)
    636                          (t (cons 0 (length after)))))
    637                (field (substring str (car bounds) (+ pt (cdr bounds))))
    638                (completing-file (eq (corfu--metadata-get 'category) 'file))
    639                (`(,all . ,hl) (corfu--filter-completions str table pred pt corfu--metadata))
    640                (base (or (when-let ((z (last all))) (prog1 (cdr z) (setcdr z nil))) 0))
    641                (corfu--base (substring str 0 base))
    642                (pre nil))
    643     ;; Filter the ignored file extensions. We cannot use modified predicate for
    644     ;; this filtering, since this breaks the special casing in the
    645     ;; `completion-file-name-table' for `file-exists-p' and `file-directory-p'.
    646     (when completing-file (setq all (completion-pcm--filename-try-filter all)))
    647     ;; Sort using the `display-sort-function' or the Corfu sort functions, and
    648     ;; delete duplicates with respect to `equal-including-properties'.  This is
    649     ;; a deviation from the Vertico completion UI with more aggressive
    650     ;; deduplication, where candidates are compared with `equal'.  Corfu
    651     ;; preserves candidates which differ in their text properties.  Corfu tries
    652     ;; to preserve text properties as much as possible, when calling the
    653     ;; `:exit-function' to help Capfs with candidate disambiguation.  This
    654     ;; matters in particular for Lsp backends, which produce duplicates for
    655     ;; overloaded methods.
    656     (setq all (corfu--delete-dups (funcall (or (corfu--sort-function) #'identity) all))
    657           all (corfu--move-prefix-candidates-to-front field all))
    658     (when (and completing-file (not (string-suffix-p "/" field)))
    659       (setq all (corfu--move-to-front (concat field "/") all)))
    660     (setq all (corfu--move-to-front field all)
    661           pre (if (or (eq corfu-preselect 'prompt) (not all)
    662                       (and completing-file (eq corfu-preselect 'directory)
    663                            (= (length corfu--base) (length str))
    664                            (test-completion str table pred))
    665                       (and (eq corfu-preselect 'valid)
    666                            (not (equal field (car all)))
    667                            (not (and completing-file (equal (concat field "/") (car all))))
    668                            (test-completion str table pred)))
    669                   -1 0))
    670     `((corfu--base . ,corfu--base)
    671       (corfu--metadata . ,corfu--metadata)
    672       (corfu--candidates . ,all)
    673       (corfu--total . ,(length all))
    674       (corfu--hilit . ,(or hl #'identity))
    675       (corfu--preselect . ,pre)
    676       (corfu--index . ,(or (and (>= corfu--index 0) (/= corfu--index corfu--preselect)
    677                                 (seq-position all (nth corfu--index corfu--candidates)))
    678                            pre)))))
    679 
    680 (defun corfu--update (&optional interruptible)
    681   "Update state, optionally INTERRUPTIBLE."
    682   (pcase-let* ((`(,beg ,end ,table ,pred . ,_) completion-in-region--data)
    683                (pt (- (point) beg))
    684                (str (buffer-substring-no-properties beg end))
    685                (input (cons str pt)))
    686     (unless (equal corfu--input input)
    687       ;; Redisplay such that the input is immediately shown before the expensive
    688       ;; candidate recomputation (gh:minad/corfu#48). See also corresponding
    689       ;; issue gh:minad/vertico#89.
    690       (when interruptible (redisplay))
    691       ;; Bind non-essential=t to prevent Tramp from opening new connections,
    692       ;; without the user explicitly requesting it via M-TAB.
    693       (pcase (let ((non-essential t))
    694                ;; XXX Guard against errors during candidate generation.
    695                ;; bug#61274: `dabbrev-capf' signals errors.
    696                (condition-case err
    697                    (if interruptible
    698                        (while-no-input (corfu--recompute str pt table pred))
    699                      (corfu--recompute str pt table pred))
    700                  (error
    701                   (message "Corfu completion error: %s" (error-message-string err))
    702                   t)))
    703         ('nil (keyboard-quit))
    704         ((and state (pred consp))
    705          (setq corfu--input input)
    706          (dolist (s state) (set (car s) (cdr s))))))
    707     input))
    708 
    709 (defun corfu--match-symbol-p (pattern sym)
    710   "Return non-nil if SYM is matching an element of the PATTERN list."
    711   (cl-loop with case-fold-search = nil
    712            for x in (and (symbolp sym) pattern)
    713            thereis (if (symbolp x)
    714                        (eq sym x)
    715                      (string-match-p x (symbol-name sym)))))
    716 
    717 (defun corfu--metadata-get (prop)
    718   "Return PROP from completion metadata."
    719   ;; Marginalia and various icon packages advise `completion-metadata-get' to
    720   ;; inject their annotations, but are meant only for minibuffer completion.
    721   ;; Therefore call `completion-metadata-get' without advices here.
    722   (let ((completion-extra-properties (nth 4 completion-in-region--data)))
    723     (funcall (advice--cd*r (symbol-function (compat-function completion-metadata-get)))
    724              corfu--metadata prop)))
    725 
    726 (defun corfu--format-candidates (cands)
    727   "Format annotated CANDS."
    728   (cl-loop for c in cands do
    729            (cl-loop for s in-ref c do
    730                     (setf s (replace-regexp-in-string "[ \t]*\n[ \t]*" " " s))))
    731   (let* ((cw (cl-loop for x in cands maximize (string-width (car x))))
    732          (pw (cl-loop for x in cands maximize (string-width (cadr x))))
    733          (sw (cl-loop for x in cands maximize (string-width (caddr x))))
    734          (width (+ pw cw sw))
    735          ;; -4 because of margins and some additional safety
    736          (max-width (min corfu-max-width (- (frame-width) 4))))
    737     (when (> width max-width)
    738       (setq sw (max 0 (- max-width pw cw))
    739             width (+ pw cw sw)))
    740     (when (< width corfu-min-width)
    741       (setq cw (+ cw (- corfu-min-width width))
    742             width corfu-min-width))
    743     (setq width (min width max-width))
    744     (list pw width
    745           (cl-loop for (cand prefix suffix) in cands collect
    746                    (truncate-string-to-width
    747                     (concat
    748                      prefix (make-string (max 0 (- pw (string-width prefix))) ?\s)
    749                      cand
    750                      (when (/= sw 0)
    751                        (make-string (+ (max 0 (- cw (string-width cand)))
    752                                        (max 0 (- sw (string-width suffix))))
    753                                     ?\s))
    754                      suffix)
    755                     width)))))
    756 
    757 (defun corfu--compute-scroll ()
    758   "Compute new scroll position."
    759   (let ((off (max (min corfu-scroll-margin (/ corfu-count 2)) 0))
    760         (corr (if (= corfu-scroll-margin (/ corfu-count 2)) (1- (mod corfu-count 2)) 0)))
    761     (setq corfu--scroll (min (max 0 (- corfu--total corfu-count))
    762                              (max 0 (+ corfu--index off 1 (- corfu-count))
    763                                   (min (- corfu--index off corr) corfu--scroll))))))
    764 
    765 (defun corfu--candidates-popup (pos)
    766   "Show candidates popup at POS."
    767   (corfu--compute-scroll)
    768   (pcase-let* ((last (min (+ corfu--scroll corfu-count) corfu--total))
    769                (bar (ceiling (* corfu-count corfu-count) corfu--total))
    770                (lo (min (- corfu-count bar 1) (floor (* corfu-count corfu--scroll) corfu--total)))
    771                (`(,mf . ,acands) (corfu--affixate
    772                                   (cl-loop repeat corfu-count
    773                                            for c in (nthcdr corfu--scroll corfu--candidates)
    774                                            collect (funcall corfu--hilit (substring c)))))
    775                (`(,pw ,width ,fcands) (corfu--format-candidates acands))
    776                ;; Disable the left margin if a margin formatter is active.
    777                (corfu-left-margin-width (if mf 0 corfu-left-margin-width)))
    778     ;; Nonlinearity at the end and the beginning
    779     (when (/= corfu--scroll 0)
    780       (setq lo (max 1 lo)))
    781     (when (/= last corfu--total)
    782       (setq lo (min (- corfu-count bar 2) lo)))
    783     (corfu--popup-show pos pw width fcands (- corfu--index corfu--scroll)
    784                        (and (> corfu--total corfu-count) lo) bar)))
    785 
    786 (defun corfu--range-valid-p ()
    787   "Check the completion range, return non-nil if valid."
    788   (pcase-let ((buf (current-buffer))
    789               (pt (point))
    790               (`(,beg ,end . ,_) completion-in-region--data))
    791     (and beg end
    792          (eq buf (marker-buffer beg)) (eq buf (window-buffer))
    793          (<= beg pt end)
    794          (save-excursion (goto-char beg) (<= (pos-bol) pt (pos-eol))))))
    795 
    796 (defun corfu--continue-p ()
    797   "Check if completion should continue after a command.
    798 Corfu bails out if the current buffer changed unexpectedly or if
    799 point moved out of range, see `corfu--range-valid-p'.  Also the
    800 input must satisfy the `completion-in-region-mode--predicate' and
    801 the last command must be listed in `corfu-continue-commands'."
    802   (and (corfu--range-valid-p)
    803        ;; We keep Corfu alive if a `overriding-terminal-local-map' is
    804        ;; installed, e.g., the `universal-argument-map'. It would be good to
    805        ;; think about a better criterion instead. Unfortunately relying on
    806        ;; `this-command' alone is insufficient, since the value of
    807        ;; `this-command' gets clobbered in the case of transient keymaps.
    808        (or overriding-terminal-local-map
    809            ;; Check if it is an explicitly listed continue command
    810            (corfu--match-symbol-p corfu-continue-commands this-command)
    811            (pcase-let ((`(,beg ,end . ,_) completion-in-region--data))
    812              (and (or (not corfu--input) (< beg end)) ;; Check for empty input
    813                   (or (not corfu-quit-at-boundary) ;; Check separator or predicate
    814                       (and (eq corfu-quit-at-boundary 'separator)
    815                            (or (eq this-command #'corfu-insert-separator)
    816                                ;; with separator, any further chars allowed
    817                                (seq-contains-p (car corfu--input) corfu-separator)))
    818                       (funcall completion-in-region-mode--predicate)))))))
    819 
    820 (defun corfu--preview-current-p ()
    821   "Return t if the selected candidate is previewed."
    822   (and corfu-preview-current (>= corfu--index 0) (/= corfu--index corfu--preselect)))
    823 
    824 (defun corfu--preview-current (beg end)
    825   "Show current candidate as overlay given BEG and END."
    826   (when (corfu--preview-current-p)
    827     (setq beg (+ beg (length corfu--base))
    828           corfu--preview-ov (make-overlay beg end nil))
    829     (overlay-put corfu--preview-ov 'priority 1000)
    830     (overlay-put corfu--preview-ov 'window (selected-window))
    831     (overlay-put corfu--preview-ov (if (= beg end) 'after-string 'display)
    832                  (nth corfu--index corfu--candidates))))
    833 
    834 (defun corfu--window-change (_)
    835   "Window and buffer change hook which quits Corfu."
    836   (unless (corfu--range-valid-p)
    837     (corfu-quit)))
    838 
    839 (defun corfu--post-command ()
    840   "Refresh Corfu after last command."
    841   (if (corfu--continue-p)
    842       (corfu--exhibit)
    843     (corfu-quit))
    844   (when corfu-auto
    845     (corfu--auto-post-command)))
    846 
    847 (defun corfu--goto (index)
    848   "Go to candidate with INDEX."
    849   (setq corfu--index (max corfu--preselect (min index (1- corfu--total)))))
    850 
    851 (defun corfu--exit-function (str status cands)
    852   "Call the `:exit-function' with STR and STATUS.
    853 Lookup STR in CANDS to restore text properties."
    854   (when-let ((exit (plist-get completion-extra-properties :exit-function)))
    855     (funcall exit (or (car (member str cands)) str) status)))
    856 
    857 (defun corfu--done (str status cands)
    858   "Exit completion and call the exit function with STR and STATUS.
    859 Lookup STR in CANDS to restore text properties."
    860   (let ((completion-extra-properties (nth 4 completion-in-region--data)))
    861     ;; For successful completions, amalgamate undo operations,
    862     ;; such that completion can be undone in a single step.
    863     (undo-amalgamate-change-group corfu--change-group)
    864     (corfu-quit)
    865     (corfu--exit-function str status cands)))
    866 
    867 (defun corfu--setup (beg end table pred)
    868   "Setup Corfu completion state.
    869 See `completion-in-region' for the arguments BEG, END, TABLE, PRED."
    870   (setq beg (if (markerp beg) beg (copy-marker beg))
    871         end (if (and (markerp end) (marker-insertion-type end)) end (copy-marker end t))
    872         completion-in-region--data (list beg end table pred completion-extra-properties))
    873   (completion-in-region-mode 1)
    874   (activate-change-group (setq corfu--change-group (prepare-change-group)))
    875   (setcdr (assq #'completion-in-region-mode minor-mode-overriding-map-alist) corfu-map)
    876   (add-hook 'pre-command-hook #'corfu--prepare nil 'local)
    877   (add-hook 'window-selection-change-functions #'corfu--window-change nil 'local)
    878   (add-hook 'window-buffer-change-functions #'corfu--window-change nil 'local)
    879   (add-hook 'post-command-hook #'corfu--post-command)
    880   ;; Disable default post-command handling, since we have our own
    881   ;; checks in `corfu--post-command'.
    882   (remove-hook 'post-command-hook #'completion-in-region--postch)
    883   (let ((sym (make-symbol "corfu--teardown"))
    884         (buf (current-buffer)))
    885     (fset sym (lambda ()
    886                 ;; Ensure that the tear-down runs in the correct buffer, if still alive.
    887                 (unless completion-in-region-mode
    888                   (remove-hook 'completion-in-region-mode-hook sym)
    889                   (corfu--teardown buf))))
    890     (add-hook 'completion-in-region-mode-hook sym)))
    891 
    892 (defun corfu--in-region (&rest args)
    893   "Corfu completion in region function called with ARGS."
    894   ;; XXX We can get an endless loop when `completion-in-region-function' is set
    895   ;; globally to `corfu--in-region'. This should never happen.
    896   (apply (if (corfu--popup-support-p) #'corfu--in-region-1
    897            (default-value 'completion-in-region-function))
    898          args))
    899 
    900 (defun corfu--in-region-1 (beg end table &optional pred)
    901   "Complete in region, see `completion-in-region' for BEG, END, TABLE, PRED."
    902   (barf-if-buffer-read-only)
    903   ;; Restart the completion. This can happen for example if C-M-/
    904   ;; (`dabbrev-completion') is pressed while the Corfu popup is already open.
    905   (when completion-in-region-mode (corfu-quit))
    906   (let* ((pt (max 0 (- (point) beg)))
    907          (str (buffer-substring-no-properties beg end))
    908          (metadata (completion-metadata (substring str 0 pt) table pred))
    909          (threshold (completion--cycle-threshold metadata))
    910          (completion-in-region-mode-predicate
    911           (or completion-in-region-mode-predicate #'always)))
    912     (pcase (completion-try-completion str table pred pt metadata)
    913       ('nil (corfu--message "No match") nil)
    914       ('t (goto-char end)
    915           (corfu--message "Sole match")
    916           (if (eq corfu-on-exact-match 'show)
    917               (corfu--setup beg end table pred)
    918             (corfu--exit-function
    919              str 'finished
    920              (alist-get 'corfu--candidates (corfu--recompute str pt table pred))))
    921           t)
    922       (`(,newstr . ,newpt)
    923        (setq beg (if (markerp beg) beg (copy-marker beg))
    924              end (copy-marker end t))
    925        (corfu--replace beg end newstr)
    926        (goto-char (+ beg newpt))
    927        (let* ((state (corfu--recompute newstr newpt table pred))
    928               (base (alist-get 'corfu--base state))
    929               (total (alist-get 'corfu--total state))
    930               (candidates (alist-get 'corfu--candidates state)))
    931          (if (= total 1)
    932              ;; If completion is finished and cannot be further completed, and
    933              ;; the value of `corfu-on-exact-match' is not 'show, return
    934              ;; 'finished.  Otherwise setup the Corfu popup.
    935              (if (or (eq corfu-on-exact-match 'show)
    936                      (consp (completion-try-completion
    937                              newstr table pred newpt
    938                              (completion-metadata newstr table pred))))
    939                  (corfu--setup beg end table pred)
    940                (corfu--exit-function newstr 'finished candidates))
    941            (if (or (= total 0) (not threshold)
    942                    (and (not (eq threshold t)) (< threshold total)))
    943                (corfu--setup beg end table pred)
    944              (corfu--cycle-candidates total candidates (+ (length base) beg) end)
    945              ;; Do not show Corfu when "trivially" cycling, i.e.,
    946              ;; when the completion is finished after the candidate.
    947              (unless (equal (completion-boundaries (car candidates) table pred "")
    948                             '(0 . 0))
    949                (corfu--setup beg end table pred)))))
    950        t))))
    951 
    952 (defun corfu--message (&rest msg)
    953   "Show completion MSG."
    954   (let (message-log-max) (apply #'message msg)))
    955 
    956 (defun corfu--cycle-candidates (total cands beg end)
    957   "Cycle between TOTAL number of CANDS.
    958 See `completion-in-region' for the arguments BEG, END, TABLE, PRED."
    959   (let* ((idx 0)
    960          (map (make-sparse-keymap))
    961          (replace (lambda ()
    962                     (interactive)
    963                     (corfu--replace beg end (nth idx cands))
    964                     (corfu--message "Cycling %d/%d..." (1+ idx) total)
    965                     (setq idx (mod (1+ idx) total))
    966                     (set-transient-map map))))
    967     (define-key map [remap completion-at-point] replace)
    968     (define-key map [remap corfu-complete] replace)
    969     (define-key map (vector last-command-event) replace)
    970     (funcall replace)))
    971 
    972 (defun corfu--auto-complete-deferred (&optional tick)
    973   "Initiate auto completion if TICK did not change."
    974   (when (and (not completion-in-region-mode)
    975              (or (not tick) (equal tick (corfu--auto-tick))))
    976     (pcase (while-no-input ;; Interruptible Capf query
    977              (run-hook-wrapped 'completion-at-point-functions #'corfu--capf-wrapper))
    978       (`(,fun ,beg ,end ,table . ,plist)
    979        (let ((completion-in-region-mode-predicate
    980               (lambda ()
    981                 (when-let ((newbeg (car-safe (funcall fun))))
    982                   (= newbeg beg))))
    983              (completion-extra-properties plist))
    984          (corfu--setup beg end table (plist-get plist :predicate))
    985          (corfu--exhibit 'auto))))))
    986 
    987 (defun corfu--auto-post-command ()
    988   "Post command hook which initiates auto completion."
    989   (cancel-timer corfu--auto-timer)
    990   (if (and (not completion-in-region-mode)
    991            (not defining-kbd-macro)
    992            (not buffer-read-only)
    993            (corfu--match-symbol-p corfu-auto-commands this-command)
    994            (corfu--popup-support-p))
    995       (if (<= corfu-auto-delay 0)
    996           (corfu--auto-complete-deferred)
    997         ;; Do not use `timer-set-idle-time' since this leads to
    998         ;; unpredictable pauses, in particular with `flyspell-mode'.
    999         (timer-set-time corfu--auto-timer
   1000                         (timer-relative-time nil corfu-auto-delay))
   1001         (timer-set-function corfu--auto-timer #'corfu--auto-complete-deferred
   1002                             (list (corfu--auto-tick)))
   1003         (timer-activate corfu--auto-timer))))
   1004 
   1005 (defun corfu--auto-tick ()
   1006   "Return the current tick/status of the buffer.
   1007 Auto completion is only performed if the tick did not change."
   1008   (list (selected-window) (current-buffer) (buffer-chars-modified-tick) (point)))
   1009 
   1010 (cl-defgeneric corfu--popup-show (pos off width lines &optional curr lo bar)
   1011   "Show LINES as popup at POS - OFF.
   1012 WIDTH is the width of the popup.
   1013 The current candidate CURR is highlighted.
   1014 A scroll bar is displayed from LO to LO+BAR."
   1015   (let ((lh (default-line-height)))
   1016     (with-current-buffer (corfu--make-buffer " *corfu*")
   1017       (let* ((ch (default-line-height))
   1018              (cw (default-font-width))
   1019              (ml (ceiling (* cw corfu-left-margin-width)))
   1020              (mr (ceiling (* cw corfu-right-margin-width)))
   1021              (bw (ceiling (min mr (* cw corfu-bar-width))))
   1022              (marginl (and (> ml 0) (propertize " " 'display `(space :width (,ml)))))
   1023              (marginr (and (> mr 0) (propertize " " 'display `(space :align-to right))))
   1024              (sbar (when (> bw 0)
   1025                      (concat (propertize " " 'display `(space :align-to (- right (,mr))))
   1026                              (propertize " " 'display `(space :width (,(- mr bw))))
   1027                              (propertize " " 'face 'corfu-bar 'display `(space :width (,bw))))))
   1028              (pos (posn-x-y pos))
   1029              (width (+ (* width cw) ml mr))
   1030              ;; XXX HACK: Minimum popup height must be at least 1 line of the
   1031              ;; parent frame (gh:minad/corfu#261).
   1032              (height (max lh (* (length lines) ch)))
   1033              (edge (window-inside-pixel-edges))
   1034              (border (alist-get 'internal-border-width corfu--frame-parameters))
   1035              (x (max 0 (min (+ (car edge) (- (or (car pos) 0) ml (* cw off) border))
   1036                             (- (frame-pixel-width) width))))
   1037              (yb (+ (cadr edge) (window-tab-line-height) (or (cdr pos) 0) lh))
   1038              (y (if (> (+ yb (* corfu-count ch) lh lh) (frame-pixel-height))
   1039                     (- yb height lh border border)
   1040                   yb))
   1041              (row 0))
   1042         (with-silent-modifications
   1043           (erase-buffer)
   1044           (insert (mapconcat (lambda (line)
   1045                                (let ((str (concat marginl line
   1046                                                   (if (and lo (<= lo row (+ lo bar)))
   1047                                                       sbar
   1048                                                     marginr))))
   1049                                  (when (eq row curr)
   1050                                    (add-face-text-property
   1051                                     0 (length str) 'corfu-current 'append str))
   1052                                  (cl-incf row)
   1053                                  str))
   1054                              lines "\n"))
   1055           (goto-char (point-min)))
   1056         (setq corfu--frame (corfu--make-frame corfu--frame x y
   1057                                               width height (current-buffer)))))))
   1058 
   1059 (cl-defgeneric corfu--popup-hide ()
   1060   "Hide Corfu popup."
   1061   (corfu--hide-frame corfu--frame))
   1062 
   1063 (cl-defgeneric corfu--popup-support-p ()
   1064   "Return non-nil if child frames are supported."
   1065   (display-graphic-p))
   1066 
   1067 (cl-defgeneric corfu--insert (status)
   1068   "Insert current candidate, exit with STATUS if non-nil."
   1069   ;; XXX There is a small bug here, depending on interpretation.
   1070   ;; When completing "~/emacs/master/li|/calc" where "|" is the
   1071   ;; cursor, then the candidate only includes the prefix
   1072   ;; "~/emacs/master/lisp/", but not the suffix "/calc". Default
   1073   ;; completion has the same problem when selecting in the
   1074   ;; *Completions* buffer. See bug#48356.
   1075   (pcase-let* ((`(,beg ,end . ,_) completion-in-region--data)
   1076                (str (concat corfu--base (nth corfu--index corfu--candidates))))
   1077     (corfu--replace beg end str)
   1078     (corfu--goto -1) ;; Reset selection, completion may continue.
   1079     (when status (corfu--done str status nil))
   1080     str))
   1081 
   1082 (cl-defgeneric corfu--affixate (cands)
   1083   "Annotate CANDS with annotation function."
   1084   (let* ((extras (nth 4 completion-in-region--data))
   1085          (dep (plist-get extras :company-deprecated))
   1086          (mf (let ((completion-extra-properties extras))
   1087                (run-hook-with-args-until-success 'corfu-margin-formatters corfu--metadata))))
   1088     (setq cands
   1089           (if-let ((aff (corfu--metadata-get 'affixation-function)))
   1090               (funcall aff cands)
   1091             (if-let ((ann (corfu--metadata-get 'annotation-function)))
   1092                 (cl-loop for cand in cands collect
   1093                          (let ((suff (or (funcall ann cand) "")))
   1094                            ;; The default completion UI adds the
   1095                            ;; `completions-annotations' face if no other faces are
   1096                            ;; present. We use a custom `corfu-annotations' face to
   1097                            ;; allow further styling which fits better for popups.
   1098                            (unless (text-property-not-all 0 (length suff) 'face nil suff)
   1099                              (setq suff (propertize suff 'face 'corfu-annotations)))
   1100                            (list cand "" suff)))
   1101               (cl-loop for cand in cands collect (list cand "" "")))))
   1102     (cl-loop for x in cands for (c . _) = x do
   1103              (when mf
   1104                (setf (cadr x) (funcall mf c)))
   1105              (when (and dep (funcall dep c))
   1106                (setcar x (setq c (substring c)))
   1107                (add-face-text-property 0 (length c) 'corfu-deprecated 'append c)))
   1108     (cons mf cands)))
   1109 
   1110 (cl-defgeneric corfu--prepare ()
   1111   "Insert selected candidate unless command is marked to continue completion."
   1112   (when corfu--preview-ov
   1113     (delete-overlay corfu--preview-ov)
   1114     (setq corfu--preview-ov nil))
   1115   ;; Ensure that state is initialized before next Corfu command
   1116   (when (and (symbolp this-command) (string-prefix-p "corfu-" (symbol-name this-command)))
   1117     (corfu--update))
   1118   ;; If the next command is not listed in `corfu-continue-commands', insert the
   1119   ;; currently selected candidate and bail out of completion. This way you can
   1120   ;; continue typing after selecting a candidate. The candidate will be inserted
   1121   ;; and your new input will be appended.
   1122   (and (corfu--preview-current-p) (eq corfu-preview-current 'insert)
   1123        ;; See the comment about `overriding-local-map' in `corfu--post-command'.
   1124        (not (or overriding-terminal-local-map
   1125                 (corfu--match-symbol-p corfu-continue-commands this-command)))
   1126        (corfu--insert 'exact)))
   1127 
   1128 (cl-defgeneric corfu--exhibit (&optional auto)
   1129   "Exhibit Corfu UI.
   1130 AUTO is non-nil when initializing auto completion."
   1131   (pcase-let ((`(,beg ,end ,table ,pred . ,_) completion-in-region--data)
   1132               (`(,str . ,pt) (corfu--update 'interruptible)))
   1133     (cond
   1134      ;; 1) Single exactly matching candidate and no further completion is possible.
   1135      ((and (not (equal str ""))
   1136            (equal (car corfu--candidates) str) (not (cdr corfu--candidates))
   1137            (not (eq corfu-on-exact-match 'show))
   1138            (or auto corfu-on-exact-match)
   1139            (not (consp (completion-try-completion str table pred pt corfu--metadata))))
   1140       ;; Quit directly when initializing auto completion.
   1141       (if (or auto (eq corfu-on-exact-match 'quit))
   1142           (corfu-quit)
   1143         (corfu--done (car corfu--candidates) 'finished nil)))
   1144      ;; 2) There exist candidates => Show candidates popup.
   1145      (corfu--candidates
   1146       (let ((pos (posn-at-point (+ beg (length corfu--base)))))
   1147         (corfu--preview-current beg end)
   1148         (corfu--candidates-popup pos)))
   1149      ;; 3) No candidates & `corfu-quit-no-match' & initialized => Confirmation popup.
   1150      ((pcase-exhaustive corfu-quit-no-match
   1151         ('t nil)
   1152         ('nil corfu--input)
   1153         ('separator (seq-contains-p (car corfu--input) corfu-separator)))
   1154       (corfu--popup-show (posn-at-point beg) 0 8 '(#("No match" 0 8 (face italic)))))
   1155      ;; 4) No candidates & auto completing or initialized => Quit.
   1156      ((or auto corfu--input) (corfu-quit)))))
   1157 
   1158 (cl-defgeneric corfu--teardown (buffer)
   1159   "Tear-down Corfu in BUFFER, which might be dead at this point."
   1160   (corfu--popup-hide)
   1161   (when corfu--preview-ov (delete-overlay corfu--preview-ov))
   1162   (remove-hook 'post-command-hook #'corfu--post-command)
   1163   (when (buffer-live-p buffer)
   1164     (with-current-buffer buffer
   1165       (remove-hook 'window-selection-change-functions #'corfu--window-change 'local)
   1166       (remove-hook 'window-buffer-change-functions #'corfu--window-change 'local)
   1167       (remove-hook 'pre-command-hook #'corfu--prepare 'local)
   1168       (accept-change-group corfu--change-group)))
   1169   (cl-loop for (k . v) in corfu--initial-state do (set k v)))
   1170 
   1171 (defun corfu-sort-length-alpha (list)
   1172   "Sort LIST by length and alphabetically."
   1173   (sort list #'corfu--length-string<))
   1174 
   1175 (defun corfu-quit ()
   1176   "Quit Corfu completion."
   1177   (interactive)
   1178   (completion-in-region-mode -1))
   1179 
   1180 (defun corfu-reset ()
   1181   "Reset Corfu completion.
   1182 This command can be executed multiple times by hammering the ESC key.  If a
   1183 candidate is selected, unselect the candidate.  Otherwise reset the input.  If
   1184 there hasn't been any input, then quit."
   1185   (interactive)
   1186   (if (/= corfu--index corfu--preselect)
   1187       (progn
   1188         (corfu--goto -1)
   1189         (setq this-command #'corfu-first))
   1190     ;; Cancel all changes and start new change group.
   1191     (pcase-let* ((`(,beg ,end . ,_) completion-in-region--data)
   1192                  (str (buffer-substring-no-properties beg end)))
   1193       (cancel-change-group corfu--change-group)
   1194       (activate-change-group (setq corfu--change-group (prepare-change-group)))
   1195       ;; Quit when resetting, when input did not change.
   1196       (when (equal str (buffer-substring-no-properties beg end))
   1197         (corfu-quit)))))
   1198 
   1199 (defun corfu-insert-separator ()
   1200   "Insert a separator character, inhibiting quit on completion boundary.
   1201 See `corfu-separator' for more details."
   1202   (interactive)
   1203   (insert corfu-separator))
   1204 
   1205 (defun corfu-next (&optional n)
   1206   "Go forward N candidates."
   1207   (interactive "p")
   1208   (let ((index (+ corfu--index (or n 1))))
   1209     (corfu--goto
   1210      (cond
   1211       ((not corfu-cycle) index)
   1212       ((= corfu--total 0) -1)
   1213       ((< corfu--preselect 0) (1- (mod (1+ index) (1+ corfu--total))))
   1214       (t (mod index corfu--total))))))
   1215 
   1216 (defun corfu-previous (&optional n)
   1217   "Go backward N candidates."
   1218   (interactive "p")
   1219   (corfu-next (- (or n 1))))
   1220 
   1221 (defun corfu-scroll-down (&optional n)
   1222   "Go back by N pages."
   1223   (interactive "p")
   1224   (corfu--goto (max 0 (- corfu--index (* (or n 1) corfu-count)))))
   1225 
   1226 (defun corfu-scroll-up (&optional n)
   1227   "Go forward by N pages."
   1228   (interactive "p")
   1229   (corfu-scroll-down (- (or n 1))))
   1230 
   1231 (defun corfu-first ()
   1232   "Go to first candidate.
   1233 If the first candidate is already selected, go to the prompt."
   1234   (interactive)
   1235   (corfu--goto (if (> corfu--index 0) 0 -1)))
   1236 
   1237 (defun corfu-last ()
   1238   "Go to last candidate."
   1239   (interactive)
   1240   (corfu--goto (1- corfu--total)))
   1241 
   1242 (defun corfu-prompt-beginning (arg)
   1243   "Move to beginning of the prompt line.
   1244 If the point is already the beginning of the prompt move to the
   1245 beginning of the line.  If ARG is not 1 or nil, move backward ARG - 1
   1246 lines first."
   1247   (interactive "^p")
   1248   (let ((beg (car completion-in-region--data)))
   1249     (if (or (not (eq arg 1))
   1250             (and (= corfu--preselect corfu--index) (= (point) beg)))
   1251         (move-beginning-of-line arg)
   1252       (corfu--goto -1)
   1253       (goto-char beg))))
   1254 
   1255 (defun corfu-prompt-end (arg)
   1256   "Move to end of the prompt line.
   1257 If the point is already the end of the prompt move to the end of
   1258 the line.  If ARG is not 1 or nil, move forward ARG - 1 lines
   1259 first."
   1260   (interactive "^p")
   1261   (let ((end (cadr completion-in-region--data)))
   1262     (if (or (not (eq arg 1))
   1263             (and (= corfu--preselect corfu--index) (= (point) end)))
   1264         (move-end-of-line arg)
   1265       (corfu--goto -1)
   1266       (goto-char end))))
   1267 
   1268 (defun corfu-complete ()
   1269   "Complete current input.
   1270 If a candidate is selected, insert it.  Otherwise invoke
   1271 `corfu-expand'.  Return non-nil if the input has been expanded."
   1272   (interactive)
   1273   (if (< corfu--index 0)
   1274       (corfu-expand)
   1275     ;; Continue completion with selected candidate.  Exit with status 'finished
   1276     ;; if input is a valid match and no further completion is
   1277     ;; possible. Additionally treat completion as finished if at the end of a
   1278     ;; boundary, even if other longer candidates would still match, since the
   1279     ;; user invoked `corfu-complete' with an explicitly selected candidate!
   1280     (pcase-let ((`(,_beg ,_end ,table ,pred . ,_) completion-in-region--data)
   1281                 (newstr (corfu--insert nil)))
   1282       (and (test-completion newstr table pred)
   1283            (or (not (consp (completion-try-completion
   1284                             newstr table pred (length newstr)
   1285                             (completion-metadata newstr table pred))))
   1286                (equal (completion-boundaries newstr table pred "") '(0 . 0)))
   1287            (corfu--done newstr 'finished nil))
   1288       t)))
   1289 
   1290 (defun corfu-expand ()
   1291   "Expands the common prefix of all candidates.
   1292 If the currently selected candidate is previewed, invoke
   1293 `corfu-complete' instead.  Expansion relies on the completion
   1294 styles via `completion-try-completion'.  Return non-nil if the
   1295 input has been expanded."
   1296   (interactive)
   1297   (if (corfu--preview-current-p)
   1298       (corfu-complete)
   1299     (pcase-let* ((`(,beg ,end ,table ,pred . ,_) completion-in-region--data)
   1300                  (pt (max 0 (- (point) beg)))
   1301                  (str (buffer-substring-no-properties beg end))
   1302                  (metadata (completion-metadata (substring str 0 pt) table pred)))
   1303       (pcase (completion-try-completion str table pred pt metadata)
   1304         ('t
   1305          (goto-char end)
   1306          (corfu--done str 'finished corfu--candidates)
   1307          t)
   1308         ((and `(,newstr . ,newpt) (guard (not (and (= pt newpt) (equal newstr str)))))
   1309          (corfu--replace beg end newstr)
   1310          (goto-char (+ beg newpt))
   1311          ;; Exit with status 'finished if input is a valid match
   1312          ;; and no further completion is possible.
   1313          (and (test-completion newstr table pred)
   1314               (not (consp (completion-try-completion
   1315                            newstr table pred newpt
   1316                            (completion-metadata (substring newstr 0 newpt) table pred))))
   1317               (corfu--done newstr 'finished corfu--candidates))
   1318          t)))))
   1319 
   1320 (defun corfu-insert ()
   1321   "Insert current candidate.
   1322 Quit if no candidate is selected."
   1323   (interactive)
   1324   (if (>= corfu--index 0)
   1325       (corfu--insert 'finished)
   1326     (corfu-quit)))
   1327 
   1328 (defun corfu-send ()
   1329   "Insert current candidate and send it when inside comint or eshell."
   1330   (interactive)
   1331   (corfu-insert)
   1332   (cond
   1333    ((and (derived-mode-p 'eshell-mode) (fboundp 'eshell-send-input))
   1334     (eshell-send-input))
   1335    ((and (derived-mode-p 'comint-mode) (fboundp 'comint-send-input))
   1336     (comint-send-input))))
   1337 
   1338 ;;;###autoload
   1339 (define-minor-mode corfu-mode
   1340   "COmpletion in Region FUnction."
   1341   :group 'corfu :keymap corfu-mode-map
   1342   (cond
   1343    (corfu-mode
   1344     (and corfu-auto (add-hook 'post-command-hook #'corfu--auto-post-command nil 'local))
   1345     (setq-local completion-in-region-function #'corfu--in-region))
   1346    (t
   1347     (remove-hook 'post-command-hook #'corfu--auto-post-command 'local)
   1348     (kill-local-variable 'completion-in-region-function))))
   1349 
   1350 (defcustom global-corfu-minibuffer t
   1351   "Corfu should be enabled in the minibuffer by `global-corfu-mode'.
   1352 The variable can either be t, nil or a custom predicate function.  If
   1353 the variable is set to t, Corfu is only enabled if the minibuffer has
   1354 local `completion-at-point-functions'."
   1355   :type '(choice (const t) (const nil) function))
   1356 
   1357 ;;;###autoload
   1358 (define-globalized-minor-mode global-corfu-mode
   1359   corfu-mode corfu--on
   1360   :predicate t
   1361   :group 'corfu
   1362   (remove-hook 'minibuffer-setup-hook #'corfu--minibuffer-on)
   1363   (when (and global-corfu-mode global-corfu-minibuffer)
   1364     (add-hook 'minibuffer-setup-hook #'corfu--minibuffer-on 100)))
   1365 
   1366 (defun corfu--on ()
   1367   "Enable `corfu-mode' in the current buffer respecting `global-corfu-modes'."
   1368   (unless (or noninteractive (eq (aref (buffer-name) 0) ?\s))
   1369     (corfu-mode 1)))
   1370 
   1371 (defun corfu--minibuffer-on ()
   1372   "Enable `corfu-mode' in the minibuffer respecting `global-corfu-minibuffer'."
   1373   (when (and global-corfu-minibuffer (not noninteractive)
   1374              (if (functionp global-corfu-minibuffer)
   1375                  (funcall global-corfu-minibuffer)
   1376                (local-variable-p 'completion-at-point-functions)))
   1377     (corfu-mode 1)))
   1378 
   1379 ;; Do not show Corfu commands with M-X
   1380 (dolist (sym '(corfu-next corfu-previous corfu-first corfu-last corfu-quit corfu-reset
   1381                corfu-complete corfu-insert corfu-scroll-up corfu-scroll-down corfu-expand
   1382                corfu-send corfu-insert-separator corfu-prompt-beginning corfu-prompt-end
   1383                corfu-info-location corfu-info-documentation ;; autoloads in corfu-info.el
   1384                corfu-quick-jump corfu-quick-insert corfu-quick-complete)) ;; autoloads in corfu-quick.el
   1385   (put sym 'completion-predicate #'ignore))
   1386 
   1387 (defun corfu--capf-wrapper-advice (orig fun which)
   1388   "Around advice for `completion--capf-wrapper'.
   1389 The ORIG function takes the FUN and WHICH arguments."
   1390   (if corfu-mode (corfu--capf-wrapper fun t) (funcall orig fun which)))
   1391 
   1392 (defun corfu--eldoc-advice ()
   1393   "Return non-nil if Corfu is currently not active."
   1394   (not (and corfu-mode completion-in-region-mode)))
   1395 
   1396 ;; Install advice which fixes `completion--capf-wrapper', such that it respects
   1397 ;; the completion styles for non-exclusive Capfs. See also the fixme comment in
   1398 ;; the `completion--capf-wrapper' function in minibuffer.el.
   1399 (advice-add #'completion--capf-wrapper :around #'corfu--capf-wrapper-advice)
   1400 
   1401 ;; Register Corfu with ElDoc
   1402 (advice-add #'eldoc-display-message-no-interference-p
   1403             :before-while #'corfu--eldoc-advice)
   1404 (eldoc-add-command #'corfu-complete #'corfu-insert #'corfu-expand #'corfu-send)
   1405 
   1406 (provide 'corfu)
   1407 ;;; corfu.el ends here