Skip to content

Commit 728e52d

Browse files
committed
Implement deferred highlighting for all completion styles
Every completion style needs a small bit of special code. Fortunately `completion-hilit-commonality` and `completion-pcm--hilit-commonality` are shared by multiple styles.
1 parent 7240bc9 commit 728e52d

File tree

1 file changed

+47
-34
lines changed

1 file changed

+47
-34
lines changed

vertico.el

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
(require 'seq)
3838
(eval-when-compile
39+
(require 'cl-lib)
3940
(require 'subr-x))
4041

4142
(defgroup vertico nil
@@ -105,6 +106,9 @@
105106
map)
106107
"Minibuffer keymap.")
107108

109+
(defvar-local vertico--highlight #'identity
110+
"Deferred candidate highlighting function.")
111+
108112
(defvar-local vertico--history-hash nil
109113
"History hash table.")
110114

@@ -211,21 +215,6 @@
211215
(mapcar (lambda (cand) (list cand (or (funcall ann cand) ""))) candidates)
212216
candidates)))
213217

214-
(defvar orderless-skip-highlighting)
215-
(defun vertico--highlight (input metadata candidates)
216-
"Highlight CANDIDATES with INPUT using the completion style specified by METADATA."
217-
(let* ((orderless-skip-highlighting)
218-
(highlighted (nconc
219-
(completion-all-completions input
220-
candidates
221-
nil
222-
(length input)
223-
metadata)
224-
nil)))
225-
;; Check if everything went alright, all the candidates should still be present.
226-
(if (= (length highlighted) (length candidates))
227-
highlighted candidates)))
228-
229218
(defun vertico--move-to-front (elem list)
230219
"Move ELEM to front of LIST."
231220
(if-let (found (member elem list))
@@ -242,20 +231,44 @@
242231
(lambda (x) (and (not (string-match-p ignore x)) (funcall pred x)))
243232
(lambda (x) (not (string-match-p ignore x))))))
244233

234+
(declare-function orderless-highlight-matches "ext:orderless")
235+
(defun vertico--all-completions (&rest args)
236+
"Compute all completions for ARGS with deferred highlighting."
237+
(cl-letf* ((orig-pcm (symbol-function #'completion-pcm--hilit-commonality))
238+
(orig-flex (symbol-function #'completion-flex-all-completions))
239+
((symbol-function #'completion-flex-all-completions)
240+
(lambda (&rest args)
241+
;; Unfortunately for flex we have to undo the deferred highlighting, since flex uses
242+
;; the completion-score for sorting, which is applied during highlighting.
243+
(cl-letf (((symbol-function #'completion-pcm--hilit-commonality) orig-pcm))
244+
(apply orig-flex args))))
245+
;; Defer the following highlighting functions
246+
(hl #'identity)
247+
((symbol-function #'completion-hilit-commonality)
248+
(lambda (cands prefix &optional base)
249+
(setq hl (lambda (x) (completion-hilit-commonality x prefix base)))
250+
cands))
251+
((symbol-function #'completion-pcm--hilit-commonality)
252+
(lambda (pattern cands)
253+
(setq hl (lambda (x) (completion-pcm--hilit-commonality pattern x)))
254+
cands))
255+
((symbol-function #'orderless-highlight-matches)
256+
(lambda (pattern cands)
257+
(setq hl (lambda (x) (orderless-highlight-matches pattern x)))
258+
cands)))
259+
(cons (apply #'completion-all-completions args) hl)))
260+
245261
(defun vertico--recompute-candidates (pt content bounds metadata)
246262
"Recompute candidates given PT, CONTENT, BOUNDS and METADATA."
247263
(let* ((field (substring content (car bounds) (+ pt (cdr bounds))))
248-
(all (completion-all-completions
249-
content
250-
minibuffer-completion-table
251-
(if minibuffer-completing-file-name
252-
(vertico--file-predicate)
253-
minibuffer-completion-predicate)
254-
pt metadata))
255-
(base (if-let (last (last all))
256-
(prog1 (cdr last)
257-
(setcdr last nil))
258-
0))
264+
(all-hl (vertico--all-completions content
265+
minibuffer-completion-table
266+
(if minibuffer-completing-file-name
267+
(vertico--file-predicate)
268+
minibuffer-completion-predicate)
269+
pt metadata))
270+
(all (car all-hl))
271+
(base (if-let (last (last all)) (prog1 (cdr last) (setcdr last nil)) 0))
259272
(def (or (car-safe minibuffer-default) minibuffer-default))
260273
(total (length all)))
261274
(when (<= total vertico-sort-threshold)
@@ -270,14 +283,14 @@
270283
(setq all (vertico--move-to-front field all))
271284
(when-let (group (completion-metadata-get metadata 'x-group-function))
272285
(setq all (mapcan #'cdr (funcall group all))))
273-
(list base total all)))
286+
(list base total all (cdr all-hl))))
274287

275288
(defun vertico--update-candidates (pt content bounds metadata)
276289
"Preprocess candidates given PT, CONTENT, BOUNDS and METADATA."
277290
(pcase (let ((while-no-input-ignore-events '(selection-request)))
278291
(while-no-input (vertico--recompute-candidates pt content bounds metadata)))
279292
('nil (abort-recursive-edit))
280-
(`(,base ,total ,candidates)
293+
(`(,base ,total ,candidates ,hl)
281294
(unless (and vertico--keep (< vertico--index 0))
282295
(if-let* ((old (and candidates
283296
vertico--keep
@@ -298,6 +311,7 @@
298311
(setq vertico--input (cons content pt)
299312
vertico--base base
300313
vertico--total total
314+
vertico--highlight hl
301315
vertico--candidates candidates))))
302316

303317
(defun vertico--flatten-string (prop str)
@@ -312,16 +326,16 @@
312326
(setq pos next)))
313327
(apply #'concat (nreverse chunks))))
314328

315-
(defun vertico--format-candidates (content bounds metadata)
316-
"Format current candidates with CONTENT string, BOUNDS and METADATA."
329+
(defun vertico--format-candidates (metadata)
330+
"Format current candidates with METADATA."
317331
(let* ((group (completion-metadata-get metadata 'x-group-function))
318332
(group-format (and group vertico-group-format (concat vertico-group-format "\n")))
319333
(index (min (max 0 (- vertico--index (/ vertico-count 2) (if group-format -1 0)))
320334
(max 0 (- vertico--total vertico-count))))
321335
(candidates
322336
(thread-last (seq-subseq vertico--candidates index
323337
(min (+ index vertico-count) vertico--total))
324-
(vertico--highlight (substring content (car bounds)) metadata)
338+
(funcall vertico--highlight)
325339
(vertico--annotate metadata)))
326340
(max-width (- (window-width) 4))
327341
(current-line 0) (title) (lines))
@@ -434,7 +448,7 @@
434448
(vertico--update-candidates pt content bounds metadata))
435449
(vertico--prompt-selection)
436450
(vertico--display-count)
437-
(vertico--display-candidates (vertico--format-candidates content bounds metadata))))
451+
(vertico--display-candidates (vertico--format-candidates metadata))))
438452

439453
(defun vertico--require-match ()
440454
"Return t if match is required."
@@ -525,8 +539,7 @@
525539
(setq vertico--input t
526540
vertico--candidates-ov (make-overlay (point-max) (point-max) nil t t)
527541
vertico--count-ov (make-overlay (point-min) (point-min) nil t t))
528-
(setq-local orderless-skip-highlighting t ;; Orderless optimization
529-
resize-mini-windows 'grow-only
542+
(setq-local resize-mini-windows 'grow-only
530543
truncate-lines t
531544
max-mini-window-height 1.0)
532545
(use-local-map vertico-map)

0 commit comments

Comments
 (0)