-
Notifications
You must be signed in to change notification settings - Fork 25
/
corfu-prescient.el
251 lines (205 loc) · 8.99 KB
/
corfu-prescient.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
;;; corfu-prescient.el --- prescient.el + Corfu -*- lexical-binding: t -*-
;; Copyright (C) 2022 Radian LLC and contributors
;; Author: Radian LLC <contact+prescient@radian.codes>
;; Homepage: https://github.com/radian-software/prescient.el
;; Keywords: extensions
;; Created: 23 Sep 2022
;; Package-Requires: ((emacs "27.1") (prescient "6.1.0") (corfu "1.1"))
;; SPDX-License-Identifier: MIT
;; Version: 6.3.1
;;; Commentary:
;; corfu-prescient.el provides an interface for using prescient.el
;; to sort and filter candidates in Corfu menus. To enable its
;; functionality, turn on `corfu-prescient-mode' in your init-file
;; or interactively.
;; For more information, see https://github.com/radian-software/prescient.el.
;;; Code:
;;;; Libraries and Declarations
(require 'cl-lib)
(require 'corfu)
(require 'prescient)
(require 'subr-x)
;; Remove references to `corfu--state-vars' once the next stable
;; version of Corfu is released:
(defvar corfu--state-vars)
(defvar corfu--initial-state)
;;;; Customization
(defgroup corfu-prescient nil
"Prescient adapter for Corfu."
:group 'convenience
:prefix "corfu-prescient"
:link '(url-link "https://github.com/radian-software/prescient.el"))
(defcustom corfu-prescient-enable-filtering t
"Whether the `prescient' completion style is used in Corfu."
:type 'boolean
:group 'corfu-prescient)
(defcustom corfu-prescient-enable-sorting t
"Whether prescient.el sorting is used in Corfu."
:type 'boolean
:group 'corfu-prescient)
(defcustom corfu-prescient-override-sorting nil
"Whether to force sorting by `corfu-prescient'.
If non-nil, then `corfu-prescient-mode' sets
`corfu-sort-override-function' to the function
`prescient-completion-sort'.
Changing this variable will not take effect until
`corfu-prescient-mode' has been reloaded."
:group 'corfu-prescient
:type 'boolean)
(defcustom corfu-prescient-completion-styles
prescient--completion-recommended-styles
"The completion styles used by `corfu-prescient-mode'."
:group 'corfu-prescient
:type '(repeat symbol))
(defcustom corfu-prescient-completion-category-overrides
prescient--completion-recommended-overrides
"The completion-category overrides used by `corfu-prescient-mode'."
:group 'corfu-prescient
:type '(repeat (cons symbol (repeat (cons symbol (repeat symbol))))))
;;;; Toggling Commmands
(defun corfu-prescient--toggle-refresh ()
"Refresh the Corfu UI.
This function is added to `prescient--toggle-refresh-functions'
by `corfu-prescient-mode'."
(when corfu--input
(setq corfu--input nil)
(corfu--update)))
;;;; Minor mode
(defvar corfu-prescient--old-sort-function nil
"Previous value of `corfu-sort-function'.")
(defvar corfu-prescient--old-sort-override-function nil
"Previous value of `corfu-sort-override-function'.")
(defvar corfu-prescient--old-toggle-binding nil
"Previous binding of `M-s' in `corfu-map'.")
(defun corfu-prescient--remember (&rest _)
"Advice for remembering candidates in Corfu."
(when (>= corfu--index 0)
(prescient-remember
(substring-no-properties
(nth corfu--index corfu--candidates)))))
(defvar-local corfu-prescient--local-settings nil
"Whether this buffer has local settings due to `corfu-prescient-mode'.")
(defun corfu-prescient--apply-completion-settings ()
"Apply the local completion settings."
(prescient--completion-make-vars-local)
(prescient--completion-save-completion-settings)
(prescient--completion-apply-completion-settings
:styles corfu-prescient-completion-styles
:overrides corfu-prescient-completion-category-overrides)
(setq corfu-prescient--local-settings t))
(defun corfu-prescient--undo-completion-settings ()
"Undo the local completion settings."
(prescient--completion-restore-completion-settings
:styles corfu-prescient-completion-styles
:overrides corfu-prescient-completion-category-overrides)
(prescient--completion-kill-local-vars)
(setq corfu-prescient--local-settings nil))
(defun corfu-prescient--change-completion-settings (&rest _)
"Apply or undo the local completion settings in `corfu-mode-hook'."
(if corfu-mode
(unless corfu-prescient--local-settings
(corfu-prescient--apply-completion-settings))
;; We need this here in case `corfu-mode' itself is disabled. We
;; also need to undo things when `corfu-prescient-mode' is
;; disabled, which happens in the mode's definition.
(when corfu-prescient--local-settings
(corfu-prescient--undo-completion-settings))))
;;;###autoload
(define-minor-mode corfu-prescient-mode
"Minor mode to use prescient.el in Corfu menus.
This mode will:
- if `corfu-prescient-override-sorting' is non-nil,
configure `corfu-sort-override-function' and set
`corfu-prescient-enable-filtering' to t
- if `corfu-prescient-enable-filtering' is non-nil,
configure `corfu-sort-function'
- if `corfu-prescient-enable-filtering' is non-nil:
- bind `prescient-toggle-map' to `M-s' in `corfu-map'
- change `completion-stlyes' to `corfu-prescient-completion-styles'
- apply `corfu-prescient-completion-category-overrides'
to `completion-category-overrides'
- set `completion-category-defaults' to nil
- advise `corfu-insert' to remember candidates"
:global t
:group 'prescient
(if corfu-prescient-mode
;; Turn on the mode.
(progn
;; Prevent messing up variables if we explicitly enable the
;; mode when it's already on.
(corfu-prescient-mode -1)
(setq corfu-prescient-mode t)
(when corfu-prescient-override-sorting
(setq corfu-prescient-enable-sorting t)
(cl-shiftf corfu-prescient--old-sort-override-function
corfu-sort-override-function
#'prescient-completion-sort))
(when corfu-prescient-enable-sorting
(cl-shiftf corfu-prescient--old-sort-function
corfu-sort-function
#'prescient-completion-sort))
(when corfu-prescient-enable-filtering
;; Configure changing settings in the hook.
(add-hook 'corfu-mode-hook
#'corfu-prescient--change-completion-settings)
;; Immediately apply the settings in buffers where
;; `corfu-mode' is already on.
(dolist (b (buffer-list))
(when (buffer-local-value corfu-mode b)
(with-current-buffer b
(corfu-prescient--apply-completion-settings))))
;; Bind toggling commands.
(setq corfu-prescient--old-toggle-binding
(lookup-key corfu-map (kbd "M-s")))
(define-key corfu-map (kbd "M-s") prescient-toggle-map)
;; Make sure Corfu refreshes immediately.
(add-hook 'prescient--toggle-refresh-functions
#'corfu-prescient--toggle-refresh)
;; Clean up the local versions of the toggling variables
;; after the Corfu pop-up closes. For the toggling vars, it
;; is the commands themselves that make the variables buffer
;; local.
(if (boundp 'corfu--state-vars)
(cl-callf cl-union corfu--state-vars prescient--toggle-vars
:test #'eq)
(cl-callf cl-union corfu--initial-state
(mapcar (lambda (k) (cons k (symbol-value k)))
prescient--toggle-vars)
:test #'eq :key #'car)))
;; While sorting might not be enabled in Corfu, it might
;; still be enabled in another UI, such as Selectrum or Vertico.
;; Therefore, we still want to remember candidates.
(advice-add 'corfu--insert :before #'corfu-prescient--remember))
;; Turn off mode.
;; Undo sorting settings.
(when (eq corfu-sort-function #'prescient-completion-sort)
(setq corfu-sort-function corfu-prescient--old-sort-function))
(when (eq corfu-sort-override-function #'prescient-completion-sort)
(setq corfu-sort-override-function
corfu-prescient--old-sort-override-function))
;; Unbind toggling commands and unhook refresh function.
(when (equal (lookup-key corfu-map (kbd "M-s"))
prescient-toggle-map)
(define-key corfu-map (kbd "M-s")
corfu-prescient--old-toggle-binding))
(remove-hook 'prescient--toggle-refresh-functions
#'corfu-prescient--toggle-refresh)
(if (boundp 'corfu--state-vars)
(cl-callf cl-set-difference corfu--state-vars
prescient--toggle-vars
:test #'eq)
(cl-callf cl-set-difference corfu--initial-state
(mapcar (lambda (k) (cons k (symbol-value k)))
prescient--toggle-vars)
:test #'eq :key #'car))
;; Undo filtering settings.
(remove-hook 'corfu-mode-hook
#'corfu-prescient--change-completion-settings)
(dolist (b (buffer-list))
(when (buffer-local-value 'corfu-prescient--local-settings b)
(with-current-buffer b
(corfu-prescient--undo-completion-settings))))
;; Undo remembrance settings.
(advice-remove 'corfu-insert #'corfu-prescient--remember)))
(provide 'corfu-prescient)
;;; corfu-prescient.el ends here