[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 19d2a40 004/167: Add flx sorting
From: |
Oleh Krehel |
Subject: |
[elpa] master 19d2a40 004/167: Add flx sorting |
Date: |
Tue, 08 Dec 2015 10:49:33 +0000 |
branch: master
commit 19d2a407cb7fd8688b426106f063b971fd91e6a8
Author: Oleh Krehel <address@hidden>
Commit: Oleh Krehel <address@hidden>
Add flx sorting
* ivy.el (ivy--flx-cache): New defvar.
(ivy--filter): Since flx is costly, move the caching to an earlier
point. This means immediate return for when the input hasn't changed,
i.e. for "C-n" or "C-p". When flx is installed, and
(eq ivy--regex-function 'ivy--regex-fuzzy) for current function (through
`ivy-re-builders-alist'), then sort the final candidates with
`ivy--flx-sort'.
(ivy--flx-sort): New defun. In the worst case when some error pops up return
the same list. In the best case sort the `cands' that all match `name'
by closeness to `name'.
How to use:
1. Have flx installed - (require 'flx) should succeed.
2. Configure `ivy-re-builders-alist' appropriately to use
`ivy--regex-fuzzy', for example:
(setq ivy-re-builders-alist
'((t . ivy--regex-fuzzy)))
Fixes #207
---
ivy.el | 168 +++++++++++++++++++++++++++++++++++++++-------------------------
1 files changed, 103 insertions(+), 65 deletions(-)
diff --git a/ivy.el b/ivy.el
index 9b3e698..0dab956 100644
--- a/ivy.el
+++ b/ivy.el
@@ -1445,74 +1445,112 @@ all of the text contained in the minibuffer."
(font-lock-append-text-property 0 (length str) 'face face str))))
str)
+(declare-function flx-make-string-cache "ext:flx")
+(declare-function flx-score "ext:flx")
+
+(defvar ivy--flx-cache nil)
+
+(eval-after-load 'flx
+ '(setq ivy--flx-cache (flx-make-string-cache)))
+
(defun ivy--filter (name candidates)
"Return all items that match NAME in CANDIDATES.
CANDIDATES are assumed to be static."
- (let* ((re (funcall ivy--regex-function name))
- (re-str (if (listp re) (caar re) re))
- (matcher (ivy-state-matcher ivy-last))
- (case-fold-search (string= name (downcase name)))
- (cands (cond
- (matcher
- (funcall matcher re candidates))
- ((and (equal re ivy--old-re)
- ivy--old-cands)
- ivy--old-cands)
- ((and ivy--old-re
- (stringp re)
- (stringp ivy--old-re)
- (not (string-match "\\\\" ivy--old-re))
- (not (equal ivy--old-re ""))
- (memq (cl-search
- (if (string-match "\\\\)\\'" ivy--old-re)
- (substring ivy--old-re 0 -2)
- ivy--old-re)
- re)
- '(0 2)))
- (ignore-errors
- (cl-remove-if-not
- (lambda (x) (string-match re x))
- ivy--old-cands)))
- (t
- (let ((re-list (if (stringp re) (list (cons re t)) re))
- (res candidates))
- (dolist (re re-list)
- (setq res
- (ignore-errors
- (funcall
- (if (cdr re)
- #'cl-remove-if-not
- #'cl-remove-if)
- (let ((re (car re)))
- (lambda (x) (string-match re x)))
- res))))
- res))))
- (tail (nthcdr ivy--index ivy--old-cands))
- idx)
- (when (and tail ivy--old-cands (not (equal "^" ivy--old-re)))
- (unless (and (not (equal re-str ivy--old-re))
- (or (setq ivy--index
- (or
- (cl-position (if (and (> (length re-str) 0)
- (eq ?^ (aref re-str 0)))
- (substring re-str 1)
- re-str) cands
- :test #'equal)
- (and ivy--directory
- (cl-position
- (concat re-str "/") cands
- :test #'equal))))))
- (while (and tail (null idx))
- ;; Compare with eq to handle equal duplicates in cands
- (setq idx (cl-position (pop tail) cands)))
- (setq ivy--index (or idx 0))))
- (when (and (string= name "") (not (equal ivy--old-re "")))
- (setq ivy--index
- (or (cl-position (ivy-state-preselect ivy-last)
- cands :test #'equal)
- ivy--index)))
- (setq ivy--old-re (if cands re-str ""))
- (setq ivy--old-cands cands)))
+ (let ((re (funcall ivy--regex-function name)))
+ (if (and (equal re ivy--old-re)
+ ivy--old-cands)
+ ;; quick caching for "C-n", "C-p" etc.
+ ivy--old-cands
+ (let* ((re-str (if (listp re) (caar re) re))
+ (matcher (ivy-state-matcher ivy-last))
+ (case-fold-search (string= name (downcase name)))
+ (cands (cond
+ (matcher
+ (funcall matcher re candidates))
+ ((and ivy--old-re
+ (stringp re)
+ (stringp ivy--old-re)
+ (not (string-match "\\\\" ivy--old-re))
+ (not (equal ivy--old-re ""))
+ (memq (cl-search
+ (if (string-match "\\\\)\\'" ivy--old-re)
+ (substring ivy--old-re 0 -2)
+ ivy--old-re)
+ re)
+ '(0 2)))
+ (ignore-errors
+ (cl-remove-if-not
+ (lambda (x) (string-match re x))
+ ivy--old-cands)))
+ (t
+ (let ((re-list (if (stringp re) (list (cons re t)) re))
+ (res candidates))
+ (dolist (re re-list)
+ (setq res
+ (ignore-errors
+ (funcall
+ (if (cdr re)
+ #'cl-remove-if-not
+ #'cl-remove-if)
+ (let ((re (car re)))
+ (lambda (x) (string-match re x)))
+ res))))
+ res))))
+ (tail (nthcdr ivy--index ivy--old-cands))
+ idx)
+ (when (and tail ivy--old-cands (not (equal "^" ivy--old-re)))
+ (unless (and (not (equal re-str ivy--old-re))
+ (or (setq ivy--index
+ (or
+ (cl-position (if (and (> (length re-str) 0)
+ (eq ?^ (aref re-str
0)))
+ (substring re-str 1)
+ re-str) cands
+ :test #'equal)
+ (and ivy--directory
+ (cl-position
+ (concat re-str "/") cands
+ :test #'equal))))))
+ (while (and tail (null idx))
+ ;; Compare with eq to handle equal duplicates in cands
+ (setq idx (cl-position (pop tail) cands)))
+ (setq ivy--index (or idx 0))))
+ (when (and (string= name "") (not (equal ivy--old-re "")))
+ (setq ivy--index
+ (or (cl-position (ivy-state-preselect ivy-last)
+ cands :test #'equal)
+ ivy--index)))
+ (setq ivy--old-re (if cands re-str ""))
+ (when (and (require 'flx nil 'noerror)
+ (eq ivy--regex-function 'ivy--regex-fuzzy))
+ (setq cands (ivy--flx-sort name cands)))
+ (setq ivy--old-cands cands)))))
+
+(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) 200))
+ (let* ((flx-name (if (string-match "^\\^" name)
+ (substring name 1)
+ name))
+ (cands-with-score
+ (delq nil
+ (mapcar
+ (lambda (x)
+ (let ((score (car (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)
+ (> (car x) (car y)))))
+ cands))
+ cands)
+ (error
+ cands)))
(defvar ivy-format-function 'ivy-format-function-default
"Function to transform the list of candidates into a string.
- [elpa] master updated (a0561bf -> 1f3fa31), Oleh Krehel, 2015/12/08
- [elpa] master a71d5c8 001/167: ivy.el (ivy--insert-prompt): Improve truncation, Oleh Krehel, 2015/12/08
- [elpa] master e70ca07 003/167: ivy.el (ivy-call): "C-M-n" should not leave the minibuffer, Oleh Krehel, 2015/12/08
- [elpa] master 4ad797b 002/167: Improve "C-g" out of a long-running async process, Oleh Krehel, 2015/12/08
- [elpa] master 41b5850 005/167: ivy.el (ivy--regex-fuzzy): Add minibuffer highlighting, Oleh Krehel, 2015/12/08
- [elpa] master 19d2a40 004/167: Add flx sorting,
Oleh Krehel <=
- [elpa] master 3146501 010/167: ivy.el (ivy-resume): Don't regexp-quote preselect, Oleh Krehel, 2015/12/08
- [elpa] master f04aec8 009/167: ivy-hydra.el: Bind "t" to toggle-truncate-lines, Oleh Krehel, 2015/12/08
- [elpa] master 82a317c 011/167: Make swiper compatible with visual-line-mode, Oleh Krehel, 2015/12/08
- [elpa] master 45b54e3 015/167: counsel.el (counsel--gg-count): Fix for "'" in query, Oleh Krehel, 2015/12/08
- [elpa] master 6bf3239 006/167: ivy.el (ivy-yank-word): Add only one space each time, Oleh Krehel, 2015/12/08
- [elpa] master 73e5799 012/167: Fix counsel-git-grep not updating to 0 candidates, Oleh Krehel, 2015/12/08
- [elpa] master dfb41d0 016/167: Implement ivy-avy, Oleh Krehel, 2015/12/08
- [elpa] master eda091b 017/167: Customize ivy-avy and fix compile warnings, Oleh Krehel, 2015/12/08
- [elpa] master fa49a81 018/167: Fix "End of buffer" for swiper and visual-line-mode, Oleh Krehel, 2015/12/08
- [elpa] master 1714220 022/167: swiper.el (swiper-font-lock-ensure): Exclude help-mode, Oleh Krehel, 2015/12/08