branch: master
commit 93d1adc57a698b70278b7c5e76a4a3a18e8ebaf5
Author: Oleh Krehel <[email protected]>
Commit: Oleh Krehel <[email protected]>
Properly support matching ignoring order
* ivy.el (ivy--regex-ignore-order): Return a list of regexps rather than
a single regexp.
(ivy--re-filter): New defun, extracted from `ivy--filter'.
(ivy--filter): Update.
(ivy--format-minibuffer-line): Add special highlighting for
`ivy--regex-ignore-order'.
* counsel.el (counsel--find-file-matcher): Use `ivy--re-filter'.
Fixes #296
Fixes #329
---
counsel.el | 5 +--
ivy.el | 144 ++++++++++++++++++++++++++++++++----------------------------
2 files changed, 77 insertions(+), 72 deletions(-)
diff --git a/counsel.el b/counsel.el
index 89d3107..b40d409 100644
--- a/counsel.el
+++ b/counsel.el
@@ -488,10 +488,7 @@ Possible value: \"\\(?:\\`[#.]\\)\\|\\(?:[#~]\\'\\)\"."
(defun counsel--find-file-matcher (regexp candidates)
"Return REGEXP-matching CANDIDATES.
Skip some dotfiles unless `ivy-text' requires them."
- (let ((res (cl-remove-if-not
- (lambda (x)
- (string-match regexp x))
- candidates)))
+ (let ((res (ivy--re-filter regexp candidates)))
(if (or (null counsel-find-file-ignore-regexp)
(string-match counsel-find-file-ignore-regexp ivy-text))
res
diff --git a/ivy.el b/ivy.el
index 16b8d81..2728cb2 100644
--- a/ivy.el
+++ b/ivy.el
@@ -1499,30 +1499,15 @@ When GREEDY is non-nil, join words in a greedy way."
(defun ivy--regex-ignore-order (str)
"Re-build regex from STR by splitting at spaces.
-Ignore the order of each group.
-
-ATTENTION: This is just a proof of concept and may not work as
-expected. Besides ignoring the order of the tokens where 'foo'
-and 'bar', 'bar' and 'foo' are matched, it also matches multiple
-occurrences of 'foo' and 'bar'. To ignore the sort order and avoid
-multiple matches, use `ivy-restrict-to-matches' instead.
-"
+Ignore the order of each group."
(let* ((subs (split-string str " +" t))
(len (length subs)))
(cl-case len
- (1
- (setq ivy--subexps 0)
- (car subs))
+ (0
+ "")
(t
- (setq ivy--subexps len)
- (let ((all (mapconcat #'identity subs "\\|")))
- (mapconcat
- (lambda (x)
- (if (string-match "\\`\\\\(.*\\\\)\\'" x)
- x
- (format "\\(%s\\)" x)))
- (make-list len all)
- ".*?"))))))
+ (mapcar (lambda (x) (cons x t))
+ subs)))))
(defun ivy--regex-plus (str)
"Build a regex sequence from STR.
@@ -1534,10 +1519,10 @@ match. Everything after \"!\" should not match."
"")
(1
(if (string-equal (substring str 0 1) "!")
- (list
- (cons "" t)
- (list (ivy--regex (car parts))))
- (ivy--regex (car parts))))
+ (list
+ (cons "" t)
+ (list (ivy--regex (car parts))))
+ (ivy--regex (car parts))))
(2
(let ((res
(mapcar #'list
@@ -1807,6 +1792,25 @@ You can toggle this to make `case-fold-search' nil
regardless of input."
;; reset cache so that the candidate list updates
(setq ivy--old-re nil))
+(defun ivy--re-filter (re candidates)
+ "Return all RE matching CANDIDATES.
+RE is a list of cons cells, with a regexp car and a boolean cdr.
+When the cdr is t, the car must match.
+Otherwise, the car must not match."
+ (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-str (car re)))
+ (lambda (x) (string-match re-str x)))
+ res))))
+ res))
+
(defun ivy--filter (name candidates)
"Return all items that match NAME in CANDIDATES.
CANDIDATES are assumed to be static."
@@ -1821,15 +1825,15 @@ CANDIDATES are assumed to be static."
(and ivy-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)
+ (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)
@@ -1839,21 +1843,14 @@ CANDIDATES are assumed to be static."
(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-str (car re)))
- (lambda (x) (string-match re-str x)))
- res))))
- res)))))
+ (ivy--re-filter re candidates)))))
(ivy--recompute-index name re-str cands)
- (setq ivy--old-re (if cands re-str ""))
+ (setq ivy--old-re
+ (if (eq ivy--regex-function 'ivy--regex-ignore-order)
+ re
+ (if cands
+ re-str
+ "")))
(setq ivy--old-cands (ivy--sort name cands))))))
(defcustom ivy-sort-matches-functions-alist '((t . nil))
@@ -2097,27 +2094,38 @@ SEPARATOR is used to join the candidates."
(defun ivy--format-minibuffer-line (str)
(let ((start 0)
(str (copy-sequence str)))
- (when (and (eq ivy-display-style 'fancy)
- (not (eq ivy--regex-function 'ivy--regex-fuzzy)))
- (unless ivy--old-re
- (setq ivy--old-re (funcall ivy--regex-function ivy-text)))
- (while (and (string-match ivy--old-re str start)
- (> (- (match-end 0) (match-beginning 0)) 0))
- (setq start (match-end 0))
- (let ((i 0))
- (while (<= i ivy--subexps)
- (let ((face
- (cond ((zerop ivy--subexps)
- (cadr ivy-minibuffer-faces))
- ((zerop i)
- (car ivy-minibuffer-faces))
- (t
- (nth (1+ (mod (+ i 2) (1- (length
ivy-minibuffer-faces))))
- ivy-minibuffer-faces)))))
- (ivy-add-face-text-property
- (match-beginning i) (match-end i)
- face str))
- (cl-incf i)))))
+ (cond ((eq ivy--regex-function 'ivy--regex-ignore-order)
+ (when (consp ivy--old-re)
+ (let ((i 1))
+ (dolist (re ivy--old-re)
+ (when (string-match (car re) str)
+ (ivy-add-face-text-property
+ (match-beginning 0) (match-end 0)
+ (nth (1+ (mod (+ i 2) (1- (length ivy-minibuffer-faces))))
+ ivy-minibuffer-faces)
+ str))
+ (cl-incf i)))))
+ ((and (eq ivy-display-style 'fancy)
+ (not (eq ivy--regex-function 'ivy--regex-fuzzy)))
+ (unless ivy--old-re
+ (setq ivy--old-re (funcall ivy--regex-function ivy-text)))
+ (while (and (string-match ivy--old-re str start)
+ (> (- (match-end 0) (match-beginning 0)) 0))
+ (setq start (match-end 0))
+ (let ((i 0))
+ (while (<= i ivy--subexps)
+ (let ((face
+ (cond ((zerop ivy--subexps)
+ (cadr ivy-minibuffer-faces))
+ ((zerop i)
+ (car ivy-minibuffer-faces))
+ (t
+ (nth (1+ (mod (+ i 2) (1- (length
ivy-minibuffer-faces))))
+ ivy-minibuffer-faces)))))
+ (ivy-add-face-text-property
+ (match-beginning i) (match-end i)
+ face str))
+ (cl-incf i))))))
str))
(defun ivy--format (cands)