dotemacs

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

corfu-history.el (3703B)


      1 ;;; corfu-history.el --- Sorting by history for Corfu -*- lexical-binding: t -*-
      2 
      3 ;; Copyright (C) 2022  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.23"))
     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 <http://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 ;;; Code:
     35 
     36 (require 'corfu)
     37 (eval-when-compile
     38   (require 'cl-lib))
     39 
     40 (defcustom corfu-history-length nil
     41   "Corfu history length."
     42   :type '(choice (const nil) integer)
     43   :group 'corfu)
     44 
     45 (defvar corfu-history--hash nil
     46   "Hash table of Corfu candidates.")
     47 
     48 (defvar corfu-history nil
     49   "History of Corfu candidates.")
     50 
     51 (defun corfu-history--sort-predicate (x y)
     52   "Sorting predicate which compares X and Y."
     53   (pcase-let ((`(,sx . ,hx) x)
     54               (`(,sy . ,hy) y))
     55     (or (< hx hy)
     56       (and (= hx hy)
     57            (or (< (length sx) (length sy))
     58                (and (= (length sx) (length sy))
     59                     (string< sx sy)))))))
     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                               (+ (lsh (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 (defun corfu-history--insert (&rest _)
     80   "Advice for `corfu--insert'."
     81   (when (>= corfu--index 0)
     82     (add-to-history 'corfu-history
     83                     (nth corfu--index corfu--candidates)
     84                     corfu-history-length)
     85     (setq corfu-history--hash nil)))
     86 
     87 ;;;###autoload
     88 (define-minor-mode corfu-history-mode
     89   "Update Corfu history and sort completions by history."
     90   :global t
     91   :group 'corfu
     92   (cond
     93    (corfu-history-mode
     94     (setq corfu-sort-function #'corfu-history--sort)
     95     (advice-add #'corfu--insert :before #'corfu-history--insert))
     96    (t
     97     (setq corfu-sort-function #'corfu-sort-length-alpha)
     98     (advice-remove #'corfu--insert #'corfu-history--insert))))
     99 
    100 (provide 'corfu-history)
    101 ;;; corfu-history.el ends here