From 96f69f8217c27409981c94b7ee1e04c2b9cece20 Mon Sep 17 00:00:00 2001 From: Eric Danan Date: Wed, 22 Aug 2018 02:04:23 +0200 Subject: [PATCH] cp-grep, cp-ag, cp-rg: support kept directories Also support globally ignored file suffixes. Separate out `counsel-projectile-git-grep` from `counsel-projectile-grep` and give it a key binding. --- README.md | 8 ++ counsel-projectile.el | 168 ++++++++++++++++++++++++------------------ 2 files changed, 105 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 344d43c..c3fe965 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ New commands: | Key binding | Command | Description | | :------------------- | :------------------------------- | :-------------------------------------------------- | | C-c p SPC | `counsel-projectile` | Jump to a project buffer or file, or switch project | +| C-c p s i | `counsel-projectile-git-grep` | Search project with git grep | | C-c p O c | `counsel-projectile-org-capture` | Capture into project | | C-c p O a | `counsel-projectile-org-capture` | Open project agenda | ## The `counsel-projectile` command @@ -123,6 +124,7 @@ This command is a replacement for `projectile-switch-project`. It adds the possi | E | Edit project directory-local variables | | v | Open project in vc-dir / magit / monky | | s g | Search project with grep: call `counsel-projectile-grep` (see below) | +| s i | Search project with git grep: call `counsel-projectile-git-grep` (see below) | | s s | Search project with ag: call `counsel-projectile-ag` (see below) | | s r | Search project with rg: call `counsel-projectile-rg` (see below) | | x s | Invoke shell from the project root | @@ -178,6 +180,12 @@ The key binding C-c C-k can also be used from the minibuffer to kill Default key binding: C-c p s g. This command is a replacement for `projectile-grep`. It searches all project files with `grep`, taking advantage of ivy's support for updating the list of candidates after each input (dynamic collections). Each canidate corresponds to a matching line in some project file, and there is only one action that opens that file at that line. + +If inside a git project and the variable `projectile-use-git-grep` is non-nil, then `counsel-projectile-grep` uses `git grep` instead of `grep`, by calling the function `counsel-projectile-git-grep` (see below). +## The `counsel-projectile-git-grep` command +Default key binding: C-c p s i. + +This command is similar to `counsel-projectile-grep` (see above) but uses `git grep` instead of `grep` (hence it only works in git projects). ## The `counsel-projectile-ag` command Default key binding: C-c p s s. diff --git a/counsel-projectile.el b/counsel-projectile.el index 7a1b2e5..5bed3d9 100644 --- a/counsel-projectile.el +++ b/counsel-projectile.el @@ -588,15 +588,12 @@ of `(ivy-thing-at-point)' by hitting \"M-n\" in the minibuffer." (sexp :tag "Custom expression")) :group 'counsel-projectile) -(defvar counsel-projectile-grep-base-command "grep -rnE %s -- %%s ." - "Format string to use in `cousel-projectile-grep-function' to +(defvar counsel-projectile-grep-base-command "grep -rnEI %s -- %%s %s" + "Format string to use in `cousel-projectile-grep' to construct the command.") (defvar counsel-projectile-grep-command nil) -(defvar counsel-projectile-grep-options-history nil - "History for `counsel-projectile-grep' options.") - (defun counsel-projectile-grep-function (string) "Grep for STRING in the current project." (or (counsel-more-chars) @@ -636,11 +633,12 @@ construct the command.") (insert (format "%d candidates:\n" (length cands))) (ivy--occur-insert-lines cands))) +;;;###autoload (defun counsel-projectile-grep (&optional options-or-cmd) "Search the current project with grep. If inside a git project and `projectile-use-git-grep' is non-nil, -use `counsel-git-grep'. Otherwise use grep recursively. +use git grep. Otherwise use grep recursively. OPTIONS-OR-CMD, if non-nil, is a string containing either additional options to be passed to grep, or an alternative git @@ -648,36 +646,31 @@ grep command. It is read from the minibuffer if the function is called with a prefix argument." (interactive) (if (and (eq (projectile-project-vcs) 'git) - projectile-use-git-grep) - (let ((counsel-prompt-function - (lambda () - (ivy-add-prompt-count - (format "%s: " (projectile-prepend-project-name (ivy-state-prompt ivy-last))))))) - (counsel-git-grep (or current-prefix-arg options-or-cmd) - counsel-projectile-grep-initial-input)) - (counsel-require-program (car (split-string counsel-projectile-grep-base-command))) - (let* ((ignored-files (mapconcat (lambda (i) - (concat "--exclude=" - (shell-quote-argument i) - " ")) - (projectile-ignored-files-rel) - "")) - (ignored-dirs (mapconcat (lambda (i) - (concat "--exclude-dir=" - (shell-quote-argument i) - " ")) - (projectile-ignored-directories-rel) - "")) - (ignored (concat ignored-files ignored-dirs)) - (options - (if current-prefix-arg - (read-string (projectile-prepend-project-name "grep options: ") - ignored - 'counsel-projectile-grep-options-history) - (concat ignored options-or-cmd))) + projectile-use-git-grep) + (counsel-projectile-git-grep options-or-cmd) + (let* ((path + (mapconcat 'shell-quote-argument + (projectile-normalise-paths + (car (projectile-parse-dirconfig-file))) + " ")) + (ignored-files + (mapconcat (lambda (i) + (concat "--exclude=" (shell-quote-argument i))) + (append + (projectile--globally-ignored-file-suffixes-glob) + (projectile-ignored-files-rel)) + " ")) + (ignored-dirs + (mapconcat (lambda (i) + (concat "--exclude-dir=" (shell-quote-argument i))) + (projectile-ignored-directories-rel) + " ")) + (ignored (concat ignored-files " " ignored-dirs)) (default-directory (projectile-project-root))) + (counsel-require-program + (car (split-string counsel-projectile-grep-base-command))) (setq counsel-projectile-grep-command - (format counsel-projectile-grep-base-command options)) + (format counsel-projectile-grep-base-command ignored path)) (ivy-set-prompt 'counsel-projectile-grep counsel-prompt-function) (ivy-read (projectile-prepend-project-name "grep") #'counsel-projectile-grep-function @@ -695,6 +688,30 @@ called with a prefix argument." (ivy-set-occur 'counsel-projectile-grep 'counsel-projectile-grep-occur) (ivy-set-display-transformer 'counsel-projectile-grep 'counsel-projectile-grep-transformer) +;;;###autoload +(defun counsel-projectile-git-grep (&optional cmd) + "Search the current project with git grep. + +CMD, if non-nil, is a string containing an alternative git grep +command. It is read from the minibuffer if the function is called +with a prefix argument." + (interactive) + (let* ((path + (mapconcat 'shell-quote-argument + (projectile-normalise-paths + (car (projectile-parse-dirconfig-file))) + " ")) + (counsel-git-grep-cmd-default + (concat (concat (string-trim-right counsel-git-grep-cmd-default " \\.") + " " path))) + (counsel-prompt-function + (lambda () + (ivy-add-prompt-count + (format "%s: " (projectile-prepend-project-name + (ivy-state-prompt ivy-last))))))) + (counsel-git-grep (or current-prefix-arg cmd) + counsel-projectile-grep-initial-input))) + ;;* counsel-projectile-ag (defcustom counsel-projectile-ag-initial-input nil @@ -712,9 +729,6 @@ of `(ivy-thing-at-point)' by hitting \"M-n\" in the minibuffer." (sexp :tag "Custom expression")) :group 'counsel-projectile) -(defvar counsel-projectile-ag-options-history nil - "History for `counsel-projectile-ag' options.") - ;;;###autoload (defun counsel-projectile-ag (&optional options) "Search the current project with ag. @@ -723,23 +737,26 @@ OPTIONS, if non-nil, is a string containing additional options to be passed to ag. It is read from the minibuffer if the function is called with a prefix argument." (interactive) - (let* ((ignored (mapconcat (lambda (i) - (concat "--ignore " - (shell-quote-argument i) - " ")) - (append (projectile-ignored-files-rel) - (projectile-ignored-directories-rel)) - "")) - (options - (if current-prefix-arg - (read-string (projectile-prepend-project-name "ag options: ") - ignored - 'counsel-projectile-ag-options-history) - (concat ignored options)))) + (let* ((path (mapconcat 'shell-quote-argument + (projectile-normalise-paths + (car (projectile-parse-dirconfig-file))) + " ")) + (ignored + (mapconcat (lambda (i) + (concat "--ignore " (shell-quote-argument i))) + (append + (projectile--globally-ignored-file-suffixes-glob) + (projectile-ignored-files-rel) + (projectile-ignored-directories-rel)) + " ")) + (counsel-ag-base-command + (format (string-trim-right counsel-ag-base-command " \\.") + (concat ignored " %s " path)))) (counsel-ag (eval counsel-projectile-ag-initial-input) (projectile-project-root) options - (projectile-prepend-project-name "ag")))) + (projectile-prepend-project-name + (car (split-string counsel-ag-base-command)))))) ;;* counsel-projectile-rg @@ -758,9 +775,6 @@ of `(ivy-thing-at-point)' by hitting \"M-n\" in the minibuffer." (sexp :tag "Custom expression")) :group 'counsel-projectile) -(defvar counsel-projectile-rg-options-history nil - "History for `counsel-projectile-rg' options.") - ;;;###autoload (defun counsel-projectile-rg (&optional options) "Search the current project with rg. @@ -769,23 +783,27 @@ OPTIONS, if non-nil, is a string containing additional options to be passed to rg. It is read from the minibuffer if the function is called with a prefix argument." (interactive) - (let* ((ignored (mapconcat (lambda (i) - (concat "--glob " - (shell-quote-argument (concat "!" i)) - " ")) - (append (projectile-ignored-files-rel) - (projectile-ignored-directories-rel)) - "")) - (options - (if current-prefix-arg - (read-string (projectile-prepend-project-name "rg options: ") - ignored - 'counsel-projectile-rg-options-history) - (concat ignored options)))) + (let* ((path + (mapconcat 'shell-quote-argument + (projectile-normalise-paths + (car (projectile-parse-dirconfig-file))) + " ")) + (ignored + (mapconcat (lambda (i) + (concat "--glob !" (shell-quote-argument i))) + (append + (projectile--globally-ignored-file-suffixes-glob) + (projectile-ignored-files-rel) + (projectile-ignored-directories-rel)) + " ")) + (counsel-rg-base-command + (format (string-trim-right counsel-rg-base-command " \\.") + (concat ignored " %s " path)))) (counsel-rg (eval counsel-projectile-rg-initial-input) (projectile-project-root) options - (projectile-prepend-project-name "rg")))) + (projectile-prepend-project-name + (car (split-string counsel-rg-base-command)))))) ;;* counsel-projectile-org-capture @@ -1044,6 +1062,8 @@ candidates list of `counsel-projectile-switch-project'." "open project in vc-dir / magit / monky") ("sg" counsel-projectile-switch-project-action-grep "search project with grep") + ("si" counsel-projectile-switch-project-action-git-grep + "search project with git grep") ("ss" counsel-projectile-switch-project-action-ag "search project with ag") ("sr" counsel-projectile-switch-project-action-rg @@ -1179,17 +1199,22 @@ action." (counsel-projectile-switch-project-by-name project))) (defun counsel-projectile-switch-project-action-grep (project) - "Search PROJECT with `grep'." + "Search PROJECT with grep." (let ((projectile-switch-project-action 'counsel-projectile-grep)) (counsel-projectile-switch-project-by-name project))) +(defun counsel-projectile-switch-project-action-git-grep (project) + "Search PROJECT with git grep." + (let ((projectile-switch-project-action 'counsel-projectile-git-grep)) + (counsel-projectile-switch-project-by-name project))) + (defun counsel-projectile-switch-project-action-ag (project) - "Search PROJECT with `ag'." + "Search PROJECT with ag." (let ((projectile-switch-project-action 'counsel-projectile-ag)) (counsel-projectile-switch-project-by-name project))) (defun counsel-projectile-switch-project-action-rg (project) - "Search PROJECT with `rg'." + "Search PROJECT with rg." (let ((projectile-switch-project-action 'counsel-projectile-rg)) (counsel-projectile-switch-project-by-name project))) @@ -1383,6 +1408,7 @@ If not inside a project, call `counsel-projectile-switch-project'." (projectile-ripgrep . counsel-projectile-rg) (projectile-switch-project . counsel-projectile-switch-project) (" " . counsel-projectile) + ("si" . counsel-projectile-git-grep) ("Oc" . counsel-projectile-org-capture) ("Oa" . counsel-projectile-org-agenda)) "Alist of counsel-projectile key bindings.