Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ivy--flx-sort more intelligent #843

Merged
merged 1 commit into from
Dec 29, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 62 additions & 20 deletions ivy.el
Original file line number Diff line number Diff line change
Expand Up @@ -2703,26 +2703,68 @@ no sorting is done.")
(defun ivy--flx-sort (name cands)
"Sort according to closeness to string NAME the string list CANDS."
(condition-case nil
(if (and cands
(< (length cands) ivy-flx-limit))
(let* ((flx-name (if (string-match "^\\^" name)
(substring name 1)
name))
(cands-with-score
(delq nil
(mapcar
(lambda (x)
(let ((score (flx-score x flx-name ivy--flx-cache)))
(and score
(cons score x))))
cands))))
(if cands-with-score
(mapcar #'cdr
(sort cands-with-score
(lambda (x y)
(> (caar x) (caar y)))))
cands))
cands)
(let* (;; an optimized regex for fuzzy matching
;; "abc" → "\\`[^a]*a[^b]*b[^c]*c"
(fuzzy-regex (if (= (elt name 0) ?^)
(concat "^"
(regexp-quote (substring name 1 2))
(mapconcat
(lambda (x)
(setq x (string x))
(concat "[^" x "]*" (regexp-quote x)))
(substring name 2)
""))
(concat "^"
(mapconcat
(lambda (x)
(setq x (string x))
(concat "[^" x "]*" (regexp-quote x)))
name
""))))

;; strip off the leading "^" for flx matching
(flx-name (if (string-match "^\\^" name)
(substring name 1)
name))

(cands-left)
(cands-to-sort))

;; filter out non-matching candidates
(dolist (cand cands)
(when (string-match fuzzy-regex cand)
(push cand cands-left)))

;; pre-sort the candidates by length before partitioning
(setq cands-left (sort cands-left
(lambda (c1 c2)
(< (length c1)
(length c2)))))

;; partition the candidates into sorted and unsorted groups
(dotimes (_n (min (length cands-left) ivy-flx-limit))
(push (pop cands-left) cands-to-sort))

(append
;; compute all of the flx scores in one pass and sort
(mapcar #'car
(sort (mapcar
(lambda (cand)
(cons cand
(car (flx-score cand
flx-name
ivy--flx-cache))))
cands-to-sort)
(lambda (c1 c2)
;; break ties by length
(if (/= (cdr c1) (cdr c2))
(> (cdr c1)
(cdr c2))
(< (length (car c1))
(length (car c2)))))))

;; add the unsorted candidates
cands-left))
(error
cands)))

Expand Down