branch: externals/corfu commit bfbe9ddf991ad846f8cabe3a0338919e5b2eb255 Author: Daniel Mendler <m...@daniel-mendler.de> Commit: Daniel Mendler <m...@daniel-mendler.de>
Add corfu-history and corfu-info extensions --- corfu.el | 56 ++---------------------- extensions/corfu-history.el | 101 ++++++++++++++++++++++++++++++++++++++++++++ extensions/corfu-indexed.el | 2 +- extensions/corfu-info.el | 95 +++++++++++++++++++++++++++++++++++++++++ extensions/corfu-quick.el | 36 ++++++++-------- 5 files changed, 218 insertions(+), 72 deletions(-) diff --git a/corfu.el b/corfu.el index 403efa44e6..5daf7043ba 100644 --- a/corfu.el +++ b/corfu.el @@ -240,8 +240,8 @@ The completion backend can override this with (define-key map "\C-g" #'corfu-quit) (define-key map "\r" #'corfu-insert) (define-key map "\t" #'corfu-complete) - (define-key map "\eg" #'corfu-show-location) - (define-key map "\eh" #'corfu-show-documentation) + (define-key map "\eg" 'corfu-info-location) + (define-key map "\eh" 'corfu-info-documentation) (define-key map (concat "\e" " ") #'corfu-insert-separator) ;; Avoid ugly warning map) "Corfu keymap used when popup is shown.") @@ -949,56 +949,6 @@ See `corfu-separator' for more details." (interactive) (corfu--goto (1- corfu--total))) -(defun corfu--restore-on-next-command () - "Restore window configuration before next command." - (let ((config (current-window-configuration)) - (other other-window-scroll-buffer) - (restore (make-symbol "corfu--restore"))) - (fset restore - (lambda () - (setq other-window-scroll-buffer other) - (unless (memq this-command '(scroll-other-window scroll-other-window-down)) - (when (memq this-command '(corfu-quit corfu-reset)) - (setq this-command #'ignore)) - (remove-hook 'pre-command-hook restore) - (set-window-configuration config)))) - (add-hook 'pre-command-hook restore))) - -;; Company support, taken from `company.el', see `company-show-doc-buffer'. -(defun corfu-show-documentation () - "Show documentation of current candidate." - (interactive) - (when (< corfu--index 0) - (user-error "No candidate selected")) - (if-let* ((fun (plist-get corfu--extra :company-doc-buffer)) - (res (funcall fun (nth corfu--index corfu--candidates)))) - (let ((buf (or (car-safe res) res))) - (corfu--restore-on-next-command) - (setq other-window-scroll-buffer (get-buffer buf)) - (set-window-start (display-buffer buf t) (or (cdr-safe res) (point-min)))) - (user-error "No documentation available"))) - -;; Company support, taken from `company.el', see `company-show-location'. -(defun corfu-show-location () - "Show location of current candidate." - (interactive) - (when (< corfu--index 0) - (user-error "No candidate selected")) - (if-let* ((fun (plist-get corfu--extra :company-location)) - (loc (funcall fun (nth corfu--index corfu--candidates)))) - (let ((buf (or (and (bufferp (car loc)) (car loc)) (find-file-noselect (car loc) t)))) - (corfu--restore-on-next-command) - (setq other-window-scroll-buffer buf) - (with-selected-window (display-buffer buf t) - (save-restriction - (widen) - (if (bufferp (car loc)) - (goto-char (cdr loc)) - (goto-char (point-min)) - (forward-line (1- (cdr loc)))) - (set-window-start nil (point))))) - (user-error "No candidate location available"))) - (defun corfu-complete () "Try to complete current input. If a candidate is selected, insert it." @@ -1273,7 +1223,7 @@ The ORIG function takes the FUN and WHICH arguments." ;; Emacs 28: Do not show Corfu commands with M-X (dolist (sym '(corfu-next corfu-previous corfu-first corfu-last corfu-quit corfu-reset corfu-complete corfu-insert corfu-scroll-up corfu-scroll-down - corfu-show-location corfu-show-documentation corfu-insert-separator)) + corfu-insert-separator)) (put sym 'completion-predicate #'ignore)) (provide 'corfu) diff --git a/extensions/corfu-history.el b/extensions/corfu-history.el new file mode 100644 index 0000000000..ff90779671 --- /dev/null +++ b/extensions/corfu-history.el @@ -0,0 +1,101 @@ +;;; corfu-history.el --- Sorting by history for Corfu -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; Author: Daniel Mendler <m...@daniel-mendler.de> +;; Maintainer: Daniel Mendler <m...@daniel-mendler.de> +;; Created: 2021 +;; Version: 0.1 +;; Package-Requires: ((emacs "27.1") (corfu "0.21")) +;; Homepage: https://github.com/minad/corfu + +;; This file is part of GNU Emacs. + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Sort candidates by their history position. Maintain a list of +;; recently selected candidates. + +;;; Code: + +(require 'corfu) + +(defcustom corfu-history-length nil + "Corfu history length." + :type '(choice (const nil) integer) + :group 'corfu) + +(defvar corfu-history--hash nil + "Hash table of Corfu candidates.") + +(defvar corfu-history nil + "History of Corfu candidates.") + +(defun corfu-history--sort-predicate (x y) + "Sorting predicate which compares X and Y." + (or (< (cdr x) (cdr y)) + (and (= (cdr x) (cdr y)) + (string< (car x) (car y))))) + +(defun corfu-history--sort (candidates) + "Sort CANDIDATES by history." + (unless corfu-history--hash + (let ((index 0)) + (setq corfu-history--hash (make-hash-table :test #'equal :size (length corfu-history))) + (dolist (elt corfu-history) + (unless (gethash elt corfu-history--hash) + (puthash elt index corfu-history--hash)) + (setq index (1+ index))))) + ;; Decorate each candidate with (index<<13) + length. This way we sort first by index and then by + ;; length. We assume that the candidates are shorter than 2**13 characters and that the history is + ;; shorter than 2**16 entries. + (let ((cand candidates)) + (while cand + (setcar cand (cons (car cand) + (+ (lsh (gethash (car cand) corfu-history--hash #xFFFF) 13) + (length (car cand))))) + (pop cand))) + (setq candidates (sort candidates #'corfu-history--sort-predicate)) + ;; Drop decoration from the candidates + (let ((cand candidates)) + (while cand + (setcar cand (caar cand)) + (pop cand))) + candidates) + +(defun corfu-history--insert (&rest _) + "Advice for `corfu--insert'." + (when (>= corfu--index 0) + (add-to-history 'corfu-history + (nth corfu--index corfu--candidates) + corfu-history-length) + (setq corfu-history--hash nil))) + +;;;###autoload +(define-minor-mode corfu-history-mode + "Update Corfu history and sort completions by history." + :global t + :group 'corfu + (cond + (corfu-history-mode + (setq corfu-sort-function #'corfu-history--sort) + (advice-add #'corfu--insert :before #'corfu-history--insert)) + (t + (setq corfu-sort-function #'corfu-sort-length-alpha) + (advice-remove #'corfu--insert #'corfu-history--insert)))) + +(provide 'corfu-history) +;;; corfu-history.el ends here diff --git a/extensions/corfu-indexed.el b/extensions/corfu-indexed.el index c88c23486d..19cf876574 100644 --- a/extensions/corfu-indexed.el +++ b/extensions/corfu-indexed.el @@ -6,7 +6,7 @@ ;; Maintainer: Daniel Mendler <m...@daniel-mendler.de> ;; Created: 2022 ;; Version: 0.1 -;; Package-Requires: ((emacs "27.1")) +;; Package-Requires: ((emacs "27.1") (corfu "0.21")) ;; Homepage: https://github.com/minad/corfu ;; This file is part of GNU Emacs. diff --git a/extensions/corfu-info.el b/extensions/corfu-info.el new file mode 100644 index 0000000000..1662a916d7 --- /dev/null +++ b/extensions/corfu-info.el @@ -0,0 +1,95 @@ +;;; corfu-info.el --- Show candidate information in separate buffer -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; Author: Daniel Mendler <m...@daniel-mendler.de> +;; Maintainer: Daniel Mendler <m...@daniel-mendler.de> +;; Created: 2021 +;; Version: 0.1 +;; Package-Requires: ((emacs "27.1") (corfu "0.21")) +;; Homepage: https://github.com/minad/corfu + +;; This file is part of GNU Emacs. + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This Corfu extension provides commands to show additional information +;; to the candidates in a separate buffer. + +;;; Code: + +(require 'corfu) +(eval-when-compile + (require 'subr-x)) + +(defun corfu-info--restore-on-next-command () + "Restore window configuration before next command." + (let ((config (current-window-configuration)) + (other other-window-scroll-buffer) + (restore (make-symbol "corfu--restore"))) + (fset restore + (lambda () + (setq other-window-scroll-buffer other) + (unless (memq this-command '(scroll-other-window scroll-other-window-down)) + (when (memq this-command '(corfu-quit corfu-reset)) + (setq this-command #'ignore)) + (remove-hook 'pre-command-hook restore) + (set-window-configuration config)))) + (add-hook 'pre-command-hook restore))) + +;;;###autoload +(defun corfu-info-documentation () + "Show documentation of current candidate." + (interactive) + ;; Company support, taken from `company.el', see `company-show-doc-buffer'. + (when (< corfu--index 0) + (user-error "No candidate selected")) + (if-let* ((fun (plist-get corfu--extra :company-doc-buffer)) + (res (funcall fun (nth corfu--index corfu--candidates)))) + (let ((buf (or (car-safe res) res))) + (corfu-info--restore-on-next-command) + (setq other-window-scroll-buffer (get-buffer buf)) + (set-window-start (display-buffer buf t) (or (cdr-safe res) (point-min)))) + (user-error "No documentation available"))) + +;;;###autoload +(defun corfu-info-location () + "Show location of current candidate." + (interactive) + ;; Company support, taken from `company.el', see `company-show-location'. + (when (< corfu--index 0) + (user-error "No candidate selected")) + (if-let* ((fun (plist-get corfu--extra :company-location)) + (loc (funcall fun (nth corfu--index corfu--candidates)))) + (let ((buf (or (and (bufferp (car loc)) (car loc)) (find-file-noselect (car loc) t)))) + (corfu-info--restore-on-next-command) + (setq other-window-scroll-buffer buf) + (with-selected-window (display-buffer buf t) + (save-restriction + (widen) + (if (bufferp (car loc)) + (goto-char (cdr loc)) + (goto-char (point-min)) + (forward-line (1- (cdr loc)))) + (set-window-start nil (point))))) + (user-error "No candidate location available"))) + +;; Emacs 28: Do not show Corfu commands with M-X +(put #'corfu-info-location 'completion-predicate #'ignore) +(put #'corfu-info-documentation 'completion-predicate #'ignore) + +(provide 'corfu-info) +;;; corfu-info.el ends here diff --git a/extensions/corfu-quick.el b/extensions/corfu-quick.el index 5dbacd42f2..7c824904ee 100644 --- a/extensions/corfu-quick.el +++ b/extensions/corfu-quick.el @@ -2,11 +2,11 @@ ;; Copyright (C) 2021 Free Software Foundation, Inc. -;; Author: Luis Henriquez-Perez <l...@luishp.xyz> +;; Author: Luis Henriquez-Perez <l...@luishp.xyz>, Daniel Mendler <m...@daniel-mendler.de> ;; Maintainer: Daniel Mendler <m...@daniel-mendler.de> ;; Created: 2022 -;; Version: 0.19 -;; Package-Requires: ((emacs "27.1")) +;; Version: 0.1 +;; Package-Requires: ((emacs "27.1") (corfu "0.21")) ;; Homepage: https://github.com/minad/corfu ;; This file is part of GNU Emacs. @@ -72,22 +72,22 @@ the candidate that corresponds to QUICK-KEYS.") (defun corfu-quick--keys (index) "Return `corfu-quick' keys for candidate at INDEX." (let ((length1 (seq-length corfu-quick1)) - (length2 (seq-length corfu-quick2)) - (key1 "") - (key2 "")) + (length2 (seq-length corfu-quick2)) + (key1 "") + (key2 "")) (if (< index length1) - (setq key1 (char-to-string (seq-elt corfu-quick1 index))) + (setq key1 (char-to-string (seq-elt corfu-quick1 index))) (setq key1 (char-to-string (seq-elt corfu-quick1 (% (- index length1) length1)))) (setq key2 (char-to-string (seq-elt corfu-quick2 (% (- index length1) length2))))) (concat (propertize key2 'face 'corfu-quick1) - (propertize key1 'face 'corfu-quick2)))) + (propertize key1 'face 'corfu-quick2)))) (defun corfu-quick--format-candidates (orig candidates) "Advice for `corfu--format-candidates' that adds quick keys to candidates. See `corfu--format-candidates'." (let ((updated-candidates nil) - (quick-keys nil) - (index 0)) + (quick-keys nil) + (index 0)) (setq corfu-quick--alist nil) (pcase-dolist (`(,candidate ,prefix ,suffix) candidates) (setq quick-keys (corfu-quick--keys index)) @@ -100,18 +100,18 @@ See `corfu--format-candidates'." (defun corfu-quick--read () "Read quick keys and return index of candidate specified by quick keys." (cl-letf* ((old-fn (symbol-function #'corfu--format-candidates)) - (new-fn (apply-partially #'corfu-quick--format-candidates old-fn)) - ((symbol-function #'corfu--format-candidates) new-fn)) + (new-fn (apply-partially #'corfu-quick--format-candidates old-fn)) + ((symbol-function #'corfu--format-candidates) new-fn)) (corfu--candidates-popup (point)) (let* ((key (read-key)) - (quick-keys (char-to-string key))) + (quick-keys (char-to-string key))) (when (seq-contains-p corfu-quick2 key) - (cl-letf* ((orig-fn (symbol-function #'corfu-quick--keys)) - ((symbol-function #'corfu-quick--keys) (lambda (index) (seq-rest (funcall orig-fn index))))) - (corfu--candidates-popup (point))) - (setq quick-keys (char-to-string (read-key)))) + (cl-letf* ((orig-fn (symbol-function #'corfu-quick--keys)) + ((symbol-function #'corfu-quick--keys) (lambda (index) (seq-rest (funcall orig-fn index))))) + (corfu--candidates-popup (point))) + (setq quick-keys (char-to-string (read-key)))) (or (alist-get quick-keys corfu-quick--alist 0 nil #'string=) - (corfu-quick-exit))))) + (corfu-quick-exit))))) ;;;###autoload (defun corfu-quick-jump ()