├── .elpaignore ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── config.yml ├── .gitignore ├── CHANGELOG.org ├── LICENSE ├── README.org ├── corfu.el └── extensions ├── corfu-echo.el ├── corfu-history.el ├── corfu-indexed.el ├── corfu-info.el ├── corfu-popupinfo.el └── corfu-quick.el /.elpaignore: -------------------------------------------------------------------------------- 1 | LICENSE 2 | .elpaignore 3 | .github -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 3 | name: 🐞 Bug report 4 | about: Report a bug. Do not use this for questions, support or feature requests. 5 | --- 6 | Thank you for reporting a bug. 7 | 8 | Please use the latest stable release of Emacs 30.1 and start with `emacs -Q` or 9 | `package-isolate` in order to only load a minimal set of packages. This way your 10 | Emacs configuration is not loaded. 11 | 12 | Please provide precise information and the exact steps to reproduce the issue. 13 | This is important to ensure that your problem can be reproduced on a different 14 | machine. 15 | 16 | If you are not really sure if your issue is a bug, please open a discussion 17 | instead. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: "🙏 Please support my work on Corfu and my other Emacs packages" 4 | url: https://github.com/sponsors/minad 5 | about: Thanks! Your support helps dedicating time to project maintenance and development. 6 | - name: "💡 Suggest a feature ➞ Please create a discussion" 7 | url: https://github.com/minad/corfu/discussions/categories/ideas 8 | about: Start a new discussion suggesting an improvement or a feature. 9 | - name: "🧑‍🤝‍🧑 Ask the community for support" 10 | url: https://www.reddit.com/r/emacs 11 | about: Please be kind and support others. 12 | - name: "🤓 Ask the maintainer for support ➞ Please create a discussion" 13 | url: https://github.com/minad/corfu/discussions/categories/q-a 14 | about: Please keep in mind that my bandwidth is limited. 15 | - name: "🔍 Search through old issues or discussions" 16 | url: https://github.com/search?q=repo%3Aminad%2Fcorfu&type=issues 17 | about: The same question may have been asked before. 18 | - name: "📝 Corfu wiki" 19 | url: https://github.com/minad/corfu/wiki 20 | about: Additional configuration tips are covered there. Feel free to edit! 21 | - name: "📖 Corfu manual" 22 | url: https://github.com/minad/corfu/blob/main/README.org 23 | about: The manual covers the basic setup and workflow. 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *-autoloads.el 2 | *-pkg.el 3 | *.elc 4 | *.info 5 | *.texi 6 | *~ 7 | \#*\# 8 | /README-elpa 9 | -------------------------------------------------------------------------------- /CHANGELOG.org: -------------------------------------------------------------------------------- 1 | #+title: corfu.el - Changelog 2 | #+author: Daniel Mendler 3 | #+language: en 4 | 5 | * Version 2.2 (2025-05-26) 6 | 7 | - Guard Corfu hooks to automatically print stack traces in order to ease 8 | debugging. 9 | 10 | * Version 2.1 (2025-04-22) 11 | 12 | - =corfu-history-duplicate= and =corfu-history-decay=: New customization options to 13 | adjust the rank of duplicate history elements, such that they appear earlier 14 | in the completion list. 15 | - =corfu-history-mode=: Add =corfu-history= to =savehist-minibuffer-history-variables= 16 | in order to save the history if =savehist-mode= is enabled. 17 | 18 | * Version 2.0 (2025-03-28) 19 | 20 | - ~corfu-quick~: Ensure that popup does not move. 21 | 22 | * Version 1.7 (2025-01-28) 23 | 24 | - Bugfixes only. 25 | 26 | * Version 1.6 (2024-12-22) 27 | 28 | - Require Emacs 28.1. 29 | - Use fringe to display scroll bar. This change improves performance and makes 30 | sure that the scroll bar cannot be pushed outside the child frame by the 31 | content. This affects for example ~cape-emoji~. 32 | - Improve suffix alignment. 33 | - Remember popup width during completion to avoid width fluctuations. Basically 34 | the popup is only allowed to grow. 35 | - ~corfu-insert-separator~: Jump back to prompt if a candidate is previewed. 36 | 37 | * Version 1.5 (2024-07-26) 38 | 39 | - New customization variable =global-corfu-minibuffer= to enable Corfu in the 40 | minibuffer. 41 | - Unbind =C-a= in =corfu-map=. This binding is only needed in modes which override 42 | =C-a= instead of remapping ~move-beginning-of-line~. 43 | - Unbind == in ~corfu-map~. This binding is only needed in modes which bind 44 | == instead of =TAB=, as was the case in old versions of Org. If you use such 45 | a mode, please report this as a bug for this mode. In the meantime you can use 46 | =(keymap-set corfu-map "" #'corfu-complete)=. 47 | - Add new command ~corfu-send~ as alternative to ~corfu-insert~. 48 | - =corfu-popupinfo=: Support both =face= and =font-lock-face= highlighting. 49 | - Bump Compat dependency to Compat 30. 50 | 51 | * Version 1.4 (2024-05-23) 52 | 53 | - ~corfu-auto-commands~: Add ~delete-backward-char~. 54 | 55 | * Version 1.3 (2024-04-05) 56 | 57 | - Preserve currently selected candidate on further input. This matters if 58 | candidate preview is disabled (~corfu-preview-current=nil~). 59 | - Add new command ~corfu-expand~ bound to ~M-TAB~ by default. The command expands 60 | the input via ~completion-try-completion~, for example the ~basic~ completion 61 | style expands the common prefix of all candidates. 62 | 63 | * Version 1.2 (2024-01-23) 64 | 65 | - Support the EXWM window manager. 66 | - Optimization: Reduce auto completion timer overhead. 67 | - Use ~internal-border-width~ instead of ~child-frame-border-width~. 68 | - Internal refactoring: Do not use buffer-local variables. 69 | - Internal refactoring: Store ~completion-extra-properties~ as part of 70 | ~completion-in-region--data~. 71 | 72 | * Version 1.1 (2023-12-27) 73 | 74 | - Deduplicate candidates with respect to ~equal-including-properties~, such that 75 | backends can provide equal candidate strings, which only differ in their text 76 | properties and annotations. 77 | - Ensure that the string passed to the ~:exit-function~ retains the candidate 78 | properties, when possible. The properties are guaranteed to exist when a 79 | candidate is selected explicitly, but may be missing when candidates are 80 | completed in a stepwise manner. 81 | - ~corfu-on-exact-match~: Add value ~show~ to the customization option. With this 82 | setting the Corfu popup will be shown even if there is only a single matching 83 | candidate. 84 | 85 | * Version 1.0 (2023-12-01) 86 | 87 | - Bug fixes. 88 | - =corfu-quick=: Use a slightly different scheme to support more candidates. 89 | - =corfu-reset=: Quit immediately if input did not change. 90 | - Support =completion-lazy-hilit=. 91 | 92 | * Version 0.38 (2023-08-14) 93 | 94 | - =corfu-quick=: Bugfix. 95 | - =corfu-mode-map=: Add mode map. 96 | - Replace =corfu-excluded-modes= with =global-corfu-modes=, the Emacs 28 convention 97 | for globalized minor modes. 98 | 99 | * Version 0.37 (2023-07-02) 100 | 101 | - Bugfixes. 102 | - Improve child frame display code, =corfu--popup-show= takes a =posn= argument. 103 | - Ensure that the popup font matches the font of the parent frame. 104 | - Close popup when window selection changes. 105 | - Remove =corfu-history-length=. Instead set the =history-length= property of 106 | =corfu-history= variable. 107 | - =corfu-info-documentation=, =corfu-info-location=: Make buffer and window 108 | persistent if called with prefix argument. 109 | 110 | * Version 0.36 (2023-03-27) 111 | 112 | - Drop obsolete =corfu-preselect-first=. 113 | - =corfu-popupinfo-delay= and =corfu-echo-delay=: Remove support for value =t=. 114 | Instant updates are not recommended. It is still possible to use a small value 115 | for the delay. 116 | - Rename =corfu-excluded-modes= to =corfu-exclude-modes= (Naming convention). 117 | - Remove call to =undo-boundary=, which caused issues with auto completion. 118 | 119 | * Version 0.35 (2023-02-17) 120 | 121 | - =corfu-popupinfo=: Take more text into account when computing popup width. 122 | - =corfu-popupinfo=: Change keybindings, remap =corfu-info-documentation/location=. 123 | - =corfu-popupinfo=: Add commands =corfu-popupinfo-beginning/end=. 124 | - =corfu-popupinfo=: Improve popup placement. 125 | - Add =corfu-prompt-beginning= and =corfu-prompt-end= commands. 126 | - Add =corfu-preselect= option, deprecate =corfu-preselect-first=. 127 | - Use =cl-defgeneric= internally as mechanism to allow extensions to override 128 | functionality, e.g., the candidate formatting and display. 129 | 130 | * Version 0.34 (2022-12-03) 131 | 132 | - Bugfixes 133 | - Popup frame code updated for Emacs 29. Please report any issues. 134 | - =corfu-popupinfo-direction=: Variable must be a list of directions. 135 | - Support height adjustments of =corfu-default= face 136 | 137 | * Version 0.33 (2022-11-21) 138 | 139 | - =corfu-popupinfo=: Bugfixes 140 | 141 | * Version 0.31 (2022-11-20) 142 | 143 | - =corfu-echo=, =corfu-quick=: Bugfixes for interaction issue. 144 | 145 | * Version 0.30 (2022-11-19) 146 | 147 | - =corfu-popupinfo=: Bugfixes and improvements. 148 | 149 | * Version 0.29 (2022-11-19) 150 | 151 | - BREAKING: Extract the =corfu-echo= extension from =corfu.el=. In order to see echo 152 | messages, enable =corfu-echo-mode=. You probably want to enable either 153 | =corfu-echo-mode= or =corfu-popupinfo-mode=. 154 | - BREAKING: Rename =corfu-echo-documentation= to =corfu-echo-delay=. 155 | - Add =corfu-popupinfo= extension to display candidate documentation and location 156 | in a small child frame next to the candidate menu. This extension has been 157 | contributed by Yuwei Tian who assigned copyright to the FSF. The extension 158 | supersedes Yuwei's =corfu-doc= package. 159 | 160 | * Version 0.28 (2022-10-16) 161 | 162 | - Start of changelog. 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+title: corfu.el - COmpletion in Region FUnction 2 | #+author: Daniel Mendler 3 | #+language: en 4 | #+export_file_name: corfu.texi 5 | #+texinfo_dir_category: Emacs misc features 6 | #+texinfo_dir_title: Corfu: (corfu). 7 | #+texinfo_dir_desc: COmpletion in Region FUnction 8 | 9 | #+html: GNU Emacs 10 | #+html: GNU ELPA 11 | #+html: GNU-devel ELPA 12 | #+html: MELPA 13 | #+html: MELPA Stable 14 | 15 | Corfu enhances in-buffer completion with a small completion popup. The current 16 | candidates are shown in a popup below or above the point, and can be selected by 17 | moving up and down. Corfu is the minimalistic in-buffer completion counterpart 18 | of the [[https://github.com/minad/vertico][Vertico]] minibuffer UI. 19 | 20 | Corfu is a small package, which relies on the Emacs completion facilities and 21 | concentrates on providing a polished completion UI. In-buffer completion UIs in 22 | Emacs can hook into ~completion-in-region~, which implements the interaction with 23 | the user. Completions at point are either provided by commands like 24 | ~dabbrev-completion~ or by pluggable backends (~completion-at-point-functions~, 25 | Capfs) and are then passed to ~completion-in-region~. Many programming, text and 26 | shell major modes implement a Capf. Corfu does not include its own completion 27 | backends. The Emacs built-in Capfs and the Capfs provided by third-party 28 | programming language packages are often sufficient. Additional Capfs and 29 | completion utilities are provided by the separate [[https://github.com/minad/cape][Cape]] package. 30 | 31 | *NOTE*: Corfu relies on child frames to show the popup. Emacs 31 supports child 32 | frames also for terminal Emacs. On older Emacs versions, you can use the 33 | [[https://codeberg.org/akib/emacs-corfu-terminal][corfu-terminal]] package. 34 | 35 | #+html: 36 | 37 | #+html: 38 | 39 | #+html: 40 | 41 | #+html: 42 | 43 | #+toc: headlines 8 44 | 45 | * Features 46 | 47 | - Timer-based auto-completions (/off/ by default). 48 | - Popup display with scrollbar indicator and arrow key navigation. 49 | - The popup can be summoned explicitly by pressing =TAB= at any time. 50 | - The current candidate is inserted with =TAB= and selected with =RET=. 51 | - Sorting by prefix, string length and alphabetically, optionally by history. 52 | - The selected candidate is previewed (configurable via ~corfu-preview-current~). 53 | - The selected candidate is automatically committed on further input by default. 54 | (configurable via ~corfu-preview-current~). 55 | - Supports the [[https://github.com/oantolin/orderless][Orderless]] completion style. The filter string can contain 56 | arbitrary characters, after inserting a space via =M-SPC= (configurable via 57 | ~corfu-quit-at-boundary~ and ~corfu-separator~). 58 | - Lazy candidate highlighting for performance. 59 | - Support for candidate annotations (=annotation-function=, =affixation-function=). 60 | - Deprecated candidates are displayed as crossed out. 61 | - Icons are provided by external packages via margin formatter functions. 62 | - Rich set of extensions: Quick keys, Index keys, Sorting by history, Candidate 63 | documentation in echo area, popup or separate buffer. 64 | 65 | * Installation 66 | 67 | Corfu is available from [[https://elpa.gnu.org/packages/corfu.html][GNU ELPA]]. You can install it directly via =M-x 68 | package-install RET corfu RET=. After installation, activate the global minor 69 | mode with =M-x global-corfu-mode RET=. For completion press =M-TAB= (or =TAB=) within 70 | a buffer. Auto completion is disabled by default for safety and unobtrusiveness. 71 | 72 | * Key bindings 73 | 74 | Corfu uses a transient keymap ~corfu-map~ which is active while the popup is 75 | shown. The keymap defines the following remappings of fundamental commands and 76 | bindings: 77 | 78 | | Binding/Remapping | Corfu command | 79 | |--------------------------+--------------------------| 80 | | ~move-beginning-of-line~ | ~corfu-prompt-beginning~ | 81 | | ~move-end-of-line~ | ~corfu-prompt-end~ | 82 | | ~beginning-of-buffer~ | ~corfu-first~ | 83 | | ~end-of-buffer~ | ~corfu-last~ | 84 | | ~scroll-down-command~ | ~corfu-scroll-down~ | 85 | | ~scroll-up-command~ | ~corfu-scroll-up~ | 86 | | ~next-line~, =down=, =M-n= | ~corfu-next~ | 87 | | ~previous-line~, =up=, =M-p= | ~corfu-previous~ | 88 | | ~completion-at-point~, =TAB= | ~corfu-complete~ | 89 | | =M-TAB= | ~corfu-expand~ | 90 | | =RET= | ~corfu-insert~ | 91 | | =M-g= | ~corfu-info-location~ | 92 | | =M-h= | ~corfu-info-documentation~ | 93 | | =M-SPC= | ~corfu-insert-separator~ | 94 | | =C-g= | ~corfu-quit~ | 95 | | ~keyboard-escape-quit~ | ~corfu-reset~ | 96 | 97 | * Configuration 98 | 99 | In order to configure Corfu and other packages in your init.el, you may want to 100 | use ~use-package~. Corfu is flexibly customizable via ~corfu-*~ customization 101 | variables, such that you can adapt it precisely to your requirements. However in 102 | order to quickly try out the Corfu completion package, it should be sufficient 103 | to activate ~global-corfu-mode~. You can experiment with manual completion for 104 | example in an Elisp buffer or in an Eshell or Shell buffer. 105 | 106 | Auto completion is disabled by default in Corfu. Note that completion can be 107 | vulnerable to arbitrary code execution in untrusted files. In particular the 108 | ~elisp-completion-at-point~ completion function performs macro expansion and code 109 | evaluation. Auto completion can be enabled by setting ~corfu-auto~ to t locally or 110 | globally before enabling the local ~corfu-mode~ or the ~global-corfu-mode~. 111 | 112 | Here is an example configuration: 113 | 114 | #+begin_src emacs-lisp 115 | (use-package corfu 116 | ;; Optional customizations 117 | ;; :custom 118 | ;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' 119 | ;; (corfu-quit-at-boundary nil) ;; Never quit at completion boundary 120 | ;; (corfu-quit-no-match nil) ;; Never quit, even if there is no match 121 | ;; (corfu-preview-current nil) ;; Disable current candidate preview 122 | ;; (corfu-preselect 'prompt) ;; Preselect the prompt 123 | ;; (corfu-on-exact-match nil) ;; Configure handling of exact matches 124 | 125 | ;; Enable Corfu only for certain modes. See also `global-corfu-modes'. 126 | ;; :hook ((prog-mode . corfu-mode) 127 | ;; (shell-mode . corfu-mode) 128 | ;; (eshell-mode . corfu-mode)) 129 | 130 | :init 131 | 132 | ;; Recommended: Enable Corfu globally. Recommended since many modes provide 133 | ;; Capfs and Dabbrev can be used globally (M-/). See also the customization 134 | ;; variable `global-corfu-modes' to exclude certain modes. 135 | (global-corfu-mode) 136 | 137 | ;; Enable optional extension modes: 138 | ;; (corfu-history-mode) 139 | ;; (corfu-popupinfo-mode) 140 | ) 141 | 142 | ;; A few more useful configurations... 143 | (use-package emacs 144 | :custom 145 | ;; TAB cycle if there are only few candidates 146 | ;; (completion-cycle-threshold 3) 147 | 148 | ;; Enable indentation+completion using the TAB key. 149 | ;; `completion-at-point' is often bound to M-TAB. 150 | (tab-always-indent 'complete) 151 | 152 | ;; Emacs 30 and newer: Disable Ispell completion function. 153 | ;; Try `cape-dict' as an alternative. 154 | (text-mode-ispell-word-completion nil) 155 | 156 | ;; Hide commands in M-x which do not apply to the current mode. Corfu 157 | ;; commands are hidden, since they are not used via M-x. This setting is 158 | ;; useful beyond Corfu. 159 | (read-extended-command-predicate #'command-completion-default-include-p)) 160 | #+end_src 161 | 162 | Dabbrev completion is based on =completion-in-region= and can be used with Corfu. 163 | You may want to swap the =dabbrev-completion= with the =dabbrev-expand= key for 164 | easier access, if you prefer completion. Also take a look at the =cape-dabbrev= 165 | completion at point function provided by my [[https://github.com/minad/cape][Cape]] package. 166 | 167 | #+begin_src emacs-lisp 168 | ;; Use Dabbrev with Corfu! 169 | (use-package dabbrev 170 | ;; Swap M-/ and C-M-/ 171 | :bind (("M-/" . dabbrev-completion) 172 | ("C-M-/" . dabbrev-expand)) 173 | :config 174 | (add-to-list 'dabbrev-ignored-buffer-regexps "\\` ") 175 | ;; Available since Emacs 29 (Use `dabbrev-ignored-buffer-regexps' on older Emacs) 176 | (add-to-list 'dabbrev-ignored-buffer-modes 'authinfo-mode) 177 | (add-to-list 'dabbrev-ignored-buffer-modes 'doc-view-mode) 178 | (add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode) 179 | (add-to-list 'dabbrev-ignored-buffer-modes 'tags-table-mode)) 180 | #+end_src 181 | 182 | If you start to configure Corfu more thoroughly, I recommend to give the 183 | Orderless completion style a try for filtering. Orderless completion offers more 184 | flexible filtering than the default completion styles. Note that Orderless is 185 | not a necessity; Corfu can be used just as well with the default completion 186 | styles. 187 | 188 | #+begin_src emacs-lisp 189 | ;; Optionally use the `orderless' completion style. 190 | (use-package orderless 191 | :custom 192 | ;; (orderless-style-dispatchers '(orderless-affix-dispatch)) 193 | ;; (orderless-component-separator #'orderless-escapable-split-on-space) 194 | (completion-styles '(orderless basic)) 195 | (completion-category-defaults nil) 196 | (completion-category-overrides '((file (styles partial-completion))))) 197 | #+end_src 198 | 199 | The =basic= completion style is specified as fallback in addition to =orderless= in 200 | order to ensure that completion commands which rely on dynamic completion 201 | tables, e.g., ~completion-table-dynamic~ or ~completion-table-in-turn~, work 202 | correctly. Additionally enable =partial-completion= for file path expansion. 203 | =partial-completion= is important for file wildcard support. Multiple files can be 204 | opened at once with =find-file= if you enter a wildcard. You may also give the 205 | =initials= completion style a try. 206 | 207 | See also the [[https://github.com/minad/corfu/wiki][Corfu Wiki]] and the [[https://github.com/minad/cape][Cape manual]] for additional Capf configuration 208 | tips. For more general documentation read the chapter about completion in the 209 | [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Completion.html][Emacs manual]]. If you want to create your own Capfs, you can find documentation 210 | about completion in the [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion.html][Elisp manual]]. 211 | 212 | ** Auto completion 213 | 214 | Auto completion is disabled by default for safety and unobtrusiveness. Note that 215 | completion can be vulnerable to arbitrary code execution. Auto completion can be 216 | enabled by setting ~corfu-auto~ to t. Only enable auto completion locally in 217 | trusted buffers or globally if you edit trusted files only. 218 | 219 | You may want to configure Corfu to quit completion eagerly, such that the 220 | completion popup stays out of your way when it appeared unexpectedly. 221 | 222 | #+begin_src emacs-lisp 223 | ;; Enable auto completion and configure quitting 224 | (setq corfu-auto t 225 | corfu-quit-no-match 'separator) ;; or t 226 | #+end_src 227 | 228 | I suggest to experiment with the various settings and key bindings to find a 229 | configuration which works for you. There is no one perfect configuration which 230 | fits all. Some people like auto completion, some like manual completion, some 231 | want to cycle with TAB and some with the arrow keys. 232 | 233 | In case you like auto completion settings, where the completion popup appears 234 | immediately, better use a cheap completion style like =basic=, which performs 235 | prefix filtering. See the next section about setting Corfu-only completion 236 | styles. In this case Corfu completion should still be fast in buffers with 237 | efficient completion backends. You can try the following settings in an Elisp 238 | buffer or the Emacs scratch buffer. Note that such settings can slow down Emacs 239 | due to the high load on the Lisp runtime and garbage collector. 240 | 241 | #+begin_src emacs-lisp 242 | (setq corfu-auto t 243 | corfu-auto-delay 0 ;; TOO SMALL - NOT RECOMMENDED! 244 | corfu-auto-prefix 0) ;; TOO SMALL - NOT RECOMMENDED! 245 | 246 | (add-hook 'corfu-mode-hook 247 | (lambda () 248 | ;; Settings only for Corfu 249 | (setq-local completion-styles '(basic) 250 | completion-category-overrides nil 251 | completion-category-defaults nil))) 252 | #+end_src 253 | 254 | ** Buffer-local/Corfu-only completion styles 255 | 256 | Sometimes it makes sense to use separate completion style settings for 257 | minibuffer completion and in-buffer Corfu completion. For example inside the 258 | minibuffer you may prefer advanced Orderless completion, while for Corfu, faster 259 | prefix completion is needed or literal-only completion is sufficient. 260 | 261 | This matters in particular if you use aggressive auto completion settings, where 262 | the completion popup appears immediately. Then a cheap completion style like 263 | =basic= should be used, which performs prefix filtering only. 264 | 265 | Such Corfu-only configurations are possible by setting the ~completion-styles~ 266 | variables buffer-locally, as follows: 267 | 268 | #+begin_src emacs-lisp 269 | (orderless-define-completion-style orderless-literal-only 270 | (orderless-style-dispatchers nil) 271 | (orderless-matching-styles '(orderless-literal))) 272 | 273 | (add-hook 'corfu-mode-hook 274 | (lambda () 275 | (setq-local completion-styles '(orderless-literal-only basic) 276 | completion-category-overrides nil 277 | completion-category-defaults nil))) 278 | #+end_src 279 | 280 | If you want to combine fast prefix filtering and Orderless filtering you can 281 | still do that by defining a custom Orderless completion style via 282 | =orderless-define-completion-style=. We use a custom style dispatcher, which 283 | enables efficient prefix filtering for input shorter than 4 characters. 284 | 285 | #+begin_src emacs-lisp 286 | (defun orderless-fast-dispatch (word index total) 287 | (and (= index 0) (= total 1) (length< word 4) 288 | (cons 'orderless-literal-prefix word))) 289 | 290 | (orderless-define-completion-style orderless-fast 291 | (orderless-style-dispatchers '(orderless-fast-dispatch)) 292 | (orderless-matching-styles '(orderless-literal orderless-regexp))) 293 | 294 | (setq corfu-auto t 295 | corfu-auto-delay 0 ;; TOO SMALL - NOT RECOMMENDED 296 | corfu-auto-prefix 0) ;; TOO SMALL - NOT RECOMMENDED 297 | 298 | (add-hook 'corfu-mode-hook 299 | (lambda () 300 | (setq-local completion-styles '(orderless-fast basic) 301 | completion-category-overrides nil 302 | completion-category-defaults nil))) 303 | #+end_src 304 | 305 | ** Completing in the minibuffer 306 | 307 | Corfu can be used for completion in the minibuffer, since it relies on child 308 | frames to display the candidates. The Corfu popup floats on top of the Emacs 309 | frame and can be shown even if it doesn't fit inside the minibuffer. 310 | 311 | ~global-corfu-mode~ activates ~corfu-mode~ in the minibuffer if the variable 312 | ~global-corfu-minibuffer~ is non-nil. In order to avoid interference with 313 | specialised minibuffer completion UIs like Vertico or Mct, Corfu is only enabled 314 | if the minibuffer sets the variable ~completion-at-point-functions~ locally. This 315 | way minibuffers with completion can be detected, such that minibuffer commands 316 | like ~M-:~ (~eval-expression~) or ~M-!~ (~shell-command~) are enhanced with Corfu 317 | completion. 318 | 319 | If needed, one can also enable Corfu more generally in all minibuffers, as long 320 | as no completion UI is active. In the following example we set 321 | ~global-corfu-minibuffer~ to a predicate function, which checks for Mct and 322 | Vertico. Furthermore we ensure that Corfu is not enabled if a password is read 323 | from the minibuffer. 324 | 325 | #+begin_src emacs-lisp 326 | (setq global-corfu-minibuffer 327 | (lambda () 328 | (not (or (bound-and-true-p mct--active) 329 | (bound-and-true-p vertico--input) 330 | (eq (current-local-map) read-passwd-map))))) 331 | #+end_src 332 | 333 | ** Completing in the Eshell or Shell 334 | 335 | When completing in the Eshell I recommend conservative local settings without 336 | auto completion, such that the completion behavior is similar to widely used 337 | shells like Bash, Zsh or Fish. 338 | 339 | #+begin_src emacs-lisp 340 | (add-hook 'eshell-mode-hook (lambda () 341 | (setq-local corfu-auto nil) 342 | (corfu-mode))) 343 | #+end_src 344 | 345 | When pressing =RET= while the Corfu popup is visible, the ~corfu-insert~ command 346 | will be invoked. This command does inserts the currently selected candidate, but 347 | it does not send the prompt input to Eshell or the Comint process. Therefore you 348 | often have to press =RET= twice which feels like an unnecessary double 349 | confirmation. Fortunately it is easy to improve this by using the command 350 | ~corfu-send~ instead. 351 | 352 | #+begin_src emacs-lisp 353 | (keymap-set corfu-map "RET" #'corfu-send) 354 | #+end_src 355 | 356 | Shell completion uses the flexible Pcomplete mechanism internally, which allows 357 | you to program the completions per shell command. If you want to know more, look 358 | into this [[https://www.masteringemacs.org/article/pcomplete-context-sensitive-completion-emacs][blog post]], which shows how to configure Pcomplete for git commands. 359 | Since Emacs 29, Pcomplete offers the =pcomplete-from-help= function which parses 360 | the ~--help~ output of a command and produces completions for command line 361 | options. 362 | 363 | Pcomplete has a few bugs on Emacs 28. We can work around the issues 364 | with the [[https://github.com/minad/cape][Cape]] library (Completion at point extensions). Cape provides wrappers 365 | which sanitize the Pcomplete function. On Emacs 29 the advices should not be 366 | necessary anymore, since most relevant bugs have been fixed. In case you 367 | discover any remaining Pcomplete issues, please report them upstream. 368 | 369 | #+begin_src emacs-lisp 370 | ;; Sanitize the `pcomplete-completions-at-point' Capf. The Capf has undesired 371 | ;; side effects on Emacs 28. These advices are not needed on Emacs 29 and newer. 372 | (when (< emacs-major-version 29) 373 | (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) 374 | (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify)) 375 | #+end_src 376 | 377 | ** Orderless completion 378 | 379 | [[https://github.com/oantolin/orderless][Orderless]] is an advanced completion style that supports multi-component search 380 | filters separated by a configurable character (space, by default). Normally, 381 | entering characters like space which lie outside the completion region 382 | boundaries (words, typically) causes Corfu to quit. This behavior is helpful 383 | with auto-completion, which may pop-up when not desired, e.g. on entering a new 384 | variable name. Just keep typing and Corfu will get out of the way. 385 | 386 | But orderless search terms can contain arbitrary characters; they are also 387 | interpreted as regular expressions. To use orderless, set ~corfu-separator~ (a 388 | space, by default) to the primary character of your orderless component 389 | separator. 390 | 391 | Then, when a new orderless component is desired, use =M-SPC= 392 | (~corfu-insert-separator~) to enter the first component separator in the input, 393 | and arbitrary orderless search terms and new separators can be entered 394 | thereafter. 395 | 396 | To treat the entire input as Orderless input, you can set the customization 397 | option ~corfu-quit-at-boundary~ to nil. This disables the predicate which checks 398 | if the current completion boundary has been left. In contrast, if you always 399 | want to quit at the boundary, set ~corfu-quit-at-boundary~ to t. By default 400 | ~corfu-quit-at-boundary~ is set to ~separator~ which quits at completion boundaries 401 | as long as no separator has been inserted with ~corfu-insert-separator~. 402 | 403 | Finally, there exists the user option ~corfu-quit-no-match~ which is set to 404 | =separator= by default. With this setting Corfu stays alive as soon as you start 405 | advanced filtering with a ~corfu-separator~ even if there are no matches, for 406 | example due to a typo. As long as no separator character has been inserted with 407 | ~corfu-insert-separator~, Corfu will still quit if there are no matches. This 408 | ensures that the Corfu popup goes away quickly if completion is not possible. 409 | 410 | In the following we show two configurations, one which works best with auto 411 | completion and one which may work better with manual completion if you prefer to 412 | always use =SPC= to separate the Orderless components. 413 | 414 | #+begin_src emacs-lisp 415 | ;; Auto completion example 416 | (use-package corfu 417 | :custom 418 | (corfu-auto t) ;; Enable auto completion 419 | ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space 420 | :bind 421 | ;; Another key binding can be used, such as S-SPC. 422 | ;; (:map corfu-map ("M-SPC" . corfu-insert-separator)) 423 | :init 424 | (global-corfu-mode)) 425 | 426 | ;; Manual completion example 427 | (use-package corfu 428 | :custom 429 | ;; (corfu-separator ?_) ;; Set to orderless separator, if not using space 430 | :bind 431 | ;; Configure SPC for separator insertion 432 | (:map corfu-map ("SPC" . corfu-insert-separator)) 433 | :init 434 | (global-corfu-mode)) 435 | #+end_src 436 | 437 | ** TAB-only completion 438 | 439 | By default, Corfu steals both the ~RET~ and ~TAB~ keys, when the Corfu popup is 440 | open. This can feel intrusive, in particular in combination with auto 441 | completion. ~RET~ may accidentally commit an automatically selected candidate, 442 | while you actually wanted to start a new line. As an alternative we can unbind 443 | the ~RET~ key completely from ~corfu-map~ or reserve the ~RET~ key only in shell 444 | modes using a menu-item filter. 445 | 446 | #+begin_src emacs-lisp 447 | ;; TAB-only configuration 448 | (use-package corfu 449 | :custom 450 | (corfu-auto t) ;; Enable auto completion 451 | (corfu-preselect 'directory) ;; Select the first candidate, except for directories 452 | 453 | :init 454 | (global-corfu-mode) 455 | 456 | :config 457 | ;; Free the RET key for less intrusive behavior. 458 | ;; Option 1: Unbind RET completely 459 | ;; (keymap-unset corfu-map "RET") 460 | ;; Option 2: Use RET only in shell modes 461 | (keymap-set corfu-map "RET" `( menu-item "" nil :filter 462 | ,(lambda (&optional _) 463 | (and (derived-mode-p 'eshell-mode 'comint-mode) 464 | #'corfu-send))))) 465 | #+end_src 466 | 467 | ** TAB-and-Go completion 468 | 469 | You may be interested in configuring Corfu in TAB-and-Go style. Pressing TAB 470 | moves to the next candidate and further input will then commit the selection. 471 | Note that further input will not expand snippets or templates, which may not be 472 | desired but which leads overall to a more predictable behavior. In order to 473 | force snippet expansion, confirm a candidate explicitly with ~RET~. 474 | 475 | #+begin_src emacs-lisp 476 | (use-package corfu 477 | ;; TAB-and-Go customizations 478 | :custom 479 | (corfu-cycle t) ;; Enable cycling for `corfu-next/previous' 480 | (corfu-preselect 'prompt) ;; Always preselect the prompt 481 | 482 | ;; Use TAB for cycling, default is `corfu-complete'. 483 | :bind 484 | (:map corfu-map 485 | ("TAB" . corfu-next) 486 | ([tab] . corfu-next) 487 | ("S-TAB" . corfu-previous) 488 | ([backtab] . corfu-previous)) 489 | 490 | :init 491 | (global-corfu-mode)) 492 | #+end_src 493 | 494 | ** Transfer completion to the minibuffer 495 | 496 | Sometimes it is useful to transfer the Corfu completion session to the 497 | minibuffer, since the minibuffer offers richer interaction features. In 498 | particular, [[https://github.com/oantolin/embark][Embark]] is available in the minibuffer, such that you can act on the 499 | candidates or export/collect the candidates to a separate buffer. We could add 500 | Corfu support to Embark in the future, such that export or collect is possible 501 | directly from Corfu. Nevertheless, the ability to transfer the Corfu completion 502 | to the minibuffer is even more powerful, since further completion is possible. 503 | 504 | The command ~corfu-move-to-minibuffer~ is defined here in terms of 505 | ~consult-completion-in-region~, which uses the minibuffer completion UI via 506 | ~completing-read~. 507 | 508 | #+begin_src emacs-lisp 509 | (defun corfu-move-to-minibuffer () 510 | (interactive) 511 | (pcase completion-in-region--data 512 | (`(,beg ,end ,table ,pred ,extras) 513 | (let ((completion-extra-properties extras) 514 | completion-cycle-threshold completion-cycling) 515 | (consult-completion-in-region beg end table pred))))) 516 | (keymap-set corfu-map "M-m" #'corfu-move-to-minibuffer) 517 | (add-to-list 'corfu-continue-commands #'corfu-move-to-minibuffer) 518 | #+end_src 519 | 520 | * Extensions 521 | :properties: 522 | :custom_id: extensions 523 | :end: 524 | 525 | We maintain small extension packages to Corfu in this repository in the 526 | subdirectory [[https://github.com/minad/corfu/tree/main/extensions][extensions/]]. The extensions are installed together with Corfu if 527 | you pull the package from ELPA. The extensions are inactive by default and can 528 | be enabled manually if desired. Furthermore it is possible to install all of the 529 | files separately, both ~corfu.el~ and the ~corfu-*.el~ extensions. Currently the 530 | following extensions come with the Corfu ELPA package: 531 | 532 | - [[https://github.com/minad/corfu/blob/main/extensions/corfu-echo.el][corfu-echo]]: =corfu-echo-mode= displays a brief candidate documentation in the 533 | echo area. 534 | - [[https://github.com/minad/corfu/blob/main/extensions/corfu-history.el][corfu-history]]: =corfu-history-mode= remembers selected candidates and sorts the 535 | candidates by their history position and frequency. 536 | - [[https://github.com/minad/corfu/blob/main/extensions/corfu-indexed.el][corfu-indexed]]: =corfu-indexed-mode= allows you to select indexed candidates with 537 | prefix arguments. 538 | - [[https://github.com/minad/corfu/blob/main/extensions/corfu-info.el][corfu-info]]: Actions to access the candidate location and documentation. 539 | - [[https://github.com/minad/corfu/blob/main/extensions/corfu-popupinfo.el][corfu-popupinfo]]: Display candidate documentation or source in a popup next to 540 | the candidate menu. 541 | - [[https://github.com/minad/corfu/blob/main/extensions/corfu-quick.el][corfu-quick]]: Commands to select using Avy-style quick keys. 542 | 543 | See the Commentary of those files for configuration details. 544 | 545 | * Complementary packages 546 | 547 | Corfu works well together with all packages providing code completion via the 548 | ~completion-at-point-functions~. Many modes and packages already provide a Capf 549 | out of the box. Nevertheless you may want to look into complementary packages to 550 | enhance your setup. 551 | 552 | - [[https://code.bsdgeek.org/adam/corfu-candidate-overlay][corfu-candidate-overlay]]: Shows as-you-type auto-suggestion candidate overlay 553 | with a visual indication of whether there are many or exactly one candidate 554 | available (works only with =corfu-auto= disabled). 555 | 556 | - [[https://codeberg.org/akib/emacs-corfu-terminal][corfu-terminal]]: Child frames are supported by terminal Emacs 31 out of the 557 | box. On older Emacs versions, this package provides an overlay-based popup 558 | display. 559 | 560 | - [[https://github.com/oantolin/orderless][Orderless]]: Corfu supports completion styles, including the advanced =orderless= 561 | completion style, where the filter expressions are separated by spaces or 562 | another character (see ~corfu-separator~). 563 | 564 | - [[https://github.com/minad/cape][Cape]]: Provides additional Capf backends and =completion-in-region= commands. 565 | Among others, the package supplies the file completion backend =cape-file= and 566 | the Dabbrev backend =cape-dabbrev=. Cape provides the ~cape-company-to-capf~ 567 | adapter to reuse Company backends in Corfu. 568 | 569 | - [[https://github.com/jdtsmith/kind-icon][kind-icon]], [[https://github.com/LuigiPiucco/nerd-icons-corfu][nerd-icons-corfu]]: Icons are supported by Corfu via external 570 | packages. The nerd-icons-corfu package relies on the Nerd icon font, which is 571 | supported on terminal, while kind-icon uses SVGs from monochromatic icon 572 | sets, or colored-coded text badges for terminal or simpler appearance. 573 | 574 | - [[https://github.com/minad/tempel][Tempel]]: Tiny template/snippet package with templates in Lisp syntax, which 575 | can be used in conjunction with Corfu. 576 | 577 | - [[https://github.com/minad/vertico][Vertico]]: You may also want to look into my Vertico package. Vertico is the 578 | minibuffer completion counterpart of Corfu. 579 | 580 | * Alternatives 581 | 582 | - [[https://github.com/company-mode/company-mode][Company]]: Company is a widely used and mature completion package, which 583 | implements a similar UI as Corfu. While Corfu relies exclusively on the 584 | standard Emacs completion API (Capfs), Company defines its own API for the 585 | backends. Company includes its own completion backends, following its own API, 586 | which are incompatible with the Emacs completion infrastructure. Company 587 | provides an adapter ~company-capf~ to handle Capfs as a Company backend. As a 588 | result of this design, Company is a more complex package than Corfu, three 589 | times as large, even without backends. Company by default uses overlays for 590 | the popup in contrast to the child frames used by Corfu. Overall both packages 591 | work well, but Company integrates less tightly with Emacs. The 592 | ~completion-styles~ support is more limited and the ~completion-at-point~ command 593 | and the ~completion-in-region~ function do not invoke Company. 594 | 595 | - [[https://github.com/minad/consult][consult-completion-in-region]]: The Consult package provides the function 596 | ~consult-completion-in-region~ which can be set as ~completion-in-region-function~ 597 | such that it handles ~completion-at-point~. The function works by transferring 598 | the in-buffer completion to the minibuffer. In the minibuffer, the minibuffer 599 | completion UI, for example [[https://github.com/minad/vertico][Vertico]] takes over. If you prefer to perform all 600 | your completions in the minibuffer ~consult-completion-in-region~ is your best 601 | option. 602 | 603 | * Debugging Corfu 604 | 605 | Corfu will automatically print a stack trace to the =*Messages*= buffer when an 606 | error is detected. The stack trace allows you to narrow down the exact code 607 | location which caused the error. 608 | 609 | When Capfs do not yield the expected result, you can wrap a Capf with 610 | ~cape-capf-debug~ from the [[https://github.com/minad/cape][Cape]] package, creating a new Capf, which adds 611 | completion log messages for debugging. The completion log messages are added to 612 | the =*Messages*= buffer. 613 | 614 | #+begin_src emacs-lisp 615 | (setq completion-at-point-functions (list (cape-capf-debug #'cape-dict))) 616 | #+end_src 617 | 618 | Sometimes you will find errors inside Capfs. Such errors are bugs in the Capfs 619 | must be fixed there, since they Corfu cannot work around them. 620 | 621 | * Contributions 622 | 623 | Since this package is part of [[https://elpa.gnu.org/packages/corfu.html][GNU ELPA]] contributions require a copyright 624 | assignment to the FSF. 625 | -------------------------------------------------------------------------------- /corfu.el: -------------------------------------------------------------------------------- 1 | ;;; corfu.el --- COmpletion in Region FUnction -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2021-2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Daniel Mendler 6 | ;; Maintainer: Daniel Mendler 7 | ;; Created: 2021 8 | ;; Version: 2.2 9 | ;; Package-Requires: ((emacs "28.1") (compat "30")) 10 | ;; URL: 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 . 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 "Website" "https://github.com/minad/corfu") 47 | :link '(url-link :tag "Wiki" "https://github.com/minad/corfu/wiki") 48 | :link '(emacs-library-link :tag "Library Source" "corfu.el") 49 | :group 'convenience 50 | :group 'tools 51 | :group 'matching 52 | :prefix "corfu-") 53 | 54 | (defcustom corfu-count 10 55 | "Maximal number of candidates to show." 56 | :type 'natnum) 57 | 58 | (defcustom corfu-scroll-margin 2 59 | "Number of lines at the top and bottom when scrolling. 60 | The value should lie between 0 and corfu-count/2." 61 | :type 'natnum) 62 | 63 | (defcustom corfu-min-width 15 64 | "Popup minimum width in characters." 65 | :type 'natnum) 66 | 67 | (defcustom corfu-max-width 100 68 | "Popup maximum width in characters." 69 | :type 'natnum) 70 | 71 | (defcustom corfu-cycle nil 72 | "Enable cycling for `corfu-next' and `corfu-previous'." 73 | :type 'boolean) 74 | 75 | (defcustom corfu-on-exact-match 'insert 76 | "Configure how a single exact match should be handled. 77 | - nil: No special handling, continue completion. 78 | - insert: Insert candidate, quit and call the `:exit-function'. 79 | - quit: Quit completion without further action. 80 | - show: Initiate completion even for a single match only." 81 | :type '(choice (const insert) (const show) (const quit) (const nil))) 82 | 83 | (defcustom corfu-continue-commands 84 | '(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. In order to preserve correct popup 149 | alignment, the length and display width of the returned string must 150 | precisely span the same number of characters of the fixed-width popup 151 | font. For example the kind-icon package returns a string of length 3 152 | with a display width of 3 characters." 153 | :type 'hook) 154 | 155 | (defcustom corfu-sort-function #'corfu-sort-length-alpha 156 | "Default sorting function. 157 | This function is used if the completion table does not specify a 158 | `display-sort-function'." 159 | :type `(choice 160 | (const :tag "No sorting" nil) 161 | (const :tag "By length and alpha" ,#'corfu-sort-length-alpha) 162 | (function :tag "Custom function"))) 163 | 164 | (defcustom corfu-sort-override-function nil 165 | "Override sort function which overrides the `display-sort-function'. 166 | This function is used even if a completion table specifies its 167 | own sort function." 168 | :type '(choice (const nil) function)) 169 | 170 | (defcustom corfu-auto-prefix 3 171 | "Minimum length of prefix for auto completion. 172 | The completion backend can override this with 173 | :company-prefix-length. It is *not recommended* to use a small 174 | prefix length (below 2), since this will create high load for 175 | Emacs. See also `corfu-auto-delay'." 176 | :type 'natnum) 177 | 178 | (defcustom corfu-auto-delay 0.2 179 | "Delay for auto completion. 180 | It is *not recommended* to use a short delay or even 0, since 181 | this will create high load for Emacs, in particular if executing 182 | the completion backend is costly." 183 | :type 'float) 184 | 185 | (defcustom corfu-auto-commands 186 | '("self-insert-command\\'" "delete-backward-char\\'" "\\`backward-delete-char" 187 | c-electric-colon c-electric-lt-gt c-electric-slash c-scope-operator) 188 | "Commands which initiate auto completion. 189 | The list can container either command symbols or regular expressions." 190 | :type '(repeat (choice regexp symbol))) 191 | 192 | (defcustom corfu-auto nil 193 | "Enable auto completion. 194 | Auto completion is disabled by default for safety and unobtrusiveness. 195 | Note that auto completion is particularly dangerous in untrusted files 196 | since some completion functions may perform arbitrary code execution, 197 | notably the Emacs built-in `elisp-completion-at-point' . See also the 198 | settings `corfu-auto-delay', `corfu-auto-prefix' and 199 | `corfu-auto-commands'." 200 | :type 'boolean) 201 | 202 | (defgroup corfu-faces nil 203 | "Faces used by Corfu." 204 | :group 'corfu 205 | :group 'faces) 206 | 207 | (defface corfu-default 208 | '((((class color) (min-colors 88) (background dark)) :background "#191a1b") 209 | (((class color) (min-colors 88) (background light)) :background "#f0f0f0") 210 | (t :background "gray")) 211 | "Default face, foreground and background colors used for the popup.") 212 | 213 | (defface corfu-current 214 | '((((class color) (min-colors 88) (background dark)) 215 | :background "#00415e" :foreground "white" :extend t) 216 | (((class color) (min-colors 88) (background light)) 217 | :background "#c0efff" :foreground "black" :extend t) 218 | (t :background "blue" :foreground "white" :extend t)) 219 | "Face used to highlight the currently selected candidate.") 220 | 221 | (defface corfu-bar 222 | '((((class color) (min-colors 88) (background dark)) :background "#a8a8a8") 223 | (((class color) (min-colors 88) (background light)) :background "#505050") 224 | (t :background "gray")) 225 | "The background color is used for the scrollbar indicator.") 226 | 227 | (defface corfu-border 228 | '((((class color) (min-colors 88) (background dark)) :background "#323232") 229 | (((class color) (min-colors 88) (background light)) :background "#d7d7d7") 230 | (t :background "gray")) 231 | "The background color used for the thin border.") 232 | 233 | (defface corfu-annotations 234 | '((t :inherit completions-annotations)) 235 | "Face used for annotations.") 236 | 237 | (defface corfu-deprecated 238 | '((t :inherit shadow :strike-through t)) 239 | "Face used for deprecated candidates.") 240 | 241 | (defvar-keymap corfu-mode-map 242 | :doc "Keymap used when `corfu-mode' is active.") 243 | 244 | (defvar-keymap corfu-map 245 | :doc "Keymap used when popup is shown." 246 | " " #'corfu-prompt-beginning 247 | " " #'corfu-prompt-end 248 | " " #'corfu-first 249 | " " #'corfu-last 250 | " " #'corfu-scroll-down 251 | " " #'corfu-scroll-up 252 | " " #'corfu-next 253 | " " #'corfu-previous 254 | " " #'corfu-complete 255 | " " #'corfu-reset 256 | "" #'corfu-next 257 | "" #'corfu-previous 258 | "M-n" #'corfu-next 259 | "M-p" #'corfu-previous 260 | "C-g" #'corfu-quit 261 | "RET" #'corfu-insert 262 | "TAB" #'corfu-complete 263 | "M-TAB" #'corfu-expand 264 | "M-g" 'corfu-info-location 265 | "M-h" 'corfu-info-documentation 266 | "M-SPC" #'corfu-insert-separator) 267 | 268 | (defvar corfu--auto-timer (timer-create) 269 | "Auto completion timer.") 270 | 271 | (defvar corfu--candidates nil 272 | "List of candidates.") 273 | 274 | (defvar corfu--metadata nil 275 | "Completion metadata.") 276 | 277 | (defvar corfu--base "" 278 | "Base string, which is concatenated with the candidate.") 279 | 280 | (defvar corfu--total 0 281 | "Length of the candidate list `corfu--candidates'.") 282 | 283 | (defvar corfu--hilit #'identity 284 | "Lazy candidate highlighting function.") 285 | 286 | (defvar corfu--index -1 287 | "Index of current candidate or negative for prompt selection.") 288 | 289 | (defvar corfu--preselect -1 290 | "Index of preselected candidate, negative for prompt selection.") 291 | 292 | (defvar corfu--scroll 0 293 | "Scroll position.") 294 | 295 | (defvar corfu--input nil 296 | "Cons of last prompt contents and point.") 297 | 298 | (defvar corfu--preview-ov nil 299 | "Current candidate overlay.") 300 | 301 | (defvar corfu--change-group nil 302 | "Undo change group.") 303 | 304 | (defvar corfu--frame nil 305 | "Popup frame.") 306 | 307 | (defvar corfu--width 0 308 | "Popup width of current completion to reduce width fluctuations.") 309 | 310 | (defconst corfu--initial-state 311 | (mapcar 312 | (lambda (k) (cons k (symbol-value k))) 313 | '(corfu--base 314 | corfu--candidates 315 | corfu--hilit 316 | corfu--index 317 | corfu--preselect 318 | corfu--scroll 319 | corfu--input 320 | corfu--total 321 | corfu--preview-ov 322 | corfu--change-group 323 | corfu--metadata 324 | corfu--width)) 325 | "Initial Corfu state.") 326 | 327 | (defvar corfu--frame-parameters 328 | '((no-accept-focus . t) 329 | (no-focus-on-map . t) 330 | (min-width . t) 331 | (min-height . t) 332 | (border-width . 0) 333 | (outer-border-width . 0) 334 | (internal-border-width . 1) 335 | (child-frame-border-width . 1) 336 | (vertical-scroll-bars . nil) 337 | (horizontal-scroll-bars . nil) 338 | (menu-bar-lines . 0) 339 | (tool-bar-lines . 0) 340 | (tab-bar-lines . 0) 341 | (tab-bar-lines-keep-state . t) 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 | (inhibit-double-buffering . t)) ;; Avoid display artifacts on X/Gtk builds 349 | "Default child frame parameters.") 350 | 351 | (defvar corfu--buffer-parameters 352 | '((mode-line-format . nil) 353 | (header-line-format . nil) 354 | (tab-line-format . nil) 355 | (tab-bar-format . nil) 356 | (frame-title-format . "") 357 | (truncate-lines . t) 358 | (cursor-in-non-selected-windows . nil) 359 | (cursor-type . nil) 360 | (show-trailing-whitespace . nil) 361 | (display-line-numbers . nil) 362 | (left-fringe-width . 0) 363 | (right-fringe-width . 0) 364 | (left-margin-width . 0) 365 | (right-margin-width . 0) 366 | (fringes-outside-margins . 0) 367 | (fringe-indicator-alist (continuation) (truncation)) 368 | (indicate-empty-lines . nil) 369 | (indicate-buffer-boundaries . nil) 370 | (buffer-read-only . t)) 371 | "Default child frame buffer parameters.") 372 | 373 | (defvar corfu--mouse-ignore-map 374 | (let ((map (define-keymap "" #'ignore))) 375 | (dotimes (i 7) 376 | (dolist (k '(mouse down-mouse drag-mouse double-mouse triple-mouse)) 377 | (keymap-set map (format "<%s-%s>" k (1+ i)) #'ignore))) 378 | map) 379 | "Ignore all mouse clicks.") 380 | 381 | (defun corfu--replace (beg end str) 382 | "Replace range between BEG and END with STR." 383 | (unless (equal str (buffer-substring-no-properties beg end)) 384 | ;; bug#55205: completion--replace removed properties as an unwanted 385 | ;; side-effect. We also don't want to leave text properties. 386 | (completion--replace beg end (substring-no-properties str)))) 387 | 388 | (defun corfu--capf-wrapper (fun &optional prefix) 389 | "Wrapper for `completion-at-point' FUN. 390 | The wrapper determines if the Capf is applicable at the current position 391 | and performs sanity checking on the returned result. For non-exclusive 392 | Capfs, the wrapper checks if the current input can be completed. PREFIX 393 | is a prefix length override, which is 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 corfu--gtk-resize-child-frames 432 | (let ((case-fold-search t)) 433 | ;; XXX HACK to fix resizing on gtk3/gnome taken from posframe.el 434 | ;; More information: 435 | ;; * https://github.com/minad/corfu/issues/17 436 | ;; * https://gitlab.gnome.org/GNOME/mutter/-/issues/840 437 | ;; * https://lists.gnu.org/archive/html/emacs-devel/2020-02/msg00001.html 438 | (and (string-match-p "gtk3" system-configuration-features) 439 | (string-match-p "gnome\\|cinnamon" 440 | (or (getenv "XDG_CURRENT_DESKTOP") 441 | (getenv "DESKTOP_SESSION") "")) 442 | 'resize-mode))) 443 | 444 | ;; Not present on non-gtk/non-x builds 445 | (defvar x-gtk-resize-child-frames) 446 | (defvar x-fast-protocol-requests) 447 | 448 | ;; Function adapted from posframe.el by tumashu 449 | (defun corfu--make-frame (frame x y width height) 450 | "Show current buffer in child frame at X/Y with WIDTH/HEIGHT. 451 | FRAME is the existing frame." 452 | (when-let (((frame-live-p frame)) 453 | (timer (frame-parameter frame 'corfu--hide-timer))) 454 | (cancel-timer timer) 455 | (set-frame-parameter frame 'corfu--hide-timer nil)) 456 | (let* ((window-min-height 1) 457 | (window-min-width 1) 458 | (inhibit-redisplay t) 459 | (x-fast-protocol-requests t) 460 | (x-gtk-resize-child-frames corfu--gtk-resize-child-frames) 461 | (before-make-frame-hook) 462 | (after-make-frame-functions) 463 | (parent (window-frame))) 464 | (unless (and (frame-live-p frame) 465 | (eq (frame-parent frame) 466 | (and (not (bound-and-true-p exwm--connection)) parent)) 467 | ;; If there is more than one window, `frame-root-window' may 468 | ;; return nil. Recreate the frame in this case. 469 | (window-live-p (frame-root-window frame))) 470 | (when frame (delete-frame frame)) 471 | (setq frame (make-frame 472 | `((parent-frame . ,parent) 473 | (minibuffer . ,(minibuffer-window parent)) 474 | (width . 0) (height . 0) (visibility . nil) 475 | (right-fringe . ,right-fringe-width) 476 | (left-fringe . ,left-fringe-width) 477 | ,@corfu--frame-parameters)))) 478 | ;; XXX HACK Setting the same frame-parameter/face-background is not a nop. 479 | ;; Check before applying the setting. Without the check, the frame flickers 480 | ;; on Mac. We have to apply the face background before adjusting the frame 481 | ;; parameter, otherwise the border is not updated. 482 | (let ((new (face-attribute 'corfu-border :background nil 'default))) 483 | (unless (equal (face-attribute 'internal-border :background frame 'default) new) 484 | (set-face-background 'internal-border new frame)) 485 | ;; XXX The Emacs Mac Port does not support `internal-border', we also have 486 | ;; to set `child-frame-border'. 487 | (unless (or (not (facep 'child-frame-border)) 488 | (equal (face-attribute 'child-frame-border :background frame 'default) new)) 489 | (set-face-background 'child-frame-border new frame))) 490 | ;; Reset frame parameters if they changed. For example `tool-bar-mode' 491 | ;; overrides the parameter `tool-bar-lines' for every frame, including child 492 | ;; frames. The child frame API is a pleasure to work with. It is full of 493 | ;; lovely surprises. 494 | (let* ((win (frame-root-window frame)) 495 | (is (frame-parameters frame)) 496 | (should `((background-color 497 | . ,(face-attribute 'corfu-default :background nil 'default)) 498 | (font . ,(frame-parameter parent 'font)) 499 | (right-fringe . ,right-fringe-width) 500 | (left-fringe . ,left-fringe-width) 501 | ,@corfu--frame-parameters)) 502 | (diff (cl-loop for p in should for (k . v) = p 503 | unless (equal (alist-get k is) v) collect p))) 504 | (when diff (modify-frame-parameters frame diff)) 505 | ;; XXX HACK: `set-window-buffer' must be called to force fringe update. 506 | (when (or diff (not (eq (window-buffer win) (current-buffer)))) 507 | (set-window-buffer win (current-buffer))) 508 | ;; Disallow selection of root window (gh:minad/corfu#63) 509 | (set-window-parameter win 'no-delete-other-windows t) 510 | (set-window-parameter win 'no-other-window t) 511 | ;; Mark window as dedicated to prevent frame reuse (gh:minad/corfu#60) 512 | (set-window-dedicated-p win t)) 513 | (redirect-frame-focus frame parent) 514 | (set-frame-size frame width height t) 515 | (pcase-let ((`(,px . ,py) (frame-position frame))) 516 | (unless (and (= x px) (= y py)) 517 | (set-frame-position frame x y)))) 518 | (make-frame-visible frame) 519 | ;; Unparent child frame if EXWM is used, otherwise EXWM buffers are drawn on 520 | ;; top of the Corfu child frame. 521 | (when (and (bound-and-true-p exwm--connection) (frame-parent frame)) 522 | (redisplay t) 523 | (set-frame-parameter frame 'parent-frame nil)) 524 | frame) 525 | 526 | (defun corfu--hide-frame-deferred (frame) 527 | "Deferred hiding of child FRAME." 528 | (when (and (frame-live-p frame) (frame-visible-p frame)) 529 | (set-frame-parameter frame 'corfu--hide-timer nil) 530 | (make-frame-invisible frame) 531 | (with-current-buffer (window-buffer (frame-root-window frame)) 532 | (with-silent-modifications 533 | (delete-region (point-min) (point-max)))))) 534 | 535 | (defun corfu--hide-frame (frame) 536 | "Hide child FRAME." 537 | (when (and (frame-live-p frame) (frame-visible-p frame) 538 | (not (frame-parameter frame 'corfu--hide-timer))) 539 | (set-frame-parameter frame 'corfu--hide-timer 540 | (run-at-time 0 nil #'corfu--hide-frame-deferred frame)))) 541 | 542 | (defun corfu--move-to-front (elem list) 543 | "Move ELEM to front of LIST." 544 | ;; In contrast to Vertico, this function handles duplicates. See also the 545 | ;; special deduplication function `corfu--delete-dups' based on 546 | ;; `equal-including-properties' 547 | (nconc (cl-loop for x in list if (equal x elem) collect x) 548 | (delete elem list))) 549 | 550 | (defun corfu--filter-completions (&rest args) 551 | "Compute all completions for ARGS with lazy highlighting." 552 | (dlet ((completion-lazy-hilit t) (completion-lazy-hilit-fn nil)) 553 | (static-if (>= emacs-major-version 30) 554 | (cons (apply #'completion-all-completions args) completion-lazy-hilit-fn) 555 | (cl-letf* ((orig-pcm (symbol-function #'completion-pcm--hilit-commonality)) 556 | (orig-flex (symbol-function #'completion-flex-all-completions)) 557 | ((symbol-function #'completion-flex-all-completions) 558 | (lambda (&rest args) 559 | ;; Unfortunately for flex we have to undo the lazy highlighting, since flex uses 560 | ;; the completion-score for sorting, which is applied during highlighting. 561 | (cl-letf (((symbol-function #'completion-pcm--hilit-commonality) orig-pcm)) 562 | (apply orig-flex args)))) 563 | ((symbol-function #'completion-pcm--hilit-commonality) 564 | (lambda (pattern cands) 565 | (setq completion-lazy-hilit-fn 566 | (lambda (x) 567 | ;; `completion-pcm--hilit-commonality' sometimes throws an internal error 568 | ;; for example when entering "/sudo:://u". 569 | (condition-case nil 570 | (car (completion-pcm--hilit-commonality pattern (list x))) 571 | (t x)))) 572 | cands)) 573 | ((symbol-function #'completion-hilit-commonality) 574 | (lambda (cands prefix &optional base) 575 | (setq completion-lazy-hilit-fn 576 | (lambda (x) (car (completion-hilit-commonality (list x) prefix base)))) 577 | (and cands (nconc cands base))))) 578 | (cons (apply #'completion-all-completions args) completion-lazy-hilit-fn))))) 579 | 580 | (defsubst corfu--length-string< (x y) 581 | "Sorting predicate which compares X and Y first by length then by `string<'." 582 | (or (< (length x) (length y)) (and (= (length x) (length y)) (string< x y)))) 583 | 584 | (defmacro corfu--partition! (list form) 585 | "Evaluate FORM for every element and partition LIST." 586 | (cl-with-gensyms (head1 head2 tail1 tail2) 587 | `(let* ((,head1 (cons nil nil)) 588 | (,head2 (cons nil nil)) 589 | (,tail1 ,head1) 590 | (,tail2 ,head2)) 591 | (while ,list 592 | (if (let ((it (car ,list))) ,form) 593 | (progn 594 | (setcdr ,tail1 ,list) 595 | (pop ,tail1)) 596 | (setcdr ,tail2 ,list) 597 | (pop ,tail2)) 598 | (pop ,list)) 599 | (setcdr ,tail1 (cdr ,head2)) 600 | (setcdr ,tail2 nil) 601 | (setq ,list (cdr ,head1))))) 602 | 603 | (defun corfu--move-prefix-candidates-to-front (field cands) 604 | "Move CANDS which match prefix of FIELD to the beginning." 605 | (let* ((word (substring field 0 606 | (seq-position field corfu-separator))) 607 | (len (length word))) 608 | (corfu--partition! 609 | cands 610 | (and (>= (length it) len) 611 | (eq t (compare-strings word 0 len it 0 len 612 | completion-ignore-case)))))) 613 | 614 | ;; bug#6581: `equal-including-properties' uses `eq' for properties until 29.1. 615 | ;; Approximate by comparing `text-properties-at' position 0. 616 | (defalias 'corfu--equal-including-properties 617 | (static-if (< emacs-major-version 29) 618 | (lambda (x y) 619 | (and (equal x y) 620 | (equal (text-properties-at 0 x) (text-properties-at 0 y)))) 621 | #'equal-including-properties)) 622 | 623 | (defun corfu--delete-dups (list) 624 | "Delete `equal-including-properties' consecutive duplicates from LIST." 625 | (let ((beg list)) 626 | (while (cdr beg) 627 | (let ((end (cdr beg))) 628 | (while (equal (car beg) (car end)) (pop end)) 629 | ;; The deduplication is quadratic in the number of duplicates. We can 630 | ;; avoid the quadratic complexity with a hash table which takes 631 | ;; properties into account (available since Emacs 28). 632 | (while (not (eq beg end)) 633 | (let ((dup beg)) 634 | (while (not (eq (cdr dup) end)) 635 | (if (corfu--equal-including-properties (car beg) (cadr dup)) 636 | (setcdr dup (cddr dup)) 637 | (pop dup)))) 638 | (pop beg))))) 639 | list) 640 | 641 | (defun corfu--sort-function () 642 | "Return the sorting function." 643 | (or corfu-sort-override-function 644 | (corfu--metadata-get 'display-sort-function) 645 | corfu-sort-function)) 646 | 647 | (defun corfu--recompute (str pt table pred) 648 | "Recompute state from STR, PT, TABLE and PRED." 649 | (pcase-let* ((before (substring str 0 pt)) 650 | (after (substring str pt)) 651 | (corfu--metadata (completion-metadata before table pred)) 652 | ;; bug#47678: `completion-boundaries' fails for `partial-completion' 653 | ;; if the cursor is moved before the slashes of "~//". 654 | ;; See also vertico.el which has the same issue. 655 | (bounds (condition-case nil 656 | (completion-boundaries before table pred after) 657 | (t (cons 0 (length after))))) 658 | (field (substring str (car bounds) (+ pt (cdr bounds)))) 659 | (completing-file (eq (corfu--metadata-get 'category) 'file)) 660 | (`(,all . ,hl) (corfu--filter-completions str table pred pt corfu--metadata)) 661 | (base (or (when-let ((z (last all))) (prog1 (cdr z) (setcdr z nil))) 0)) 662 | (corfu--base (substring str 0 base)) 663 | (pre nil)) 664 | ;; Filter the ignored file extensions. We cannot use modified predicate for 665 | ;; this filtering, since this breaks the special casing in the 666 | ;; `completion-file-name-table' for `file-exists-p' and `file-directory-p'. 667 | (when completing-file (setq all (completion-pcm--filename-try-filter all))) 668 | ;; Sort using the `display-sort-function' or the Corfu sort functions, and 669 | ;; delete duplicates with respect to `equal-including-properties'. This is 670 | ;; a deviation from the Vertico completion UI with more aggressive 671 | ;; deduplication, where candidates are compared with `equal'. Corfu 672 | ;; preserves candidates which differ in their text properties. Corfu tries 673 | ;; to preserve text properties as much as possible, when calling the 674 | ;; `:exit-function' to help Capfs with candidate disambiguation. This 675 | ;; matters in particular for Lsp backends, which produce duplicates for 676 | ;; overloaded methods. 677 | (setq all (corfu--delete-dups (funcall (or (corfu--sort-function) #'identity) all)) 678 | all (corfu--move-prefix-candidates-to-front field all)) 679 | (when (and completing-file (not (string-suffix-p "/" field))) 680 | (setq all (corfu--move-to-front (concat field "/") all))) 681 | (setq all (corfu--move-to-front field all) 682 | pre (if (or (eq corfu-preselect 'prompt) (not all) 683 | (and completing-file (eq corfu-preselect 'directory) 684 | (= (length corfu--base) (length str)) 685 | (test-completion str table pred)) 686 | (and (eq corfu-preselect 'valid) 687 | (not (equal field (car all))) 688 | (not (and completing-file (equal (concat field "/") (car all)))) 689 | (test-completion str table pred))) 690 | -1 0)) 691 | `((corfu--base . ,corfu--base) 692 | (corfu--metadata . ,corfu--metadata) 693 | (corfu--candidates . ,all) 694 | (corfu--total . ,(length all)) 695 | (corfu--hilit . ,(or hl #'identity)) 696 | (corfu--preselect . ,pre) 697 | (corfu--index . ,(or (and (>= corfu--index 0) (/= corfu--index corfu--preselect) 698 | (seq-position all (nth corfu--index corfu--candidates))) 699 | pre))))) 700 | 701 | (defun corfu--update (&optional interruptible) 702 | "Update state, optionally INTERRUPTIBLE." 703 | (pcase-let* ((`(,beg ,end ,table ,pred . ,_) completion-in-region--data) 704 | (pt (- (point) beg)) 705 | (str (buffer-substring-no-properties beg end)) 706 | (input (cons str pt))) 707 | (unless (equal corfu--input input) 708 | ;; Redisplay such that the input is immediately shown before the expensive 709 | ;; candidate recomputation (gh:minad/corfu#48). See also corresponding 710 | ;; issue gh:minad/vertico#89. 711 | (when interruptible (redisplay)) 712 | ;; Bind non-essential=t to prevent Tramp from opening new connections, 713 | ;; without the user explicitly requesting it via M-TAB. 714 | (pcase (let ((non-essential t)) 715 | (if interruptible 716 | (while-no-input (corfu--recompute str pt table pred)) 717 | (corfu--recompute str pt table pred))) 718 | ('nil (keyboard-quit)) 719 | ((and state (pred consp)) 720 | (setq corfu--input input) 721 | (dolist (s state) (set (car s) (cdr s)))))) 722 | input)) 723 | 724 | (defun corfu--match-symbol-p (pattern sym) 725 | "Return non-nil if SYM is matching an element of the PATTERN list." 726 | (cl-loop with case-fold-search = nil 727 | for x in (and (symbolp sym) pattern) 728 | thereis (if (symbolp x) 729 | (eq sym x) 730 | (string-match-p x (symbol-name sym))))) 731 | 732 | (defun corfu--metadata-get (prop) 733 | "Return PROP from completion metadata." 734 | ;; Marginalia and various icon packages advise `completion-metadata-get' to 735 | ;; inject their annotations, but are meant only for minibuffer completion. 736 | ;; Therefore call `completion-metadata-get' without advices here. 737 | (let ((completion-extra-properties (nth 4 completion-in-region--data))) 738 | (funcall (advice--cd*r (symbol-function (compat-function completion-metadata-get))) 739 | corfu--metadata prop))) 740 | 741 | (defun corfu--format-candidates (cands) 742 | "Format annotated CANDS." 743 | (cl-loop for c in cands do 744 | (cl-loop for s in-ref c do 745 | (setf s (replace-regexp-in-string "[ \t]*\n[ \t]*" " " s)))) 746 | (let* ((cw (cl-loop for x in cands maximize (string-width (car x)))) 747 | (pw (cl-loop for x in cands maximize (string-width (cadr x)))) 748 | (sw (cl-loop for x in cands maximize (string-width (caddr x)))) 749 | (width (min (max corfu--width corfu-min-width (+ pw cw sw)) 750 | ;; -4 because of margins and some additional safety 751 | corfu-max-width (- (frame-width) 4))) 752 | (trunc (not (display-graphic-p)))) 753 | (setq corfu--width width) 754 | (list pw width 755 | (cl-loop 756 | for (cand prefix suffix) in cands collect 757 | (let ((s (concat 758 | prefix (make-string (- pw (string-width prefix)) ?\s) cand 759 | (when (> sw 0) 760 | (make-string (max 0 (- width pw (string-width cand) 761 | (string-width suffix))) 762 | ?\s)) 763 | suffix))) 764 | (if trunc (truncate-string-to-width s width) s)))))) 765 | 766 | (defun corfu--compute-scroll () 767 | "Compute new scroll position." 768 | (let ((off (max (min corfu-scroll-margin (/ corfu-count 2)) 0)) 769 | (corr (if (= corfu-scroll-margin (/ corfu-count 2)) (1- (mod corfu-count 2)) 0))) 770 | (setq corfu--scroll (min (max 0 (- corfu--total corfu-count)) 771 | (max 0 (+ corfu--index off 1 (- corfu-count)) 772 | (min (- corfu--index off corr) corfu--scroll)))))) 773 | 774 | (defun corfu--candidates-popup (pos) 775 | "Show candidates popup at POS." 776 | (corfu--compute-scroll) 777 | (pcase-let* ((last (min (+ corfu--scroll corfu-count) corfu--total)) 778 | (bar (ceiling (* corfu-count corfu-count) corfu--total)) 779 | (lo (min (- corfu-count bar 1) (floor (* corfu-count corfu--scroll) corfu--total))) 780 | (`(,mf . ,acands) 781 | (corfu--affixate 782 | (cl-loop 783 | repeat corfu-count for c in (nthcdr corfu--scroll corfu--candidates) 784 | collect (funcall corfu--hilit 785 | ;; bug#77754: Highlight unquoted string. 786 | (substring (or (get-text-property 787 | 0 'completion--unquoted c) c)))))) 788 | (`(,pw ,width ,fcands) (corfu--format-candidates acands)) 789 | ;; Disable the left margin if a margin formatter is active. 790 | (corfu-left-margin-width (if mf 0 corfu-left-margin-width))) 791 | ;; Nonlinearity at the end and the beginning 792 | (when (/= corfu--scroll 0) 793 | (setq lo (max 1 lo))) 794 | (when (/= last corfu--total) 795 | (setq lo (min (- corfu-count bar 2) lo))) 796 | (corfu--popup-show pos pw width fcands (- corfu--index corfu--scroll) 797 | (and (> corfu--total corfu-count) lo) bar))) 798 | 799 | (defun corfu--range-valid-p () 800 | "Check the completion range, return non-nil if valid." 801 | (pcase-let ((buf (current-buffer)) 802 | (pt (point)) 803 | (`(,beg ,end . ,_) completion-in-region--data)) 804 | (and beg end 805 | (eq buf (marker-buffer beg)) (eq buf (window-buffer)) 806 | (<= beg pt end) 807 | (save-excursion (goto-char beg) (<= (pos-bol) pt (pos-eol)))))) 808 | 809 | (defun corfu--continue-p () 810 | "Check if completion should continue after a command. 811 | Corfu bails out if the current buffer changed unexpectedly or if 812 | point moved out of range, see `corfu--range-valid-p'. Also the 813 | input must satisfy the `completion-in-region-mode--predicate' and 814 | the last command must be listed in `corfu-continue-commands'." 815 | (and (corfu--range-valid-p) 816 | ;; We keep Corfu alive if a `overriding-terminal-local-map' is 817 | ;; installed, e.g., the `universal-argument-map'. It would be good to 818 | ;; think about a better criterion instead. Unfortunately relying on 819 | ;; `this-command' alone is insufficient, since the value of 820 | ;; `this-command' gets clobbered in the case of transient keymaps. 821 | (or overriding-terminal-local-map 822 | ;; Check if it is an explicitly listed continue command 823 | (corfu--match-symbol-p corfu-continue-commands this-command) 824 | (pcase-let ((`(,beg ,end . ,_) completion-in-region--data)) 825 | (and (or (not corfu--input) (< beg end)) ;; Check for empty input 826 | (or (not corfu-quit-at-boundary) ;; Check separator or predicate 827 | (and (eq corfu-quit-at-boundary 'separator) 828 | (or (eq this-command #'corfu-insert-separator) 829 | ;; with separator, any further chars allowed 830 | (seq-contains-p (car corfu--input) corfu-separator))) 831 | (funcall completion-in-region-mode--predicate))))))) 832 | 833 | (defun corfu--preview-current-p () 834 | "Return t if the selected candidate is previewed." 835 | (and corfu-preview-current (>= corfu--index 0) (/= corfu--index corfu--preselect))) 836 | 837 | (defun corfu--preview-current (beg end) 838 | "Show current candidate as overlay given BEG and END." 839 | (when (corfu--preview-current-p) 840 | (corfu--preview-delete) 841 | (setq beg (+ beg (length corfu--base)) 842 | corfu--preview-ov (make-overlay beg end nil)) 843 | (overlay-put corfu--preview-ov 'priority 1000) 844 | (overlay-put corfu--preview-ov 'window (selected-window)) 845 | (overlay-put corfu--preview-ov (if (= beg end) 'after-string 'display) 846 | (substring-no-properties (nth corfu--index corfu--candidates))))) 847 | 848 | (defun corfu--preview-delete () 849 | "Delete the preview overlay." 850 | (when corfu--preview-ov 851 | (delete-overlay corfu--preview-ov) 852 | (setq corfu--preview-ov nil))) 853 | 854 | (defun corfu--window-change (_) 855 | "Window and buffer change hook which quits Corfu." 856 | (unless (corfu--range-valid-p) 857 | (corfu-quit))) 858 | 859 | (defun corfu--debug (&rest _) 860 | "Debugger used by `corfu--protect'." 861 | (let ((inhibit-message t)) 862 | (require 'backtrace) 863 | (declare-function backtrace-to-string "backtrace") 864 | (message "Corfu detected an error:\n%s" (backtrace-to-string))) 865 | (let (message-log-max) 866 | (message "%s %s" 867 | (propertize "Corfu detected an error:" 'face 'error) 868 | (substitute-command-keys "Press \\[view-echo-area-messages] to see the stack trace"))) 869 | nil) 870 | 871 | (defun corfu--protect (fun) 872 | "Protect FUN such that errors are caught. 873 | If an error occurs, the FUN is retried with `debug-on-error' enabled and 874 | the stack trace is shown in the *Messages* buffer." 875 | (static-if (fboundp 'handler-bind) ;; Available on Emacs 30 876 | (ignore-errors 877 | (handler-bind ((error #'corfu--debug)) 878 | (funcall fun))) 879 | (when (or debug-on-error (condition-case nil 880 | (progn (funcall fun) nil) 881 | (error t))) 882 | (let ((debug-on-error t) 883 | (debugger #'corfu--debug)) 884 | (condition-case nil 885 | (funcall fun) 886 | ((debug error) nil)))))) 887 | 888 | (defun corfu--post-command () 889 | "Refresh Corfu after last command." 890 | (corfu--protect 891 | (lambda () 892 | (if (corfu--continue-p) 893 | (corfu--exhibit) 894 | (corfu-quit)) 895 | (when corfu-auto 896 | (corfu--auto-post-command))))) 897 | 898 | (defun corfu--goto (index) 899 | "Go to candidate with INDEX." 900 | (setq corfu--index (max corfu--preselect (min index (1- corfu--total))))) 901 | 902 | (defun corfu--exit-function (str status cands) 903 | "Call the `:exit-function' with STR and STATUS. 904 | Lookup STR in CANDS to restore text properties." 905 | (when-let ((exit (plist-get completion-extra-properties :exit-function))) 906 | (funcall exit (or (car (member str cands)) str) status))) 907 | 908 | (defun corfu--done (str status cands) 909 | "Exit completion and call the exit function with STR and STATUS. 910 | Lookup STR in CANDS to restore text properties." 911 | (let ((completion-extra-properties (nth 4 completion-in-region--data))) 912 | ;; For successful completions, amalgamate undo operations, 913 | ;; such that completion can be undone in a single step. 914 | (undo-amalgamate-change-group corfu--change-group) 915 | (corfu-quit) 916 | (corfu--exit-function str status cands))) 917 | 918 | (defun corfu--setup (beg end table pred) 919 | "Setup Corfu completion state. 920 | See `completion-in-region' for the arguments BEG, END, TABLE, PRED." 921 | (setq beg (if (markerp beg) beg (copy-marker beg)) 922 | end (if (and (markerp end) (marker-insertion-type end)) end (copy-marker end t)) 923 | completion-in-region--data (list beg end table pred completion-extra-properties)) 924 | (completion-in-region-mode) 925 | (activate-change-group (setq corfu--change-group (prepare-change-group))) 926 | (setcdr (assq #'completion-in-region-mode minor-mode-overriding-map-alist) corfu-map) 927 | (add-hook 'pre-command-hook #'corfu--prepare nil 'local) 928 | (add-hook 'window-selection-change-functions #'corfu--window-change nil 'local) 929 | (add-hook 'window-buffer-change-functions #'corfu--window-change nil 'local) 930 | (add-hook 'post-command-hook #'corfu--post-command) 931 | ;; Disable default post-command handling, since we have our own 932 | ;; checks in `corfu--post-command'. 933 | (remove-hook 'post-command-hook #'completion-in-region--postch) 934 | (let ((sym (make-symbol "corfu--teardown")) 935 | (buf (current-buffer))) 936 | (fset sym (lambda () 937 | ;; Ensure that the tear-down runs in the correct buffer, if still alive. 938 | (unless completion-in-region-mode 939 | (remove-hook 'completion-in-region-mode-hook sym) 940 | (corfu--teardown buf)))) 941 | (add-hook 'completion-in-region-mode-hook sym))) 942 | 943 | (defun corfu--in-region (&rest args) 944 | "Corfu completion in region function called with ARGS." 945 | ;; XXX We can get an endless loop when `completion-in-region-function' is set 946 | ;; globally to `corfu--in-region'. This should never happen. 947 | (apply (if (corfu--popup-support-p) #'corfu--in-region-1 948 | (default-value 'completion-in-region-function)) 949 | args)) 950 | 951 | (defun corfu--in-region-1 (beg end table &optional pred) 952 | "Complete in region, see `completion-in-region' for BEG, END, TABLE, PRED." 953 | (barf-if-buffer-read-only) 954 | ;; Restart the completion. This can happen for example if C-M-/ 955 | ;; (`dabbrev-completion') is pressed while the Corfu popup is already open. 956 | (when completion-in-region-mode (corfu-quit)) 957 | (let* ((pt (max 0 (- (point) beg))) 958 | (str (buffer-substring-no-properties beg end)) 959 | (metadata (completion-metadata (substring str 0 pt) table pred)) 960 | (threshold (completion--cycle-threshold metadata)) 961 | (completion-in-region-mode-predicate 962 | (or completion-in-region-mode-predicate #'always))) 963 | (pcase (completion-try-completion str table pred pt metadata) 964 | ('nil (corfu--message "No match") nil) 965 | ('t (goto-char end) 966 | (corfu--message "Sole match") 967 | (if (eq corfu-on-exact-match 'show) 968 | (corfu--setup beg end table pred) 969 | (corfu--exit-function 970 | str 'finished 971 | (alist-get 'corfu--candidates (corfu--recompute str pt table pred)))) 972 | t) 973 | (`(,newstr . ,newpt) 974 | (setq beg (if (markerp beg) beg (copy-marker beg)) 975 | end (copy-marker end t)) 976 | (corfu--replace beg end newstr) 977 | (goto-char (+ beg newpt)) 978 | (let* ((state (corfu--recompute newstr newpt table pred)) 979 | (base (alist-get 'corfu--base state)) 980 | (total (alist-get 'corfu--total state)) 981 | (cands (alist-get 'corfu--candidates state))) 982 | (cond 983 | ((<= total 1) 984 | ;; If completion is finished and cannot be extended further and 985 | ;; `corfu-on-exact-match' is not 'show, return 'finished. Otherwise 986 | ;; setup the popup. 987 | (if (and (= total 1) 988 | (or (eq corfu-on-exact-match 'show) 989 | (consp (completion-try-completion 990 | newstr table pred newpt 991 | (completion-metadata newstr table pred))))) 992 | (corfu--setup beg end table pred) 993 | (corfu--exit-function newstr 'finished cands))) 994 | ;; Too many candidates for cycling -> Setup popup. 995 | ((or (not threshold) (and (not (eq threshold t)) (< threshold total))) 996 | (corfu--setup beg end table pred)) 997 | (t 998 | ;; Cycle through candidates. 999 | (corfu--cycle-candidates total cands (+ (length base) beg) end) 1000 | ;; Do not show Corfu when completion is finished after the candidate. 1001 | (unless (equal (completion-boundaries (car cands) table pred "") '(0 . 0)) 1002 | (corfu--setup beg end table pred))))) 1003 | t)))) 1004 | 1005 | (defun corfu--message (&rest msg) 1006 | "Show completion MSG." 1007 | (let (message-log-max) (apply #'message msg))) 1008 | 1009 | (defun corfu--cycle-candidates (total cands beg end) 1010 | "Cycle between TOTAL number of CANDS. 1011 | See `completion-in-region' for the arguments BEG, END, TABLE, PRED." 1012 | (let* ((idx 0) 1013 | (map (make-sparse-keymap)) 1014 | (replace (lambda () 1015 | (interactive) 1016 | (corfu--replace beg end (nth idx cands)) 1017 | (corfu--message "Cycling %d/%d..." (1+ idx) total) 1018 | (setq idx (mod (1+ idx) total)) 1019 | (set-transient-map map)))) 1020 | (define-key map [remap completion-at-point] replace) 1021 | (define-key map [remap corfu-complete] replace) 1022 | (define-key map (vector last-command-event) replace) 1023 | (funcall replace))) 1024 | 1025 | (defun corfu--auto-complete-deferred (&optional tick) 1026 | "Initiate auto completion if TICK did not change." 1027 | (corfu--protect 1028 | (lambda () 1029 | (when (and (not completion-in-region-mode) 1030 | (or (not tick) (equal tick (corfu--auto-tick)))) 1031 | (pcase (while-no-input ;; Interruptible Capf query 1032 | (run-hook-wrapped 'completion-at-point-functions #'corfu--capf-wrapper)) 1033 | (`(,fun ,beg ,end ,table . ,plist) 1034 | (let ((completion-in-region-mode-predicate 1035 | (lambda () 1036 | (when-let ((newbeg (car-safe (funcall fun)))) 1037 | (= newbeg beg)))) 1038 | (completion-extra-properties plist)) 1039 | (corfu--setup beg end table (plist-get plist :predicate)) 1040 | (corfu--exhibit 'auto)))))))) 1041 | 1042 | (defun corfu--auto-post-command () 1043 | "Post command hook which initiates auto completion." 1044 | (corfu--protect 1045 | (lambda () 1046 | (cancel-timer corfu--auto-timer) 1047 | (when (and (not completion-in-region-mode) 1048 | (not defining-kbd-macro) 1049 | (not buffer-read-only) 1050 | (corfu--match-symbol-p corfu-auto-commands this-command) 1051 | (corfu--popup-support-p)) 1052 | (if (<= corfu-auto-delay 0) 1053 | (corfu--auto-complete-deferred) 1054 | ;; Do not use `timer-set-idle-time' since this leads to 1055 | ;; unpredictable pauses, in particular with `flyspell-mode'. 1056 | (timer-set-time corfu--auto-timer 1057 | (timer-relative-time nil corfu-auto-delay)) 1058 | (timer-set-function corfu--auto-timer #'corfu--auto-complete-deferred 1059 | (list (corfu--auto-tick))) 1060 | (timer-activate corfu--auto-timer)))))) 1061 | 1062 | (defun corfu--auto-tick () 1063 | "Return the current tick/status of the buffer. 1064 | Auto completion is only performed if the tick did not change." 1065 | (list (selected-window) (current-buffer) (buffer-chars-modified-tick) (point))) 1066 | 1067 | (cl-defgeneric corfu--popup-show (pos off width lines &optional curr lo bar) 1068 | "Show LINES as popup at POS - OFF. 1069 | WIDTH is the width of the popup. 1070 | The current candidate CURR is highlighted. 1071 | A scroll bar is displayed from LO to LO+BAR." 1072 | (let ((lh (max (default-line-height) (cdr (posn-object-width-height pos))))) 1073 | (with-current-buffer (corfu--make-buffer " *corfu*") 1074 | (let* ((ch (default-line-height)) 1075 | (cw (default-font-width)) 1076 | ;; bug#74214, bug#37755, bug#37689: Even for larger fringes, fringe 1077 | ;; bitmaps can only have a width between 1 and 16. Therefore we 1078 | ;; restrict the fringe width to 16 pixel. This restriction may 1079 | ;; cause problem on HDPi systems. Hopefully Emacs will adopt 1080 | ;; larger fringe bitmaps in the future and lift the restriction. 1081 | (ml (min 16 (ceiling (* cw corfu-left-margin-width)))) 1082 | (mr (min 16 (ceiling (* cw corfu-right-margin-width)))) 1083 | (bw (min mr (ceiling (* cw corfu-bar-width)))) 1084 | (fringe (display-graphic-p)) 1085 | (marginl (and (not fringe) (propertize " " 'display `(space :width (,ml))))) 1086 | (sbar (if fringe 1087 | #(" " 0 1 (display (right-fringe corfu--bar corfu--bar))) 1088 | (concat 1089 | (propertize " " 'display `(space :align-to (- right (,bw)))) 1090 | (propertize " " 'face 'corfu-bar 'display `(space :width (,bw)))))) 1091 | (cbar (if fringe 1092 | #(" " 0 1 (display (left-fringe corfu--nil corfu-current)) 1093 | 1 2 (display (right-fringe corfu--bar corfu--cbar))) 1094 | sbar)) 1095 | (cmargin (and fringe 1096 | #(" " 0 1 (display (left-fringe corfu--nil corfu-current)) 1097 | 1 2 (display (right-fringe corfu--nil corfu-current))))) 1098 | (pos (posn-x-y pos)) 1099 | (width (+ (* width cw) (if fringe 0 (+ ml mr)))) 1100 | ;; XXX HACK: Minimum popup height must be at least 1 line of the 1101 | ;; parent frame (gh:minad/corfu#261). 1102 | (height (max lh (* (length lines) ch))) 1103 | (edge (window-inside-pixel-edges)) 1104 | (border (alist-get 'internal-border-width corfu--frame-parameters)) 1105 | (x (max 0 (min (+ (car edge) (- (or (car pos) 0) ml (* cw off) border)) 1106 | (- (frame-pixel-width) width)))) 1107 | (yb (+ (cadr edge) (or (cdr pos) 0) lh 1108 | (static-if (< emacs-major-version 31) (window-tab-line-height) 0))) 1109 | (y (if (> (+ yb (* corfu-count ch) lh lh) (frame-pixel-height)) 1110 | (- yb height lh border border) 1111 | yb)) 1112 | (bmp (logxor (1- (ash 1 mr)) (1- (ash 1 bw))))) 1113 | (setq left-fringe-width (if fringe ml 0) right-fringe-width (if fringe mr 0)) 1114 | ;; Define an inverted corfu--bar face 1115 | (unless (equal (and (facep 'corfu--bar) (face-attribute 'corfu--bar :foreground)) 1116 | (face-attribute 'corfu-bar :background)) 1117 | (set-face-attribute (make-face 'corfu--bar) nil 1118 | :foreground (face-attribute 'corfu-bar :background))) 1119 | (unless (or (= right-fringe-width 0) (eq (get 'corfu--bar 'corfu--bmp) bmp)) 1120 | (put 'corfu--bar 'corfu--bmp bmp) 1121 | (define-fringe-bitmap 'corfu--bar (vector (lognot bmp)) 1 mr '(top periodic)) 1122 | (define-fringe-bitmap 'corfu--nil [0] 1 1) 1123 | ;; Fringe bitmaps require symbol face specification, define internal face. 1124 | (set-face-attribute (make-face 'corfu--cbar) nil 1125 | :inherit '(corfu--bar corfu-current))) 1126 | (with-silent-modifications 1127 | (delete-region (point-min) (point-max)) 1128 | (apply #'insert 1129 | (cl-loop for row from 0 for line in lines collect 1130 | (let ((str (concat marginl line 1131 | (if (and lo (<= lo row (+ lo bar))) 1132 | (if (eq row curr) cbar sbar) 1133 | (and (eq row curr) cmargin)) 1134 | "\n"))) 1135 | (when (eq row curr) 1136 | (add-face-text-property 1137 | 0 (length str) 'corfu-current 'append str)) 1138 | str))) 1139 | (goto-char (point-min))) 1140 | (setq corfu--frame (corfu--make-frame corfu--frame x y width height)))))) 1141 | 1142 | (cl-defgeneric corfu--popup-hide () 1143 | "Hide Corfu popup." 1144 | (corfu--hide-frame corfu--frame)) 1145 | 1146 | (cl-defgeneric corfu--popup-support-p () 1147 | "Return non-nil if child frames are supported." 1148 | (or (display-graphic-p) (featurep 'tty-child-frames))) 1149 | 1150 | (cl-defgeneric corfu--insert (status) 1151 | "Insert current candidate, exit with STATUS if non-nil." 1152 | ;; XXX There is a small bug here, depending on interpretation. 1153 | ;; When completing "~/emacs/master/li|/calc" where "|" is the 1154 | ;; cursor, then the candidate only includes the prefix 1155 | ;; "~/emacs/master/lisp/", but not the suffix "/calc". Default 1156 | ;; completion has the same problem when selecting in the 1157 | ;; *Completions* buffer. See bug#48356. 1158 | (pcase-let* ((`(,beg ,end . ,_) completion-in-region--data) 1159 | (str (concat corfu--base (nth corfu--index corfu--candidates)))) 1160 | (corfu--replace beg end str) 1161 | (corfu--goto -1) ;; Reset selection, completion may continue. 1162 | (when status (corfu--done str status nil)) 1163 | str)) 1164 | 1165 | (cl-defgeneric corfu--affixate (cands) 1166 | "Annotate CANDS with annotation function." 1167 | (let* ((dep (corfu--metadata-get 'company-deprecated)) 1168 | (mf (let ((completion-extra-properties (nth 4 completion-in-region--data))) 1169 | (run-hook-with-args-until-success 'corfu-margin-formatters corfu--metadata)))) 1170 | (setq cands 1171 | (if-let ((aff (corfu--metadata-get 'affixation-function))) 1172 | (funcall aff cands) 1173 | (if-let ((ann (corfu--metadata-get 'annotation-function))) 1174 | (cl-loop for cand in cands collect 1175 | (let ((suff (or (funcall ann cand) ""))) 1176 | ;; The default completion UI adds the 1177 | ;; `completions-annotations' face if no other faces are 1178 | ;; present. We use a custom `corfu-annotations' face to 1179 | ;; allow further styling which fits better for popups. 1180 | (unless (text-property-not-all 0 (length suff) 'face nil suff) 1181 | (setq suff (propertize suff 'face 'corfu-annotations))) 1182 | (list cand "" suff))) 1183 | (cl-loop for cand in cands collect (list cand "" ""))))) 1184 | (cl-loop for x in cands for (c . _) = x do 1185 | (when mf 1186 | (setf (cadr x) (funcall mf c))) 1187 | (when (and dep (funcall dep c)) 1188 | (setcar x (setq c (substring c))) 1189 | (add-face-text-property 0 (length c) 'corfu-deprecated 'append c))) 1190 | (cons mf cands))) 1191 | 1192 | (cl-defgeneric corfu--prepare () 1193 | "Insert selected candidate unless command is marked to continue completion." 1194 | (corfu--preview-delete) 1195 | ;; Ensure that state is initialized before next Corfu command 1196 | (when (and (symbolp this-command) (string-prefix-p "corfu-" (symbol-name this-command))) 1197 | (corfu--update)) 1198 | ;; If the next command is not listed in `corfu-continue-commands', insert the 1199 | ;; currently selected candidate and bail out of completion. This way you can 1200 | ;; continue typing after selecting a candidate. The candidate will be inserted 1201 | ;; and your new input will be appended. 1202 | (and (corfu--preview-current-p) (eq corfu-preview-current 'insert) 1203 | ;; See the comment about `overriding-local-map' in `corfu--post-command'. 1204 | (not (or overriding-terminal-local-map 1205 | (corfu--match-symbol-p corfu-continue-commands this-command))) 1206 | (corfu--insert 'exact))) 1207 | 1208 | (cl-defgeneric corfu--exhibit (&optional auto) 1209 | "Exhibit Corfu UI. 1210 | AUTO is non-nil when initializing auto completion." 1211 | (pcase-let ((`(,beg ,end ,table ,pred . ,_) completion-in-region--data) 1212 | (`(,str . ,pt) (corfu--update 'interruptible))) 1213 | (cond 1214 | ;; 1) Single exactly matching candidate and no further completion is possible. 1215 | ((and (not (equal str "")) 1216 | (equal (car corfu--candidates) str) (not (cdr corfu--candidates)) 1217 | (not (eq corfu-on-exact-match 'show)) 1218 | (or auto corfu-on-exact-match) 1219 | (not (consp (completion-try-completion str table pred pt corfu--metadata)))) 1220 | ;; Quit directly when initializing auto completion. 1221 | (if (or auto (eq corfu-on-exact-match 'quit)) 1222 | (corfu-quit) 1223 | (corfu--done (car corfu--candidates) 'finished nil))) 1224 | ;; 2) There exist candidates => Show candidates popup. 1225 | (corfu--candidates 1226 | (let ((pos (posn-at-point (+ beg (length corfu--base))))) 1227 | (corfu--preview-current beg end) 1228 | (corfu--candidates-popup pos))) 1229 | ;; 3) No candidates & `corfu-quit-no-match' & initialized => Confirmation popup. 1230 | ((pcase-exhaustive corfu-quit-no-match 1231 | ('t nil) 1232 | ('nil corfu--input) 1233 | ('separator (seq-contains-p (car corfu--input) corfu-separator))) 1234 | (corfu--popup-show (posn-at-point beg) 0 8 '(#("No match" 0 8 (face italic))))) 1235 | ;; 4) No candidates & auto completing or initialized => Quit. 1236 | ((or auto corfu--input) (corfu-quit))))) 1237 | 1238 | (cl-defgeneric corfu--teardown (buffer) 1239 | "Tear-down Corfu in BUFFER, which might be dead at this point." 1240 | (corfu--popup-hide) 1241 | (corfu--preview-delete) 1242 | (remove-hook 'post-command-hook #'corfu--post-command) 1243 | (when (buffer-live-p buffer) 1244 | (with-current-buffer buffer 1245 | (remove-hook 'window-selection-change-functions #'corfu--window-change 'local) 1246 | (remove-hook 'window-buffer-change-functions #'corfu--window-change 'local) 1247 | (remove-hook 'pre-command-hook #'corfu--prepare 'local) 1248 | (accept-change-group corfu--change-group))) 1249 | (cl-loop for (k . v) in corfu--initial-state do (set k v))) 1250 | 1251 | (defun corfu-sort-length-alpha (list) 1252 | "Sort LIST by length and alphabetically." 1253 | (sort list #'corfu--length-string<)) 1254 | 1255 | (defun corfu-quit () 1256 | "Quit Corfu completion." 1257 | (interactive) 1258 | (completion-in-region-mode -1)) 1259 | 1260 | (defun corfu-reset () 1261 | "Reset Corfu completion. 1262 | This command can be executed multiple times by hammering the ESC key. If a 1263 | candidate is selected, unselect the candidate. Otherwise reset the input. If 1264 | there hasn't been any input, then quit." 1265 | (interactive) 1266 | (if (/= corfu--index corfu--preselect) 1267 | (progn 1268 | (corfu--goto -1) 1269 | (setq this-command #'corfu-first)) 1270 | ;; Cancel all changes and start new change group. 1271 | (pcase-let* ((`(,beg ,end . ,_) completion-in-region--data) 1272 | (str (buffer-substring-no-properties beg end))) 1273 | (cancel-change-group corfu--change-group) 1274 | (activate-change-group (setq corfu--change-group (prepare-change-group))) 1275 | ;; Quit when resetting, when input did not change. 1276 | (when (equal str (buffer-substring-no-properties beg end)) 1277 | (corfu-quit))))) 1278 | 1279 | (defun corfu-insert-separator () 1280 | "Insert a separator character, inhibiting quit on completion boundary. 1281 | If the currently selected candidate is previewed, jump to the input 1282 | prompt instead. See `corfu-separator' for more details." 1283 | (interactive) 1284 | (if (not (corfu--preview-current-p)) 1285 | (insert corfu-separator) 1286 | (corfu--goto -1) 1287 | (unless (or (= (car completion-in-region--data) (point)) 1288 | (= (char-before) corfu-separator)) 1289 | (insert corfu-separator)))) 1290 | 1291 | (defun corfu-next (&optional n) 1292 | "Go forward N candidates." 1293 | (interactive "p") 1294 | (let ((index (+ corfu--index (or n 1)))) 1295 | (corfu--goto 1296 | (cond 1297 | ((not corfu-cycle) index) 1298 | ((= corfu--total 0) -1) 1299 | ((< corfu--preselect 0) (1- (mod (1+ index) (1+ corfu--total)))) 1300 | (t (mod index corfu--total)))))) 1301 | 1302 | (defun corfu-previous (&optional n) 1303 | "Go backward N candidates." 1304 | (interactive "p") 1305 | (corfu-next (- (or n 1)))) 1306 | 1307 | (defun corfu-scroll-down (&optional n) 1308 | "Go back by N pages." 1309 | (interactive "p") 1310 | (corfu--goto (max 0 (- corfu--index (* (or n 1) corfu-count))))) 1311 | 1312 | (defun corfu-scroll-up (&optional n) 1313 | "Go forward by N pages." 1314 | (interactive "p") 1315 | (corfu-scroll-down (- (or n 1)))) 1316 | 1317 | (defun corfu-first () 1318 | "Go to first candidate. 1319 | If the first candidate is already selected, go to the prompt." 1320 | (interactive) 1321 | (corfu--goto (if (> corfu--index 0) 0 -1))) 1322 | 1323 | (defun corfu-last () 1324 | "Go to last candidate." 1325 | (interactive) 1326 | (corfu--goto (1- corfu--total))) 1327 | 1328 | (defun corfu-prompt-beginning (arg) 1329 | "Move to beginning of the prompt line. 1330 | If the point is already the beginning of the prompt move to the 1331 | beginning of the line. If ARG is not 1 or nil, move backward ARG - 1 1332 | lines first." 1333 | (interactive "^p") 1334 | (let ((beg (car completion-in-region--data))) 1335 | (if (or (not (eq arg 1)) 1336 | (and (= corfu--preselect corfu--index) (= (point) beg))) 1337 | (move-beginning-of-line arg) 1338 | (corfu--goto -1) 1339 | (goto-char beg)))) 1340 | 1341 | (defun corfu-prompt-end (arg) 1342 | "Move to end of the prompt line. 1343 | If the point is already the end of the prompt move to the end of 1344 | the line. If ARG is not 1 or nil, move forward ARG - 1 lines 1345 | first." 1346 | (interactive "^p") 1347 | (let ((end (cadr completion-in-region--data))) 1348 | (if (or (not (eq arg 1)) 1349 | (and (= corfu--preselect corfu--index) (= (point) end))) 1350 | (move-end-of-line arg) 1351 | (corfu--goto -1) 1352 | (goto-char end)))) 1353 | 1354 | (defun corfu-complete () 1355 | "Complete current input. 1356 | If a candidate is selected, insert it. Otherwise invoke 1357 | `corfu-expand'. Return non-nil if the input has been expanded." 1358 | (interactive) 1359 | (if (< corfu--index 0) 1360 | (corfu-expand) 1361 | ;; Continue completion with selected candidate. Exit with status 'finished 1362 | ;; if input is a valid match and no further completion is 1363 | ;; possible. Additionally treat completion as finished if at the end of a 1364 | ;; boundary, even if other longer candidates would still match, since the 1365 | ;; user invoked `corfu-complete' with an explicitly selected candidate! 1366 | (pcase-let ((`(,_beg ,_end ,table ,pred . ,_) completion-in-region--data) 1367 | (newstr (corfu--insert nil))) 1368 | (and (test-completion newstr table pred) 1369 | (or (not (consp (completion-try-completion 1370 | newstr table pred (length newstr) 1371 | (completion-metadata newstr table pred)))) 1372 | (equal (completion-boundaries newstr table pred "") '(0 . 0))) 1373 | (corfu--done newstr 'finished nil)) 1374 | t))) 1375 | 1376 | (defun corfu-expand () 1377 | "Expands the common prefix of all candidates. 1378 | If the currently selected candidate is previewed, invoke 1379 | `corfu-complete' instead. Expansion relies on the completion 1380 | styles via `completion-try-completion'. Return non-nil if the 1381 | input has been expanded." 1382 | (interactive) 1383 | (if (corfu--preview-current-p) 1384 | (corfu-complete) 1385 | (pcase-let* ((`(,beg ,end ,table ,pred . ,_) completion-in-region--data) 1386 | (pt (max 0 (- (point) beg))) 1387 | (str (buffer-substring-no-properties beg end))) 1388 | (pcase (completion-try-completion str table pred pt corfu--metadata) 1389 | ('t 1390 | (goto-char end) 1391 | (corfu--done str 'finished corfu--candidates) 1392 | t) 1393 | ((and `(,newstr . ,newpt) (guard (not (and (= pt newpt) (equal newstr str))))) 1394 | (corfu--replace beg end newstr) 1395 | (goto-char (+ beg newpt)) 1396 | ;; Exit with status 'finished if input is a valid match 1397 | ;; and no further completion is possible. 1398 | (and (test-completion newstr table pred) 1399 | (not (consp (completion-try-completion 1400 | newstr table pred newpt 1401 | (completion-metadata (substring newstr 0 newpt) table pred)))) 1402 | (corfu--done newstr 'finished corfu--candidates)) 1403 | t))))) 1404 | 1405 | (defun corfu-insert () 1406 | "Insert current candidate. 1407 | Quit if no candidate is selected." 1408 | (interactive) 1409 | (if (>= corfu--index 0) 1410 | (corfu--insert 'finished) 1411 | (corfu-quit))) 1412 | 1413 | (defun corfu-send () 1414 | "Insert current candidate and send it when inside comint or eshell." 1415 | (interactive) 1416 | (corfu-insert) 1417 | (cond 1418 | ((and (derived-mode-p 'eshell-mode) (fboundp 'eshell-send-input)) 1419 | (eshell-send-input)) 1420 | ((and (derived-mode-p 'comint-mode) (fboundp 'comint-send-input)) 1421 | (comint-send-input)))) 1422 | 1423 | ;;;###autoload 1424 | (define-minor-mode corfu-mode 1425 | "COmpletion in Region FUnction." 1426 | :group 'corfu :keymap corfu-mode-map 1427 | (cond 1428 | (corfu-mode 1429 | (and corfu-auto (add-hook 'post-command-hook #'corfu--auto-post-command nil 'local)) 1430 | (setq-local completion-in-region-function #'corfu--in-region)) 1431 | (t 1432 | (remove-hook 'post-command-hook #'corfu--auto-post-command 'local) 1433 | (kill-local-variable 'completion-in-region-function)))) 1434 | 1435 | (defcustom global-corfu-modes t 1436 | "List of modes where Corfu should be enabled by `global-corfu-mode'. 1437 | The variable can either be t, nil or a list of t, nil, mode 1438 | symbols or elements of the form (not modes). Examples: 1439 | - Enable everywhere, except in Org: ((not org-mode) t). 1440 | - Enable in programming modes except Python: ((not python-mode) prog-mode). 1441 | - Enable only in text modes: (text-mode)." 1442 | :type '(choice (const t) (repeat sexp)) 1443 | :group 'corfu) 1444 | 1445 | ;; TODO use `:predicate' on Emacs 29 1446 | (defcustom global-corfu-minibuffer t 1447 | "Corfu should be enabled in the minibuffer by `global-corfu-mode'. 1448 | The variable can either be t, nil or a custom predicate function. If 1449 | the variable is set to t, Corfu is only enabled if the minibuffer has 1450 | local `completion-at-point-functions'." 1451 | :type '(choice (const t) (const nil) function) 1452 | :group 'corfu) 1453 | 1454 | ;;;###autoload 1455 | (define-globalized-minor-mode global-corfu-mode 1456 | corfu-mode corfu--on 1457 | :group 'corfu 1458 | (remove-hook 'minibuffer-setup-hook #'corfu--minibuffer-on) 1459 | (when (and global-corfu-mode global-corfu-minibuffer) 1460 | (add-hook 'minibuffer-setup-hook #'corfu--minibuffer-on 100))) 1461 | 1462 | (defun corfu--on () 1463 | "Enable `corfu-mode' in the current buffer respecting `global-corfu-modes'." 1464 | (when (and (not noninteractive) (not (eq (aref (buffer-name) 0) ?\s)) 1465 | ;; TODO use `:predicate' on Emacs 29 1466 | (or (eq t global-corfu-modes) 1467 | (eq t (cl-loop for p in global-corfu-modes thereis 1468 | (pcase-exhaustive p 1469 | ('t t) 1470 | ('nil 0) 1471 | ((pred symbolp) (and (derived-mode-p p) t)) 1472 | (`(not . ,m) (and (seq-some #'derived-mode-p m) 0))))))) 1473 | (corfu-mode))) 1474 | 1475 | (defun corfu--minibuffer-on () 1476 | "Enable `corfu-mode' in the minibuffer respecting `global-corfu-minibuffer'." 1477 | (when (and global-corfu-minibuffer (not noninteractive) 1478 | (if (functionp global-corfu-minibuffer) 1479 | (funcall global-corfu-minibuffer) 1480 | (local-variable-p 'completion-at-point-functions))) 1481 | (corfu-mode))) 1482 | 1483 | ;; Do not show Corfu commands with M-X 1484 | (dolist (sym '( corfu-next corfu-previous corfu-first corfu-last corfu-quit corfu-reset 1485 | corfu-complete corfu-insert corfu-scroll-up corfu-scroll-down corfu-expand 1486 | corfu-send corfu-insert-separator corfu-prompt-beginning corfu-prompt-end 1487 | corfu-info-location corfu-info-documentation ;; autoloads in corfu-info.el 1488 | corfu-quick-jump corfu-quick-insert corfu-quick-complete)) ;; autoloads in corfu-quick.el 1489 | (put sym 'completion-predicate #'ignore)) 1490 | 1491 | (defun corfu--capf-wrapper-advice (orig fun which) 1492 | "Around advice for `completion--capf-wrapper'. 1493 | The ORIG function takes the FUN and WHICH arguments." 1494 | (if corfu-mode (corfu--capf-wrapper fun t) (funcall orig fun which))) 1495 | 1496 | (defun corfu--eldoc-advice () 1497 | "Return non-nil if Corfu is currently not active." 1498 | (not (and corfu-mode completion-in-region-mode))) 1499 | 1500 | ;; Install advice which fixes `completion--capf-wrapper', such that it respects 1501 | ;; the completion styles for non-exclusive Capfs. See also the fixme comment in 1502 | ;; the `completion--capf-wrapper' function in minibuffer.el. 1503 | (advice-add #'completion--capf-wrapper :around #'corfu--capf-wrapper-advice) 1504 | 1505 | ;; Register Corfu with ElDoc 1506 | (advice-add #'eldoc-display-message-no-interference-p 1507 | :before-while #'corfu--eldoc-advice) 1508 | (eldoc-add-command #'corfu-complete #'corfu-insert #'corfu-expand #'corfu-send) 1509 | 1510 | (provide 'corfu) 1511 | ;;; corfu.el ends here 1512 | -------------------------------------------------------------------------------- /extensions/corfu-echo.el: -------------------------------------------------------------------------------- 1 | ;;; corfu-echo.el --- Show candidate documentation in echo area -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2021-2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Daniel Mendler 6 | ;; Maintainer: Daniel Mendler 7 | ;; Created: 2022 8 | ;; Version: 2.2 9 | ;; Package-Requires: ((emacs "28.1") (compat "30") (corfu "2.2")) 10 | ;; URL: https://github.com/minad/corfu 11 | 12 | ;; This file is part of GNU Emacs. 13 | 14 | ;; This program is free software: you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation, either version 3 of the License, or 17 | ;; (at your option) any later version. 18 | 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program. If not, see . 26 | 27 | ;;; Commentary: 28 | 29 | ;; Show candidate documentation in echo area. Enable `corfu-echo-mode'. 30 | 31 | ;;; Code: 32 | 33 | (require 'corfu) 34 | (eval-when-compile 35 | (require 'subr-x)) 36 | 37 | (defface corfu-echo 38 | '((t :inherit completions-annotations)) 39 | "Face used for echo area messages." 40 | :group 'corfu-faces) 41 | 42 | (defcustom corfu-echo-delay '(2.0 . 1.0) 43 | "Show documentation string in the echo area after that number of seconds. 44 | The value can be a pair of two floats to specify initial and 45 | subsequent delay." 46 | :type '(choice (const :tag "Never" nil) 47 | (number :tag "Delay in seconds") 48 | (cons :tag "Two Delays" 49 | (choice :tag "Initial " number) 50 | (choice :tag "Subsequent" number))) 51 | :group 'corfu) 52 | 53 | (defvar corfu-echo--timer nil 54 | "Echo area message timer.") 55 | 56 | (defvar corfu-echo--message nil 57 | "Last echo message.") 58 | 59 | (defun corfu-echo--cancel (&optional msg) 60 | "Cancel echo timer and refresh MSG." 61 | (when corfu-echo--timer 62 | (cancel-timer corfu-echo--timer) 63 | (setq corfu-echo--timer nil)) 64 | (corfu-echo--show msg) 65 | (unless corfu-echo--message 66 | (setq corfu-echo--timer nil 67 | corfu-echo--message nil))) 68 | 69 | (defun corfu-echo--show (msg) 70 | "Show MSG in echo area." 71 | (when (or msg corfu-echo--message) 72 | (setq msg (or msg "") 73 | corfu-echo--message msg) 74 | (corfu--message "%s" (if (text-property-not-all 0 (length msg) 'face nil msg) 75 | msg 76 | (propertize msg 'face 'corfu-echo))))) 77 | 78 | ;;;###autoload 79 | (define-minor-mode corfu-echo-mode 80 | "Show candidate documentation in echo area." 81 | :global t :group 'corfu) 82 | 83 | (cl-defmethod corfu--exhibit :after (&context (corfu-echo-mode (eql t)) &optional _auto) 84 | (if-let (((not (minibufferp))) 85 | (delay (if (consp corfu-echo-delay) 86 | (funcall (if corfu-echo--message #'cdr #'car) 87 | corfu-echo-delay) 88 | corfu-echo-delay)) 89 | (fun (corfu--metadata-get 'company-docsig)) 90 | (cand (and (>= corfu--index 0) 91 | (nth corfu--index corfu--candidates)))) 92 | (if (<= delay 0) 93 | (corfu-echo--show (funcall fun cand)) 94 | (corfu-echo--cancel) 95 | (setq corfu-echo--timer 96 | (run-at-time delay nil 97 | (lambda () 98 | (corfu-echo--show (funcall fun cand)))))) 99 | (corfu-echo--cancel))) 100 | 101 | (cl-defmethod corfu--teardown :before (_buf &context (corfu-echo-mode (eql t))) 102 | (corfu-echo--cancel)) 103 | 104 | (cl-defmethod corfu--prepare :before (&context (corfu-echo-mode (eql t))) 105 | ;; The refreshing is needed to prevent flicker if corfu-echo-delay=t. 106 | (corfu-echo--cancel corfu-echo--message)) 107 | 108 | (provide 'corfu-echo) 109 | ;;; corfu-echo.el ends here 110 | -------------------------------------------------------------------------------- /extensions/corfu-history.el: -------------------------------------------------------------------------------- 1 | ;;; corfu-history.el --- Sorting by history for Corfu -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2022-2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Daniel Mendler 6 | ;; Maintainer: Daniel Mendler 7 | ;; Created: 2022 8 | ;; Version: 2.2 9 | ;; Package-Requires: ((emacs "28.1") (compat "30") (corfu "2.2")) 10 | ;; URL: https://github.com/minad/corfu 11 | 12 | ;; This file is part of GNU Emacs. 13 | 14 | ;; This program is free software: you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation, either version 3 of the License, or 17 | ;; (at your option) any later version. 18 | 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program. If not, see . 26 | 27 | ;;; Commentary: 28 | 29 | ;; Enable `corfu-history-mode' to sort candidates by their history position. 30 | ;; The recently selected candidates are stored in the `corfu-history' variable. 31 | ;; If `history-delete-duplicates' is nil, duplicate elements are ranked higher 32 | ;; with exponential decay. In order to save the history across Emacs sessions, 33 | ;; enable `savehist-mode'. 34 | ;; 35 | ;; (corfu-history-mode) 36 | ;; (savehist-mode) 37 | 38 | ;;; Code: 39 | 40 | (require 'corfu) 41 | (eval-when-compile 42 | (require 'cl-lib)) 43 | 44 | (defvar corfu-history nil 45 | "History of Corfu candidates. 46 | The maximum length is determined by the variable `history-length' 47 | or the property `history-length' of `corfu-history'.") 48 | 49 | (defvar corfu-history--hash nil 50 | "Hash table of Corfu candidates.") 51 | 52 | (defcustom corfu-history-duplicate 10 53 | "History position shift for duplicate history elements. 54 | The more often a duplicate element occurs in the history, the earlier it 55 | appears in the completion list. The shift decays exponentially with 56 | `corfu-history-decay'. Note that duplicates occur only if 57 | `history-delete-duplicates' is disabled." 58 | :type 'number 59 | :group 'corfu) 60 | 61 | (defcustom corfu-history-decay 10 62 | "Exponential decay for the position shift of duplicate elements. 63 | The shift will decay away after `corfu-history-duplicate' times 64 | `corfu-history-decay' history elements." 65 | :type 'number 66 | :group 'corfu) 67 | 68 | (defun corfu-history--sort-predicate (x y) 69 | "Sorting predicate which compares X and Y." 70 | (or (< (cdr x) (cdr y)) 71 | (and (= (cdr x) (cdr y)) 72 | (corfu--length-string< (car x) (car y))))) 73 | 74 | (defun corfu-history--sort (cands) 75 | "Sort CANDS by history." 76 | (unless corfu-history--hash 77 | (let ((ht (make-hash-table :test #'equal :size (length corfu-history))) 78 | (decay (/ -1.0 (* corfu-history-duplicate corfu-history-decay)))) 79 | (cl-loop for elem in corfu-history for idx from 0 80 | for r = (if-let ((r (gethash elem ht))) 81 | ;; Reduce duplicate rank with exponential decay. 82 | (- r (round (* corfu-history-duplicate (exp (* decay idx))))) 83 | ;; Never outrank the most recent element. 84 | (if (= idx 0) (/ most-negative-fixnum 2) idx)) 85 | do (puthash elem r ht)) 86 | (setq corfu-history--hash ht))) 87 | (cl-loop for ht = corfu-history--hash for max = most-positive-fixnum 88 | for cand on cands do 89 | (setcar cand (cons (car cand) (gethash (car cand) ht max)))) 90 | (setq cands (sort cands #'corfu-history--sort-predicate)) 91 | (cl-loop for cand on cands do (setcar cand (caar cand))) 92 | cands) 93 | 94 | ;;;###autoload 95 | (define-minor-mode corfu-history-mode 96 | "Update Corfu history and sort completions by history." 97 | :global t :group 'corfu 98 | (if corfu-history-mode 99 | (add-function :override corfu-sort-function #'corfu-history--sort) 100 | (remove-function corfu-sort-function #'corfu-history--sort))) 101 | 102 | (cl-defmethod corfu--insert :before (_status &context (corfu-history-mode (eql t))) 103 | (when (>= corfu--index 0) 104 | (unless (or (not (bound-and-true-p savehist-mode)) 105 | (memq 'corfu-history (bound-and-true-p savehist-ignored-variables))) 106 | (defvar savehist-minibuffer-history-variables) 107 | (add-to-list 'savehist-minibuffer-history-variables 'corfu-history)) 108 | (add-to-history 'corfu-history 109 | (substring-no-properties 110 | (nth corfu--index corfu--candidates))) 111 | (setq corfu-history--hash nil))) 112 | 113 | (provide 'corfu-history) 114 | ;;; corfu-history.el ends here 115 | -------------------------------------------------------------------------------- /extensions/corfu-indexed.el: -------------------------------------------------------------------------------- 1 | ;;; corfu-indexed.el --- Select indexed candidates -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2022-2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Luis Henriquez-Perez , Daniel Mendler 6 | ;; Maintainer: Daniel Mendler 7 | ;; Created: 2022 8 | ;; Version: 2.2 9 | ;; Package-Requires: ((emacs "28.1") (compat "30") (corfu "2.2")) 10 | ;; URL: https://github.com/minad/corfu 11 | 12 | ;; This file is part of GNU Emacs. 13 | 14 | ;; This program is free software: you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation, either version 3 of the License, or 17 | ;; (at your option) any later version. 18 | 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program. If not, see . 26 | 27 | ;;; Commentary: 28 | 29 | ;; This package is a Corfu extension, which prefixes candidates with indices if 30 | ;; enabled via `corfu-indexed-mode'. It allows you to select candidates with 31 | ;; prefix arguments. This is designed to be a faster alternative to selecting a 32 | ;; candidate with `corfu-next' and `corfu-previous'. 33 | 34 | ;;; Code: 35 | 36 | (require 'corfu) 37 | (eval-when-compile 38 | (require 'cl-lib)) 39 | 40 | (defface corfu-indexed 41 | '((default :height 0.75) 42 | (((class color) (min-colors 88) (background dark)) 43 | :foreground "#f4f4f4" :background "#323232") 44 | (((class color) (min-colors 88) (background light)) 45 | :foreground "#404148" :background "#d7d7d7") 46 | (t :background "black")) 47 | "Face used for the candidate index prefix." 48 | :group 'corfu-faces) 49 | 50 | (defcustom corfu-indexed-start 0 51 | "Start of the indexing." 52 | :group 'corfu 53 | :type 'natnum) 54 | 55 | (defvar corfu-indexed--commands 56 | '(corfu-insert corfu-complete) 57 | "Commands that should be indexed.") 58 | 59 | ;;;###autoload 60 | (define-minor-mode corfu-indexed-mode 61 | "Prefix candidates with indices." 62 | :global t :group 'corfu) 63 | 64 | (cl-defmethod corfu--prepare :before (&context (corfu-indexed-mode (eql t))) 65 | (when (and prefix-arg (memq this-command corfu-indexed--commands)) 66 | (let ((index (+ corfu--scroll 67 | (- (prefix-numeric-value prefix-arg) 68 | corfu-indexed-start)))) 69 | (if (and (>= index 0) 70 | (< index corfu--total) 71 | (< index (+ corfu--scroll corfu-count))) 72 | (setq corfu--index index) 73 | (message "Out of range") 74 | (setq this-command #'ignore))))) 75 | 76 | (cl-defmethod corfu--affixate :around (cands &context (corfu-indexed-mode (eql t))) 77 | (setq cands (cdr (cl-call-next-method cands))) 78 | (let* ((space #(" " 0 1 (face (:height 0.5 :inherit corfu-indexed)))) 79 | (width (if (length> cands (- 10 corfu-indexed-start)) 2 1)) 80 | (fmt (concat space 81 | (propertize (format "%%%ds" width) 82 | 'face 'corfu-indexed) 83 | space)) 84 | (align 85 | (propertize (make-string width ?\s) 86 | 'display 87 | `(space :align-to (+ left ,(1+ width)))))) 88 | (cl-loop for cand in cands for index from corfu-indexed-start do 89 | (setf (cadr cand) 90 | (concat 91 | (propertize " " 'display (format fmt index)) 92 | align 93 | (cadr cand)))) 94 | (cons t cands))) 95 | 96 | (provide 'corfu-indexed) 97 | ;;; corfu-indexed.el ends here 98 | -------------------------------------------------------------------------------- /extensions/corfu-info.el: -------------------------------------------------------------------------------- 1 | ;;; corfu-info.el --- Show candidate information in separate buffer -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2022-2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Daniel Mendler 6 | ;; Maintainer: Daniel Mendler 7 | ;; Created: 2022 8 | ;; Version: 2.2 9 | ;; Package-Requires: ((emacs "28.1") (compat "30") (corfu "2.2")) 10 | ;; URL: https://github.com/minad/corfu 11 | 12 | ;; This file is part of GNU Emacs. 13 | 14 | ;; This program is free software: you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation, either version 3 of the License, or 17 | ;; (at your option) any later version. 18 | 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program. If not, see . 26 | 27 | ;;; Commentary: 28 | 29 | ;; This Corfu extension provides commands to show additional information to the 30 | ;; candidates in a separate buffer. The commands `corfu-info-location' and 31 | ;; `corfu-info-documentation' are bound by default in the `corfu-map' to M-g and 32 | ;; M-h respectively. 33 | 34 | ;;; Code: 35 | 36 | (require 'corfu) 37 | (eval-when-compile 38 | (require 'subr-x)) 39 | 40 | (defun corfu-info--restore-on-next-command () 41 | "Restore window configuration before next command." 42 | (let ((config (current-window-configuration)) 43 | (other other-window-scroll-buffer) 44 | (restore (make-symbol "corfu--restore"))) 45 | (fset restore 46 | (lambda () 47 | (setq other-window-scroll-buffer other) 48 | (unless (memq this-command '(scroll-other-window scroll-other-window-down)) 49 | (when (memq this-command '(corfu-quit corfu-reset)) 50 | (setq this-command #'ignore)) 51 | (remove-hook 'pre-command-hook restore) 52 | (set-window-configuration config)))) 53 | (add-hook 'pre-command-hook restore))) 54 | 55 | (defun corfu-info--display-buffer (buffer name) 56 | "Display BUFFER and return window displaying the buffer. 57 | Make the buffer persistent with NAME if non-nil." 58 | (if name 59 | (unless (buffer-local-value 'buffer-file-name buffer) 60 | (if-let ((old (get-buffer name))) 61 | (setq buffer (prog1 old (kill-buffer buffer))) 62 | (with-current-buffer buffer 63 | (rename-buffer name)))) 64 | (corfu-info--restore-on-next-command)) 65 | (setq other-window-scroll-buffer buffer) 66 | (display-buffer buffer t)) 67 | 68 | ;;;###autoload 69 | (defun corfu-info-documentation (&optional arg) 70 | "Show documentation of current candidate. 71 | If called with a prefix ARG, the buffer is persistent." 72 | (interactive "P") 73 | ;; Company support, taken from `company.el', see `company-show-doc-buffer'. 74 | (when (< corfu--index 0) 75 | (user-error "No candidate selected")) 76 | (let ((cand (nth corfu--index corfu--candidates))) 77 | (if-let ((fun (corfu--metadata-get 'company-doc-buffer)) 78 | (res (funcall fun cand))) 79 | (set-window-start (corfu-info--display-buffer 80 | (get-buffer (or (car-safe res) res)) 81 | (and arg (format "*corfu doc: %s*" cand))) 82 | (or (cdr-safe res) (point-min))) 83 | (user-error "No documentation available for `%s'" cand)))) 84 | 85 | ;;;###autoload 86 | (defun corfu-info-location (&optional arg) 87 | "Show location of current candidate. 88 | If called with a prefix ARG, the buffer is persistent." 89 | (interactive "P") 90 | ;; Company support, taken from `company.el', see `company-show-location'. 91 | (when (< corfu--index 0) 92 | (user-error "No candidate selected")) 93 | (let ((cand (nth corfu--index corfu--candidates))) 94 | (if-let ((fun (corfu--metadata-get 'company-location)) 95 | ;; BUG: company-location may throw errors if location is not found 96 | (loc (ignore-errors (funcall fun cand)))) 97 | (with-selected-window 98 | (corfu-info--display-buffer 99 | (or (and (bufferp (car loc)) (car loc)) 100 | (find-file-noselect (car loc) t)) 101 | (and arg (format "*corfu loc: %s*" cand))) 102 | (without-restriction 103 | (goto-char (point-min)) 104 | (when-let ((pos (cdr loc))) 105 | (if (bufferp (car loc)) 106 | (goto-char pos) 107 | (forward-line (1- pos)))) 108 | (set-window-start nil (point)))) 109 | (user-error "No location available for `%s'" cand)))) 110 | 111 | (provide 'corfu-info) 112 | ;;; corfu-info.el ends here 113 | -------------------------------------------------------------------------------- /extensions/corfu-popupinfo.el: -------------------------------------------------------------------------------- 1 | ;;; corfu-popupinfo.el --- Candidate information popup for Corfu -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2021-2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Yuwei Tian , Daniel Mendler 6 | ;; Maintainer: Daniel Mendler 7 | ;; Created: 2022 8 | ;; Version: 2.2 9 | ;; Package-Requires: ((emacs "28.1") (compat "30") (corfu "2.2")) 10 | ;; URL: https://github.com/minad/corfu 11 | 12 | ;; This file is part of GNU Emacs. 13 | 14 | ;; This program is free software: you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation, either version 3 of the License, or 17 | ;; (at your option) any later version. 18 | 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program. If not, see . 26 | 27 | ;;; Commentary: 28 | 29 | ;; Display an information popup for completion candidate when using 30 | ;; Corfu. The popup displays either the candidate documentation or the 31 | ;; candidate location. The `corfu-popupinfo-mode' must be enabled 32 | ;; globally. Set `corfu-popupinfo-delay' to nil if the info popup should 33 | ;; not update automatically. If the popup should not appear initially, 34 | ;; but update automatically afterwards, use `(setq corfu-popupinfo-delay 35 | ;; (cons nil 1.0))'. 36 | 37 | ;; For manual toggling the commands `corfu-popupinfo-toggle', 38 | ;; `corfu-popupinfo-location' and `corfu-popupinfo-documentation' are 39 | ;; bound in the `corfu-popupinfo-map'. 40 | 41 | ;;; Code: 42 | 43 | (require 'corfu) 44 | (eval-when-compile 45 | (require 'cl-lib) 46 | (require 'subr-x)) 47 | 48 | (defface corfu-popupinfo 49 | '((t :inherit corfu-default)) 50 | "Face used for the info popup." 51 | :group 'corfu-faces) 52 | 53 | (defcustom corfu-popupinfo-delay '(2.0 . 1.0) 54 | "Automatically update info popup after that number of seconds. 55 | 56 | The value can be a pair of two floats to specify initial and 57 | subsequent delay. If the value is non-nil or the car of the pair 58 | is non-nil, the popup will automatically appear for the 59 | preselected candidate. Otherwise the popup can be requested 60 | manually via `corfu-popupinfo-toggle', 61 | `corfu-popupinfo-documentation' and `corfu-popupinfo-location'. 62 | 63 | It is *not recommended* to use a short delay or even 0, since 64 | this will create high load for Emacs. Retrieving the 65 | documentation from the backend is usually expensive." 66 | :type '(choice (const :tag "Never" nil) 67 | (number :tag "Delay in seconds") 68 | (cons :tag "Two Delays" 69 | (choice :tag "Initial " 70 | (choice (const nil) number)) 71 | (choice :tag "Subsequent" 72 | (choice (const nil) number)))) 73 | :group 'corfu) 74 | 75 | (defcustom corfu-popupinfo-hide t 76 | "Hide the popup during the transition between candidates." 77 | :type 'boolean 78 | :group 'corfu) 79 | 80 | (defcustom corfu-popupinfo-max-width 80 81 | "The maximum width of the info popup in characters." 82 | :type 'natnum 83 | :group 'corfu) 84 | 85 | (defcustom corfu-popupinfo-min-width 30 86 | "The minimum width of the info popup in characters." 87 | :type 'natnum 88 | :group 'corfu) 89 | 90 | (defcustom corfu-popupinfo-max-height 10 91 | "The maximum height of the info popup in characters." 92 | :type 'natnum 93 | :group 'corfu) 94 | 95 | (defcustom corfu-popupinfo-min-height 1 96 | "The minimum height of the info popup in characters." 97 | :type 'natnum 98 | :group 'corfu) 99 | 100 | (defcustom corfu-popupinfo-resize t 101 | "Resize the info popup automatically if non-nil." 102 | :type 'boolean 103 | :group 'corfu) 104 | 105 | (defcustom corfu-popupinfo-direction '(right left vertical) 106 | "Preferred directions for the popup in order." 107 | :type '(repeat 108 | (choice 109 | (const left) 110 | (const right) 111 | (const vertical) 112 | (const force-left) 113 | (const force-right) 114 | (const force-vertical))) 115 | :group 'corfu) 116 | 117 | (defvar-keymap corfu-popupinfo-map 118 | :doc "Additional keymap activated in popupinfo mode." 119 | "M-t" #'corfu-popupinfo-toggle 120 | " " #'corfu-popupinfo-documentation 121 | " " #'corfu-popupinfo-location 122 | " " #'corfu-popupinfo-scroll-up 123 | " " #'corfu-popupinfo-scroll-down 124 | " " #'corfu-popupinfo-end 125 | " " #'corfu-popupinfo-beginning) 126 | 127 | (defvar corfu-popupinfo--buffer-parameters 128 | '((truncate-partial-width-windows . nil) 129 | (truncate-lines . nil) 130 | (left-margin-width . 1) 131 | (right-margin-width . 1) 132 | (word-wrap . t) 133 | (char-property-alias-alist (face font-lock-face))) 134 | "Buffer parameters.") 135 | 136 | (defvar corfu-popupinfo--frame nil 137 | "Info popup child frame.") 138 | 139 | (defvar corfu-popupinfo--timer nil 140 | "Corfu info popup auto display timer.") 141 | 142 | (defvar corfu-popupinfo--toggle 'init 143 | "Toggle state.") 144 | 145 | (defvar corfu-popupinfo--function 146 | #'corfu-popupinfo--get-documentation 147 | "Function called to obtain documentation string.") 148 | 149 | (defvar corfu-popupinfo--candidate nil 150 | "Completion candidate for the info popup.") 151 | 152 | (defvar corfu-popupinfo--coordinates nil 153 | "Coordinates of the candidate popup. 154 | The coordinates list has the form (LEFT TOP RIGHT BOTTOM) where 155 | all values are in pixels relative to the origin. See 156 | `frame-edges' for details.") 157 | 158 | (defvar corfu-popupinfo--lock-dir nil 159 | "Locked position direction of the info popup.") 160 | 161 | (defconst corfu-popupinfo--buffer " *corfu-popupinfo*" 162 | "Buffer used by the popup.") 163 | 164 | (defconst corfu-popupinfo--initial-state 165 | (mapcar 166 | (lambda (k) (cons k (symbol-value k))) 167 | '(corfu-popupinfo--candidate 168 | corfu-popupinfo--coordinates 169 | corfu-popupinfo--lock-dir 170 | corfu-popupinfo--toggle 171 | corfu-popupinfo--function)) 172 | "Initial state of `corfu-popupinfo-mode'.") 173 | 174 | (defun corfu-popupinfo--visible-p (&optional frame) 175 | "Return non-nil if FRAME is visible." 176 | (setq frame (or frame corfu-popupinfo--frame)) 177 | (and (frame-live-p frame) (frame-visible-p frame))) 178 | 179 | (defun corfu-popupinfo--get-location (candidate) 180 | "Get source at location of CANDIDATE." 181 | (save-excursion 182 | (let ((old-buffers (buffer-list)) (buffer nil)) 183 | (unwind-protect 184 | (when-let 185 | ((fun (corfu--metadata-get 'company-location)) 186 | ;; BUG: company-location may throw errors if location is not found 187 | (loc (ignore-errors (funcall fun candidate))) 188 | ((setq buffer 189 | (or (and (bufferp (car loc)) (car loc)) 190 | (get-file-buffer (car loc)) 191 | (let ((inhibit-message t) 192 | (message-log-max nil) 193 | (inhibit-redisplay t) 194 | (enable-dir-local-variables nil) 195 | (enable-local-variables :safe) 196 | (non-essential t) 197 | (delay-mode-hooks t) 198 | (find-file-hook '(global-font-lock-mode-check-buffers))) 199 | (find-file-noselect (car loc) t)))))) 200 | (with-current-buffer buffer 201 | (save-excursion 202 | (without-restriction 203 | (goto-char (point-min)) 204 | (when-let ((pos (cdr loc))) 205 | (if (bufferp (car loc)) 206 | (goto-char pos) 207 | (forward-line (1- pos)))) 208 | (let ((beg (point))) 209 | ;; Support a little bit of scrolling. 210 | (forward-line (* 10 corfu-popupinfo-max-height)) 211 | (when jit-lock-mode 212 | (jit-lock-fontify-now beg (point))) 213 | (let ((res (buffer-substring beg (point)))) 214 | (and (not (string-blank-p res)) res))))))) 215 | (when (and buffer (not (memq buffer old-buffers))) 216 | (kill-buffer buffer)))))) 217 | 218 | (defun corfu-popupinfo--get-documentation (candidate) 219 | "Get the documentation for CANDIDATE." 220 | (when-let ((fun (corfu--metadata-get 'company-doc-buffer)) 221 | (res (save-excursion 222 | (let ((inhibit-message t) 223 | (message-log-max nil) 224 | (inhibit-redisplay t) 225 | ;; Reduce print length for elisp backend (#249) 226 | (print-level 3) 227 | (print-length (* corfu-popupinfo-max-width 228 | corfu-popupinfo-max-height))) 229 | (funcall fun candidate))))) 230 | (with-current-buffer (or (car-safe res) res) 231 | (setq res (string-trim 232 | (replace-regexp-in-string 233 | "[\n\t ]*\\[back\\][\n\t ]*" "" 234 | (buffer-string)))) 235 | (and (not (string-blank-p res)) res)))) 236 | 237 | (defun corfu-popupinfo--size () 238 | "Return popup size as pair." 239 | (let* ((cw (default-font-width)) 240 | (lh (default-line-height)) 241 | (margin 242 | (* cw (+ (alist-get 'left-margin-width corfu-popupinfo--buffer-parameters) 243 | (alist-get 'right-margin-width corfu-popupinfo--buffer-parameters)))) 244 | (max-height (* lh corfu-popupinfo-max-height)) 245 | (max-width (* cw corfu-popupinfo-max-width))) 246 | (or (when corfu-popupinfo-resize 247 | (with-current-buffer corfu-popupinfo--buffer 248 | (cl-letf* (((window-dedicated-p) nil) 249 | ((window-buffer) (current-buffer)) 250 | (size (window-text-pixel-size 251 | nil (point-min) (point-max) 252 | ;; Use 3*max-height as y-limit, to take more text 253 | ;; into account. 254 | max-width (* 3 max-height)))) 255 | ;; Check that width is not exceeded. Otherwise use full height, 256 | ;; since lines will get wrapped. 257 | (when (<= (car size) max-width) 258 | (cons (+ margin (car size)) 259 | ;; XXX HACK: Ensure that popup has at least a height of 1, 260 | ;; which is the minimum frame height (#261). Maybe we 261 | ;; should ask upstream how smaller frames can be created. 262 | ;; I only managed to create smaller frames by setting 263 | ;; `window-safe-min-height' to 0, which feels problematic. 264 | (min (max (cdr size) lh) max-height)))))) 265 | (cons (+ margin max-width) max-height)))) 266 | 267 | (defun corfu-popupinfo--frame-geometry (frame) 268 | "Return position and size geometric attributes of FRAME. 269 | 270 | The geometry represents the position and size in pixels 271 | in the form of (X Y WIDTH HEIGHT)." 272 | (pcase-let ((`(,x . ,y) (frame-position frame))) 273 | (list x y (frame-pixel-width frame) (frame-pixel-height frame)))) 274 | 275 | (defun corfu-popupinfo--fits-p (size area) 276 | "Check if SIZE fits into the AREA. 277 | 278 | SIZE is in the form (WIDTH . HEIGHT). 279 | AREA is in the form (X Y WIDTH HEIGHT DIR)." 280 | (and (>= (nth 2 area) (car size)) (>= (nth 3 area) (cdr size)))) 281 | 282 | (defun corfu-popupinfo--larger-p (area1 area2) 283 | "Check if AREA1 is larger than AREA2. 284 | 285 | AREA1 and AREA2 are both in the form (X Y WIDTH HEIGHT DIR)." 286 | (>= (* (nth 2 area1) (nth 3 area1)) (* (nth 2 area2) (nth 3 area2)))) 287 | 288 | (defun corfu-popupinfo--area (ps) 289 | "Calculate the display area for the info popup. 290 | 291 | PS is the pixel size of the popup. The calculated area is in the 292 | form (X Y WIDTH HEIGHT DIR)." 293 | (pcase-let* 294 | ((cw (default-font-width)) 295 | (lh (default-line-height)) 296 | (border (alist-get 'internal-border-width corfu--frame-parameters)) 297 | (`(,_pfx ,_pfy ,pfw ,pfh) 298 | (corfu-popupinfo--frame-geometry (frame-parent corfu--frame))) 299 | (`(,cfx ,cfy ,cfw ,cfh) (corfu-popupinfo--frame-geometry corfu--frame)) 300 | ;; Candidates popup below input 301 | (below (>= cfy (+ lh (cadr (window-inside-pixel-edges)) 302 | (window-tab-line-height) 303 | (or (cdr (posn-x-y (posn-at-point (point)))) 0)))) 304 | ;; Popups aligned at top 305 | (top-aligned (or below (< (cdr ps) cfh))) 306 | ;; Left display area 307 | (ahy (if top-aligned 308 | cfy 309 | (max 0 (- (+ cfy cfh) border border (cdr ps))))) 310 | (ahh (if top-aligned 311 | (min (- pfh cfy) (cdr ps)) 312 | (min (- (+ cfy cfh) border border) (cdr ps)))) 313 | (al (list (max 0 (- cfx (car ps) border)) ahy 314 | (min (- cfx border) (car ps)) ahh 'left)) 315 | ;; Right display area 316 | (arx (+ cfx cfw (- border))) 317 | (ar (list arx ahy (min (- pfw arx border border) (car ps)) ahh 'right)) 318 | ;; Vertical display area 319 | (avw (min (car ps) (- pfw cfx border border))) 320 | (av (if below 321 | (list cfx (+ cfy cfh (- border)) avw (min (- pfh cfy cfh border) (cdr ps)) 'vertical) 322 | (let ((h (min (- cfy border border) (cdr ps)))) 323 | (list cfx (max 0 (- cfy h border)) avw h 'vertical))))) 324 | (unless (and corfu-popupinfo--lock-dir 325 | (corfu-popupinfo--fits-p 326 | (cons (* cw corfu-popupinfo-min-width) (* lh corfu-popupinfo-min-height)) 327 | (pcase corfu-popupinfo--lock-dir ('left al) ('right ar) ('vertical av)))) 328 | (setq corfu-popupinfo--lock-dir nil)) 329 | (or 330 | (cl-loop for dir in corfu-popupinfo-direction thereis 331 | (pcase dir 332 | ((or 'force-right (guard (eq corfu-popupinfo--lock-dir 'right))) ar) 333 | ((or 'force-left (guard (eq corfu-popupinfo--lock-dir 'left))) al) 334 | ((or 'force-vertical (guard (eq corfu-popupinfo--lock-dir 'vertical))) av) 335 | ((and 'right (guard (corfu-popupinfo--fits-p ps ar))) ar) 336 | ((and 'left (guard (corfu-popupinfo--fits-p ps al))) al) 337 | ((and 'vertical (guard (corfu-popupinfo--fits-p ps av))) av))) 338 | (let ((ah (if (corfu-popupinfo--larger-p ar al) ar al))) 339 | (if (corfu-popupinfo--larger-p av ah) av ah))))) 340 | 341 | (defun corfu-popupinfo--show (candidate) 342 | "Show the info popup for CANDIDATE." 343 | (when corfu-popupinfo--timer 344 | (cancel-timer corfu-popupinfo--timer) 345 | (setq corfu-popupinfo--timer nil)) 346 | (when (corfu-popupinfo--visible-p corfu--frame) 347 | (let* ((cand-changed 348 | (not (and (corfu-popupinfo--visible-p) 349 | (corfu--equal-including-properties 350 | candidate corfu-popupinfo--candidate)))) 351 | (new-coords (frame-edges corfu--frame 'inner-edges)) 352 | (coords-changed (not (equal new-coords corfu-popupinfo--coordinates)))) 353 | (when cand-changed 354 | (if-let ((content (funcall corfu-popupinfo--function candidate))) 355 | (with-current-buffer (corfu--make-buffer corfu-popupinfo--buffer) 356 | (with-silent-modifications 357 | (erase-buffer) 358 | (insert content) 359 | (goto-char (point-min))) 360 | (dolist (var corfu-popupinfo--buffer-parameters) 361 | (set (make-local-variable (car var)) (cdr var))) 362 | (when-let ((m (memq 'corfu-default (alist-get 'default face-remapping-alist)))) 363 | (setcar m 'corfu-popupinfo))) 364 | (corfu-popupinfo--hide) 365 | (setq cand-changed nil coords-changed nil))) 366 | (when (or cand-changed coords-changed) 367 | (pcase-let* ((border (alist-get 'internal-border-width corfu--frame-parameters)) 368 | (`(,area-x ,area-y ,area-w ,area-h ,area-d) 369 | (corfu-popupinfo--area 370 | (if cand-changed 371 | (corfu-popupinfo--size) 372 | (cons 373 | (- (frame-pixel-width corfu-popupinfo--frame) border border) 374 | (- (frame-pixel-height corfu-popupinfo--frame) border border))))) 375 | (margin-quirk (not corfu-popupinfo--frame))) 376 | (with-current-buffer corfu-popupinfo--buffer 377 | (setq corfu-popupinfo--frame 378 | (corfu--make-frame corfu-popupinfo--frame 379 | area-x area-y area-w area-h) 380 | corfu-popupinfo--toggle t 381 | corfu-popupinfo--lock-dir area-d 382 | corfu-popupinfo--candidate candidate 383 | corfu-popupinfo--coordinates new-coords) 384 | ;; XXX HACK: Force margin update. For some reason, the call to 385 | ;; `set-window-buffer' in `corfu--make-frame' is not effective the 386 | ;; first time. Why does Emacs have all these quirks? 387 | (when margin-quirk 388 | (set-window-buffer (frame-root-window corfu-popupinfo--frame) 389 | corfu-popupinfo--buffer)))))))) 390 | 391 | (defun corfu-popupinfo--hide () 392 | "Clear the info popup buffer content and hide it." 393 | (corfu--hide-frame corfu-popupinfo--frame)) 394 | 395 | (defun corfu-popupinfo-end (&optional n) 396 | "Scroll text of info popup window to its end. 397 | 398 | If arg N is omitted or nil, scroll to end. If a numerical value, 399 | put point N/10 of the way from the end. If the info popup is not 400 | visible, the other window is moved to beginning or end." 401 | (interactive "P") 402 | (if (corfu-popupinfo--visible-p) 403 | (with-selected-frame corfu-popupinfo--frame 404 | (with-current-buffer corfu-popupinfo--buffer 405 | (with-no-warnings 406 | (end-of-buffer n)))) 407 | (end-of-buffer-other-window n))) 408 | 409 | (defun corfu-popupinfo-beginning (&optional n) 410 | "Scroll text of info popup window to beginning of buffer. 411 | 412 | See `corfu-popupinfo-end' for the argument N." 413 | (interactive "P") 414 | (corfu-popupinfo-end (- 10 (if (numberp n) n 0)))) 415 | 416 | (defun corfu-popupinfo-scroll-up (&optional n) 417 | "Scroll text of info popup window upward N lines. 418 | 419 | If ARG is omitted or nil, scroll upward by a near full screen. 420 | See `scroll-up' for details. If the info popup is not visible, 421 | the other window is scrolled." 422 | (interactive "p") 423 | (if (corfu-popupinfo--visible-p) 424 | (with-selected-frame corfu-popupinfo--frame 425 | (with-current-buffer corfu-popupinfo--buffer 426 | (scroll-up n))) 427 | (scroll-other-window n))) 428 | 429 | (defun corfu-popupinfo-scroll-down (&optional n) 430 | "Scroll text of info popup window down N lines. 431 | 432 | See `corfu-popupinfo-scroll-up' for more details." 433 | (interactive "p") 434 | (corfu-popupinfo-scroll-up (- (or n 1)))) 435 | 436 | (defun corfu-popupinfo--toggle (fun) 437 | "Set documentation getter FUN and toggle popup." 438 | (when (< corfu--index 0) 439 | (corfu-popupinfo--hide) 440 | (user-error "No candidate selected")) 441 | (setq corfu-popupinfo--toggle 442 | (not (and (corfu-popupinfo--visible-p) 443 | (eq corfu-popupinfo--function fun)))) 444 | (if (not corfu-popupinfo--toggle) 445 | (corfu-popupinfo--hide) 446 | (setq corfu-popupinfo--function fun 447 | corfu-popupinfo--candidate nil) 448 | (let ((cand (nth corfu--index corfu--candidates))) 449 | (corfu-popupinfo--show cand) 450 | (unless (corfu-popupinfo--visible-p) 451 | (user-error "No %s available for `%s'" 452 | (car (last (split-string (symbol-name fun) "-+"))) 453 | cand))))) 454 | 455 | (defun corfu-popupinfo-documentation () 456 | "Show or hide documentation in popup. 457 | Behaves like `corfu-popupinfo-toggle'." 458 | (interactive) 459 | (corfu-popupinfo--toggle #'corfu-popupinfo--get-documentation)) 460 | 461 | (defun corfu-popupinfo-location () 462 | "Show or hide location in popup. 463 | Behaves like `corfu-popupinfo-toggle'." 464 | (interactive) 465 | (corfu-popupinfo--toggle #'corfu-popupinfo--get-location)) 466 | 467 | (defun corfu-popupinfo-toggle () 468 | "Toggle the info popup display or hide. 469 | 470 | When using this command to manually hide the info popup, it will 471 | not be displayed until this command is called again, even if 472 | `corfu-popupinfo-delay' is non-nil." 473 | (interactive) 474 | (corfu-popupinfo--toggle corfu-popupinfo--function)) 475 | 476 | ;;;###autoload 477 | (define-minor-mode corfu-popupinfo-mode 478 | "Corfu info popup minor mode." 479 | :global t :group 'corfu) 480 | 481 | (cl-defmethod corfu--exhibit :after (&context (corfu-popupinfo-mode (eql t)) &optional _auto) 482 | (when completion-in-region-mode 483 | (setf (alist-get #'corfu-popupinfo-mode minor-mode-overriding-map-alist) 484 | corfu-popupinfo-map) 485 | (when corfu-popupinfo--timer 486 | (cancel-timer corfu-popupinfo--timer) 487 | (setq corfu-popupinfo--timer nil)) 488 | (if (and (>= corfu--index 0) (corfu-popupinfo--visible-p corfu--frame)) 489 | (let ((cand (nth corfu--index corfu--candidates))) 490 | (if-let ((delay (if (consp corfu-popupinfo-delay) 491 | (funcall (if (eq corfu-popupinfo--toggle 'init) #'car #'cdr) 492 | corfu-popupinfo-delay) 493 | corfu-popupinfo-delay)) 494 | (corfu-popupinfo--toggle)) 495 | (if (or (<= delay 0) 496 | (and (corfu--equal-including-properties 497 | cand corfu-popupinfo--candidate) 498 | (corfu-popupinfo--visible-p))) 499 | (corfu-popupinfo--show cand) 500 | (when (corfu-popupinfo--visible-p) 501 | (cond 502 | (corfu-popupinfo-hide 503 | (corfu-popupinfo--hide)) 504 | (corfu-popupinfo--candidate 505 | (corfu-popupinfo--show corfu-popupinfo--candidate)))) 506 | (setq corfu-popupinfo--timer 507 | (run-at-time delay nil #'corfu-popupinfo--show cand))) 508 | (unless (corfu--equal-including-properties 509 | cand corfu-popupinfo--candidate) 510 | (corfu-popupinfo--hide)))) 511 | (corfu-popupinfo--hide)))) 512 | 513 | (cl-defmethod corfu--teardown :before (_buf &context (corfu-popupinfo-mode (eql t))) 514 | (corfu-popupinfo--hide) 515 | (cl-loop for (k . v) in corfu-popupinfo--initial-state do (set k v)) 516 | (cl-callf2 assq-delete-all #'corfu-popupinfo-mode minor-mode-overriding-map-alist)) 517 | 518 | ;; Do not show Corfu commands with M-X 519 | (dolist (sym '( corfu-popupinfo-scroll-down corfu-popupinfo-scroll-up 520 | corfu-popupinfo-documentation corfu-popupinfo-location 521 | corfu-popupinfo-beginning corfu-popupinfo-end 522 | corfu-popupinfo-toggle)) 523 | (put sym 'completion-predicate #'ignore)) 524 | 525 | (provide 'corfu-popupinfo) 526 | ;;; corfu-popupinfo.el ends here 527 | -------------------------------------------------------------------------------- /extensions/corfu-quick.el: -------------------------------------------------------------------------------- 1 | ;;; corfu-quick.el --- Quick keys for Corfu -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2022-2025 Free Software Foundation, Inc. 4 | 5 | ;; Author: Luis Henriquez-Perez , Daniel Mendler 6 | ;; Maintainer: Daniel Mendler 7 | ;; Created: 2022 8 | ;; Version: 2.2 9 | ;; Package-Requires: ((emacs "28.1") (compat "30") (corfu "2.2")) 10 | ;; URL: https://github.com/minad/corfu 11 | 12 | ;; This file is part of GNU Emacs. 13 | 14 | ;; This program is free software: you can redistribute it and/or modify 15 | ;; it under the terms of the GNU General Public License as published by 16 | ;; the Free Software Foundation, either version 3 of the License, or 17 | ;; (at your option) any later version. 18 | 19 | ;; This program is distributed in the hope that it will be useful, 20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | ;; GNU General Public License for more details. 23 | 24 | ;; You should have received a copy of the GNU General Public License 25 | ;; along with this program. If not, see . 26 | 27 | ;;; Commentary: 28 | 29 | ;; This package is a Corfu extension, which prefixes candidates with 30 | ;; quick keys. Typing these quick keys allows you to select the 31 | ;; candidate in front of them. This is designed to be a faster 32 | ;; alternative to selecting a candidate with `corfu-next' and 33 | ;; `corfu-previous'. 34 | ;; (keymap-set corfu-map "M-q" #'corfu-quick-complete) 35 | ;; (keymap-set corfu-map "C-q" #'corfu-quick-insert) 36 | 37 | ;;; Code: 38 | 39 | (require 'corfu) 40 | (eval-when-compile 41 | (require 'cl-lib)) 42 | 43 | (defcustom corfu-quick1 "asdfgh" 44 | "First level quick keys." 45 | :type 'string 46 | :group 'corfu) 47 | 48 | (defcustom corfu-quick2 "jkluionm" 49 | "Second level quick keys." 50 | :type 'string 51 | :group 'corfu) 52 | 53 | (defface corfu-quick1 54 | '((((class color) (min-colors 88) (background dark)) 55 | :background "#0050af" :foreground "white" :inherit bold) 56 | (((class color) (min-colors 88) (background light)) 57 | :background "#7feaff" :foreground "black" :inherit bold) 58 | (t :background "blue" :foreground "white" :inherit bold)) 59 | "Face used for the first quick key." 60 | :group 'corfu-faces) 61 | 62 | (defface corfu-quick2 63 | '((((class color) (min-colors 88) (background dark)) 64 | :background "#7f1f7f" :foreground "white" :inherit bold) 65 | (((class color) (min-colors 88) (background light)) 66 | :background "#ffaaff" :foreground "black" :inherit bold) 67 | (t :background "magenta" :foreground "white" :inherit bold)) 68 | "Face used for the second quick key." 69 | :group 'corfu-faces) 70 | 71 | (defun corfu-quick--keys (two idx) ;; See vertico-quick--keys 72 | "Format quick keys prefix. 73 | IDX is the current candidate index. 74 | TWO is non-nil if two keys should be displayed." 75 | (let ((fst (length corfu-quick1)) 76 | (snd (length corfu-quick2))) 77 | (if (>= idx fst) 78 | (let ((first (elt corfu-quick2 (mod (/ (- idx fst) fst) snd))) 79 | (second (elt corfu-quick1 (mod (- idx fst) fst)))) 80 | (cond 81 | ((eq first two) 82 | (list 83 | (propertize (char-to-string second) 'face 'corfu-quick1) 84 | (cons second (+ corfu--scroll idx)))) 85 | (two 86 | (list "")) 87 | (t 88 | (list 89 | (concat (propertize (char-to-string first) 'face 'corfu-quick1) 90 | (propertize (char-to-string second) 'face 'corfu-quick2)) 91 | (cons first (list first)))))) 92 | (let ((first (elt corfu-quick1 (mod idx fst)))) 93 | (if two 94 | (list "") 95 | (list 96 | (propertize (char-to-string first) 'face 'corfu-quick1) 97 | (cons first (+ corfu--scroll idx)))))))) 98 | 99 | (defun corfu-quick--read (&optional first) 100 | "Read quick key given FIRST pressed key." 101 | (cl-letf* ((list nil) 102 | (orig (symbol-function #'corfu--format-candidates)) 103 | ((symbol-function #'corfu--format-candidates) 104 | (lambda (cands) 105 | (setq cands (funcall orig cands)) 106 | (cl-loop for cand in-ref (nth 2 cands) for index from 0 do 107 | (pcase-let ((`(,keys . ,events) (corfu-quick--keys first index))) 108 | (setf list (nconc events list) 109 | cand (concat keys (substring cand (min (length cand) (length keys))))))) 110 | cands))) 111 | (corfu--candidates-popup 112 | (posn-at-point (+ (car completion-in-region--data) (length corfu--base)))) 113 | (alist-get (read-key) list))) 114 | 115 | ;;;###autoload 116 | (defun corfu-quick-jump () 117 | "Jump to candidate using quick keys." 118 | (interactive) 119 | (when (fboundp 'corfu-echo--cancel) 120 | (corfu-echo--cancel)) 121 | (if (= corfu--total 0) 122 | (and (message "No match") nil) 123 | (let ((idx (corfu-quick--read))) 124 | (when (consp idx) (setq idx (corfu-quick--read (car idx)))) 125 | (when idx (setq corfu--index idx))))) 126 | 127 | ;;;###autoload 128 | (defun corfu-quick-insert () 129 | "Insert candidate using quick keys." 130 | (interactive) 131 | (when (corfu-quick-jump) 132 | (corfu-insert))) 133 | 134 | ;;;###autoload 135 | (defun corfu-quick-complete () 136 | "Complete candidate using quick keys." 137 | (interactive) 138 | (when (corfu-quick-jump) 139 | (corfu-complete))) 140 | 141 | (provide 'corfu-quick) 142 | ;;; corfu-quick.el ends here 143 | --------------------------------------------------------------------------------