comparison lisp/progmodes/grep.el @ 53139:df3033332097

* progmodes/grep.el: New file with grep code from compile.el. (grep): New defcustom group. (grep-window-height): New defcustom, like compilation-window-height. (grep-auto-highlight): New defcustom, like compile-auto-highlight. (grep-scroll-output): New defcustom, like compilation-scroll-output. (grep-command, grep-use-null-device, grep-find-command) (grep-tree-files-aliases, grep-tree-ignore-case) (grep-tree-ignore-CVS-directories): Move to grep custom group. (grep-setup-hook): New hook variable. (grep-mode-map): New keymap for grep commands. Add Grep menu. (grep-use-compilation-buffer): New defcustom. (grep-last-buffer): New defvar, override compilation-last-buffer. (grep): Add optional arg HIGHLIGHT-REGEXP. Doc fix. Call compile-internal with args highlight-regexp and grep-mode-map.
author Kim F. Storm <storm@cua.dk>
date Sun, 23 Nov 2003 20:56:10 +0000
parents
children a0151f86f01e
comparison
equal deleted inserted replaced
53138:7cf5bcb7a8ad 53139:df3033332097
1 ;;; grep.el --- run compiler as inferior of Emacs, parse error messages
2
3 ;; Copyright (C) 1985, 86, 87, 93, 94, 95, 96, 97, 98, 1999, 2001, 2002
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
77 (defcustom grep-command nil
78 "The default grep command for \\[grep].
79 If the grep program used supports an option to always include file names
80 in its output (such as the `-H' option to GNU grep), it's a good idea to
81 include it when specifying `grep-command'.
82
83 The default value of this variable is set up by `grep-compute-defaults';
84 call that function before using this variable in your program."
85 :type '(choice string
86 (const :tag "Not Set" nil))
87 :group 'grep)
88
89 (defcustom grep-use-null-device 'auto-detect
90 "If t, append the value of `null-device' to `grep' commands.
91 This is done to ensure that the output of grep includes the filename of
92 any match in the case where only a single file is searched, and is not
93 necessary if the grep program used supports the `-H' option.
94
95 The default value of this variable is set up by `grep-compute-defaults';
96 call that function before using this variable in your program."
97 :type 'boolean
98 :type '(choice (const :tag "Do Not Append Null Device" nil)
99 (const :tag "Append Null Device" t)
100 (other :tag "Not Set" auto-detect))
101 :group 'grep)
102
103 (defcustom grep-find-command nil
104 "The default find command for \\[grep-find].
105 The default value of this variable is set up by `grep-compute-defaults';
106 call that function before using this variable in your program."
107 :type '(choice string
108 (const :tag "Not Set" nil))
109 :group 'grep)
110
111 (defcustom grep-tree-command nil
112 "The default find command for \\[grep-tree].
113 The default value of this variable is set up by `grep-compute-defaults';
114 call that function before using this variable in your program.
115 The following place holders should be present in the string:
116 <D> - base directory for find
117 <X> - find options to restrict or expand the directory list
118 <F> - find options to limit the files matched
119 <C> - place to put -i if case insensitive grep
120 <R> - the regular expression searched for."
121 :type '(choice string
122 (const :tag "Not Set" nil))
123 :version "21.4"
124 :group 'grep)
125
126 (defcustom grep-tree-files-aliases '(
127 ("ch" . "*.[ch]")
128 ("c" . "*.c")
129 ("h" . "*.h")
130 ("m" . "[Mm]akefile*")
131 ("asm" . "*.[sS]")
132 ("all" . "*")
133 ("el" . "*.el")
134 )
135 "*Alist of aliases for the FILES argument to `grep-tree'."
136 :type 'alist
137 :group 'grep)
138
139 (defcustom grep-tree-ignore-case t
140 "*If non-nil, `grep-tree' ignores case in matches."
141 :type 'boolean
142 :group 'grep)
143
144 (defcustom grep-tree-ignore-CVS-directories t
145 "*If non-nil, `grep-tree' does no recurse into CVS directories."
146 :type 'boolean
147 :group 'grep)
148
149 ;;;###autoload
150 (defcustom grep-setup-hook nil
151 "List of hook functions run by `grep-process-setup' (see `run-hooks')."
152 :type 'hook
153 :group 'grep)
154
155 (defvar grep-mode-map
156 (let ((map (cons 'keymap compilation-minor-mode-map)))
157 (define-key map " " 'scroll-up)
158 (define-key map "\^?" 'scroll-down)
159
160 (define-key map [remap next-line] 'compilation-next-error)
161 (define-key map [remap previous-line] 'compilation-previous-error)
162
163 (define-key map "\r" 'compile-goto-error) ;; ?
164 (define-key map "n" 'next-error-no-select)
165 (define-key map "p" 'previous-error-no-select)
166 (define-key map "{" 'compilation-previous-file)
167 (define-key map "}" 'compilation-next-file)
168 (define-key map "\t" 'compilation-next-file)
169
170 ;; Set up the menu-bar
171 (define-key map [menu-bar grep]
172 (cons "Grep" (make-sparse-keymap "Grep")))
173
174 (define-key map [menu-bar grep compilation-kill-compilation]
175 '("Kill Grep" . kill-compilation))
176 (define-key map [menu-bar grep compilation-separator2]
177 '("----" . nil))
178 (define-key map [menu-bar grep compilation-compile]
179 '("Compile..." . compile))
180 (define-key map [menu-bar grep compilation-grep]
181 '("Another grep" . grep))
182 (define-key map [menu-bar grep compilation-recompile]
183 '("Repeat grep" . recompile))
184 (define-key map [menu-bar grep compilation-separator2]
185 '("----" . nil))
186 (define-key map [menu-bar grep compilation-first-error]
187 '("First Match" . first-error))
188 (define-key map [menu-bar grep compilation-previous-error]
189 '("Previous Match" . previous-error))
190 (define-key map [menu-bar grep compilation-next-error]
191 '("Next Match" . next-error))
192 map)
193 "Keymap for grep buffers.
194 `compilation-minor-mode-map' is a cdr of this.")
195
196 ;;;; TODO --- refine this!!
197
198 (defcustom grep-use-compilation-buffer t
199 "When non-nil, grep specific commands update `compilation-last-buffer'.
200 This means that standard compile commands like \\[next-error] and \\[compile-goto-error]
201 can be used to navigate between grep matches (the default).
202 Otherwise, the grep specific commands like \\[grep-next-match] must
203 be used to navigate between grep matches."
204 :type 'boolean
205 :group 'grep)
206
207 ;; override compilation-last-buffer
208 (defvar grep-last-buffer nil
209 "The most recent grep buffer.
210 A grep buffer becomes most recent when its process is started
211 or when it is used with \\[grep-next-match].
212 Notice that using \\[next-error] or \\[compile-goto-error] modifies
213 `complation-last-buffer' rather than `grep-last-buffer'.")
214
215 ;; Note: the character class after the optional drive letter does not
216 ;; include a space to support file names with blanks.
217 (defvar grep-regexp-alist
218 '(("\\([a-zA-Z]?:?[^:(\t\n]+\\)[:( \t]+\\([0-9]+\\)[:) \t]" 1 2))
219 "Regexp used to match grep hits. See `compilation-error-regexp-alist'.")
220
221 (defvar grep-program
222 ;; Currently zgrep has trouble. It runs egrep instead of grep,
223 ;; and it doesn't pass along long options right.
224 "grep"
225 ;; (if (equal (condition-case nil ; in case "zgrep" isn't in exec-path
226 ;; (call-process "zgrep" nil nil nil
227 ;; "foo" null-device)
228 ;; (error nil))
229 ;; 1)
230 ;; "zgrep"
231 ;; "grep")
232 "The default grep program for `grep-command' and `grep-find-command'.
233 This variable's value takes effect when `grep-compute-defaults' is called.")
234
235 (defvar find-program "find"
236 "The default find program for `grep-find-command'.
237 This variable's value takes effect when `grep-compute-defaults' is called.")
238
239 (defvar grep-find-use-xargs nil
240 "Whether \\[grep-find] uses the `xargs' utility by default.
241
242 If nil, it uses `find -exec'; if `gnu', it uses `find -print0' and `xargs -0';
243 if not nil and not `gnu', it uses `find -print' and `xargs'.
244
245 This variable's value takes effect when `grep-compute-defaults' is called.")
246
247 ;; History of grep commands.
248 (defvar grep-history nil)
249 (defvar grep-find-history nil)
250
251 (defun grep-process-setup ()
252 "Setup compilation variables and buffer for `grep'.
253 Set up `compilation-exit-message-function' and `compilation-window-height'.
254 Sets `grep-last-buffer' and runs `grep-setup-hook'."
255 (setq grep-last-buffer (current-buffer))
256 (set (make-local-variable 'compilation-exit-message-function)
257 (lambda (status code msg)
258 (if (eq status 'exit)
259 (cond ((zerop code)
260 '("finished (matches found)\n" . "matched"))
261 ((= code 1)
262 '("finished with no matches found\n" . "no match"))
263 (t
264 (cons msg code)))
265 (cons msg code))))
266 (if grep-window-height
267 (set (make-local-variable 'compilation-window-height)
268 grep-window-height))
269 (set (make-local-variable 'compile-auto-highlight)
270 grep-auto-highlight)
271 (set (make-local-variable 'compilation-scroll-output)
272 grep-scroll-output)
273 (run-hooks 'grep-setup-hook))
274
275 (defun grep-compute-defaults ()
276 (unless (or (not grep-use-null-device) (eq grep-use-null-device t))
277 (setq grep-use-null-device
278 (with-temp-buffer
279 (let ((hello-file (expand-file-name "HELLO" data-directory)))
280 (not
281 (and (equal (condition-case nil
282 (if grep-command
283 ;; `grep-command' is already set, so
284 ;; use that for testing.
285 (call-process-shell-command
286 grep-command nil t nil
287 "^English" hello-file)
288 ;; otherwise use `grep-program'
289 (call-process grep-program nil t nil
290 "-nH" "^English" hello-file))
291 (error nil))
292 0)
293 (progn
294 (goto-char (point-min))
295 (looking-at
296 (concat (regexp-quote hello-file)
297 ":[0-9]+:English")))))))))
298 (unless grep-command
299 (setq grep-command
300 (let ((required-options (if grep-use-null-device "-n" "-nH")))
301 (if (equal (condition-case nil ; in case "grep" isn't in exec-path
302 (call-process grep-program nil nil nil
303 "-e" "foo" null-device)
304 (error nil))
305 1)
306 (format "%s %s -e " grep-program required-options)
307 (format "%s %s " grep-program required-options)))))
308 (unless grep-find-use-xargs
309 (setq grep-find-use-xargs
310 (if (and
311 (equal (call-process "find" nil nil nil
312 null-device "-print0")
313 0)
314 (equal (call-process "xargs" nil nil nil
315 "-0" "-e" "echo")
316 0))
317 'gnu)))
318 (unless grep-find-command
319 (setq grep-find-command
320 (cond ((eq grep-find-use-xargs 'gnu)
321 (format "%s . -type f -print0 | xargs -0 -e %s"
322 find-program grep-command))
323 (grep-find-use-xargs
324 (format "%s . -type f -print | xargs %s"
325 find-program grep-command))
326 (t (cons (format "%s . -type f -exec %s {} %s \\;"
327 find-program grep-command null-device)
328 (+ 22 (length grep-command)))))))
329 (unless grep-tree-command
330 (setq grep-tree-command
331 (let* ((glen (length grep-program))
332 (gcmd (concat grep-program " <C>" (substring grep-command glen))))
333 (cond ((eq grep-find-use-xargs 'gnu)
334 (format "%s <D> <X> -type f <F> -print0 | xargs -0 -e %s <R>"
335 find-program gcmd))
336 (grep-find-use-xargs
337 (format "%s <D> <X> -type f <F> -print | xargs %s <R>"
338 find-program gcmd))
339 (t (format "%s <D> <X> -type f <F> -exec %s <R> {} %s \\;"
340 find-program gcmd null-device)))))))
341
342 (defun grep-default-command ()
343 (let ((tag-default
344 (funcall (or find-tag-default-function
345 (get major-mode 'find-tag-default-function)
346 ;; We use grep-tag-default instead of
347 ;; find-tag-default, to avoid loading etags.
348 'grep-tag-default)))
349 (sh-arg-re "\\(\\(?:\"\\(?:[^\"]\\|\\\\\"\\)+\"\\|'[^']+'\\|[^\"' \t\n]\\)+\\)")
350 (grep-default (or (car grep-history) grep-command)))
351 ;; Replace the thing matching for with that around cursor.
352 (when (or (string-match
353 (concat "[^ ]+\\s +\\(?:-[^ ]+\\s +\\)*"
354 sh-arg-re "\\(\\s +\\(\\S +\\)\\)?")
355 grep-default)
356 ;; If the string is not yet complete.
357 (string-match "\\(\\)\\'" grep-default))
358 (unless (or (not (stringp buffer-file-name))
359 (when (match-beginning 2)
360 (save-match-data
361 (string-match
362 (wildcard-to-regexp
363 (file-name-nondirectory
364 (match-string 3 grep-default)))
365 (file-name-nondirectory buffer-file-name)))))
366 (setq grep-default (concat (substring grep-default
367 0 (match-beginning 2))
368 " *."
369 (file-name-extension buffer-file-name))))
370 (replace-match (or tag-default "") t t grep-default 1))))
371
372 ;;;###autoload
373 (defun grep (command-args &optional highlight-regexp)
374 "Run grep, with user-specified args, and collect output in a buffer.
375 While grep runs asynchronously, you can use \\[next-error] (M-x next-error),
376 or \\<grep-minor-mode-map>\\[compile-goto-error] in the grep \
377 output buffer, to go to the lines
378 where grep found matches.
379
380 This command uses a special history list for its COMMAND-ARGS, so you can
381 easily repeat a grep command.
382
383 A prefix argument says to default the argument based upon the current
384 tag the cursor is over, substituting it into the last grep command
385 in the grep command history (or into `grep-command'
386 if that history list is empty).
387
388 If specified, optional second arg HIGHLIGHT-REGEXP is the regexp to
389 temporarily highlight in visited source lines."
390 (interactive
391 (progn
392 (unless (and grep-command
393 (or (not grep-use-null-device) (eq grep-use-null-device t)))
394 (grep-compute-defaults))
395 (let ((default (grep-default-command)))
396 (list (read-from-minibuffer "Run grep (like this): "
397 (if current-prefix-arg
398 default grep-command)
399 nil nil 'grep-history
400 (if current-prefix-arg nil default))))))
401
402 ;; Setting process-setup-function makes exit-message-function work
403 ;; even when async processes aren't supported.
404 (let* ((compilation-process-setup-function 'grep-process-setup)
405 (buf (compile-internal (if (and grep-use-null-device null-device)
406 (concat command-args " " null-device)
407 command-args)
408 "No more grep hits" "grep"
409 ;; Give it a simpler regexp to match.
410 nil grep-regexp-alist
411 nil nil nil nil nil nil
412 highlight-regexp grep-mode-map)))))
413
414 ;; This is a copy of find-tag-default from etags.el.
415 (defun grep-tag-default ()
416 (save-excursion
417 (while (looking-at "\\sw\\|\\s_")
418 (forward-char 1))
419 (when (or (re-search-backward "\\sw\\|\\s_"
420 (save-excursion (beginning-of-line) (point))
421 t)
422 (re-search-forward "\\(\\sw\\|\\s_\\)+"
423 (save-excursion (end-of-line) (point))
424 t))
425 (goto-char (match-end 0))
426 (buffer-substring (point)
427 (progn (forward-sexp -1)
428 (while (looking-at "\\s'")
429 (forward-char 1))
430 (point))))))
431
432 ;;;###autoload
433 (defun grep-find (command-args)
434 "Run grep via find, with user-specified args COMMAND-ARGS.
435 Collect output in a buffer.
436 While find runs asynchronously, you can use the \\[next-error] command
437 to find the text that grep hits refer to.
438
439 This command uses a special history list for its arguments, so you can
440 easily repeat a find command."
441 (interactive
442 (progn
443 (unless grep-find-command
444 (grep-compute-defaults))
445 (list (read-from-minibuffer "Run find (like this): "
446 grep-find-command nil nil
447 'grep-find-history))))
448 (let ((null-device nil)) ; see grep
449 (grep command-args)))
450
451 (defun grep-expand-command-macros (command &optional regexp files dir excl case-fold)
452 "Patch grep COMMAND replacing <D>, etc."
453 (setq command
454 (replace-regexp-in-string "<D>"
455 (or dir ".") command t t))
456 (setq command
457 (replace-regexp-in-string "<X>"
458 (or excl "") command t t))
459 (setq command
460 (replace-regexp-in-string "<F>"
461 (or files "") command t t))
462 (setq command
463 (replace-regexp-in-string "<C>"
464 (if case-fold "-i" "") command t t))
465 (setq command
466 (replace-regexp-in-string "<R>"
467 (or regexp "") command t t))
468 command)
469
470 (defvar grep-tree-last-regexp "")
471 (defvar grep-tree-last-files (car (car grep-tree-files-aliases)))
472
473 ;;;###autoload
474 (defun grep-tree (regexp files dir &optional subdirs)
475 "Grep for REGEXP in FILES in directory tree rooted at DIR.
476 Collect output in a buffer.
477 Interactively, prompt separately for each search parameter.
478 With prefix arg, reuse previous REGEXP.
479 The search is limited to file names matching shell pattern FILES.
480 FILES may use abbreviations defined in `grep-tree-files-aliases', e.g.
481 entering `ch' is equivalent to `*.[ch]'.
482
483 While find runs asynchronously, you can use the \\[next-error] command
484 to find the text that grep hits refer to.
485
486 This command uses a special history list for its arguments, so you can
487 easily repeat a find command.
488
489 When used non-interactively, optional arg SUBDIRS limits the search to
490 those sub directories of DIR."
491 (interactive
492 (let* ((regexp
493 (if current-prefix-arg
494 grep-tree-last-regexp
495 (let* ((default (current-word))
496 (spec (read-string
497 (concat "Search for"
498 (if (and default (> (length default) 0))
499 (format " (default %s): " default) ": ")))))
500 (if (equal spec "") default spec))))
501 (files
502 (read-string (concat "Search for \"" regexp "\" in files (default " grep-tree-last-files "): ")))
503 (dir
504 (read-directory-name "Base directory: " nil default-directory t)))
505 (list regexp files dir)))
506 (unless grep-tree-command
507 (grep-compute-defaults))
508 (unless (and (stringp files) (> (length files) 0))
509 (setq files grep-tree-last-files))
510 (when files
511 (setq grep-tree-last-files files)
512 (let ((mf (assoc files grep-tree-files-aliases)))
513 (if mf
514 (setq files (cdr mf)))))
515 (let ((command-args (grep-expand-command-macros
516 grep-tree-command
517 (setq grep-tree-last-regexp regexp)
518 (and files (concat "-name '" files "'"))
519 (if subdirs
520 (if (stringp subdirs)
521 subdirs
522 (mapconcat 'identity subdirs " "))
523 nil) ;; we change default-directory to dir
524 (and grep-tree-ignore-CVS-directories "-path '*/CVS' -prune -o ")
525 grep-tree-ignore-case))
526 (default-directory dir)
527 (null-device nil)) ; see grep
528 (grep command-args regexp)))
529
530
531 (provide 'grep)
532
533 ;;; grep.el ends here
534