|
36 | 36 |
|
37 | 37 | (require 'seq)
|
38 | 38 | (eval-when-compile
|
| 39 | + (require 'cl-lib) |
39 | 40 | (require 'subr-x))
|
40 | 41 |
|
41 | 42 | (defgroup vertico nil
|
|
105 | 106 | map)
|
106 | 107 | "Minibuffer keymap.")
|
107 | 108 |
|
| 109 | +(defvar-local vertico--highlight #'identity |
| 110 | + "Deferred candidate highlighting function.") |
| 111 | + |
108 | 112 | (defvar-local vertico--history-hash nil
|
109 | 113 | "History hash table.")
|
110 | 114 |
|
|
211 | 215 | (mapcar (lambda (cand) (list cand (or (funcall ann cand) ""))) candidates)
|
212 | 216 | candidates)))
|
213 | 217 |
|
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 |
| - |
229 | 218 | (defun vertico--move-to-front (elem list)
|
230 | 219 | "Move ELEM to front of LIST."
|
231 | 220 | (if-let (found (member elem list))
|
|
242 | 231 | (lambda (x) (and (not (string-match-p ignore x)) (funcall pred x)))
|
243 | 232 | (lambda (x) (not (string-match-p ignore x))))))
|
244 | 233 |
|
| 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 | + |
245 | 261 | (defun vertico--recompute-candidates (pt content bounds metadata)
|
246 | 262 | "Recompute candidates given PT, CONTENT, BOUNDS and METADATA."
|
247 | 263 | (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)) |
259 | 272 | (def (or (car-safe minibuffer-default) minibuffer-default))
|
260 | 273 | (total (length all)))
|
261 | 274 | (when (<= total vertico-sort-threshold)
|
|
270 | 283 | (setq all (vertico--move-to-front field all))
|
271 | 284 | (when-let (group (completion-metadata-get metadata 'x-group-function))
|
272 | 285 | (setq all (mapcan #'cdr (funcall group all))))
|
273 |
| - (list base total all))) |
| 286 | + (list base total all (cdr all-hl)))) |
274 | 287 |
|
275 | 288 | (defun vertico--update-candidates (pt content bounds metadata)
|
276 | 289 | "Preprocess candidates given PT, CONTENT, BOUNDS and METADATA."
|
277 | 290 | (pcase (let ((while-no-input-ignore-events '(selection-request)))
|
278 | 291 | (while-no-input (vertico--recompute-candidates pt content bounds metadata)))
|
279 | 292 | ('nil (abort-recursive-edit))
|
280 |
| - (`(,base ,total ,candidates) |
| 293 | + (`(,base ,total ,candidates ,hl) |
281 | 294 | (unless (and vertico--keep (< vertico--index 0))
|
282 | 295 | (if-let* ((old (and candidates
|
283 | 296 | vertico--keep
|
|
298 | 311 | (setq vertico--input (cons content pt)
|
299 | 312 | vertico--base base
|
300 | 313 | vertico--total total
|
| 314 | + vertico--highlight hl |
301 | 315 | vertico--candidates candidates))))
|
302 | 316 |
|
303 | 317 | (defun vertico--flatten-string (prop str)
|
|
312 | 326 | (setq pos next)))
|
313 | 327 | (apply #'concat (nreverse chunks))))
|
314 | 328 |
|
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." |
317 | 331 | (let* ((group (completion-metadata-get metadata 'x-group-function))
|
318 | 332 | (group-format (and group vertico-group-format (concat vertico-group-format "\n")))
|
319 | 333 | (index (min (max 0 (- vertico--index (/ vertico-count 2) (if group-format -1 0)))
|
320 | 334 | (max 0 (- vertico--total vertico-count))))
|
321 | 335 | (candidates
|
322 | 336 | (thread-last (seq-subseq vertico--candidates index
|
323 | 337 | (min (+ index vertico-count) vertico--total))
|
324 |
| - (vertico--highlight (substring content (car bounds)) metadata) |
| 338 | + (funcall vertico--highlight) |
325 | 339 | (vertico--annotate metadata)))
|
326 | 340 | (max-width (- (window-width) 4))
|
327 | 341 | (current-line 0) (title) (lines))
|
|
434 | 448 | (vertico--update-candidates pt content bounds metadata))
|
435 | 449 | (vertico--prompt-selection)
|
436 | 450 | (vertico--display-count)
|
437 |
| - (vertico--display-candidates (vertico--format-candidates content bounds metadata)))) |
| 451 | + (vertico--display-candidates (vertico--format-candidates metadata)))) |
438 | 452 |
|
439 | 453 | (defun vertico--require-match ()
|
440 | 454 | "Return t if match is required."
|
|
525 | 539 | (setq vertico--input t
|
526 | 540 | vertico--candidates-ov (make-overlay (point-max) (point-max) nil t t)
|
527 | 541 | 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 |
530 | 543 | truncate-lines t
|
531 | 544 | max-mini-window-height 1.0)
|
532 | 545 | (use-local-map vertico-map)
|
|
0 commit comments