Commit | Line | Data |
---|---|---|
d9c54a06 | 1 | ;;; grep.el --- run `grep' and display the results |
318e2976 | 2 | |
ab422c4d PE |
3 | ;; Copyright (C) 1985-1987, 1993-1999, 2001-2013 Free Software |
4 | ;; 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 | ||
b1fc2b50 | 12 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
318e2976 | 13 | ;; it under the terms of the GNU General Public License as published by |
b1fc2b50 GM |
14 | ;; the Free Software Foundation, either version 3 of the License, or |
15 | ;; (at your option) any later version. | |
318e2976 KS |
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 | |
b1fc2b50 | 23 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
318e2976 KS |
24 | |
25 | ;;; Commentary: | |
26 | ||
27 | ;; This package provides the grep facilities documented in the Emacs | |
28 | ;; user's manual. | |
29 | ||
30 | ;;; Code: | |
31 | ||
32 | (require 'compile) | |
33 | ||
d70b5ac5 | 34 | |
318e2976 | 35 | (defgroup grep nil |
d9c54a06 | 36 | "Run `grep' and display the results." |
318e2976 KS |
37 | :group 'tools |
38 | :group 'processes) | |
39 | ||
d4bbd646 CY |
40 | (defvar grep-host-defaults-alist nil |
41 | "Default values depending on target host. | |
42 | `grep-compute-defaults' returns default values for every local or | |
43 | remote host `grep' runs. These values can differ from host to | |
44 | host. Once computed, the default values are kept here in order | |
45 | to avoid computing them again.") | |
46 | ||
47 | (defun grep-apply-setting (symbol value) | |
48 | "Set SYMBOL to VALUE, and update `grep-host-defaults-alist'. | |
49 | SYMBOL should be one of `grep-command', `grep-template', | |
50 | `grep-use-null-device', `grep-find-command', | |
51 | `grep-find-template', `grep-find-use-xargs', or | |
52 | `grep-highlight-matches'." | |
53 | (when grep-host-defaults-alist | |
54 | (let* ((host-id | |
55 | (intern (or (file-remote-p default-directory) "localhost"))) | |
56 | (host-defaults (assq host-id grep-host-defaults-alist)) | |
57 | (defaults (assq nil grep-host-defaults-alist))) | |
58 | (setcar (cdr (assq symbol host-defaults)) value) | |
59 | (setcar (cdr (assq symbol defaults)) value))) | |
60 | (set-default symbol value)) | |
318e2976 KS |
61 | |
62 | ;;;###autoload | |
63 | (defcustom grep-window-height nil | |
fb7ada5f | 64 | "Number of lines in a grep window. If nil, use `compilation-window-height'." |
318e2976 KS |
65 | :type '(choice (const :tag "Default" nil) |
66 | integer) | |
bf247b6e | 67 | :version "22.1" |
318e2976 KS |
68 | :group 'grep) |
69 | ||
00889cf9 | 70 | (defcustom grep-highlight-matches 'auto-detect |
a601d313 | 71 | "Use special markers to highlight grep matches. |
277df088 JL |
72 | |
73 | Some grep programs are able to surround matches with special | |
74 | markers in grep output. Such markers can be used to highlight | |
a915d7a1 GM |
75 | matches in grep mode. This requires `font-lock-mode' to be active |
76 | in grep buffers, so if you have globally disabled font-lock-mode, | |
77 | you will not get highlighting. | |
277df088 | 78 | |
a601d313 | 79 | This option sets the environment variable GREP_COLORS to specify |
277df088 JL |
80 | markers for highlighting and GREP_OPTIONS to add the --color |
81 | option in front of any explicit grep options before starting | |
a58c94a2 JL |
82 | the grep. |
83 | ||
a601d313 JL |
84 | When this option is `auto', grep uses `--color=auto' to highlight |
85 | matches only when it outputs to a terminal (when `grep' is the last | |
86 | command in the pipe), thus avoiding the use of any potentially-harmful | |
87 | escape sequences when standard output goes to a file or pipe. | |
88 | ||
89 | To make grep highlight matches even into a pipe, you need the option | |
90 | `always' that forces grep to use `--color=always' to unconditionally | |
91 | output escape sequences. | |
92 | ||
d4bbd646 | 93 | In interactive usage, the actual value of this variable is set up |
a601d313 JL |
94 | by `grep-compute-defaults' when the default value is `auto-detect'. |
95 | To change the default value, use Customize or call the function | |
96 | `grep-apply-setting'." | |
00889cf9 JL |
97 | :type '(choice (const :tag "Do not highlight matches with grep markers" nil) |
98 | (const :tag "Highlight matches with grep markers" t) | |
a601d313 JL |
99 | (const :tag "Use --color=always" always) |
100 | (const :tag "Use --color=auto" auto) | |
00889cf9 | 101 | (other :tag "Not Set" auto-detect)) |
d4bbd646 | 102 | :set 'grep-apply-setting |
bf247b6e | 103 | :version "22.1" |
277df088 JL |
104 | :group 'grep) |
105 | ||
318e2976 | 106 | (defcustom grep-scroll-output nil |
fb7ada5f | 107 | "Non-nil to scroll the *grep* buffer window as output appears. |
318e2976 KS |
108 | |
109 | Setting it causes the grep commands to put point at the end of their | |
110 | output window so that the end of the output is always visible rather | |
333f9019 | 111 | than the beginning." |
318e2976 | 112 | :type 'boolean |
bf247b6e | 113 | :version "22.1" |
318e2976 KS |
114 | :group 'grep) |
115 | ||
9b19858e | 116 | ;;;###autoload |
318e2976 KS |
117 | (defcustom grep-command nil |
118 | "The default grep command for \\[grep]. | |
119 | If the grep program used supports an option to always include file names | |
120 | in its output (such as the `-H' option to GNU grep), it's a good idea to | |
121 | include it when specifying `grep-command'. | |
122 | ||
d4bbd646 CY |
123 | In interactive usage, the actual value of this variable is set up |
124 | by `grep-compute-defaults'; to change the default value, use | |
125 | Customize or call the function `grep-apply-setting'." | |
318e2976 KS |
126 | :type '(choice string |
127 | (const :tag "Not Set" nil)) | |
d4bbd646 | 128 | :set 'grep-apply-setting |
318e2976 KS |
129 | :group 'grep) |
130 | ||
0acfb7ce KS |
131 | (defcustom grep-template nil |
132 | "The default command to run for \\[lgrep]. | |
0acfb7ce KS |
133 | The following place holders should be present in the string: |
134 | <C> - place to put -i if case insensitive grep. | |
135 | <F> - file names and wildcards to search. | |
7ae62430 | 136 | <X> - file names and wildcards to exclude. |
0acfb7ce | 137 | <R> - the regular expression searched for. |
d4bbd646 CY |
138 | <N> - place to insert null-device. |
139 | ||
140 | In interactive usage, the actual value of this variable is set up | |
141 | by `grep-compute-defaults'; to change the default value, use | |
142 | Customize or call the function `grep-apply-setting'." | |
0acfb7ce KS |
143 | :type '(choice string |
144 | (const :tag "Not Set" nil)) | |
d4bbd646 | 145 | :set 'grep-apply-setting |
0acfb7ce KS |
146 | :version "22.1" |
147 | :group 'grep) | |
148 | ||
318e2976 KS |
149 | (defcustom grep-use-null-device 'auto-detect |
150 | "If t, append the value of `null-device' to `grep' commands. | |
151 | This is done to ensure that the output of grep includes the filename of | |
152 | any match in the case where only a single file is searched, and is not | |
153 | necessary if the grep program used supports the `-H' option. | |
154 | ||
d4bbd646 CY |
155 | In interactive usage, the actual value of this variable is set up |
156 | by `grep-compute-defaults'; to change the default value, use | |
157 | Customize or call the function `grep-apply-setting'." | |
318e2976 KS |
158 | :type '(choice (const :tag "Do Not Append Null Device" nil) |
159 | (const :tag "Append Null Device" t) | |
160 | (other :tag "Not Set" auto-detect)) | |
d4bbd646 | 161 | :set 'grep-apply-setting |
318e2976 KS |
162 | :group 'grep) |
163 | ||
9b19858e | 164 | ;;;###autoload |
318e2976 KS |
165 | (defcustom grep-find-command nil |
166 | "The default find command for \\[grep-find]. | |
d4bbd646 CY |
167 | In interactive usage, the actual value of this variable is set up |
168 | by `grep-compute-defaults'; to change the default value, use | |
169 | Customize or call the function `grep-apply-setting'." | |
318e2976 KS |
170 | :type '(choice string |
171 | (const :tag "Not Set" nil)) | |
d4bbd646 | 172 | :set 'grep-apply-setting |
318e2976 KS |
173 | :group 'grep) |
174 | ||
0acfb7ce KS |
175 | (defcustom grep-find-template nil |
176 | "The default command to run for \\[rgrep]. | |
318e2976 KS |
177 | The following place holders should be present in the string: |
178 | <D> - base directory for find | |
179 | <X> - find options to restrict or expand the directory list | |
180 | <F> - find options to limit the files matched | |
181 | <C> - place to put -i if case insensitive grep | |
d4bbd646 CY |
182 | <R> - the regular expression searched for. |
183 | In interactive usage, the actual value of this variable is set up | |
184 | by `grep-compute-defaults'; to change the default value, use | |
185 | Customize or call the function `grep-apply-setting'." | |
318e2976 KS |
186 | :type '(choice string |
187 | (const :tag "Not Set" nil)) | |
d4bbd646 | 188 | :set 'grep-apply-setting |
bf247b6e | 189 | :version "22.1" |
318e2976 KS |
190 | :group 'grep) |
191 | ||
2796180f | 192 | (defcustom grep-files-aliases |
7ae62430 JL |
193 | '(("all" . "* .*") |
194 | ("el" . "*.el") | |
195 | ("ch" . "*.[ch]") | |
2796180f | 196 | ("c" . "*.c") |
01623c28 | 197 | ("cc" . "*.cc *.cxx *.cpp *.C *.CC *.c++") |
7ae62430 | 198 | ("cchh" . "*.cc *.[ch]xx *.[ch]pp *.[CHh] *.CC *.HH *.[ch]++") |
01623c28 | 199 | ("hh" . "*.hxx *.hpp *.[Hh] *.HH *.h++") |
2796180f | 200 | ("h" . "*.h") |
7ae62430 | 201 | ("l" . "[Cc]hange[Ll]og*") |
2796180f | 202 | ("m" . "[Mm]akefile*") |
7ae62430 JL |
203 | ("tex" . "*.tex") |
204 | ("texi" . "*.texi") | |
205 | ("asm" . "*.[sS]")) | |
fb7ada5f | 206 | "Alist of aliases for the FILES argument to `lgrep' and `rgrep'." |
318e2976 KS |
207 | :type 'alist |
208 | :group 'grep) | |
209 | ||
a2e548a9 | 210 | (defcustom grep-find-ignored-directories |
1f0bee0a | 211 | vc-directory-exclusion-list |
fb7ada5f | 212 | "List of names of sub-directories which `rgrep' shall not recurse into. |
49405d0e SS |
213 | If an element is a cons cell, the car is called on the search directory |
214 | to determine whether cdr should not be recursed into." | |
7ae62430 JL |
215 | :type '(choice (repeat :tag "Ignored directories" string) |
216 | (const :tag "No ignored directories" nil)) | |
217 | :group 'grep) | |
218 | ||
219 | (defcustom grep-find-ignored-files | |
220 | (cons ".#*" (delq nil (mapcar (lambda (s) | |
221 | (unless (string-match-p "/\\'" s) | |
222 | (concat "*" s))) | |
223 | completion-ignored-extensions))) | |
fb7ada5f | 224 | "List of file names which `rgrep' and `lgrep' shall exclude. |
7ae62430 JL |
225 | If an element is a cons cell, the car is called on the search directory |
226 | to determine whether cdr should not be excluded." | |
227 | :type '(choice (repeat :tag "Ignored file" string) | |
228 | (const :tag "No ignored files" nil)) | |
318e2976 KS |
229 | :group 'grep) |
230 | ||
bb72b9d0 | 231 | (defcustom grep-error-screen-columns nil |
fb7ada5f | 232 | "If non-nil, column numbers in grep hits are screen columns. |
bb72b9d0 DP |
233 | See `compilation-error-screen-columns'" |
234 | :type '(choice (const :tag "Default" nil) | |
235 | integer) | |
bf247b6e | 236 | :version "22.1" |
bb72b9d0 DP |
237 | :group 'grep) |
238 | ||
318e2976 KS |
239 | ;;;###autoload |
240 | (defcustom grep-setup-hook nil | |
241 | "List of hook functions run by `grep-process-setup' (see `run-hooks')." | |
242 | :type 'hook | |
243 | :group 'grep) | |
244 | ||
245 | (defvar grep-mode-map | |
86c7460f SS |
246 | (let ((map (make-sparse-keymap))) |
247 | (set-keymap-parent map compilation-minor-mode-map) | |
ce3cefcc | 248 | (define-key map " " 'scroll-up-command) |
958614cf | 249 | (define-key map [?\S-\ ] 'scroll-down-command) |
ce3cefcc | 250 | (define-key map "\^?" 'scroll-down-command) |
0443d889 | 251 | (define-key map "\C-c\C-f" 'next-error-follow-minor-mode) |
318e2976 | 252 | |
318e2976 KS |
253 | (define-key map "\r" 'compile-goto-error) ;; ? |
254 | (define-key map "n" 'next-error-no-select) | |
255 | (define-key map "p" 'previous-error-no-select) | |
256 | (define-key map "{" 'compilation-previous-file) | |
257 | (define-key map "}" 'compilation-next-file) | |
94c713b2 JL |
258 | (define-key map "\t" 'compilation-next-error) |
259 | (define-key map [backtab] 'compilation-previous-error) | |
318e2976 KS |
260 | |
261 | ;; Set up the menu-bar | |
262 | (define-key map [menu-bar grep] | |
263 | (cons "Grep" (make-sparse-keymap "Grep"))) | |
264 | ||
265 | (define-key map [menu-bar grep compilation-kill-compilation] | |
38805987 DN |
266 | '(menu-item "Kill Grep" kill-compilation |
267 | :help "Kill the currently running grep process")) | |
268 | (define-key map [menu-bar grep compilation-separator2] '("----")) | |
318e2976 | 269 | (define-key map [menu-bar grep compilation-compile] |
38805987 DN |
270 | '(menu-item "Compile..." compile |
271 | :help "Compile the program including the current buffer. Default: run `make'")) | |
5d91db30 JL |
272 | (define-key map [menu-bar grep compilation-rgrep] |
273 | '(menu-item "Recursive grep..." rgrep | |
274 | :help "User-friendly recursive grep in directory tree")) | |
275 | (define-key map [menu-bar grep compilation-lgrep] | |
276 | '(menu-item "Local grep..." lgrep | |
277 | :help "User-friendly grep in a directory")) | |
278 | (define-key map [menu-bar grep compilation-grep-find] | |
279 | '(menu-item "Grep via Find..." grep-find | |
280 | :help "Run grep via find, with user-specified args")) | |
318e2976 | 281 | (define-key map [menu-bar grep compilation-grep] |
38805987 DN |
282 | '(menu-item "Another grep..." grep |
283 | :help "Run grep, with user-specified args, and collect output in a buffer.")) | |
318e2976 | 284 | (define-key map [menu-bar grep compilation-recompile] |
38805987 DN |
285 | '(menu-item "Repeat grep" recompile |
286 | :help "Run grep again")) | |
287 | (define-key map [menu-bar grep compilation-separator2] '("----")) | |
318e2976 | 288 | (define-key map [menu-bar grep compilation-first-error] |
38805987 DN |
289 | '(menu-item "First Match" first-error |
290 | :help "Restart at the first match, visit corresponding location")) | |
318e2976 | 291 | (define-key map [menu-bar grep compilation-previous-error] |
38805987 DN |
292 | '(menu-item "Previous Match" previous-error |
293 | :help "Visit the previous match and corresponding location")) | |
318e2976 | 294 | (define-key map [menu-bar grep compilation-next-error] |
38805987 DN |
295 | '(menu-item "Next Match" next-error |
296 | :help "Visit the next match and corresponding location")) | |
318e2976 KS |
297 | map) |
298 | "Keymap for grep buffers. | |
299 | `compilation-minor-mode-map' is a cdr of this.") | |
300 | ||
38cd9f17 | 301 | (defvar grep-mode-tool-bar-map |
30b72491 CY |
302 | ;; When bootstrapping, tool-bar-map is not properly initialized yet, |
303 | ;; so don't do anything. | |
304 | (when (keymapp (butlast tool-bar-map)) | |
305 | (let ((map (butlast (copy-keymap tool-bar-map))) | |
306 | (help (last tool-bar-map))) ;; Keep Help last in tool bar | |
307 | (tool-bar-local-item | |
308 | "left-arrow" 'previous-error-no-select 'previous-error-no-select map | |
309 | :rtl "right-arrow" | |
310 | :help "Goto previous match") | |
311 | (tool-bar-local-item | |
312 | "right-arrow" 'next-error-no-select 'next-error-no-select map | |
313 | :rtl "left-arrow" | |
314 | :help "Goto next match") | |
315 | (tool-bar-local-item | |
316 | "cancel" 'kill-compilation 'kill-compilation map | |
317 | :enable '(let ((buffer (compilation-find-buffer))) | |
318 | (get-buffer-process buffer)) | |
319 | :help "Stop grep") | |
320 | (tool-bar-local-item | |
321 | "refresh" 'recompile 'recompile map | |
322 | :help "Restart grep") | |
323 | (append map help)))) | |
38cd9f17 | 324 | |
ab55f76f SM |
325 | (defalias 'kill-grep 'kill-compilation) |
326 | ||
318e2976 KS |
327 | ;;;; TODO --- refine this!! |
328 | ||
6b3a84f2 SM |
329 | ;; (defcustom grep-use-compilation-buffer t |
330 | ;; "When non-nil, grep specific commands update `compilation-last-buffer'. | |
331 | ;; This means that standard compile commands like \\[next-error] and \\[compile-goto-error] | |
332 | ;; can be used to navigate between grep matches (the default). | |
333 | ;; Otherwise, the grep specific commands like \\[grep-next-match] must | |
334 | ;; be used to navigate between grep matches." | |
335 | ;; :type 'boolean | |
336 | ;; :group 'grep) | |
318e2976 KS |
337 | |
338 | ;; override compilation-last-buffer | |
339 | (defvar grep-last-buffer nil | |
340 | "The most recent grep buffer. | |
f911a445 | 341 | A grep buffer becomes most recent when you select Grep mode in it. |
318e2976 | 342 | Notice that using \\[next-error] or \\[compile-goto-error] modifies |
da6062e6 | 343 | `compilation-last-buffer' rather than `grep-last-buffer'.") |
318e2976 | 344 | |
9b19858e | 345 | ;;;###autoload |
2f7f4bee | 346 | (defconst grep-regexp-alist |
bfe5d7f0 JL |
347 | '( |
348 | ;; Rule to match column numbers is commented out since no known grep | |
349 | ;; produces them | |
350 | ;; ("^\\(.+?\\)\\(:[ \t]*\\)\\([1-9][0-9]*\\)\\2\\(?:\\([1-9][0-9]*\\)\\(?:-\\([1-9][0-9]*\\)\\)?\\2\\)?" | |
351 | ;; 1 3 (4 . 5)) | |
352 | ;; Note that we want to use as tight a regexp as we can to try and | |
353 | ;; handle weird file names (with colons in them) as well as possible. | |
354 | ;; E.g. we use [1-9][0-9]* rather than [0-9]+ so as to accept ":034:" | |
355 | ;; in file names. | |
356 | ("^\\(.+?\\)\\(:[ \t]*\\)\\([1-9][0-9]*\\)\\2" | |
0527e251 JL |
357 | 1 3 |
358 | ;; Calculate column positions (col . end-col) of first grep match on a line | |
359 | ((lambda () | |
360 | (when grep-highlight-matches | |
361 | (let* ((beg (match-end 0)) | |
362 | (end (save-excursion (goto-char beg) (line-end-position))) | |
98da8c0f | 363 | (mbeg (text-property-any beg end 'font-lock-face grep-match-face))) |
0527e251 JL |
364 | (when mbeg |
365 | (- mbeg beg))))) | |
366 | . | |
367 | (lambda () | |
368 | (when grep-highlight-matches | |
369 | (let* ((beg (match-end 0)) | |
370 | (end (save-excursion (goto-char beg) (line-end-position))) | |
98da8c0f | 371 | (mbeg (text-property-any beg end 'font-lock-face grep-match-face)) |
0527e251 JL |
372 | (mend (and mbeg (next-single-property-change mbeg 'font-lock-face nil end)))) |
373 | (when mend | |
374 | (- mend beg))))))) | |
ba65be24 | 375 | ("^Binary file \\(.+\\) matches$" 1 nil nil 0 1)) |
318e2976 KS |
376 | "Regexp used to match grep hits. See `compilation-error-regexp-alist'.") |
377 | ||
5ddce96c GM |
378 | (defvar grep-first-column 0 ; bug#10594 |
379 | "Value to use for `compilation-first-column' in grep buffers.") | |
380 | ||
ab55f76f SM |
381 | (defvar grep-error "grep hit" |
382 | "Message to print when no matches are found.") | |
383 | ||
384 | ;; Reverse the colors because grep hits are not errors (though we jump there | |
385 | ;; with `next-error'), and unreadable files can't be gone to. | |
386 | (defvar grep-hit-face compilation-info-face | |
387 | "Face name to use for grep hits.") | |
388 | ||
7462142d | 389 | (defvar grep-error-face 'compilation-error |
ab55f76f SM |
390 | "Face name to use for grep error messages.") |
391 | ||
f30c45e1 JL |
392 | (defvar grep-match-face 'match |
393 | "Face name to use for grep matches.") | |
394 | ||
35952129 JL |
395 | (defvar grep-context-face 'shadow |
396 | "Face name to use for grep context lines.") | |
397 | ||
ab55f76f SM |
398 | (defvar grep-mode-font-lock-keywords |
399 | '(;; Command output lines. | |
ab55f76f SM |
400 | (": \\(.+\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or address\\)\\)$" |
401 | 1 grep-error-face) | |
402 | ;; remove match from grep-regexp-alist before fontifying | |
c2c04c28 | 403 | ("^Grep[/a-zA-z]* started.*" |
1a0a6cef | 404 | (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t)) |
c2c04c28 | 405 | ("^Grep[/a-zA-z]* finished \\(?:(\\(matches found\\))\\|with \\(no matches found\\)\\).*" |
1a0a6cef | 406 | (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t) |
7462142d JL |
407 | (1 compilation-info-face nil t) |
408 | (2 compilation-warning-face nil t)) | |
c2c04c28 | 409 | ("^Grep[/a-zA-z]* \\(exited abnormally\\|interrupt\\|killed\\|terminated\\)\\(?:.*with code \\([0-9]+\\)\\)?.*" |
1a0a6cef | 410 | (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t) |
7462142d | 411 | (1 grep-error-face) |
cf115520 | 412 | (2 grep-error-face nil t)) |
7a1d7ba7 JL |
413 | ;; "filename-linenumber-" format is used for context lines in GNU grep, |
414 | ;; "filename=linenumber=" for lines with function names in "git grep -p". | |
415 | ("^.+?[-=][0-9]+[-=].*\n" (0 grep-context-face))) | |
ab55f76f SM |
416 | "Additional things to highlight in grep output. |
417 | This gets tacked on the end of the generated expressions.") | |
418 | ||
9b19858e | 419 | ;;;###autoload |
6bdad9ae | 420 | (defvar grep-program (purecopy "grep") |
318e2976 KS |
421 | "The default grep program for `grep-command' and `grep-find-command'. |
422 | This variable's value takes effect when `grep-compute-defaults' is called.") | |
423 | ||
9b19858e | 424 | ;;;###autoload |
6bdad9ae | 425 | (defvar find-program (purecopy "find") |
318e2976 KS |
426 | "The default find program for `grep-find-command'. |
427 | This variable's value takes effect when `grep-compute-defaults' is called.") | |
428 | ||
1571d112 | 429 | ;;;###autoload |
6bdad9ae | 430 | (defvar xargs-program (purecopy "xargs") |
1571d112 JB |
431 | "The default xargs program for `grep-find-command'. |
432 | See `grep-find-use-xargs'. | |
433 | This variable's value takes effect when `grep-compute-defaults' is called.") | |
434 | ||
9b19858e | 435 | ;;;###autoload |
318e2976 | 436 | (defvar grep-find-use-xargs nil |
f3ca7378 GM |
437 | "How to invoke find and grep. |
438 | If `exec', use `find -exec {} ;'. | |
439 | If `exec-plus' use `find -exec {} +'. | |
7f3afa3d | 440 | If `gnu', use `find -print0' and `xargs -0'. |
f3ca7378 | 441 | Any other value means to use `find -print' and `xargs'. |
318e2976 KS |
442 | |
443 | This variable's value takes effect when `grep-compute-defaults' is called.") | |
444 | ||
445 | ;; History of grep commands. | |
9b19858e | 446 | ;;;###autoload |
3adbe224 | 447 | (defvar grep-history nil "History list for grep.") |
9b19858e | 448 | ;;;###autoload |
3adbe224 | 449 | (defvar grep-find-history nil "History list for grep-find.") |
318e2976 | 450 | |
0acfb7ce KS |
451 | ;; History of lgrep and rgrep regexp and files args. |
452 | (defvar grep-regexp-history nil) | |
7ae62430 | 453 | (defvar grep-files-history nil) |
0acfb7ce | 454 | |
9b19858e | 455 | ;;;###autoload |
318e2976 KS |
456 | (defun grep-process-setup () |
457 | "Setup compilation variables and buffer for `grep'. | |
ab55f76f | 458 | Set up `compilation-exit-message-function' and run `grep-setup-hook'." |
a601d313 | 459 | (when (eq grep-highlight-matches 'auto-detect) |
a58c94a2 | 460 | (grep-compute-defaults)) |
a601d313 | 461 | (unless (or (eq grep-highlight-matches 'auto-detect) |
90439906 JL |
462 | (null grep-highlight-matches) |
463 | ;; Don't output color escapes if they can't be | |
464 | ;; highlighted with `font-lock-face' by `grep-filter'. | |
465 | (null font-lock-mode)) | |
6b8d0852 JL |
466 | ;; `setenv' modifies `process-environment' let-bound in `compilation-start' |
467 | ;; Any TERM except "dumb" allows GNU grep to use `--color=auto' | |
468 | (setenv "TERM" "emacs-grep") | |
8f0e19af EZ |
469 | (setenv "GREP_OPTIONS" |
470 | (concat (getenv "GREP_OPTIONS") | |
a601d313 | 471 | " --color=" (if (eq grep-highlight-matches 'always) |
8f0e19af | 472 | "always" "auto"))) |
6b8d0852 | 473 | ;; GREP_COLOR is used in GNU grep 2.5.1, but deprecated in later versions |
983203ee | 474 | (setenv "GREP_COLOR" "01;31") |
6b8d0852 | 475 | ;; GREP_COLORS is used in GNU grep 2.5.2 and later versions |
f62bd846 | 476 | (setenv "GREP_COLORS" "mt=01;31:fn=:ln=:bn=:se=:sl=:cx=:ne")) |
318e2976 KS |
477 | (set (make-local-variable 'compilation-exit-message-function) |
478 | (lambda (status code msg) | |
479 | (if (eq status 'exit) | |
262a1439 JL |
480 | ;; This relies on the fact that `compilation-start' |
481 | ;; sets buffer-modified to nil before running the command, | |
482 | ;; so the buffer is still unmodified if there is no output. | |
483 | (cond ((and (zerop code) (buffer-modified-p)) | |
318e2976 | 484 | '("finished (matches found)\n" . "matched")) |
46c5cf66 | 485 | ((not (buffer-modified-p)) |
318e2976 KS |
486 | '("finished with no matches found\n" . "no match")) |
487 | (t | |
488 | (cons msg code))) | |
489 | (cons msg code)))) | |
318e2976 KS |
490 | (run-hooks 'grep-setup-hook)) |
491 | ||
d9c54a06 CY |
492 | (defun grep-filter () |
493 | "Handle match highlighting escape sequences inserted by the grep process. | |
494 | This function is called from `compilation-filter-hook'." | |
495 | (save-excursion | |
c89be45f | 496 | (forward-line 0) |
f62bd846 | 497 | (let ((end (point)) beg) |
d9c54a06 | 498 | (goto-char compilation-filter-start) |
c89be45f | 499 | (forward-line 0) |
f62bd846 | 500 | (setq beg (point)) |
c89be45f SM |
501 | ;; Only operate on whole lines so we don't get caught with part of an |
502 | ;; escape sequence in one chunk and the rest in another. | |
503 | (when (< (point) end) | |
504 | (setq end (copy-marker end)) | |
505 | ;; Highlight grep matches and delete marking sequences. | |
f62bd846 | 506 | (while (re-search-forward "\033\\[0?1;31m\\(.*?\\)\033\\[[0-9]*m" end 1) |
c89be45f SM |
507 | (replace-match (propertize (match-string 1) |
508 | 'face nil 'font-lock-face grep-match-face) | |
509 | t t)) | |
510 | ;; Delete all remaining escape sequences | |
f62bd846 | 511 | (goto-char beg) |
c89be45f SM |
512 | (while (re-search-forward "\033\\[[0-9;]*[mK]" end 1) |
513 | (replace-match "" t t)))))) | |
d9c54a06 | 514 | |
0acfb7ce | 515 | (defun grep-probe (command args &optional func result) |
458c8d31 MA |
516 | (let (process-file-side-effects) |
517 | (equal (condition-case nil | |
518 | (apply (or func 'process-file) command args) | |
519 | (error nil)) | |
520 | (or result 0)))) | |
0acfb7ce | 521 | |
9b19858e | 522 | ;;;###autoload |
318e2976 | 523 | (defun grep-compute-defaults () |
2e2eead3 MA |
524 | ;; Keep default values. |
525 | (unless grep-host-defaults-alist | |
526 | (add-to-list | |
527 | 'grep-host-defaults-alist | |
528 | (cons nil | |
529 | `((grep-command ,grep-command) | |
530 | (grep-template ,grep-template) | |
531 | (grep-use-null-device ,grep-use-null-device) | |
532 | (grep-find-command ,grep-find-command) | |
533 | (grep-find-template ,grep-find-template) | |
534 | (grep-find-use-xargs ,grep-find-use-xargs) | |
535 | (grep-highlight-matches ,grep-highlight-matches))))) | |
536 | (let* ((host-id | |
85e7298f | 537 | (intern (or (file-remote-p default-directory) "localhost"))) |
2e2eead3 MA |
538 | (host-defaults (assq host-id grep-host-defaults-alist)) |
539 | (defaults (assq nil grep-host-defaults-alist))) | |
05c7c7b1 | 540 | ;; There are different defaults on different hosts. They must be |
2e2eead3 | 541 | ;; computed for every host once. |
d4bbd646 CY |
542 | (dolist (setting '(grep-command grep-template |
543 | grep-use-null-device grep-find-command | |
544 | grep-find-template grep-find-use-xargs | |
545 | grep-highlight-matches)) | |
546 | (set setting | |
7860d2e3 CY |
547 | (cadr (or (assq setting host-defaults) |
548 | (assq setting defaults))))) | |
05c7c7b1 MA |
549 | |
550 | (unless (or (not grep-use-null-device) (eq grep-use-null-device t)) | |
551 | (setq grep-use-null-device | |
552 | (with-temp-buffer | |
553 | (let ((hello-file (expand-file-name "HELLO" data-directory))) | |
554 | (not | |
555 | (and (if grep-command | |
556 | ;; `grep-command' is already set, so | |
557 | ;; use that for testing. | |
558 | (grep-probe grep-command | |
559 | `(nil t nil "^English" ,hello-file) | |
560 | #'call-process-shell-command) | |
561 | ;; otherwise use `grep-program' | |
562 | (grep-probe grep-program | |
563 | `(nil t nil "-nH" "^English" ,hello-file))) | |
564 | (progn | |
565 | (goto-char (point-min)) | |
566 | (looking-at | |
567 | (concat (regexp-quote hello-file) | |
568 | ":[0-9]+:English"))))))))) | |
569 | (unless (and grep-command grep-find-command | |
570 | grep-template grep-find-template) | |
571 | (let ((grep-options | |
572 | (concat (if grep-use-null-device "-n" "-nH") | |
573 | (if (grep-probe grep-program | |
574 | `(nil nil nil "-e" "foo" ,null-device) | |
575 | nil 1) | |
576 | " -e")))) | |
577 | (unless grep-command | |
578 | (setq grep-command | |
579 | (format "%s %s " grep-program grep-options))) | |
580 | (unless grep-template | |
581 | (setq grep-template | |
7ae62430 | 582 | (format "%s <X> <C> %s <R> <F>" grep-program grep-options))) |
05c7c7b1 MA |
583 | (unless grep-find-use-xargs |
584 | (setq grep-find-use-xargs | |
585 | (cond | |
f3ca7378 GM |
586 | ((grep-probe find-program |
587 | `(nil nil nil ,null-device "-exec" "echo" | |
588 | "{}" "+")) | |
589 | 'exec-plus) | |
05c7c7b1 MA |
590 | ((and |
591 | (grep-probe find-program `(nil nil nil ,null-device "-print0")) | |
6fccd6e8 | 592 | (grep-probe xargs-program `(nil nil nil "-0" "echo"))) |
05c7c7b1 MA |
593 | 'gnu) |
594 | (t | |
595 | 'exec)))) | |
596 | (unless grep-find-command | |
597 | (setq grep-find-command | |
0acfb7ce | 598 | (cond ((eq grep-find-use-xargs 'gnu) |
362b9d48 GM |
599 | ;; Windows shells need the program file name |
600 | ;; after the pipe symbol be quoted if they use | |
601 | ;; forward slashes as directory separators. | |
6fccd6e8 | 602 | (format "%s . -type f -print0 | \"%s\" -0 %s" |
1571d112 | 603 | find-program xargs-program grep-command)) |
f3ca7378 | 604 | ((memq grep-find-use-xargs '(exec exec-plus)) |
05c7c7b1 | 605 | (let ((cmd0 (format "%s . -type f -exec %s" |
f3ca7378 GM |
606 | find-program grep-command)) |
607 | (null (if grep-use-null-device | |
608 | (format "%s " null-device) | |
609 | ""))) | |
05c7c7b1 | 610 | (cons |
f3ca7378 GM |
611 | (if (eq grep-find-use-xargs 'exec-plus) |
612 | (format "%s %s{} +" cmd0 null) | |
613 | (format "%s {} %s%s" cmd0 null | |
614 | (shell-quote-argument ";"))) | |
05c7c7b1 | 615 | (1+ (length cmd0))))) |
84d797c9 | 616 | (t |
362b9d48 | 617 | (format "%s . -type f -print | \"%s\" %s" |
1571d112 | 618 | find-program xargs-program grep-command))))) |
05c7c7b1 MA |
619 | (unless grep-find-template |
620 | (setq grep-find-template | |
621 | (let ((gcmd (format "%s <C> %s <R>" | |
f3ca7378 GM |
622 | grep-program grep-options)) |
623 | (null (if grep-use-null-device | |
624 | (format "%s " null-device) | |
625 | ""))) | |
05c7c7b1 | 626 | (cond ((eq grep-find-use-xargs 'gnu) |
6fccd6e8 | 627 | (format "%s . <X> -type f <F> -print0 | \"%s\" -0 %s" |
1571d112 | 628 | find-program xargs-program gcmd)) |
05c7c7b1 | 629 | ((eq grep-find-use-xargs 'exec) |
f3ca7378 GM |
630 | (format "%s . <X> -type f <F> -exec %s {} %s%s" |
631 | find-program gcmd null | |
05c7c7b1 | 632 | (shell-quote-argument ";"))) |
f3ca7378 GM |
633 | ((eq grep-find-use-xargs 'exec-plus) |
634 | (format "%s . <X> -type f <F> -exec %s %s{} +" | |
635 | find-program gcmd null)) | |
05c7c7b1 | 636 | (t |
362b9d48 | 637 | (format "%s . <X> -type f <F> -print | \"%s\" %s" |
1571d112 | 638 | find-program xargs-program gcmd)))))))) |
a601d313 | 639 | (when (eq grep-highlight-matches 'auto-detect) |
05c7c7b1 MA |
640 | (setq grep-highlight-matches |
641 | (with-temp-buffer | |
642 | (and (grep-probe grep-program '(nil t nil "--help")) | |
643 | (progn | |
644 | (goto-char (point-min)) | |
645 | (search-forward "--color" nil t)) | |
a601d313 JL |
646 | ;; Windows and DOS pipes fail `isatty' detection in Grep. |
647 | (if (memq system-type '(windows-nt ms-dos)) | |
648 | 'always 'auto))))) | |
05c7c7b1 MA |
649 | |
650 | ;; Save defaults for this host. | |
6e3aa3f5 MA |
651 | (setq grep-host-defaults-alist |
652 | (delete (assq host-id grep-host-defaults-alist) | |
653 | grep-host-defaults-alist)) | |
654 | (add-to-list | |
655 | 'grep-host-defaults-alist | |
656 | (cons host-id | |
657 | `((grep-command ,grep-command) | |
658 | (grep-template ,grep-template) | |
659 | (grep-use-null-device ,grep-use-null-device) | |
660 | (grep-find-command ,grep-find-command) | |
661 | (grep-find-template ,grep-find-template) | |
662 | (grep-find-use-xargs ,grep-find-use-xargs) | |
663 | (grep-highlight-matches ,grep-highlight-matches)))))) | |
318e2976 | 664 | |
7cb0d0ef KS |
665 | (defun grep-tag-default () |
666 | (or (and transient-mark-mode mark-active | |
667 | (/= (point) (mark)) | |
668 | (buffer-substring-no-properties (point) (mark))) | |
669 | (funcall (or find-tag-default-function | |
670 | (get major-mode 'find-tag-default-function) | |
671 | 'find-tag-default)) | |
672 | "")) | |
673 | ||
318e2976 | 674 | (defun grep-default-command () |
1571d112 | 675 | "Compute the default grep command for \\[universal-argument] \\[grep] to offer." |
7cb0d0ef | 676 | (let ((tag-default (shell-quote-argument (grep-tag-default))) |
13eb1bde RS |
677 | ;; This a regexp to match single shell arguments. |
678 | ;; Could someone please add comments explaining it? | |
318e2976 KS |
679 | (sh-arg-re "\\(\\(?:\"\\(?:[^\"]\\|\\\\\"\\)+\"\\|'[^']+'\\|[^\"' \t\n]\\)+\\)") |
680 | (grep-default (or (car grep-history) grep-command))) | |
13eb1bde | 681 | ;; In the default command, find the arg that specifies the pattern. |
318e2976 KS |
682 | (when (or (string-match |
683 | (concat "[^ ]+\\s +\\(?:-[^ ]+\\s +\\)*" | |
684 | sh-arg-re "\\(\\s +\\(\\S +\\)\\)?") | |
685 | grep-default) | |
686 | ;; If the string is not yet complete. | |
687 | (string-match "\\(\\)\\'" grep-default)) | |
13eb1bde RS |
688 | ;; Maybe we will replace the pattern with the default tag. |
689 | ;; But first, maybe replace the file name pattern. | |
690 | (condition-case nil | |
691 | (unless (or (not (stringp buffer-file-name)) | |
692 | (when (match-beginning 2) | |
693 | (save-match-data | |
694 | (string-match | |
695 | (wildcard-to-regexp | |
696 | (file-name-nondirectory | |
697 | (match-string 3 grep-default))) | |
698 | (file-name-nondirectory buffer-file-name))))) | |
699 | (setq grep-default (concat (substring grep-default | |
700 | 0 (match-beginning 2)) | |
701 | " *." | |
702 | (file-name-extension buffer-file-name)))) | |
703 | ;; In case wildcard-to-regexp gets an error | |
704 | ;; from invalid data. | |
705 | (error nil)) | |
706 | ;; Now replace the pattern with the default tag. | |
02b73b97 | 707 | (replace-match tag-default t t grep-default 1)))) |
318e2976 | 708 | |
0acfb7ce | 709 | |
318e2976 | 710 | ;;;###autoload |
0acfb7ce KS |
711 | (define-compilation-mode grep-mode "Grep" |
712 | "Sets `grep-last-buffer' and `compilation-window-height'." | |
713 | (setq grep-last-buffer (current-buffer)) | |
38cd9f17 | 714 | (set (make-local-variable 'tool-bar-map) grep-mode-tool-bar-map) |
0acfb7ce KS |
715 | (set (make-local-variable 'compilation-error-face) |
716 | grep-hit-face) | |
717 | (set (make-local-variable 'compilation-error-regexp-alist) | |
718 | grep-regexp-alist) | |
b7cf2c79 SM |
719 | ;; compilation-directory-matcher can't be nil, so we set it to a regexp that |
720 | ;; can never match. | |
79106a44 | 721 | (set (make-local-variable 'compilation-directory-matcher) '("\\`a\\`")) |
0acfb7ce KS |
722 | (set (make-local-variable 'compilation-process-setup-function) |
723 | 'grep-process-setup) | |
d9c54a06 | 724 | (set (make-local-variable 'compilation-disable-input) t) |
0527e251 JL |
725 | (set (make-local-variable 'compilation-error-screen-columns) |
726 | grep-error-screen-columns) | |
d9c54a06 | 727 | (add-hook 'compilation-filter-hook 'grep-filter nil t)) |
0acfb7ce KS |
728 | |
729 | ||
730 | ;;;###autoload | |
731 | (defun grep (command-args) | |
318e2976 KS |
732 | "Run grep, with user-specified args, and collect output in a buffer. |
733 | While grep runs asynchronously, you can use \\[next-error] (M-x next-error), | |
d02766ab CY |
734 | or \\<grep-mode-map>\\[compile-goto-error] in the *grep* \ |
735 | buffer, to go to the lines where grep found | |
736 | matches. To kill the grep job before it finishes, type \\[kill-compilation]. | |
318e2976 | 737 | |
215c3847 | 738 | For doing a recursive `grep', see the `rgrep' command. For running |
9d4a3d39 | 739 | `grep' in a specific directory, see `lgrep'. |
215c3847 | 740 | |
1571d112 JB |
741 | This command uses a special history list for its COMMAND-ARGS, so you |
742 | can easily repeat a grep command. | |
318e2976 KS |
743 | |
744 | A prefix argument says to default the argument based upon the current | |
745 | tag the cursor is over, substituting it into the last grep command | |
1571d112 JB |
746 | in the grep command history (or into `grep-command' if that history |
747 | list is empty)." | |
318e2976 KS |
748 | (interactive |
749 | (progn | |
0acfb7ce | 750 | (grep-compute-defaults) |
318e2976 | 751 | (let ((default (grep-default-command))) |
286d4b3b SM |
752 | (list (read-shell-command "Run grep (like this): " |
753 | (if current-prefix-arg default grep-command) | |
754 | 'grep-history | |
755 | (if current-prefix-arg nil default)))))) | |
318e2976 KS |
756 | |
757 | ;; Setting process-setup-function makes exit-message-function work | |
758 | ;; even when async processes aren't supported. | |
19b3905f JL |
759 | (compilation-start (if (and grep-use-null-device null-device) |
760 | (concat command-args " " null-device) | |
761 | command-args) | |
0acfb7ce | 762 | 'grep-mode)) |
ab55f76f | 763 | |
318e2976 | 764 | |
318e2976 KS |
765 | ;;;###autoload |
766 | (defun grep-find (command-args) | |
767 | "Run grep via find, with user-specified args COMMAND-ARGS. | |
768 | Collect output in a buffer. | |
769 | While find runs asynchronously, you can use the \\[next-error] command | |
770 | to find the text that grep hits refer to. | |
771 | ||
772 | This command uses a special history list for its arguments, so you can | |
773 | easily repeat a find command." | |
774 | (interactive | |
775 | (progn | |
0acfb7ce | 776 | (grep-compute-defaults) |
87f54c05 | 777 | (if grep-find-command |
286d4b3b SM |
778 | (list (read-shell-command "Run find (like this): " |
779 | grep-find-command 'grep-find-history)) | |
87f54c05 RS |
780 | ;; No default was set |
781 | (read-string | |
782 | "compile.el: No `grep-find-command' command available. Press RET.") | |
783 | (list nil)))) | |
a34f7eb1 | 784 | (when command-args |
87f54c05 RS |
785 | (let ((null-device nil)) ; see grep |
786 | (grep command-args)))) | |
318e2976 | 787 | |
3d4d788a DK |
788 | ;;;###autoload |
789 | (defalias 'find-grep 'grep-find) | |
790 | ||
0acfb7ce KS |
791 | |
792 | ;; User-friendly interactive API. | |
793 | ||
794 | (defconst grep-expand-keywords | |
795 | '(("<C>" . (and cf (isearch-no-upper-case-p regexp t) "-i")) | |
796 | ("<D>" . dir) | |
797 | ("<F>" . files) | |
798 | ("<N>" . null-device) | |
799 | ("<X>" . excl) | |
800 | ("<R>" . (shell-quote-argument (or regexp "")))) | |
801 | "List of substitutions performed by `grep-expand-template'. | |
802 | If car of an element matches, the cdr is evalled in to get the | |
803 | substitution string. Note dynamic scoping of variables.") | |
804 | ||
805 | (defun grep-expand-template (template &optional regexp files dir excl) | |
806 | "Patch grep COMMAND string replacing <C>, <D>, <F>, <R>, and <X>." | |
807 | (let ((command template) | |
808 | (cf case-fold-search) | |
809 | (case-fold-search nil)) | |
810 | (dolist (kw grep-expand-keywords command) | |
811 | (if (string-match (car kw) command) | |
d0afff34 KS |
812 | (setq command |
813 | (replace-match | |
814 | (or (if (symbolp (cdr kw)) | |
41a2f8ba | 815 | (symbol-value (cdr kw)) |
d0afff34 KS |
816 | (save-match-data (eval (cdr kw)))) |
817 | "") | |
818 | t t command)))))) | |
0acfb7ce KS |
819 | |
820 | (defun grep-read-regexp () | |
821 | "Read regexp arg for interactive grep." | |
7cb0d0ef | 822 | (let ((default (grep-tag-default))) |
eb2deaff | 823 | (read-regexp |
0acfb7ce KS |
824 | (concat "Search for" |
825 | (if (and default (> (length default) 0)) | |
7cb0d0ef | 826 | (format " (default \"%s\"): " default) ": ")) |
eb2deaff | 827 | default 'grep-regexp-history))) |
0acfb7ce KS |
828 | |
829 | (defun grep-read-files (regexp) | |
830 | "Read files arg for interactive grep." | |
6830f449 JL |
831 | (let* ((bn (or (buffer-file-name) |
832 | (replace-regexp-in-string "<[0-9]+>\\'" "" (buffer-name)))) | |
208cc91f KS |
833 | (fn (and bn |
834 | (stringp bn) | |
835 | (file-name-nondirectory bn))) | |
7ae62430 JL |
836 | (default-alias |
837 | (and fn | |
df9db151 JL |
838 | (let ((aliases (remove (assoc "all" grep-files-aliases) |
839 | grep-files-aliases)) | |
7ae62430 JL |
840 | alias) |
841 | (while aliases | |
842 | (setq alias (car aliases) | |
843 | aliases (cdr aliases)) | |
df9db151 JL |
844 | (if (string-match (mapconcat |
845 | 'wildcard-to-regexp | |
846 | (split-string (cdr alias) nil t) | |
847 | "\\|") | |
848 | fn) | |
7ae62430 JL |
849 | (setq aliases nil) |
850 | (setq alias nil))) | |
851 | (cdr alias)))) | |
852 | (default-extension | |
853 | (and fn | |
854 | (let ((ext (file-name-extension fn))) | |
855 | (and ext (concat "*." ext))))) | |
208cc91f | 856 | (default |
7ae62430 JL |
857 | (or default-alias |
858 | default-extension | |
bcdf86fb KS |
859 | (car grep-files-history) |
860 | (car (car grep-files-aliases)))) | |
9136e895 | 861 | (files (completing-read |
0acfb7ce | 862 | (concat "Search for \"" regexp |
208cc91f KS |
863 | "\" in files" |
864 | (if default (concat " (default " default ")")) | |
865 | ": ") | |
9136e895 JL |
866 | 'read-file-name-internal |
867 | nil nil nil 'grep-files-history | |
7ae62430 JL |
868 | (delete-dups |
869 | (delq nil (append (list default default-alias default-extension) | |
870 | (mapcar 'car grep-files-aliases))))))) | |
0acfb7ce KS |
871 | (and files |
872 | (or (cdr (assoc files grep-files-aliases)) | |
873 | files)))) | |
318e2976 KS |
874 | |
875 | ;;;###autoload | |
32a2cf25 | 876 | (defun lgrep (regexp &optional files dir confirm) |
9d4a3d39 | 877 | "Run grep, searching for REGEXP in FILES in directory DIR. |
318e2976 | 878 | The search is limited to file names matching shell pattern FILES. |
0acfb7ce | 879 | FILES may use abbreviations defined in `grep-files-aliases', e.g. |
318e2976 KS |
880 | entering `ch' is equivalent to `*.[ch]'. |
881 | ||
6e7c574f KS |
882 | With \\[universal-argument] prefix, you can edit the constructed shell command line |
883 | before it is executed. | |
884 | With two \\[universal-argument] prefixes, directly edit and run `grep-command'. | |
318e2976 | 885 | |
0acfb7ce | 886 | Collect output in a buffer. While grep runs asynchronously, you |
1571d112 JB |
887 | can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error] \ |
888 | in the grep output buffer, | |
889 | to go to the lines where grep found matches. | |
318e2976 | 890 | |
0acfb7ce | 891 | This command shares argument histories with \\[rgrep] and \\[grep]." |
318e2976 | 892 | (interactive |
0acfb7ce KS |
893 | (progn |
894 | (grep-compute-defaults) | |
895 | (cond | |
896 | ((and grep-command (equal current-prefix-arg '(16))) | |
897 | (list (read-from-minibuffer "Run: " grep-command | |
32a2cf25 | 898 | nil nil 'grep-history))) |
0acfb7ce | 899 | ((not grep-template) |
5a0c3f56 | 900 | (error "grep.el: No `grep-template' available")) |
0acfb7ce | 901 | (t (let* ((regexp (grep-read-regexp)) |
9d4a3d39 KS |
902 | (files (grep-read-files regexp)) |
903 | (dir (read-directory-name "In directory: " | |
32a2cf25 JL |
904 | nil default-directory t)) |
905 | (confirm (equal current-prefix-arg '(4)))) | |
906 | (list regexp files dir confirm)))))) | |
0acfb7ce | 907 | (when (and (stringp regexp) (> (length regexp) 0)) |
32a2cf25 JL |
908 | (unless (and dir (file-directory-p dir) (file-readable-p dir)) |
909 | (setq dir default-directory)) | |
0acfb7ce KS |
910 | (let ((command regexp)) |
911 | (if (null files) | |
912 | (if (string= command grep-command) | |
913 | (setq command nil)) | |
9d4a3d39 | 914 | (setq dir (file-name-as-directory (expand-file-name dir))) |
0acfb7ce KS |
915 | (setq command (grep-expand-template |
916 | grep-template | |
917 | regexp | |
7ae62430 JL |
918 | files |
919 | nil | |
920 | (and grep-find-ignored-files | |
921 | (concat " --exclude=" | |
922 | (mapconcat | |
923 | #'(lambda (ignore) | |
924 | (cond ((stringp ignore) | |
925 | (shell-quote-argument ignore)) | |
926 | ((consp ignore) | |
927 | (and (funcall (car ignore) dir) | |
928 | (shell-quote-argument | |
929 | (cdr ignore)))))) | |
930 | grep-find-ignored-files | |
931 | " --exclude="))))) | |
0acfb7ce | 932 | (when command |
32a2cf25 | 933 | (if confirm |
0acfb7ce KS |
934 | (setq command |
935 | (read-from-minibuffer "Confirm: " | |
936 | command nil nil 'grep-history)) | |
c3e9438b | 937 | (add-to-history 'grep-history command)))) |
0acfb7ce | 938 | (when command |
32a2cf25 | 939 | (let ((default-directory dir)) |
9d4a3d39 KS |
940 | ;; Setting process-setup-function makes exit-message-function work |
941 | ;; even when async processes aren't supported. | |
942 | (compilation-start (if (and grep-use-null-device null-device) | |
943 | (concat command " " null-device) | |
c60c6d91 RS |
944 | command) |
945 | 'grep-mode)) | |
9d4a3d39 KS |
946 | (if (eq next-error-last-buffer (current-buffer)) |
947 | (setq default-directory dir)))))) | |
948 | ||
0acfb7ce | 949 | |
da205913 | 950 | (defvar find-name-arg) ; not autoloaded but defined in find-dired |
0acfb7ce KS |
951 | |
952 | ;;;###autoload | |
32a2cf25 | 953 | (defun rgrep (regexp &optional files dir confirm) |
6e7c574f | 954 | "Recursively grep for REGEXP in FILES in directory tree rooted at DIR. |
0acfb7ce KS |
955 | The search is limited to file names matching shell pattern FILES. |
956 | FILES may use abbreviations defined in `grep-files-aliases', e.g. | |
957 | entering `ch' is equivalent to `*.[ch]'. | |
958 | ||
6e7c574f KS |
959 | With \\[universal-argument] prefix, you can edit the constructed shell command line |
960 | before it is executed. | |
961 | With two \\[universal-argument] prefixes, directly edit and run `grep-find-command'. | |
0acfb7ce | 962 | |
d02766ab CY |
963 | Collect output in a buffer. While the recursive grep is running, |
964 | you can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error] \ | |
1571d112 | 965 | in the grep output buffer, |
d02766ab CY |
966 | to visit the lines where matches were found. To kill the job |
967 | before it finishes, type \\[kill-compilation]. | |
0acfb7ce | 968 | |
1c4757d6 JL |
969 | This command shares argument histories with \\[lgrep] and \\[grep-find]. |
970 | ||
971 | When called programmatically and FILES is nil, REGEXP is expected | |
972 | to specify a command to run." | |
0acfb7ce KS |
973 | (interactive |
974 | (progn | |
975 | (grep-compute-defaults) | |
976 | (cond | |
977 | ((and grep-find-command (equal current-prefix-arg '(16))) | |
978 | (list (read-from-minibuffer "Run: " grep-find-command | |
32a2cf25 | 979 | nil nil 'grep-find-history))) |
0acfb7ce | 980 | ((not grep-find-template) |
5a0c3f56 | 981 | (error "grep.el: No `grep-find-template' available")) |
0acfb7ce KS |
982 | (t (let* ((regexp (grep-read-regexp)) |
983 | (files (grep-read-files regexp)) | |
984 | (dir (read-directory-name "Base directory: " | |
32a2cf25 JL |
985 | nil default-directory t)) |
986 | (confirm (equal current-prefix-arg '(4)))) | |
987 | (list regexp files dir confirm)))))) | |
0acfb7ce | 988 | (when (and (stringp regexp) (> (length regexp) 0)) |
32a2cf25 JL |
989 | (unless (and dir (file-directory-p dir) (file-readable-p dir)) |
990 | (setq dir default-directory)) | |
0acfb7ce | 991 | (if (null files) |
02b404de JL |
992 | (if (not (string= regexp (if (consp grep-find-command) |
993 | (car grep-find-command) | |
994 | grep-find-command))) | |
12b16734 | 995 | (compilation-start regexp 'grep-mode)) |
c0b87c3f | 996 | (setq dir (file-name-as-directory (expand-file-name dir))) |
da205913 | 997 | (require 'find-dired) ; for `find-name-arg' |
91b982a0 MA |
998 | ;; In Tramp, there could be problems if the command line is too |
999 | ;; long. We escape it, therefore. | |
c0b87c3f KS |
1000 | (let ((command (grep-expand-template |
1001 | grep-find-template | |
1002 | regexp | |
84d797c9 | 1003 | (concat (shell-quote-argument "(") |
34fac407 | 1004 | " " find-name-arg " " |
91b982a0 MA |
1005 | (mapconcat |
1006 | #'shell-quote-argument | |
1007 | (split-string files) | |
a59d76e0 | 1008 | (concat "\\\n" " -o " find-name-arg " ")) |
84d797c9 KS |
1009 | " " |
1010 | (shell-quote-argument ")")) | |
7ae62430 JL |
1011 | dir |
1012 | (concat | |
0acfb7ce | 1013 | (and grep-find-ignored-directories |
453de99f OG |
1014 | (concat "-type d " |
1015 | (shell-quote-argument "(") | |
84d797c9 KS |
1016 | ;; we should use shell-quote-argument here |
1017 | " -path " | |
49405d0e | 1018 | (mapconcat |
7ae62430 JL |
1019 | #'(lambda (ignore) |
1020 | (cond ((stringp ignore) | |
1021 | (shell-quote-argument | |
1022 | (concat "*/" ignore))) | |
1023 | ((consp ignore) | |
1024 | (and (funcall (car ignore) dir) | |
1025 | (shell-quote-argument | |
1026 | (concat "*/" | |
1027 | (cdr ignore))))))) | |
1028 | grep-find-ignored-directories | |
a59d76e0 | 1029 | "\\\n -o -path ") |
7ae62430 JL |
1030 | " " |
1031 | (shell-quote-argument ")") | |
1032 | " -prune -o ")) | |
1033 | (and grep-find-ignored-files | |
49a2697c WJ |
1034 | (concat (shell-quote-argument "!") " -type d " |
1035 | (shell-quote-argument "(") | |
7ae62430 JL |
1036 | ;; we should use shell-quote-argument here |
1037 | " -name " | |
1038 | (mapconcat | |
1039 | #'(lambda (ignore) | |
1040 | (cond ((stringp ignore) | |
1041 | (shell-quote-argument ignore)) | |
1042 | ((consp ignore) | |
1043 | (and (funcall (car ignore) dir) | |
1044 | (shell-quote-argument | |
1045 | (cdr ignore)))))) | |
1046 | grep-find-ignored-files | |
a59d76e0 | 1047 | "\\\n -o -name ") |
84d797c9 KS |
1048 | " " |
1049 | (shell-quote-argument ")") | |
7ae62430 | 1050 | " -prune -o ")))))) |
0acfb7ce | 1051 | (when command |
32a2cf25 | 1052 | (if confirm |
0acfb7ce KS |
1053 | (setq command |
1054 | (read-from-minibuffer "Confirm: " | |
1055 | command nil nil 'grep-find-history)) | |
c3e9438b | 1056 | (add-to-history 'grep-find-history command)) |
ac8cf6e6 | 1057 | (let ((default-directory dir)) |
c0b87c3f KS |
1058 | (compilation-start command 'grep-mode)) |
1059 | ;; Set default-directory if we started rgrep in the *grep* buffer. | |
1060 | (if (eq next-error-last-buffer (current-buffer)) | |
1061 | (setq default-directory dir))))))) | |
318e2976 | 1062 | |
a601d313 JL |
1063 | ;;;###autoload |
1064 | (defun zrgrep (regexp &optional files dir confirm grep-find-template) | |
1065 | "Recursively grep for REGEXP in gzipped FILES in tree rooted at DIR. | |
1066 | Like `rgrep' but uses `zgrep' for `grep-program', sets the default | |
1067 | file name to `*.gz', and sets `grep-highlight-matches' to `always'." | |
1068 | (interactive | |
ad6fc8f4 JL |
1069 | (progn |
1070 | ;; Compute standard default values. | |
a601d313 | 1071 | (grep-compute-defaults) |
ad6fc8f4 JL |
1072 | ;; Compute the default zrgrep command by running `grep-compute-defaults' |
1073 | ;; for grep program "zgrep", but not changing global values. | |
1074 | (let ((grep-program "zgrep") | |
1075 | ;; Don't change global values for variables computed | |
1076 | ;; by `grep-compute-defaults'. | |
1077 | (grep-find-template nil) | |
1078 | (grep-find-command nil) | |
1079 | (grep-host-defaults-alist nil) | |
1080 | ;; Use for `grep-read-files' | |
1081 | (grep-files-aliases '(("all" . "* .*") | |
1082 | ("gz" . "*.gz")))) | |
1083 | ;; Recompute defaults using let-bound values above. | |
1084 | (grep-compute-defaults) | |
1085 | (cond | |
1086 | ((and grep-find-command (equal current-prefix-arg '(16))) | |
1087 | (list (read-from-minibuffer "Run: " grep-find-command | |
1088 | nil nil 'grep-find-history))) | |
1089 | ((not grep-find-template) | |
1090 | (error "grep.el: No `grep-find-template' available")) | |
1091 | (t (let* ((regexp (grep-read-regexp)) | |
1092 | (files (grep-read-files regexp)) | |
1093 | (dir (read-directory-name "Base directory: " | |
1094 | nil default-directory t)) | |
1095 | (confirm (equal current-prefix-arg '(4)))) | |
1096 | (list regexp files dir confirm grep-find-template))))))) | |
a601d313 JL |
1097 | ;; Set `grep-highlight-matches' to `always' |
1098 | ;; since `zgrep' puts filters in the grep output. | |
1099 | (let ((grep-highlight-matches 'always)) | |
1100 | ;; `rgrep' uses the dynamically bound value `grep-find-template' | |
1101 | ;; from the argument `grep-find-template' whose value is computed | |
1102 | ;; in the `interactive' spec. | |
1103 | (rgrep regexp files dir confirm))) | |
1104 | ||
1105 | ;;;###autoload | |
1106 | (defalias 'rzgrep 'zrgrep) | |
318e2976 KS |
1107 | |
1108 | (provide 'grep) | |
1109 | ||
06626cf2 | 1110 | ;;; grep.el ends here |