1 ;;; grep.el --- run compiler as inferior of Emacs, parse error messages
3 ;; Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
4 ;; 2001, 2002, 2004 Free Software Foundation, Inc.
6 ;; Author: Roland McGrath <roland@gnu.org>
8 ;; Keywords: tools, processes
10 ;; This file is part of GNU Emacs.
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)
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.
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.
29 ;; This package provides the grep facilities documented in the Emacs
37 "Run compiler as inferior of Emacs, parse error messages."
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
)
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.
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"))
67 (defcustom grep-highlight-matches
'auto-detect
68 "If t, use special markers to highlight grep matches.
70 Some grep programs are able to surround matches with special
71 markers in grep output. Such markers can be used to highlight
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
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."
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
))
87 (defcustom grep-scroll-output nil
88 "*Non-nil to scroll the *grep* buffer window as output appears.
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
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'.
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
))
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.
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."
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
))
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
))
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
))
147 (defcustom grep-tree-files-aliases
'(
151 ("m" .
"[Mm]akefile*")
156 "*Alist of aliases for the FILES argument to `grep-tree'."
160 (defcustom grep-tree-ignore-case t
161 "*If non-nil, `grep-tree' ignores case in matches."
165 (defcustom grep-tree-ignore-CVS-directories t
166 "*If non-nil, `grep-tree' does no recurse into CVS directories."
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
)
179 (defcustom grep-setup-hook nil
180 "List of hook functions run by `grep-process-setup' (see `run-hooks')."
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
)
188 (define-key map
"\C-c\C-f" 'next-error-follow-minor-mode
)
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)
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
)
201 ;; Set up the menu-bar
202 (define-key map
[menu-bar grep
]
203 (cons "Grep" (make-sparse-keymap "Grep")))
205 (define-key map
[menu-bar grep compilation-kill-compilation
]
206 '("Kill Grep" . kill-compilation
))
207 (define-key map
[menu-bar grep compilation-separator2
]
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
]
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
))
224 "Keymap for grep buffers.
225 `compilation-minor-mode-map' is a cdr of this.")
227 (defalias 'kill-grep
'kill-compilation
)
229 ;;;; TODO --- refine this!!
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."
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'.")
249 (defvar grep-regexp-alist
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]+\
254 \\([0-9]+\\)\\([.:]?\\)\\([0-9]+\\)?\
255 \\(?:-\\(?:\\([0-9]+\\)\\3\\)?\\.?\\([0-9]+\\)?\\)?[: \t]" 1 (2 .
5) (4 .
6))
256 ("^\\(.+?\\)[:(]+\\([0-9]+\\)\\([:)]\\).*?\\(\033\\[01;41m\\)\\(.*?\\)\\(\033\\[00m\\(\033\\[K\\)?\\)"
258 ;; Calculate column positions (beg . end) of first grep match on a line
260 (setq compilation-error-screen-columns nil
)
261 (- (match-beginning 5) (match-end 3) 8))
263 (lambda () (- (match-end 5) (match-end 3) 8))))
264 ("^Binary file \\(.+\\) matches$" 1 nil nil
1))
265 "Regexp used to match grep hits. See `compilation-error-regexp-alist'.")
267 (defvar grep-error
"grep hit"
268 "Message to print when no matches are found.")
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.")
275 (defvar grep-error-face compilation-error-face
276 "Face name to use for grep error messages.")
278 (defvar grep-match-face
'match
279 "Face name to use for grep matches.")
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\\)\\)$"
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
)
294 (2 compilation-line-face
))
295 ;; Highlight grep matches and delete markers
296 ("\\(\033\\[01;41m\\)\\(.*?\\)\\(\033\\[00m\\(\033\\[K\\)?\\)"
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)))))
304 "Additional things to highlight in grep output.
305 This gets tacked on the end of the generated expressions.")
309 ;; Currently zgrep has trouble. It runs egrep instead of grep,
310 ;; and it doesn't pass along long options right.
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)
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.")
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.")
328 (defvar grep-find-use-xargs nil
329 "Whether \\[grep-find] uses the `xargs' utility by default.
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'.
334 This variable's value takes effect when `grep-compute-defaults' is called.")
336 ;; History of grep commands.
338 (defvar grep-history nil
)
340 (defvar grep-find-history nil
)
343 (defun grep-process-setup ()
344 "Setup compilation variables and buffer for `grep'.
345 Set up `compilation-exit-message-function' and run `grep-setup-hook'."
346 (unless (or (not grep-highlight-matches
) (eq grep-highlight-matches t
))
347 (grep-compute-defaults))
348 (when (eq grep-highlight-matches t
)
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"))
352 (set (make-local-variable 'compilation-exit-message-function
)
353 (lambda (status code msg
)
354 (if (eq status
'exit
)
356 '("finished (matches found)\n" .
"matched"))
358 '("finished with no matches found\n" .
"no match"))
362 (run-hooks 'grep-setup-hook
))
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
369 (let ((hello-file (expand-file-name "HELLO" data-directory
)))
371 (and (equal (condition-case nil
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
))
384 (goto-char (point-min))
386 (concat (regexp-quote hello-file
)
387 ":[0-9]+:English")))))))))
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
)
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
401 (equal (call-process "find" nil nil nil
402 null-device
"-print0")
404 (equal (call-process "xargs" nil nil nil
408 (unless grep-find-command
409 (setq grep-find-command
410 (cond ((eq grep-find-use-xargs
'gnu
)
411 (format "%s . -type f -print0 | xargs -0 -e %s"
412 find-program grep-command
))
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>"
427 (format "%s <D> <X> -type f <F> -print | xargs %s <R>"
429 (t (format "%s <D> <X> -type f <F> -exec %s <R> {} %s \\;"
430 find-program gcmd null-device
))))))
431 (unless (or (not grep-highlight-matches
) (eq grep-highlight-matches t
))
432 (setq grep-highlight-matches
434 (and (equal (condition-case nil
435 (call-process grep-program nil t nil
"--help")
439 (goto-char (point-min))
440 (search-forward "--color" nil t
))
443 (defun grep-default-command ()
445 (shell-quote-argument
446 (or (funcall (or find-tag-default-function
447 (get major-mode
'find-tag-default-function
)
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 +\\)\\)?")
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)
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))
470 (file-name-extension buffer-file-name
))))
471 (replace-match tag-default t t grep-default
1))))
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),
477 or \\<grep-mode-map>\\[compile-goto-error] in the grep \
478 output buffer, to go to the lines
479 where grep found matches.
481 This command uses a special history list for its COMMAND-ARGS, so you can
482 easily repeat a grep command.
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).
489 If specified, optional second arg HIGHLIGHT-REGEXP is the regexp to
490 temporarily highlight in visited source lines."
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
))))))
503 ;; Setting process-setup-function makes exit-message-function work
504 ;; even when async processes aren't supported.
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
)
509 'grep-mode nil highlight-regexp
)))
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
)
516 (set (make-local-variable 'compilation-error-regexp-alist
)
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.
526 This command uses a special history list for its arguments, so you can
527 easily repeat a find command."
530 (unless (and grep-command
531 (or (not grep-use-null-device
) (eq grep-use-null-device t
)))
532 (grep-compute-defaults))
533 (if grep-find-command
534 (list (read-from-minibuffer "Run find (like this): "
535 grep-find-command nil nil
537 ;; No default was set
539 "compile.el: No `grep-find-command' command available. Press RET.")
541 (when (and grep-find-command command-args
)
542 (let ((null-device nil
)) ; see grep
543 (grep command-args
))))
546 (defalias 'find-grep
'grep-find
)
548 (defun grep-expand-command-macros (command &optional regexp files dir excl case-fold
)
549 "Patch grep COMMAND replacing <D>, etc."
551 (replace-regexp-in-string "<D>"
552 (or dir
".") command t t
))
554 (replace-regexp-in-string "<X>"
555 (or excl
"") command t t
))
557 (replace-regexp-in-string "<F>"
558 (or files
"") command t t
))
560 (replace-regexp-in-string "<C>"
561 (if case-fold
"-i" "") command t t
))
563 (replace-regexp-in-string "<R>"
564 (or regexp
"") command t t
))
567 (defvar grep-tree-last-regexp
"")
568 (defvar grep-tree-last-files
(car (car grep-tree-files-aliases
)))
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]'.
580 While find runs asynchronously, you can use the \\[next-error] command
581 to find the text that grep hits refer to.
583 This command uses a special history list for its arguments, so you can
584 easily repeat a find command.
586 When used non-interactively, optional arg SUBDIRS limits the search to
587 those sub directories of DIR."
590 (if current-prefix-arg
591 grep-tree-last-regexp
592 (let* ((default (current-word))
595 (if (and default
(> (length default
) 0))
596 (format " (default %s): " default
) ": ")))))
597 (if (equal spec
"") default spec
))))
599 (read-string (concat "Search for \"" regexp
"\" in files (default " grep-tree-last-files
"): ")))
601 (read-directory-name "Base directory: " nil default-directory t
)))
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
))
608 (setq grep-tree-last-files files
)
609 (let ((mf (assoc files grep-tree-files-aliases
)))
611 (setq files
(cdr mf
)))))
612 (let ((command-args (grep-expand-command-macros
614 (setq grep-tree-last-regexp regexp
)
615 (and files
(concat "-name '" files
"'"))
617 (if (stringp 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
))
623 (default-directory (file-name-as-directory (expand-file-name dir
)))
624 (null-device nil
)) ; see grep
625 (grep command-args regexp
)))
630 ;; arch-tag: 5a5b9169-a79d-4f38-9c38-f69615f39c4d
631 ;;; grep.el ends here