branch: externals/corfu
commit 435787cb60d5de71c5cf63578075f07ded3bdc9c
Author: Daniel Mendler <m...@daniel-mendler.de>
Commit: Daniel Mendler <m...@daniel-mendler.de>

    Implement auto completion
---
 README.org | 52 ++++++++++++++++++++++------------------------------
 corfu.el   | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 81 insertions(+), 33 deletions(-)

diff --git a/README.org b/README.org
index adf274f..48491a3 100644
--- a/README.org
+++ b/README.org
@@ -16,14 +16,13 @@ overlay. The current candidates are shown in a popup below 
or above the point.
 Corfu can be considered the minimalistic ~completion-in-region~ counterpart of
 the [[https://github.com/minad/vertico][Vertico]] minibuffer UI.
 
-Corfu is a minimal package (~600 lines of code without whitespace and 
comments).
-In contrast to the featureful and complex Company package, Corfu concentrates 
on
-the completion UI and does not include custom completion backends. Completions
-are either provided by commands like ~dabbrev-completion~ or by pluggable
-backends (~completion-at-point-functions~, Capfs). Many programming language
-major modes implement a Capf. Furthermore the language server packages, 
[[https://github.com/joaotavora/eglot][Eglot]]
-and [[https://github.com/emacs-lsp/lsp-mode][Lsp-mode]], both use Capfs which 
talk to the LSP server to retrieve the
-completions.
+Corfu is a minimal package. In contrast to the featureful and complex Company
+package, Corfu concentrates on the completion UI and does not include custom
+completion backends. Completions are either provided by commands like
+~dabbrev-completion~ or by pluggable backends (~completion-at-point-functions~,
+Capfs). Many programming language major modes implement a Capf. Furthermore the
+language server packages, [[https://github.com/joaotavora/eglot][Eglot]] and 
[[https://github.com/emacs-lsp/lsp-mode][Lsp-mode]], both use Capfs which talk 
to the
+LSP server to retrieve the completions.
 
 *NOTE*: Corfu uses child frames to show the popup; on non-graphical displays it
 will fall back to the default setting of the ~completion-in-region-function~.
@@ -32,6 +31,7 @@ will fall back to the default setting of the 
~completion-in-region-function~.
 
 * Features
 
+- Timer-based auto-completions
 - Popup display with scrollbar indicator and arrow key navigation
 - The popup must be summoned explicitly by pressing =TAB=
 - The current candidate is inserted with =TAB= and selected with =RET=
@@ -44,9 +44,6 @@ will fall back to the default setting of the 
~completion-in-region-function~.
 - Jumping to location/documentation of current candidate (Company extension)
 - Support for ~annotation-function~ and ~affixation-function~
 
-Notable non-feature: Timer-based auto-completions are not supported. There 
exists
-an experimental implementation in the branch 
[[https://github.com/minad/corfu/tree/auto][auto]].
-
 * Configuration
 
 Corfu is available from [[http://elpa.gnu.org/packages/corfu.html][GNU ELPA]], 
such that it can be installed directly via
@@ -60,6 +57,13 @@ Orderless is not a necessity. Here is an example 
configuration:
 #+begin_src emacs-lisp
   ;; Configure corfu
   (use-package corfu
+    ;; Optional customizations
+    ;; :custom
+    ;; (corfu-cycle t)            ;; Enable cycling for `corfu-next/previous'
+    ;; (corfu-auto t)             ;; Enable auto completion
+    ;; (corfu-quit-at-boundary t) ;; Automatically quit at word boundary
+    ;; (corfu-quit-no-match t)    ;; Automatically quit if there is no match
+
     ;; Optionally use TAB for cycling, default is `corfu-complete'.
     ;; :bind (:map corfu-map
     ;;        ("TAB" . corfu-next)
@@ -70,17 +74,10 @@ Orderless is not a necessity. Here is an example 
configuration:
     ;;        (shell-mode . corfu-mode)
     ;;        (eshell-mode . corfu-mode))
 
-
     ;; Recommended: Enable Corfu globally.
     ;; This is recommended since dabbrev can be used globally (M-/).
     :init
-    (corfu-global-mode)
-
-    :config
-
-    ;; Optionally enable cycling for `corfu-next' and `corfu-previous'.
-    ;; (setq corfu-cycle t)
-  )
+    (corfu-global-mode))
 
   ;; Optionally use the `orderless' completion style.
   ;; Enable `partial-completion' for files to allow path expansion.
@@ -139,7 +136,6 @@ counterpart of Corfu.
 
 Corfu works in most scenarios. However there are a few known technical caveats.
 
-- No support for idle completions (See branch =auto= for a possible 
implementation).
 - Corfu falls back to the default ~completion-in-region-function~ on
   non-graphical displays, since is displayed using child frames.
 - The abort handling could be improved, for example the input could be undone.
@@ -151,16 +147,12 @@ Corfu works in most scenarios. However there are a few 
known technical caveats.
 
 * Alternative packages
 
-The main alternative is the featureful 
[[https://github.com/company-mode/company-mode][Company]] package. In 
particular, Company
-supports auto completion as you type. As of now, auto completion is not
-supported by Corfu. Furthermore Company has support for multiple completion
-backends.
-
-The Emacs builtin Icomplete is technically comparable to Corfu and Vertico.
-Icomplete implements both ~completion-in-region~ and minibuffer completion in a
-single package. Corfu and Vertico are two separate packages in order to 
optimize
-the UI for the two distinct use cases, also leading to code bases which are
-easier to understand.
+The main alternative is the featureful and complex 
[[https://github.com/company-mode/company-mode][Company]] package. The Emacs
+builtin Icomplete is technically comparable to Corfu and Vertico. Icomplete
+implements both ~completion-in-region~ and minibuffer completion in a single
+package. Corfu and Vertico are two separate packages in order to optimize the 
UI
+for the two distinct use cases, also leading to code bases which are easier to
+understand.
 
 * Contributions
 
diff --git a/corfu.el b/corfu.el
index a39b716..d1ede4a 100644
--- a/corfu.el
+++ b/corfu.el
@@ -80,6 +80,18 @@ filter string with spaces is allowed."
   "Width of the bar in units of the character width."
   :type 'float)
 
+(defcustom corfu-auto-prefix 3
+  "Minimum length of prefix for auto completion."
+  :type 'integer)
+
+(defcustom corfu-auto-delay 0.2
+  "Delay for auto completion."
+  :type 'float)
+
+(defcustom corfu-auto nil
+  "Enable auto completion."
+  :type 'boolean)
+
 (defgroup corfu-faces nil
   "Faces used by Corfu."
   :group 'corfu
@@ -135,6 +147,9 @@ filter string with spaces is allowed."
     map)
   "Corfu keymap used when popup is shown.")
 
+(defvar corfu--auto-timer nil
+  "Auto completion timer.")
+
 (defvar-local corfu--candidates nil
   "List of candidates.")
 
@@ -162,6 +177,10 @@ filter string with spaces is allowed."
 (defvar corfu--frame nil
   "Popup frame.")
 
+(defvar corfu--auto-commands
+  "\\`\\(.*self-insert-command\\)\\'"
+  "Commands which initiate auto completion.")
+
 (defvar corfu--continue-commands
   ;; nil is undefined command
   "\\`\\(nil\\|completion-at-point\\|corfu-.*\\|scroll-other-window.*\\)\\'"
@@ -705,10 +724,10 @@ filter string with spaces is allowed."
       ;; XXX Warning this can result in an endless loop when 
`completion-in-region-function'
       ;; is set *globally* to `corfu--completion-in-region'. This should never 
happen.
       (apply (default-value 'completion-in-region-function) args)
-    ;; Prevent restarting the completion. This can happen for example if C-M-/
+    ;; Restart the completion. This can happen for example if C-M-/
     ;; (`dabbrev-completion') is pressed while the Corfu popup is already open.
     (when (and completion-in-region-mode (not completion-cycling))
-      (user-error "Completion is already in progress"))
+      (corfu-quit))
     (let ((completion-show-inline-help)
           (completion-auto-help)
           ;; XXX Disable original predicate check, keep completion alive when
@@ -721,12 +740,49 @@ filter string with spaces is allowed."
       (prog1 (apply #'completion--in-region args)
         (corfu--setup)))))
 
+(defun corfu--auto-complete (buffer)
+  "Initiate auto completion after delay in BUFFER."
+  (setq corfu--auto-timer nil)
+  (when (and (not completion-in-region-mode)
+             (eq (current-buffer) buffer))
+    (pcase (run-hook-wrapped 'completion-at-point-functions
+                             #'completion--capf-wrapper 'all)
+      ((and `(,fun ,beg ,end ,table . ,plist) (guard (>= (- end beg) 
corfu-auto-prefix)))
+       (let ((completion-extra-properties plist)
+             (completion-in-region-mode-predicate
+              (if corfu-quit-at-boundary
+                 (lambda ()
+                   (when-let (newbeg (car-safe (funcall fun)))
+                     (= newbeg beg)))
+                (lambda () t))))
+         (setq completion-in-region--data `(,(copy-marker beg) ,(copy-marker 
end t)
+                                            ,table ,(plist-get plist 
:predicate)))
+         (completion-in-region-mode 1)
+         (corfu--setup)
+         (corfu--post-command))))))
+
+(defun corfu--auto-post-command ()
+  "Post command hook which initiates auto completion."
+  (when corfu--auto-timer
+    (cancel-timer corfu--auto-timer)
+    (setq corfu--auto-timer nil))
+  (when (and (not completion-in-region-mode)
+             (display-graphic-p)
+             (symbolp this-command)
+             (string-match-p corfu--auto-commands (symbol-name this-command)))
+    (setq corfu--auto-timer (run-with-idle-timer corfu-auto-delay nil
+                                                 #'corfu--auto-complete
+                                                 (current-buffer)))))
+
 ;;;###autoload
 (define-minor-mode corfu-mode
   "Completion Overlay Region FUnction"
   :global nil
   (if corfu-mode
-      (setq-local completion-in-region-function #'corfu--completion-in-region)
+      (progn
+        (and corfu-auto (add-hook 'post-command-hook 
#'corfu--auto-post-command nil 'local))
+        (setq-local completion-in-region-function 
#'corfu--completion-in-region))
+    (remove-hook 'post-command-hook #'corfu--auto-post-command 'local)
     (kill-local-variable 'completion-in-region-function)))
 
 ;;;###autoload

Reply via email to