From 858e2671ac683dd1f79f4f0501031a3e39e98a1e Mon Sep 17 00:00:00 2001 From: Troy Hinckley Date: Wed, 20 Mar 2019 07:29:32 -0700 Subject: [PATCH 1/4] Make find-file-occur work with multi-pass regex builders --- counsel.el | 64 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/counsel.el b/counsel.el index 41ae5eac..9d378bc3 100644 --- a/counsel.el +++ b/counsel.el @@ -82,7 +82,7 @@ complex regexes." (cl-remove-if-not #'cdr regex) ".*")) (replace-regexp-in-string - "\\\\[(){}|]\\|[()]" + "\\\\[(){}|`']\\|[()]" (lambda (s) (or (cdr (assoc s '(("\\(" . "(") ("\\)" . ")") @@ -90,7 +90,9 @@ complex regexes." (")" . "\\)") ("\\{" . "{") ("\\}" . "}") - ("\\|" . "|")))) + ("\\|" . "|") + ("\\`" . "^") + ("\\'" . "$")))) (error "Unexpected error in `counsel--elisp-to-pcre' (got match %S)" s))) regex t t))) @@ -1213,9 +1215,9 @@ INITIAL-INPUT can be given as the initial minibuffer input." (cd (ivy-state-directory ivy-last)) (counsel-cmd-to-dired (counsel--expand-ls - (format "%s | grep -i -E '%s' | xargs ls" + (format "%s | %s | xargs ls" counsel-git-cmd - (counsel--elisp-to-pcre ivy--old-re))))) + (counsel--file-name-filter))))) (defvar counsel-dired-listing-switches "-alh" "Switches passed to `ls' for `counsel-cmd-to-dired'.") @@ -1845,7 +1847,7 @@ When INITIAL-INPUT is non-nil, use it in the minibuffer during completion." (ivy-set-occur 'counsel-find-file 'counsel-find-file-occur) -(defvar counsel-find-file-occur-cmd "ls -a | grep -i -E '%s' | xargs -d '\\n' ls -d --group-directories-first" +(defvar counsel-find-file-occur-cmd "ls -a | %s | xargs -d '\\n' ls -d --group-directories-first" "Format string for `counsel-find-file-occur'.") (defvar counsel-find-file-occur-use-find (not (eq system-type 'gnu/linux)) @@ -1855,11 +1857,47 @@ When INITIAL-INPUT is non-nil, use it in the minibuffer during completion." "Expand CMD that ends in \"ls\" with switches." (concat cmd " " counsel-dired-listing-switches " | sed -e \"s/^/ /\"")) +(defvar counsel-file-name-filter-alist + '(("ag -i '%s'" . t) + ("ack -i '%s'" . t) + ("perl -ne '/(%s.*)/i && print \"$1\\n\";'" . t) + ("grep -i -E '%s'")) + "Alist of file name filtering commands. +The car is a shell command and the cdr is t when the shell +command supports look-arounds. The executable for the commands +will be checked for existence via `executable-find'. The first +one that exists will be used.") + +(defun counsel--file-name-filter (&optional use-ignore) + "Return a command that filters a file list to match ivy candidates. +If USE-IGNORE is non-nil, try to generate a command that respects +`counsel-find-file-ignore-regexp'." + (let ((regex ivy--old-re) + (ignore-re (list (counsel--elisp-to-pcre + counsel-find-file-ignore-regexp))) + (cmd (cl-find-if + (lambda (x) + (executable-find + (car (split-string (car x))))) + counsel-file-name-filter-alist))) + (and use-ignore ivy-use-ignore + counsel-find-file-ignore-regexp + (cdr cmd) + (not (string-match-p "\\`\\." ivy-text)) + (not (string-match-p counsel-find-file-ignore-regexp + (or (car ivy--old-cands) ""))) + (setq regex (if (stringp regex) + (list ignore-re (cons regex t)) + (cons ignore-re regex)))) + (setq cmd (format (car cmd) (counsel--elisp-to-pcre regex (cdr cmd)))) + (if (string-match-p "csh$" shell-file-name) + (replace-regexp-in-string "\\?!" "?\\\\!" cmd) + cmd))) + (defun counsel--occur-cmd-find () - (let* ((regex (counsel--elisp-to-pcre ivy--old-re)) - (cmd (format - "find . -maxdepth 1 | grep -i -E '%s' | xargs -I {} find {} -maxdepth 0 -ls" - regex))) + (let* ((cmd (format + "find . -maxdepth 1 | %s | xargs -I {} find {} -maxdepth 0 -ls" + (counsel--file-name-filter t)))) (concat (counsel--cmd-to-dired-by-type "d" cmd) " && " @@ -1883,8 +1921,12 @@ When INITIAL-INPUT is non-nil, use it in the minibuffer during completion." 'find-dired-filter) (counsel-cmd-to-dired (counsel--expand-ls - (format counsel-find-file-occur-cmd - (counsel--elisp-to-pcre ivy--old-re)))))) + (if (string-match-p "grep" counsel-find-file-occur-cmd) + ;; for backwards compatibility + (format counsel-find-file-occur-cmd + (counsel--elisp-to-pcre ivy--old-re)) + (format counsel-find-file-occur-cmd + (counsel--file-name-filter t))))))) (defun counsel-up-directory () "Go to the parent directory preselecting the current one. From deac1e005c300e840d22c322e57b0a9fe1f42d68 Mon Sep 17 00:00:00 2001 From: Troy Hinckley Date: Wed, 27 Mar 2019 09:18:12 -0700 Subject: [PATCH 2/4] Prevent pcre conversion from creating look-around for a single form --- counsel.el | 4 +++- ivy-test.el | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/counsel.el b/counsel.el index 9d378bc3..2acbe647 100644 --- a/counsel.el +++ b/counsel.el @@ -62,7 +62,9 @@ namely a string or a list. The return value is always a string. Note that incorrect results may be returned for sufficiently complex regexes." (if (consp regex) - (if look-around + (if (and look-around + (or (cdr regex) + (not (cdar regex)))) (concat "^" (mapconcat diff --git a/ivy-test.el b/ivy-test.el index c224bc9a..45798945 100644 --- a/ivy-test.el +++ b/ivy-test.el @@ -347,7 +347,13 @@ will bring the behavior in line with the newer Emacsen." "(?:foo|bar).*blick.*(?:(baz)|quux)")) (should (equal (counsel--elisp-to-pcre '(("ivy" . t) ("-")) t) - "^(?=.*ivy)(?!.*-)"))) + "^(?=.*ivy)(?!.*-)")) + (should (equal (counsel--elisp-to-pcre + '(("foo" . t)) t) + "foo")) + (should (equal (counsel--elisp-to-pcre + '(("foo")) t) + "^(?!.*foo)"))) (defmacro ivy--string-buffer (text &rest body) "Test helper that wraps TEXT in a temp buffer while running BODY." From fda3fb2a9158a4feb2b0f5a5e7ab2467b9adafa5 Mon Sep 17 00:00:00 2001 From: Troy Hinckley Date: Wed, 27 Mar 2019 12:29:47 -0700 Subject: [PATCH 3/4] Update style based on feedback --- counsel.el | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/counsel.el b/counsel.el index 2acbe647..f61dbf34 100644 --- a/counsel.el +++ b/counsel.el @@ -1866,7 +1866,7 @@ When INITIAL-INPUT is non-nil, use it in the minibuffer during completion." ("grep -i -E '%s'")) "Alist of file name filtering commands. The car is a shell command and the cdr is t when the shell -command supports look-arounds. The executable for the commands +command supports look-arounds. The executable for the commands will be checked for existence via `executable-find'. The first one that exists will be used.") @@ -1892,14 +1892,14 @@ If USE-IGNORE is non-nil, try to generate a command that respects (list ignore-re (cons regex t)) (cons ignore-re regex)))) (setq cmd (format (car cmd) (counsel--elisp-to-pcre regex (cdr cmd)))) - (if (string-match-p "csh$" shell-file-name) + (if (string-match-p "csh\\'" shell-file-name) (replace-regexp-in-string "\\?!" "?\\\\!" cmd) cmd))) (defun counsel--occur-cmd-find () - (let* ((cmd (format - "find . -maxdepth 1 | %s | xargs -I {} find {} -maxdepth 0 -ls" - (counsel--file-name-filter t)))) + (let ((cmd (format + "find . -maxdepth 1 | %s | xargs -I {} find {} -maxdepth 0 -ls" + (counsel--file-name-filter t)))) (concat (counsel--cmd-to-dired-by-type "d" cmd) " && " @@ -1923,11 +1923,10 @@ If USE-IGNORE is non-nil, try to generate a command that respects 'find-dired-filter) (counsel-cmd-to-dired (counsel--expand-ls - (if (string-match-p "grep" counsel-find-file-occur-cmd) - ;; for backwards compatibility - (format counsel-find-file-occur-cmd - (counsel--elisp-to-pcre ivy--old-re)) - (format counsel-find-file-occur-cmd + (format counsel-find-file-occur-cmd + (if (string-match-p "grep" counsel-find-file-occur-cmd) + ;; for backwards compatibility + (counsel--elisp-to-pcre ivy--old-re) (counsel--file-name-filter t))))))) (defun counsel-up-directory () From 585fb27d6ce33887d232fd4ad18addf8366a1823 Mon Sep 17 00:00:00 2001 From: Troy Hinckley Date: Wed, 27 Mar 2019 13:16:45 -0700 Subject: [PATCH 4/4] Add double space to match style --- counsel.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/counsel.el b/counsel.el index f61dbf34..828ae44f 100644 --- a/counsel.el +++ b/counsel.el @@ -1867,7 +1867,7 @@ When INITIAL-INPUT is non-nil, use it in the minibuffer during completion." "Alist of file name filtering commands. The car is a shell command and the cdr is t when the shell command supports look-arounds. The executable for the commands -will be checked for existence via `executable-find'. The first +will be checked for existence via `executable-find'. The first one that exists will be used.") (defun counsel--file-name-filter (&optional use-ignore)