config

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

corfu.el (65894B)


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