Skip to content

Commit

Permalink
Merge pull request #977 from seanfisk/projectile-replace-regexp
Browse files Browse the repository at this point in the history
[Fix #576] Implement `projectile-replace-regexp'
  • Loading branch information
bbatsov committed Mar 12, 2016
2 parents e9311eb + 031a56b commit 077ecc7
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* Add file local variable `projectile-project-root`, which allows overriding the project root on a per-file basis. This allows navigating a different project from, say, an org file in a another git repository.
* Add `projectile-grep-finished-hook`.
* Ignore file suffixes listed in `projectile-globally-ignored-file-suffixes` when using `projectile-grep` and `projectile-ag`.
* Add `projectile-replace-regexp`, which supports replacement by regexp within a project. `projectile-replace` is now used solely for literal replacements.

### Changes

Expand All @@ -30,6 +31,7 @@
* [#557](https://github.com/bbatsov/projectile/issues/557): stack overflow in `projectile-find-tag'.
* [#955](https://github.com/bbatsov/projectile/issues/955): Error while toggling between test and source file.
* [#952](https://github.com/bbatsov/projectile/issues/952): VCS submodules brought in even thought not descendent of project root.
* [#576](https://github.com/bbatsov/projectile/issues/576): `projectile-replace` stomps regular expressions.

## 0.13.0 (10/21/2015)

Expand Down
56 changes: 48 additions & 8 deletions projectile.el
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@
(eval-when-compile
(defvar ag-ignore-list)
(defvar ggtags-completion-table)
(defvar tags-completion-table))
(defvar tags-completion-table)
(defvar tags-loop-scan)
(defvar tags-loop-operate))

(declare-function ggtags-ensure-project "ggtags")
(declare-function ggtags-update-tags "ggtags")
Expand Down Expand Up @@ -2100,24 +2102,25 @@ files in the project."
(concat "ag --literal --nocolor --noheading -l -- "
search-term))
((executable-find "ack")
(concat "ack --noheading --nocolor -l -- "
(concat "ack --literal --noheading --nocolor -l -- "
search-term))
((and (executable-find "git")
(eq (projectile-project-vcs) 'git))
(concat "git grep -HlI "
search-term))
(concat "git grep -HlI " search-term))
(t
(concat "grep -rHlI "
search-term
" .")))))
;; -r: recursive
;; -H: show filename for each match
;; -l: show only file names with matches
;; -I: no binary files
(format "grep -rHlI %s ." search-term)))))
(projectile-files-from-cmd cmd directory))
;; we have to reject directories as a workaround to work with git submodules
(-reject #'file-directory-p
(-map #'projectile-expand-root (projectile-dir-files directory)))))

;;;###autoload
(defun projectile-replace (&optional arg)
"Replace a string in the project using `tags-query-replace'.
"Replace literal string in project using non-regexp `tags-query-replace'.
With a prefix argument ARG prompts you for a directory on which
to run the replacement."
Expand All @@ -2133,6 +2136,43 @@ to run the replacement."
(read-directory-name "Replace in directory: "))
(projectile-project-root)))
(files (projectile-files-with-string old-text directory)))
;; Adapted from `tags-query-replace' for literal strings (not regexp)
(setq tags-loop-scan `(let ,(unless (equal old-text (downcase old-text))
'((case-fold-search nil)))
(if (search-forward ',old-text nil t)
;; When we find a match, move back to
;; the beginning of it so
;; perform-replace will see it.
(goto-char (match-beginning 0))))
tags-loop-operate `(perform-replace ',old-text ',new-text t nil nil
nil multi-query-replace-map))
(tags-loop-continue (or (cons 'list files) t))))

;;;###autoload
(defun projectile-replace-regexp (&optional arg)
"Replace a regexp in the project using `tags-query-replace'.
With a prefix argument ARG prompts you for a directory on which
to run the replacement."
(interactive "P")
(let* ((old-text (read-string
(projectile-prepend-project-name "Replace regexp: ")
(projectile-symbol-or-selection-at-point)))
(new-text (read-string
(projectile-prepend-project-name
(format "Replace regexp %s with: " old-text))))
(directory (if arg
(file-name-as-directory
(read-directory-name "Replace regexp in directory: "))
(projectile-project-root)))
(files
;; We have to reject directories as a workaround to work with git submodules.
;;
;; We can't narrow the list of files with
;; `projectile-files-with-string' because those regexp tools
;; don't support Emacs regular expressions.
(-reject #'file-directory-p
(-map #'projectile-expand-root (projectile-dir-files directory)))))
(tags-query-replace old-text new-text nil (cons 'list files))))

(defun projectile-symbol-or-selection-at-point ()
Expand Down

0 comments on commit 077ecc7

Please sign in to comment.