Commit | Line | Data |
---|---|---|
318e2976 KS |
1 | ;;; grep.el --- run compiler as inferior of Emacs, parse error messages |
2 | ||
1bcd42e2 JL |
3 | ;; Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999, |
4 | ;; 2001, 2002, 2004 Free Software Foundation, Inc. | |
318e2976 KS |
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) | |
bf247b6e | 47 | :version "22.1" |
318e2976 KS |
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")) | |
bf247b6e | 64 | :version "22.1" |
318e2976 KS |
65 | :group 'grep) |
66 | ||
00889cf9 | 67 | (defcustom grep-highlight-matches 'auto-detect |
a58c94a2 | 68 | "If t, use special markers to highlight grep matches. |
277df088 JL |
69 | |
70 | Some grep programs are able to surround matches with special | |
71 | markers in grep output. Such markers can be used to highlight | |
72 | matches in grep mode. | |
73 | ||
74 | This option sets the environment variable GREP_COLOR to specify | |
75 | markers for highlighting and GREP_OPTIONS to add the --color | |
76 | option in front of any explicit grep options before starting | |
a58c94a2 JL |
77 | the grep. |
78 | ||
79 | The default value of this variable is set up by `grep-compute-defaults'; | |
80 | call that function before using this variable in your program." | |
00889cf9 JL |
81 | :type '(choice (const :tag "Do not highlight matches with grep markers" nil) |
82 | (const :tag "Highlight matches with grep markers" t) | |
83 | (other :tag "Not Set" auto-detect)) | |
bf247b6e | 84 | :version "22.1" |
277df088 JL |
85 | :group 'grep) |
86 | ||
318e2976 KS |
87 | (defcustom grep-scroll-output nil |
88 | "*Non-nil to scroll the *grep* buffer window as output appears. | |
89 | ||
90 | Setting it causes the grep commands to put point at the end of their | |
91 | output window so that the end of the output is always visible rather | |
92 | than the begining." | |
93 | :type 'boolean | |
bf247b6e | 94 | :version "22.1" |
318e2976 KS |
95 | :group 'grep) |
96 | ||
9b19858e | 97 | ;;;###autoload |
318e2976 KS |
98 | (defcustom grep-command nil |
99 | "The default grep command for \\[grep]. | |
100 | If the grep program used supports an option to always include file names | |
101 | in its output (such as the `-H' option to GNU grep), it's a good idea to | |
102 | include it when specifying `grep-command'. | |
103 | ||
104 | The default value of this variable is set up by `grep-compute-defaults'; | |
105 | call that function before using this variable in your program." | |
106 | :type '(choice string | |
107 | (const :tag "Not Set" nil)) | |
108 | :group 'grep) | |
109 | ||
110 | (defcustom grep-use-null-device 'auto-detect | |
111 | "If t, append the value of `null-device' to `grep' commands. | |
112 | This is done to ensure that the output of grep includes the filename of | |
113 | any match in the case where only a single file is searched, and is not | |
114 | necessary if the grep program used supports the `-H' option. | |
115 | ||
116 | The default value of this variable is set up by `grep-compute-defaults'; | |
117 | call that function before using this variable in your program." | |
318e2976 KS |
118 | :type '(choice (const :tag "Do Not Append Null Device" nil) |
119 | (const :tag "Append Null Device" t) | |
120 | (other :tag "Not Set" auto-detect)) | |
121 | :group 'grep) | |
122 | ||
9b19858e | 123 | ;;;###autoload |
318e2976 KS |
124 | (defcustom grep-find-command nil |
125 | "The default find command for \\[grep-find]. | |
126 | The default value of this variable is set up by `grep-compute-defaults'; | |
127 | call that function before using this variable in your program." | |
128 | :type '(choice string | |
129 | (const :tag "Not Set" nil)) | |
130 | :group 'grep) | |
131 | ||
132 | (defcustom grep-tree-command nil | |
133 | "The default find command for \\[grep-tree]. | |
134 | The default value of this variable is set up by `grep-compute-defaults'; | |
135 | call that function before using this variable in your program. | |
136 | The following place holders should be present in the string: | |
137 | <D> - base directory for find | |
138 | <X> - find options to restrict or expand the directory list | |
139 | <F> - find options to limit the files matched | |
140 | <C> - place to put -i if case insensitive grep | |
141 | <R> - the regular expression searched for." | |
142 | :type '(choice string | |
143 | (const :tag "Not Set" nil)) | |
bf247b6e | 144 | :version "22.1" |
318e2976 KS |
145 | :group 'grep) |
146 | ||
147 | (defcustom grep-tree-files-aliases '( | |
148 | ("ch" . "*.[ch]") | |
149 | ("c" . "*.c") | |
150 | ("h" . "*.h") | |
151 | ("m" . "[Mm]akefile*") | |
152 | ("asm" . "*.[sS]") | |
153 | ("all" . "*") | |
154 | ("el" . "*.el") | |
155 | ) | |
156 | "*Alist of aliases for the FILES argument to `grep-tree'." | |
157 | :type 'alist | |
158 | :group 'grep) | |
159 | ||
160 | (defcustom grep-tree-ignore-case t | |
161 | "*If non-nil, `grep-tree' ignores case in matches." | |
162 | :type 'boolean | |
163 | :group 'grep) | |
164 | ||
165 | (defcustom grep-tree-ignore-CVS-directories t | |
166 | "*If non-nil, `grep-tree' does no recurse into CVS directories." | |
167 | :type 'boolean | |
168 | :group 'grep) | |
169 | ||
bb72b9d0 DP |
170 | (defcustom grep-error-screen-columns nil |
171 | "*If non-nil, column numbers in grep hits are screen columns. | |
172 | See `compilation-error-screen-columns'" | |
173 | :type '(choice (const :tag "Default" nil) | |
174 | integer) | |
bf247b6e | 175 | :version "22.1" |
bb72b9d0 DP |
176 | :group 'grep) |
177 | ||
318e2976 KS |
178 | ;;;###autoload |
179 | (defcustom grep-setup-hook nil | |
180 | "List of hook functions run by `grep-process-setup' (see `run-hooks')." | |
181 | :type 'hook | |
182 | :group 'grep) | |
183 | ||
184 | (defvar grep-mode-map | |
185 | (let ((map (cons 'keymap compilation-minor-mode-map))) | |
186 | (define-key map " " 'scroll-up) | |
187 | (define-key map "\^?" 'scroll-down) | |
0443d889 | 188 | (define-key map "\C-c\C-f" 'next-error-follow-minor-mode) |
318e2976 | 189 | |
05218277 RS |
190 | ;; This is intolerable -- rms |
191 | ;;; (define-key map [remap next-line] 'compilation-next-error) | |
192 | ;;; (define-key map [remap previous-line] 'compilation-previous-error) | |
318e2976 KS |
193 | |
194 | (define-key map "\r" 'compile-goto-error) ;; ? | |
195 | (define-key map "n" 'next-error-no-select) | |
196 | (define-key map "p" 'previous-error-no-select) | |
197 | (define-key map "{" 'compilation-previous-file) | |
198 | (define-key map "}" 'compilation-next-file) | |
199 | (define-key map "\t" 'compilation-next-file) | |
200 | ||
201 | ;; Set up the menu-bar | |
202 | (define-key map [menu-bar grep] | |
203 | (cons "Grep" (make-sparse-keymap "Grep"))) | |
204 | ||
205 | (define-key map [menu-bar grep compilation-kill-compilation] | |
206 | '("Kill Grep" . kill-compilation)) | |
207 | (define-key map [menu-bar grep compilation-separator2] | |
208 | '("----" . nil)) | |
209 | (define-key map [menu-bar grep compilation-compile] | |
210 | '("Compile..." . compile)) | |
211 | (define-key map [menu-bar grep compilation-grep] | |
212 | '("Another grep" . grep)) | |
213 | (define-key map [menu-bar grep compilation-recompile] | |
214 | '("Repeat grep" . recompile)) | |
215 | (define-key map [menu-bar grep compilation-separator2] | |
216 | '("----" . nil)) | |
217 | (define-key map [menu-bar grep compilation-first-error] | |
218 | '("First Match" . first-error)) | |
219 | (define-key map [menu-bar grep compilation-previous-error] | |
220 | '("Previous Match" . previous-error)) | |
221 | (define-key map [menu-bar grep compilation-next-error] | |
222 | '("Next Match" . next-error)) | |
223 | map) | |
224 | "Keymap for grep buffers. | |
225 | `compilation-minor-mode-map' is a cdr of this.") | |
226 | ||
ab55f76f SM |
227 | (defalias 'kill-grep 'kill-compilation) |
228 | ||
318e2976 KS |
229 | ;;;; TODO --- refine this!! |
230 | ||
5f032b50 KS |
231 | ;;; (defcustom grep-use-compilation-buffer t |
232 | ;;; "When non-nil, grep specific commands update `compilation-last-buffer'. | |
233 | ;;; This means that standard compile commands like \\[next-error] and \\[compile-goto-error] | |
234 | ;;; can be used to navigate between grep matches (the default). | |
235 | ;;; Otherwise, the grep specific commands like \\[grep-next-match] must | |
236 | ;;; be used to navigate between grep matches." | |
237 | ;;; :type 'boolean | |
238 | ;;; :group 'grep) | |
318e2976 KS |
239 | |
240 | ;; override compilation-last-buffer | |
241 | (defvar grep-last-buffer nil | |
242 | "The most recent grep buffer. | |
243 | A grep buffer becomes most recent when its process is started | |
244 | or when it is used with \\[grep-next-match]. | |
245 | Notice that using \\[next-error] or \\[compile-goto-error] modifies | |
246 | `complation-last-buffer' rather than `grep-last-buffer'.") | |
247 | ||
9b19858e | 248 | ;;;###autoload |
318e2976 | 249 | (defvar grep-regexp-alist |
bde94a7f RS |
250 | ;; rms: I removed the code to match parens around the line number |
251 | ;; because it causes confusion and so we will find out if anyone needs it. | |
252 | ;; It causes confusion with a file name that contains a number in parens. | |
253 | '(("^\\(.+?\\)[: \t]+\ | |
bb72b9d0 | 254 | \\([0-9]+\\)\\([.:]?\\)\\([0-9]+\\)?\ |
bde94a7f | 255 | \\(?:-\\(?:\\([0-9]+\\)\\3\\)?\\.?\\([0-9]+\\)?\\)?[: \t]" 1 (2 . 5) (4 . 6)) |
e7eaabae | 256 | ("^\\(.+?\\)[:(]+\\([0-9]+\\)\\([:)]\\).*?\\(\033\\[01;41m\\)\\(.*?\\)\\(\033\\[00m\\(\033\\[K\\)?\\)" |
277df088 | 257 | 1 2 |
1bcd42e2 | 258 | ;; Calculate column positions (beg . end) of first grep match on a line |
277df088 JL |
259 | ((lambda () |
260 | (setq compilation-error-screen-columns nil) | |
261 | (- (match-beginning 5) (match-end 3) 8)) | |
262 | . | |
1bcd42e2 | 263 | (lambda () (- (match-end 5) (match-end 3) 8)))) |
ab55f76f | 264 | ("^Binary file \\(.+\\) matches$" 1 nil nil 1)) |
318e2976 KS |
265 | "Regexp used to match grep hits. See `compilation-error-regexp-alist'.") |
266 | ||
ab55f76f SM |
267 | (defvar grep-error "grep hit" |
268 | "Message to print when no matches are found.") | |
269 | ||
270 | ;; Reverse the colors because grep hits are not errors (though we jump there | |
271 | ;; with `next-error'), and unreadable files can't be gone to. | |
272 | (defvar grep-hit-face compilation-info-face | |
273 | "Face name to use for grep hits.") | |
274 | ||
275 | (defvar grep-error-face compilation-error-face | |
276 | "Face name to use for grep error messages.") | |
277 | ||
f30c45e1 JL |
278 | (defvar grep-match-face 'match |
279 | "Face name to use for grep matches.") | |
280 | ||
ab55f76f SM |
281 | (defvar grep-mode-font-lock-keywords |
282 | '(;; Command output lines. | |
283 | ("^\\([A-Za-z_0-9/\.+-]+\\)[ \t]*:" 1 font-lock-function-name-face) | |
284 | (": \\(.+\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or address\\)\\)$" | |
285 | 1 grep-error-face) | |
286 | ;; remove match from grep-regexp-alist before fontifying | |
287 | ("^Grep finished \\(?:(\\(matches found\\))\\|with \\(no matches found\\)\\).*" | |
288 | (0 '(face nil message nil help-echo nil mouse-face nil) t) | |
289 | (1 grep-hit-face nil t) | |
290 | (2 grep-error-face nil t)) | |
291 | ("^Grep \\(exited abnormally\\) with code \\([0-9]+\\).*" | |
292 | (0 '(face nil message nil help-echo nil mouse-face nil) t) | |
293 | (1 compilation-warning-face) | |
1bcd42e2 JL |
294 | (2 compilation-line-face)) |
295 | ;; Highlight grep matches and delete markers | |
e7eaabae | 296 | ("\\(\033\\[01;41m\\)\\(.*?\\)\\(\033\\[00m\\(\033\\[K\\)?\\)" |
f30c45e1 | 297 | (2 grep-match-face) |
1bcd42e2 JL |
298 | ((lambda (p)) |
299 | (progn | |
300 | ;; Delete markers with `replace-match' because it updates | |
301 | ;; the match-data, whereas `delete-region' would render it obsolete. | |
302 | (replace-match "" t t nil 3) | |
303 | (replace-match "" t t nil 1))))) | |
ab55f76f SM |
304 | "Additional things to highlight in grep output. |
305 | This gets tacked on the end of the generated expressions.") | |
306 | ||
9b19858e | 307 | ;;;###autoload |
318e2976 KS |
308 | (defvar grep-program |
309 | ;; Currently zgrep has trouble. It runs egrep instead of grep, | |
310 | ;; and it doesn't pass along long options right. | |
311 | "grep" | |
312 | ;; (if (equal (condition-case nil ; in case "zgrep" isn't in exec-path | |
313 | ;; (call-process "zgrep" nil nil nil | |
314 | ;; "foo" null-device) | |
315 | ;; (error nil)) | |
316 | ;; 1) | |
317 | ;; "zgrep" | |
318 | ;; "grep") | |
319 | "The default grep program for `grep-command' and `grep-find-command'. | |
320 | This variable's value takes effect when `grep-compute-defaults' is called.") | |
321 | ||
9b19858e | 322 | ;;;###autoload |
318e2976 KS |
323 | (defvar find-program "find" |
324 | "The default find program for `grep-find-command'. | |
325 | This variable's value takes effect when `grep-compute-defaults' is called.") | |
326 | ||
9b19858e | 327 | ;;;###autoload |
318e2976 KS |
328 | (defvar grep-find-use-xargs nil |
329 | "Whether \\[grep-find] uses the `xargs' utility by default. | |
330 | ||
331 | If nil, it uses `find -exec'; if `gnu', it uses `find -print0' and `xargs -0'; | |
332 | if not nil and not `gnu', it uses `find -print' and `xargs'. | |
333 | ||
334 | This variable's value takes effect when `grep-compute-defaults' is called.") | |
335 | ||
336 | ;; History of grep commands. | |
9b19858e | 337 | ;;;###autoload |
318e2976 | 338 | (defvar grep-history nil) |
9b19858e | 339 | ;;;###autoload |
318e2976 KS |
340 | (defvar grep-find-history nil) |
341 | ||
9b19858e | 342 | ;;;###autoload |
318e2976 KS |
343 | (defun grep-process-setup () |
344 | "Setup compilation variables and buffer for `grep'. | |
ab55f76f | 345 | Set up `compilation-exit-message-function' and run `grep-setup-hook'." |
a58c94a2 JL |
346 | (unless (or (not grep-highlight-matches) (eq grep-highlight-matches t)) |
347 | (grep-compute-defaults)) | |
348 | (when (eq grep-highlight-matches t) | |
277df088 JL |
349 | ;; Modify `process-environment' locally bound in `compilation-start' |
350 | (setenv "GREP_OPTIONS" (concat (getenv "GREP_OPTIONS") " --color=always")) | |
351 | (setenv "GREP_COLOR" "01;41")) | |
318e2976 KS |
352 | (set (make-local-variable 'compilation-exit-message-function) |
353 | (lambda (status code msg) | |
354 | (if (eq status 'exit) | |
355 | (cond ((zerop code) | |
356 | '("finished (matches found)\n" . "matched")) | |
357 | ((= code 1) | |
358 | '("finished with no matches found\n" . "no match")) | |
359 | (t | |
360 | (cons msg code))) | |
361 | (cons msg code)))) | |
318e2976 KS |
362 | (run-hooks 'grep-setup-hook)) |
363 | ||
9b19858e | 364 | ;;;###autoload |
318e2976 KS |
365 | (defun grep-compute-defaults () |
366 | (unless (or (not grep-use-null-device) (eq grep-use-null-device t)) | |
367 | (setq grep-use-null-device | |
368 | (with-temp-buffer | |
369 | (let ((hello-file (expand-file-name "HELLO" data-directory))) | |
370 | (not | |
371 | (and (equal (condition-case nil | |
372 | (if grep-command | |
373 | ;; `grep-command' is already set, so | |
374 | ;; use that for testing. | |
375 | (call-process-shell-command | |
376 | grep-command nil t nil | |
377 | "^English" hello-file) | |
378 | ;; otherwise use `grep-program' | |
379 | (call-process grep-program nil t nil | |
380 | "-nH" "^English" hello-file)) | |
381 | (error nil)) | |
382 | 0) | |
383 | (progn | |
384 | (goto-char (point-min)) | |
385 | (looking-at | |
386 | (concat (regexp-quote hello-file) | |
387 | ":[0-9]+:English"))))))))) | |
388 | (unless grep-command | |
389 | (setq grep-command | |
390 | (let ((required-options (if grep-use-null-device "-n" "-nH"))) | |
391 | (if (equal (condition-case nil ; in case "grep" isn't in exec-path | |
392 | (call-process grep-program nil nil nil | |
393 | "-e" "foo" null-device) | |
394 | (error nil)) | |
395 | 1) | |
396 | (format "%s %s -e " grep-program required-options) | |
397 | (format "%s %s " grep-program required-options))))) | |
398 | (unless grep-find-use-xargs | |
399 | (setq grep-find-use-xargs | |
400 | (if (and | |
401 | (equal (call-process "find" nil nil nil | |
402 | null-device "-print0") | |
403 | 0) | |
404 | (equal (call-process "xargs" nil nil nil | |
405 | "-0" "-e" "echo") | |
406 | 0)) | |
407 | 'gnu))) | |
408 | (unless grep-find-command | |
409 | (setq grep-find-command | |
24fa1cf1 | 410 | (cond ((eq grep-find-use-xargs 'gnu) |
318e2976 KS |
411 | (format "%s . -type f -print0 | xargs -0 -e %s" |
412 | find-program grep-command)) | |
413 | (grep-find-use-xargs | |
414 | (format "%s . -type f -print | xargs %s" | |
415 | find-program grep-command)) | |
416 | (t (cons (format "%s . -type f -exec %s {} %s \\;" | |
417 | find-program grep-command null-device) | |
418 | (+ 22 (length grep-command))))))) | |
419 | (unless grep-tree-command | |
420 | (setq grep-tree-command | |
421 | (let* ((glen (length grep-program)) | |
422 | (gcmd (concat grep-program " <C>" (substring grep-command glen)))) | |
423 | (cond ((eq grep-find-use-xargs 'gnu) | |
424 | (format "%s <D> <X> -type f <F> -print0 | xargs -0 -e %s <R>" | |
425 | find-program gcmd)) | |
426 | (grep-find-use-xargs | |
427 | (format "%s <D> <X> -type f <F> -print | xargs %s <R>" | |
428 | find-program gcmd)) | |
429 | (t (format "%s <D> <X> -type f <F> -exec %s <R> {} %s \\;" | |
00889cf9 JL |
430 | find-program gcmd null-device)))))) |
431 | (unless (or (not grep-highlight-matches) (eq grep-highlight-matches t)) | |
432 | (setq grep-highlight-matches | |
433 | (with-temp-buffer | |
a58c94a2 JL |
434 | (and (equal (condition-case nil |
435 | (call-process grep-program nil t nil "--help") | |
436 | (error nil)) | |
437 | 0) | |
438 | (progn | |
439 | (goto-char (point-min)) | |
440 | (search-forward "--color" nil t)) | |
441 | t))))) | |
318e2976 KS |
442 | |
443 | (defun grep-default-command () | |
444 | (let ((tag-default | |
02b73b97 TTN |
445 | (shell-quote-argument |
446 | (or (funcall (or find-tag-default-function | |
447 | (get major-mode 'find-tag-default-function) | |
448 | 'find-tag-default)) | |
449 | ""))) | |
318e2976 KS |
450 | (sh-arg-re "\\(\\(?:\"\\(?:[^\"]\\|\\\\\"\\)+\"\\|'[^']+'\\|[^\"' \t\n]\\)+\\)") |
451 | (grep-default (or (car grep-history) grep-command))) | |
452 | ;; Replace the thing matching for with that around cursor. | |
453 | (when (or (string-match | |
454 | (concat "[^ ]+\\s +\\(?:-[^ ]+\\s +\\)*" | |
455 | sh-arg-re "\\(\\s +\\(\\S +\\)\\)?") | |
456 | grep-default) | |
457 | ;; If the string is not yet complete. | |
458 | (string-match "\\(\\)\\'" grep-default)) | |
459 | (unless (or (not (stringp buffer-file-name)) | |
460 | (when (match-beginning 2) | |
461 | (save-match-data | |
462 | (string-match | |
463 | (wildcard-to-regexp | |
464 | (file-name-nondirectory | |
465 | (match-string 3 grep-default))) | |
466 | (file-name-nondirectory buffer-file-name))))) | |
467 | (setq grep-default (concat (substring grep-default | |
468 | 0 (match-beginning 2)) | |
469 | " *." | |
470 | (file-name-extension buffer-file-name)))) | |
02b73b97 | 471 | (replace-match tag-default t t grep-default 1)))) |
318e2976 KS |
472 | |
473 | ;;;###autoload | |
474 | (defun grep (command-args &optional highlight-regexp) | |
475 | "Run grep, with user-specified args, and collect output in a buffer. | |
476 | While grep runs asynchronously, you can use \\[next-error] (M-x next-error), | |
c426452f | 477 | or \\<grep-mode-map>\\[compile-goto-error] in the grep \ |
318e2976 KS |
478 | output buffer, to go to the lines |
479 | where grep found matches. | |
480 | ||
481 | This command uses a special history list for its COMMAND-ARGS, so you can | |
482 | easily repeat a grep command. | |
483 | ||
484 | A prefix argument says to default the argument based upon the current | |
485 | tag the cursor is over, substituting it into the last grep command | |
486 | in the grep command history (or into `grep-command' | |
487 | if that history list is empty). | |
488 | ||
489 | If specified, optional second arg HIGHLIGHT-REGEXP is the regexp to | |
490 | temporarily highlight in visited source lines." | |
491 | (interactive | |
492 | (progn | |
493 | (unless (and grep-command | |
494 | (or (not grep-use-null-device) (eq grep-use-null-device t))) | |
495 | (grep-compute-defaults)) | |
496 | (let ((default (grep-default-command))) | |
497 | (list (read-from-minibuffer "Run grep (like this): " | |
498 | (if current-prefix-arg | |
499 | default grep-command) | |
500 | nil nil 'grep-history | |
501 | (if current-prefix-arg nil default)))))) | |
502 | ||
503 | ;; Setting process-setup-function makes exit-message-function work | |
504 | ;; even when async processes aren't supported. | |
ab55f76f SM |
505 | (let ((compilation-process-setup-function 'grep-process-setup)) |
506 | (compilation-start (if (and grep-use-null-device null-device) | |
507 | (concat command-args " " null-device) | |
508 | command-args) | |
509 | 'grep-mode nil highlight-regexp))) | |
510 | ||
7f0d4d29 JD |
511 | (define-compilation-mode grep-mode "Grep" |
512 | "Sets `grep-last-buffer' and `compilation-window-height'." | |
513 | (setq grep-last-buffer (current-buffer)) | |
514 | (set (make-local-variable 'compilation-error-face) | |
515 | grep-hit-face) | |
516 | (set (make-local-variable 'compilation-error-regexp-alist) | |
517 | grep-regexp-alist)) | |
318e2976 | 518 | |
318e2976 KS |
519 | ;;;###autoload |
520 | (defun grep-find (command-args) | |
521 | "Run grep via find, with user-specified args COMMAND-ARGS. | |
522 | Collect output in a buffer. | |
523 | While find runs asynchronously, you can use the \\[next-error] command | |
524 | to find the text that grep hits refer to. | |
525 | ||
526 | This command uses a special history list for its arguments, so you can | |
527 | easily repeat a find command." | |
528 | (interactive | |
529 | (progn | |
52a7f9bd RS |
530 | (unless (and grep-command |
531 | (or (not grep-use-null-device) (eq grep-use-null-device t))) | |
318e2976 | 532 | (grep-compute-defaults)) |
87f54c05 RS |
533 | (if grep-find-command |
534 | (list (read-from-minibuffer "Run find (like this): " | |
535 | grep-find-command nil nil | |
536 | 'grep-find-history)) | |
537 | ;; No default was set | |
538 | (read-string | |
539 | "compile.el: No `grep-find-command' command available. Press RET.") | |
540 | (list nil)))) | |
541 | (when (and grep-find-command command-args) | |
542 | (let ((null-device nil)) ; see grep | |
543 | (grep command-args)))) | |
318e2976 | 544 | |
3d4d788a DK |
545 | ;;;###autoload |
546 | (defalias 'find-grep 'grep-find) | |
547 | ||
318e2976 KS |
548 | (defun grep-expand-command-macros (command &optional regexp files dir excl case-fold) |
549 | "Patch grep COMMAND replacing <D>, etc." | |
550 | (setq command | |
551 | (replace-regexp-in-string "<D>" | |
552 | (or dir ".") command t t)) | |
553 | (setq command | |
554 | (replace-regexp-in-string "<X>" | |
555 | (or excl "") command t t)) | |
556 | (setq command | |
557 | (replace-regexp-in-string "<F>" | |
558 | (or files "") command t t)) | |
559 | (setq command | |
560 | (replace-regexp-in-string "<C>" | |
561 | (if case-fold "-i" "") command t t)) | |
562 | (setq command | |
563 | (replace-regexp-in-string "<R>" | |
564 | (or regexp "") command t t)) | |
565 | command) | |
566 | ||
567 | (defvar grep-tree-last-regexp "") | |
568 | (defvar grep-tree-last-files (car (car grep-tree-files-aliases))) | |
569 | ||
570 | ;;;###autoload | |
571 | (defun grep-tree (regexp files dir &optional subdirs) | |
572 | "Grep for REGEXP in FILES in directory tree rooted at DIR. | |
573 | Collect output in a buffer. | |
574 | Interactively, prompt separately for each search parameter. | |
575 | With prefix arg, reuse previous REGEXP. | |
576 | The search is limited to file names matching shell pattern FILES. | |
577 | FILES may use abbreviations defined in `grep-tree-files-aliases', e.g. | |
578 | entering `ch' is equivalent to `*.[ch]'. | |
579 | ||
580 | While find runs asynchronously, you can use the \\[next-error] command | |
581 | to find the text that grep hits refer to. | |
582 | ||
583 | This command uses a special history list for its arguments, so you can | |
584 | easily repeat a find command. | |
585 | ||
586 | When used non-interactively, optional arg SUBDIRS limits the search to | |
587 | those sub directories of DIR." | |
588 | (interactive | |
589 | (let* ((regexp | |
590 | (if current-prefix-arg | |
591 | grep-tree-last-regexp | |
592 | (let* ((default (current-word)) | |
593 | (spec (read-string | |
594 | (concat "Search for" | |
595 | (if (and default (> (length default) 0)) | |
596 | (format " (default %s): " default) ": "))))) | |
597 | (if (equal spec "") default spec)))) | |
598 | (files | |
599 | (read-string (concat "Search for \"" regexp "\" in files (default " grep-tree-last-files "): "))) | |
600 | (dir | |
f833e227 | 601 | (read-directory-name "Base directory: " nil default-directory t))) |
318e2976 KS |
602 | (list regexp files dir))) |
603 | (unless grep-tree-command | |
604 | (grep-compute-defaults)) | |
605 | (unless (and (stringp files) (> (length files) 0)) | |
606 | (setq files grep-tree-last-files)) | |
607 | (when files | |
608 | (setq grep-tree-last-files files) | |
609 | (let ((mf (assoc files grep-tree-files-aliases))) | |
610 | (if mf | |
611 | (setq files (cdr mf))))) | |
612 | (let ((command-args (grep-expand-command-macros | |
613 | grep-tree-command | |
614 | (setq grep-tree-last-regexp regexp) | |
615 | (and files (concat "-name '" files "'")) | |
616 | (if subdirs | |
617 | (if (stringp subdirs) | |
618 | subdirs | |
619 | (mapconcat 'identity subdirs " ")) | |
620 | nil) ;; we change default-directory to dir | |
621 | (and grep-tree-ignore-CVS-directories "-path '*/CVS' -prune -o ") | |
622 | grep-tree-ignore-case)) | |
f833e227 | 623 | (default-directory (file-name-as-directory (expand-file-name dir))) |
318e2976 KS |
624 | (null-device nil)) ; see grep |
625 | (grep command-args regexp))) | |
626 | ||
627 | ||
628 | (provide 'grep) | |
629 | ||
cbd5542a | 630 | ;; arch-tag: 5a5b9169-a79d-4f38-9c38-f69615f39c4d |
06626cf2 | 631 | ;;; grep.el ends here |