Commit | Line | Data |
---|---|---|
318e2976 KS |
1 | ;;; grep.el --- run compiler as inferior of Emacs, parse error messages |
2 | ||
a60c4617 | 3 | ;; Copyright (C) 1985, 86, 87, 93, 94, 95, 96, 97, 98, 1999, 2001, 02, 2004 |
318e2976 KS |
4 | ;; Free Software Foundation, Inc. |
5 | ||
6 | ;; Author: Roland McGrath <roland@gnu.org> | |
7 | ;; Maintainer: FSF | |
8 | ;; Keywords: tools, processes | |
9 | ||
10 | ;; This file is part of GNU Emacs. | |
11 | ||
12 | ;; GNU Emacs is free software; you can redistribute it and/or modify | |
13 | ;; it under the terms of the GNU General Public License as published by | |
14 | ;; the Free Software Foundation; either version 2, or (at your option) | |
15 | ;; any later version. | |
16 | ||
17 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ;; GNU General Public License for more details. | |
21 | ||
22 | ;; You should have received a copy of the GNU General Public License | |
23 | ;; along with GNU Emacs; see the file COPYING. If not, write to the | |
24 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
25 | ;; Boston, MA 02111-1307, USA. | |
26 | ||
27 | ;;; Commentary: | |
28 | ||
29 | ;; This package provides the grep facilities documented in the Emacs | |
30 | ;; user's manual. | |
31 | ||
32 | ;;; Code: | |
33 | ||
34 | (require 'compile) | |
35 | ||
36 | (defgroup grep nil | |
37 | "Run compiler as inferior of Emacs, parse error messages." | |
38 | :group 'tools | |
39 | :group 'processes) | |
40 | ||
41 | ||
42 | ;;;###autoload | |
43 | (defcustom grep-window-height nil | |
44 | "*Number of lines in a grep window. If nil, use `compilation-window-height'." | |
45 | :type '(choice (const :tag "Default" nil) | |
46 | integer) | |
47 | :version "21.4" | |
48 | :group 'grep) | |
49 | ||
50 | (defcustom grep-auto-highlight t | |
51 | "*Specify how many grep matches to highlight (and parse) initially. | |
52 | \(Highlighting applies to an grep match when the mouse is over it.) | |
53 | If this is a number N, all grep matches in the first N lines | |
54 | are highlighted and parsed as soon as they arrive in Emacs. | |
55 | If t, highlight and parse the whole grep output as soon as it arrives. | |
56 | If nil, don't highlight or parse any of the grep buffer until you try to | |
57 | move to the error messages. | |
58 | ||
59 | Those grep matches which are not parsed and highlighted initially | |
60 | will be parsed and highlighted as soon as you try to move to them." | |
61 | :type '(choice (const :tag "All" t) | |
62 | (const :tag "None" nil) | |
63 | (integer :tag "First N lines")) | |
64 | :version "21.4" | |
65 | :group 'grep) | |
66 | ||
67 | (defcustom grep-scroll-output nil | |
68 | "*Non-nil to scroll the *grep* buffer window as output appears. | |
69 | ||
70 | Setting it causes the grep commands to put point at the end of their | |
71 | output window so that the end of the output is always visible rather | |
72 | than the begining." | |
73 | :type 'boolean | |
74 | :version "21.4" | |
75 | :group 'grep) | |
76 | ||
9b19858e | 77 | ;;;###autoload |
318e2976 KS |
78 | (defcustom grep-command nil |
79 | "The default grep command for \\[grep]. | |
80 | If the grep program used supports an option to always include file names | |
81 | in its output (such as the `-H' option to GNU grep), it's a good idea to | |
82 | include it when specifying `grep-command'. | |
83 | ||
84 | The default value of this variable is set up by `grep-compute-defaults'; | |
85 | call that function before using this variable in your program." | |
86 | :type '(choice string | |
87 | (const :tag "Not Set" nil)) | |
88 | :group 'grep) | |
89 | ||
90 | (defcustom grep-use-null-device 'auto-detect | |
91 | "If t, append the value of `null-device' to `grep' commands. | |
92 | This is done to ensure that the output of grep includes the filename of | |
93 | any match in the case where only a single file is searched, and is not | |
94 | necessary if the grep program used supports the `-H' option. | |
95 | ||
96 | The default value of this variable is set up by `grep-compute-defaults'; | |
97 | call that function before using this variable in your program." | |
98 | :type 'boolean | |
99 | :type '(choice (const :tag "Do Not Append Null Device" nil) | |
100 | (const :tag "Append Null Device" t) | |
101 | (other :tag "Not Set" auto-detect)) | |
102 | :group 'grep) | |
103 | ||
9b19858e | 104 | ;;;###autoload |
318e2976 KS |
105 | (defcustom grep-find-command nil |
106 | "The default find command for \\[grep-find]. | |
107 | The default value of this variable is set up by `grep-compute-defaults'; | |
108 | call that function before using this variable in your program." | |
109 | :type '(choice string | |
110 | (const :tag "Not Set" nil)) | |
111 | :group 'grep) | |
112 | ||
113 | (defcustom grep-tree-command nil | |
114 | "The default find command for \\[grep-tree]. | |
115 | The default value of this variable is set up by `grep-compute-defaults'; | |
116 | call that function before using this variable in your program. | |
117 | The following place holders should be present in the string: | |
118 | <D> - base directory for find | |
119 | <X> - find options to restrict or expand the directory list | |
120 | <F> - find options to limit the files matched | |
121 | <C> - place to put -i if case insensitive grep | |
122 | <R> - the regular expression searched for." | |
123 | :type '(choice string | |
124 | (const :tag "Not Set" nil)) | |
125 | :version "21.4" | |
126 | :group 'grep) | |
127 | ||
128 | (defcustom grep-tree-files-aliases '( | |
129 | ("ch" . "*.[ch]") | |
130 | ("c" . "*.c") | |
131 | ("h" . "*.h") | |
132 | ("m" . "[Mm]akefile*") | |
133 | ("asm" . "*.[sS]") | |
134 | ("all" . "*") | |
135 | ("el" . "*.el") | |
136 | ) | |
137 | "*Alist of aliases for the FILES argument to `grep-tree'." | |
138 | :type 'alist | |
139 | :group 'grep) | |
140 | ||
141 | (defcustom grep-tree-ignore-case t | |
142 | "*If non-nil, `grep-tree' ignores case in matches." | |
143 | :type 'boolean | |
144 | :group 'grep) | |
145 | ||
146 | (defcustom grep-tree-ignore-CVS-directories t | |
147 | "*If non-nil, `grep-tree' does no recurse into CVS directories." | |
148 | :type 'boolean | |
149 | :group 'grep) | |
150 | ||
bb72b9d0 DP |
151 | (defcustom grep-error-screen-columns nil |
152 | "*If non-nil, column numbers in grep hits are screen columns. | |
153 | See `compilation-error-screen-columns'" | |
154 | :type '(choice (const :tag "Default" nil) | |
155 | integer) | |
156 | :version "21.4" | |
157 | :group 'grep) | |
158 | ||
318e2976 KS |
159 | ;;;###autoload |
160 | (defcustom grep-setup-hook nil | |
161 | "List of hook functions run by `grep-process-setup' (see `run-hooks')." | |
162 | :type 'hook | |
163 | :group 'grep) | |
164 | ||
165 | (defvar grep-mode-map | |
166 | (let ((map (cons 'keymap compilation-minor-mode-map))) | |
167 | (define-key map " " 'scroll-up) | |
168 | (define-key map "\^?" 'scroll-down) | |
169 | ||
05218277 RS |
170 | ;; This is intolerable -- rms |
171 | ;;; (define-key map [remap next-line] 'compilation-next-error) | |
172 | ;;; (define-key map [remap previous-line] 'compilation-previous-error) | |
318e2976 KS |
173 | |
174 | (define-key map "\r" 'compile-goto-error) ;; ? | |
175 | (define-key map "n" 'next-error-no-select) | |
176 | (define-key map "p" 'previous-error-no-select) | |
177 | (define-key map "{" 'compilation-previous-file) | |
178 | (define-key map "}" 'compilation-next-file) | |
179 | (define-key map "\t" 'compilation-next-file) | |
180 | ||
181 | ;; Set up the menu-bar | |
182 | (define-key map [menu-bar grep] | |
183 | (cons "Grep" (make-sparse-keymap "Grep"))) | |
184 | ||
185 | (define-key map [menu-bar grep compilation-kill-compilation] | |
186 | '("Kill Grep" . kill-compilation)) | |
187 | (define-key map [menu-bar grep compilation-separator2] | |
188 | '("----" . nil)) | |
189 | (define-key map [menu-bar grep compilation-compile] | |
190 | '("Compile..." . compile)) | |
191 | (define-key map [menu-bar grep compilation-grep] | |
192 | '("Another grep" . grep)) | |
193 | (define-key map [menu-bar grep compilation-recompile] | |
194 | '("Repeat grep" . recompile)) | |
195 | (define-key map [menu-bar grep compilation-separator2] | |
196 | '("----" . nil)) | |
197 | (define-key map [menu-bar grep compilation-first-error] | |
198 | '("First Match" . first-error)) | |
199 | (define-key map [menu-bar grep compilation-previous-error] | |
200 | '("Previous Match" . previous-error)) | |
201 | (define-key map [menu-bar grep compilation-next-error] | |
202 | '("Next Match" . next-error)) | |
203 | map) | |
204 | "Keymap for grep buffers. | |
205 | `compilation-minor-mode-map' is a cdr of this.") | |
206 | ||
ab55f76f SM |
207 | (defalias 'kill-grep 'kill-compilation) |
208 | ||
318e2976 KS |
209 | ;;;; TODO --- refine this!! |
210 | ||
5f032b50 KS |
211 | ;;; (defcustom grep-use-compilation-buffer t |
212 | ;;; "When non-nil, grep specific commands update `compilation-last-buffer'. | |
213 | ;;; This means that standard compile commands like \\[next-error] and \\[compile-goto-error] | |
214 | ;;; can be used to navigate between grep matches (the default). | |
215 | ;;; Otherwise, the grep specific commands like \\[grep-next-match] must | |
216 | ;;; be used to navigate between grep matches." | |
217 | ;;; :type 'boolean | |
218 | ;;; :group 'grep) | |
318e2976 KS |
219 | |
220 | ;; override compilation-last-buffer | |
221 | (defvar grep-last-buffer nil | |
222 | "The most recent grep buffer. | |
223 | A grep buffer becomes most recent when its process is started | |
224 | or when it is used with \\[grep-next-match]. | |
225 | Notice that using \\[next-error] or \\[compile-goto-error] modifies | |
226 | `complation-last-buffer' rather than `grep-last-buffer'.") | |
227 | ||
9b19858e | 228 | ;;;###autoload |
318e2976 | 229 | (defvar grep-regexp-alist |
bb72b9d0 DP |
230 | '(("^\\(.+?\\)[:( \t]+\ |
231 | \\([0-9]+\\)\\([.:]?\\)\\([0-9]+\\)?\ | |
232 | \\(?:-\\(?:\\([0-9]+\\)\\3\\)?\\.?\\([0-9]+\\)?\\)?[:) \t]" 1 (2 . 5) (4 . 6)) | |
ab55f76f | 233 | ("^Binary file \\(.+\\) matches$" 1 nil nil 1)) |
318e2976 KS |
234 | "Regexp used to match grep hits. See `compilation-error-regexp-alist'.") |
235 | ||
ab55f76f SM |
236 | (defvar grep-error "grep hit" |
237 | "Message to print when no matches are found.") | |
238 | ||
239 | ;; Reverse the colors because grep hits are not errors (though we jump there | |
240 | ;; with `next-error'), and unreadable files can't be gone to. | |
241 | (defvar grep-hit-face compilation-info-face | |
242 | "Face name to use for grep hits.") | |
243 | ||
244 | (defvar grep-error-face compilation-error-face | |
245 | "Face name to use for grep error messages.") | |
246 | ||
247 | (defvar grep-mode-font-lock-keywords | |
248 | '(;; Command output lines. | |
249 | ("^\\([A-Za-z_0-9/\.+-]+\\)[ \t]*:" 1 font-lock-function-name-face) | |
250 | (": \\(.+\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or address\\)\\)$" | |
251 | 1 grep-error-face) | |
252 | ;; remove match from grep-regexp-alist before fontifying | |
253 | ("^Grep finished \\(?:(\\(matches found\\))\\|with \\(no matches found\\)\\).*" | |
254 | (0 '(face nil message nil help-echo nil mouse-face nil) t) | |
255 | (1 grep-hit-face nil t) | |
256 | (2 grep-error-face nil t)) | |
257 | ("^Grep \\(exited abnormally\\) with code \\([0-9]+\\).*" | |
258 | (0 '(face nil message nil help-echo nil mouse-face nil) t) | |
259 | (1 compilation-warning-face) | |
260 | (2 compilation-line-face))) | |
261 | "Additional things to highlight in grep output. | |
262 | This gets tacked on the end of the generated expressions.") | |
263 | ||
9b19858e | 264 | ;;;###autoload |
318e2976 KS |
265 | (defvar grep-program |
266 | ;; Currently zgrep has trouble. It runs egrep instead of grep, | |
267 | ;; and it doesn't pass along long options right. | |
268 | "grep" | |
269 | ;; (if (equal (condition-case nil ; in case "zgrep" isn't in exec-path | |
270 | ;; (call-process "zgrep" nil nil nil | |
271 | ;; "foo" null-device) | |
272 | ;; (error nil)) | |
273 | ;; 1) | |
274 | ;; "zgrep" | |
275 | ;; "grep") | |
276 | "The default grep program for `grep-command' and `grep-find-command'. | |
277 | This variable's value takes effect when `grep-compute-defaults' is called.") | |
278 | ||
9b19858e | 279 | ;;;###autoload |
318e2976 KS |
280 | (defvar find-program "find" |
281 | "The default find program for `grep-find-command'. | |
282 | This variable's value takes effect when `grep-compute-defaults' is called.") | |
283 | ||
9b19858e | 284 | ;;;###autoload |
318e2976 KS |
285 | (defvar grep-find-use-xargs nil |
286 | "Whether \\[grep-find] uses the `xargs' utility by default. | |
287 | ||
288 | If nil, it uses `find -exec'; if `gnu', it uses `find -print0' and `xargs -0'; | |
289 | if not nil and not `gnu', it uses `find -print' and `xargs'. | |
290 | ||
291 | This variable's value takes effect when `grep-compute-defaults' is called.") | |
292 | ||
293 | ;; History of grep commands. | |
9b19858e | 294 | ;;;###autoload |
318e2976 | 295 | (defvar grep-history nil) |
9b19858e | 296 | ;;;###autoload |
318e2976 KS |
297 | (defvar grep-find-history nil) |
298 | ||
9b19858e | 299 | ;;;###autoload |
318e2976 KS |
300 | (defun grep-process-setup () |
301 | "Setup compilation variables and buffer for `grep'. | |
ab55f76f | 302 | Set up `compilation-exit-message-function' and run `grep-setup-hook'." |
318e2976 KS |
303 | (set (make-local-variable 'compilation-exit-message-function) |
304 | (lambda (status code msg) | |
305 | (if (eq status 'exit) | |
306 | (cond ((zerop code) | |
307 | '("finished (matches found)\n" . "matched")) | |
308 | ((= code 1) | |
309 | '("finished with no matches found\n" . "no match")) | |
310 | (t | |
311 | (cons msg code))) | |
312 | (cons msg code)))) | |
318e2976 KS |
313 | (run-hooks 'grep-setup-hook)) |
314 | ||
9b19858e | 315 | ;;;###autoload |
318e2976 KS |
316 | (defun grep-compute-defaults () |
317 | (unless (or (not grep-use-null-device) (eq grep-use-null-device t)) | |
318 | (setq grep-use-null-device | |
319 | (with-temp-buffer | |
320 | (let ((hello-file (expand-file-name "HELLO" data-directory))) | |
321 | (not | |
322 | (and (equal (condition-case nil | |
323 | (if grep-command | |
324 | ;; `grep-command' is already set, so | |
325 | ;; use that for testing. | |
326 | (call-process-shell-command | |
327 | grep-command nil t nil | |
328 | "^English" hello-file) | |
329 | ;; otherwise use `grep-program' | |
330 | (call-process grep-program nil t nil | |
331 | "-nH" "^English" hello-file)) | |
332 | (error nil)) | |
333 | 0) | |
334 | (progn | |
335 | (goto-char (point-min)) | |
336 | (looking-at | |
337 | (concat (regexp-quote hello-file) | |
338 | ":[0-9]+:English"))))))))) | |
339 | (unless grep-command | |
340 | (setq grep-command | |
341 | (let ((required-options (if grep-use-null-device "-n" "-nH"))) | |
342 | (if (equal (condition-case nil ; in case "grep" isn't in exec-path | |
343 | (call-process grep-program nil nil nil | |
344 | "-e" "foo" null-device) | |
345 | (error nil)) | |
346 | 1) | |
347 | (format "%s %s -e " grep-program required-options) | |
348 | (format "%s %s " grep-program required-options))))) | |
349 | (unless grep-find-use-xargs | |
350 | (setq grep-find-use-xargs | |
351 | (if (and | |
352 | (equal (call-process "find" nil nil nil | |
353 | null-device "-print0") | |
354 | 0) | |
355 | (equal (call-process "xargs" nil nil nil | |
356 | "-0" "-e" "echo") | |
357 | 0)) | |
358 | 'gnu))) | |
359 | (unless grep-find-command | |
360 | (setq grep-find-command | |
24fa1cf1 | 361 | (cond ((eq grep-find-use-xargs 'gnu) |
318e2976 KS |
362 | (format "%s . -type f -print0 | xargs -0 -e %s" |
363 | find-program grep-command)) | |
364 | (grep-find-use-xargs | |
365 | (format "%s . -type f -print | xargs %s" | |
366 | find-program grep-command)) | |
367 | (t (cons (format "%s . -type f -exec %s {} %s \\;" | |
368 | find-program grep-command null-device) | |
369 | (+ 22 (length grep-command))))))) | |
370 | (unless grep-tree-command | |
371 | (setq grep-tree-command | |
372 | (let* ((glen (length grep-program)) | |
373 | (gcmd (concat grep-program " <C>" (substring grep-command glen)))) | |
374 | (cond ((eq grep-find-use-xargs 'gnu) | |
375 | (format "%s <D> <X> -type f <F> -print0 | xargs -0 -e %s <R>" | |
376 | find-program gcmd)) | |
377 | (grep-find-use-xargs | |
378 | (format "%s <D> <X> -type f <F> -print | xargs %s <R>" | |
379 | find-program gcmd)) | |
380 | (t (format "%s <D> <X> -type f <F> -exec %s <R> {} %s \\;" | |
381 | find-program gcmd null-device))))))) | |
382 | ||
383 | (defun grep-default-command () | |
384 | (let ((tag-default | |
385 | (funcall (or find-tag-default-function | |
386 | (get major-mode 'find-tag-default-function) | |
8a9cad92 | 387 | 'find-tag-default))) |
318e2976 KS |
388 | (sh-arg-re "\\(\\(?:\"\\(?:[^\"]\\|\\\\\"\\)+\"\\|'[^']+'\\|[^\"' \t\n]\\)+\\)") |
389 | (grep-default (or (car grep-history) grep-command))) | |
390 | ;; Replace the thing matching for with that around cursor. | |
391 | (when (or (string-match | |
392 | (concat "[^ ]+\\s +\\(?:-[^ ]+\\s +\\)*" | |
393 | sh-arg-re "\\(\\s +\\(\\S +\\)\\)?") | |
394 | grep-default) | |
395 | ;; If the string is not yet complete. | |
396 | (string-match "\\(\\)\\'" grep-default)) | |
397 | (unless (or (not (stringp buffer-file-name)) | |
398 | (when (match-beginning 2) | |
399 | (save-match-data | |
400 | (string-match | |
401 | (wildcard-to-regexp | |
402 | (file-name-nondirectory | |
403 | (match-string 3 grep-default))) | |
404 | (file-name-nondirectory buffer-file-name))))) | |
405 | (setq grep-default (concat (substring grep-default | |
406 | 0 (match-beginning 2)) | |
407 | " *." | |
408 | (file-name-extension buffer-file-name)))) | |
409 | (replace-match (or tag-default "") t t grep-default 1)))) | |
410 | ||
411 | ;;;###autoload | |
412 | (defun grep (command-args &optional highlight-regexp) | |
413 | "Run grep, with user-specified args, and collect output in a buffer. | |
414 | While grep runs asynchronously, you can use \\[next-error] (M-x next-error), | |
c426452f | 415 | or \\<grep-mode-map>\\[compile-goto-error] in the grep \ |
318e2976 KS |
416 | output buffer, to go to the lines |
417 | where grep found matches. | |
418 | ||
419 | This command uses a special history list for its COMMAND-ARGS, so you can | |
420 | easily repeat a grep command. | |
421 | ||
422 | A prefix argument says to default the argument based upon the current | |
423 | tag the cursor is over, substituting it into the last grep command | |
424 | in the grep command history (or into `grep-command' | |
425 | if that history list is empty). | |
426 | ||
427 | If specified, optional second arg HIGHLIGHT-REGEXP is the regexp to | |
428 | temporarily highlight in visited source lines." | |
429 | (interactive | |
430 | (progn | |
431 | (unless (and grep-command | |
432 | (or (not grep-use-null-device) (eq grep-use-null-device t))) | |
433 | (grep-compute-defaults)) | |
434 | (let ((default (grep-default-command))) | |
435 | (list (read-from-minibuffer "Run grep (like this): " | |
436 | (if current-prefix-arg | |
437 | default grep-command) | |
438 | nil nil 'grep-history | |
439 | (if current-prefix-arg nil default)))))) | |
440 | ||
441 | ;; Setting process-setup-function makes exit-message-function work | |
442 | ;; even when async processes aren't supported. | |
ab55f76f SM |
443 | (let ((compilation-process-setup-function 'grep-process-setup)) |
444 | (compilation-start (if (and grep-use-null-device null-device) | |
445 | (concat command-args " " null-device) | |
446 | command-args) | |
447 | 'grep-mode nil highlight-regexp))) | |
448 | ||
449 | ;;;###autoload (autoload 'grep-mode "grep" nil t) | |
7f0d4d29 JD |
450 | (define-compilation-mode grep-mode "Grep" |
451 | "Sets `grep-last-buffer' and `compilation-window-height'." | |
452 | (setq grep-last-buffer (current-buffer)) | |
453 | (set (make-local-variable 'compilation-error-face) | |
454 | grep-hit-face) | |
455 | (set (make-local-variable 'compilation-error-regexp-alist) | |
456 | grep-regexp-alist)) | |
318e2976 | 457 | |
318e2976 KS |
458 | ;;;###autoload |
459 | (defun grep-find (command-args) | |
460 | "Run grep via find, with user-specified args COMMAND-ARGS. | |
461 | Collect output in a buffer. | |
462 | While find runs asynchronously, you can use the \\[next-error] command | |
463 | to find the text that grep hits refer to. | |
464 | ||
465 | This command uses a special history list for its arguments, so you can | |
466 | easily repeat a find command." | |
467 | (interactive | |
468 | (progn | |
469 | (unless grep-find-command | |
470 | (grep-compute-defaults)) | |
87f54c05 RS |
471 | (if grep-find-command |
472 | (list (read-from-minibuffer "Run find (like this): " | |
473 | grep-find-command nil nil | |
474 | 'grep-find-history)) | |
475 | ;; No default was set | |
476 | (read-string | |
477 | "compile.el: No `grep-find-command' command available. Press RET.") | |
478 | (list nil)))) | |
479 | (when (and grep-find-command command-args) | |
480 | (let ((null-device nil)) ; see grep | |
481 | (grep command-args)))) | |
318e2976 KS |
482 | |
483 | (defun grep-expand-command-macros (command &optional regexp files dir excl case-fold) | |
484 | "Patch grep COMMAND replacing <D>, etc." | |
485 | (setq command | |
486 | (replace-regexp-in-string "<D>" | |
487 | (or dir ".") command t t)) | |
488 | (setq command | |
489 | (replace-regexp-in-string "<X>" | |
490 | (or excl "") command t t)) | |
491 | (setq command | |
492 | (replace-regexp-in-string "<F>" | |
493 | (or files "") command t t)) | |
494 | (setq command | |
495 | (replace-regexp-in-string "<C>" | |
496 | (if case-fold "-i" "") command t t)) | |
497 | (setq command | |
498 | (replace-regexp-in-string "<R>" | |
499 | (or regexp "") command t t)) | |
500 | command) | |
501 | ||
502 | (defvar grep-tree-last-regexp "") | |
503 | (defvar grep-tree-last-files (car (car grep-tree-files-aliases))) | |
504 | ||
505 | ;;;###autoload | |
506 | (defun grep-tree (regexp files dir &optional subdirs) | |
507 | "Grep for REGEXP in FILES in directory tree rooted at DIR. | |
508 | Collect output in a buffer. | |
509 | Interactively, prompt separately for each search parameter. | |
510 | With prefix arg, reuse previous REGEXP. | |
511 | The search is limited to file names matching shell pattern FILES. | |
512 | FILES may use abbreviations defined in `grep-tree-files-aliases', e.g. | |
513 | entering `ch' is equivalent to `*.[ch]'. | |
514 | ||
515 | While find runs asynchronously, you can use the \\[next-error] command | |
516 | to find the text that grep hits refer to. | |
517 | ||
518 | This command uses a special history list for its arguments, so you can | |
519 | easily repeat a find command. | |
520 | ||
521 | When used non-interactively, optional arg SUBDIRS limits the search to | |
522 | those sub directories of DIR." | |
523 | (interactive | |
524 | (let* ((regexp | |
525 | (if current-prefix-arg | |
526 | grep-tree-last-regexp | |
527 | (let* ((default (current-word)) | |
528 | (spec (read-string | |
529 | (concat "Search for" | |
530 | (if (and default (> (length default) 0)) | |
531 | (format " (default %s): " default) ": "))))) | |
532 | (if (equal spec "") default spec)))) | |
533 | (files | |
534 | (read-string (concat "Search for \"" regexp "\" in files (default " grep-tree-last-files "): "))) | |
535 | (dir | |
f833e227 | 536 | (read-directory-name "Base directory: " nil default-directory t))) |
318e2976 KS |
537 | (list regexp files dir))) |
538 | (unless grep-tree-command | |
539 | (grep-compute-defaults)) | |
540 | (unless (and (stringp files) (> (length files) 0)) | |
541 | (setq files grep-tree-last-files)) | |
542 | (when files | |
543 | (setq grep-tree-last-files files) | |
544 | (let ((mf (assoc files grep-tree-files-aliases))) | |
545 | (if mf | |
546 | (setq files (cdr mf))))) | |
547 | (let ((command-args (grep-expand-command-macros | |
548 | grep-tree-command | |
549 | (setq grep-tree-last-regexp regexp) | |
550 | (and files (concat "-name '" files "'")) | |
551 | (if subdirs | |
552 | (if (stringp subdirs) | |
553 | subdirs | |
554 | (mapconcat 'identity subdirs " ")) | |
555 | nil) ;; we change default-directory to dir | |
556 | (and grep-tree-ignore-CVS-directories "-path '*/CVS' -prune -o ") | |
557 | grep-tree-ignore-case)) | |
f833e227 | 558 | (default-directory (file-name-as-directory (expand-file-name dir))) |
318e2976 KS |
559 | (null-device nil)) ; see grep |
560 | (grep command-args regexp))) | |
561 | ||
562 | ||
563 | (provide 'grep) | |
564 | ||
7239c217 | 565 | ;;; arch-tag: 5a5b9169-a79d-4f38-9c38-f69615f39c4d |
06626cf2 | 566 | ;;; grep.el ends here |