dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

corfu-history.el (3644B)


      1 ;;; corfu-history.el --- Sorting by history for Corfu -*- lexical-binding: t -*-
      2 
      3 ;; Copyright (C) 2022-2023 Free Software Foundation, Inc.
      4 
      5 ;; Author: Daniel Mendler <mail@daniel-mendler.de>
      6 ;; Maintainer: Daniel Mendler <mail@daniel-mendler.de>
      7 ;; Created: 2022
      8 ;; Version: 0.1
      9 ;; Package-Requires: ((emacs "27.1") (corfu "0.36"))
     10 ;; Homepage: 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 <https://www.gnu.org/licenses/>.
     26 
     27 ;;; Commentary:
     28 
     29 ;; Enable `corfu-history-mode' to sort candidates by their history
     30 ;; position.  Maintain a list of recently selected candidates.  In order
     31 ;; to save the history across Emacs sessions, enable `savehist-mode' and
     32 ;; add `corfu-history' to `savehist-additional-variables'.
     33 ;;
     34 ;; (corfu-history-mode 1)
     35 ;; (savehist-mode 1)
     36 ;; (add-to-list 'savehist-additional-variables 'corfu-history)
     37 
     38 ;;; Code:
     39 
     40 (require 'corfu)
     41 (eval-when-compile
     42   (require 'cl-lib))
     43 
     44 (defcustom corfu-history-length nil
     45   "Corfu history length."
     46   :type '(choice (const nil) natnum)
     47   :group 'corfu)
     48 
     49 (defvar corfu-history--hash nil
     50   "Hash table of Corfu candidates.")
     51 
     52 (defvar corfu-history nil
     53   "History of Corfu candidates.")
     54 
     55 (defun corfu-history--sort-predicate (x y)
     56   "Sorting predicate which compares X and Y."
     57   (or (< (cdr x) (cdr y))
     58       (and (= (cdr x) (cdr y))
     59            (corfu--length-string< (car x) (car y)))))
     60 
     61 (defun corfu-history--sort (candidates)
     62   "Sort CANDIDATES by history."
     63   (unless corfu-history--hash
     64     (setq corfu-history--hash (make-hash-table :test #'equal :size (length corfu-history)))
     65     (cl-loop for elem in corfu-history for index from 0 do
     66              (unless (gethash elem corfu-history--hash)
     67                (puthash elem index corfu-history--hash))))
     68   ;; Decorate each candidate with (index<<13) + length. This way we sort first by index and then by
     69   ;; length. We assume that the candidates are shorter than 2**13 characters and that the history is
     70   ;; shorter than 2**16 entries.
     71   (cl-loop for cand on candidates do
     72            (setcar cand (cons (car cand)
     73                               (+ (ash (gethash (car cand) corfu-history--hash #xFFFF) 13)
     74                                  (length (car cand))))))
     75   (setq candidates (sort candidates #'corfu-history--sort-predicate))
     76   (cl-loop for cand on candidates do (setcar cand (caar cand)))
     77   candidates)
     78 
     79 ;;;###autoload
     80 (define-minor-mode corfu-history-mode
     81   "Update Corfu history and sort completions by history."
     82   :global t :group 'corfu
     83   (if corfu-history-mode
     84       (add-function :override corfu-sort-function #'corfu-history--sort)
     85     (remove-function corfu-sort-function #'corfu-history--sort)))
     86 
     87 (cl-defmethod corfu--insert :before (_status &context (corfu-history-mode (eql t)))
     88   (when (>= corfu--index 0)
     89     (add-to-history 'corfu-history
     90                     (substring-no-properties
     91                      (nth corfu--index corfu--candidates))
     92                     corfu-history-length)
     93     (setq corfu-history--hash nil)))
     94 
     95 (provide 'corfu-history)
     96 ;;; corfu-history.el ends here