]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/grep.el
Merge from emacs-24; up to 2012-12-06T01:39:03Z!monnier@iro.umontreal.ca
[gnu-emacs] / lisp / progmodes / grep.el
index 31100f3fac2f4db06ced43388fe23d47e7f57afc..ef321addf2448c9e84c6da147f1d7ac27765d779 100644 (file)
@@ -1,7 +1,7 @@
 ;;; grep.el --- run `grep' and display the results
 
-;; Copyright (C) 1985-1987, 1993-1999, 2001-2011
-;;   Free Software Foundation, Inc.
+;; Copyright (C) 1985-1987, 1993-1999, 2001-2013 Free Software
+;; Foundation, Inc.
 
 ;; Author: Roland McGrath <roland@gnu.org>
 ;; Maintainer: FSF
@@ -61,7 +61,7 @@ SYMBOL should be one of `grep-command', `grep-template',
 
 ;;;###autoload
 (defcustom grep-window-height nil
-  "*Number of lines in a grep window.  If nil, use `compilation-window-height'."
+  "Number of lines in a grep window.  If nil, use `compilation-window-height'."
   :type '(choice (const :tag "Default" nil)
                 integer)
   :version "22.1"
@@ -104,11 +104,11 @@ To change the default value, use Customize or call the function
   :group 'grep)
 
 (defcustom grep-scroll-output nil
-  "*Non-nil to scroll the *grep* buffer window as output appears.
+  "Non-nil to scroll the *grep* buffer window as output appears.
 
 Setting it causes the grep commands to put point at the end of their
 output window so that the end of the output is always visible rather
-than the begining."
+than the beginning."
   :type 'boolean
   :version "22.1"
   :group 'grep)
@@ -203,13 +203,13 @@ Customize or call the function `grep-apply-setting'."
     ("tex" .   "*.tex")
     ("texi" .  "*.texi")
     ("asm" .   "*.[sS]"))
-  "*Alist of aliases for the FILES argument to `lgrep' and `rgrep'."
+  "Alist of aliases for the FILES argument to `lgrep' and `rgrep'."
   :type 'alist
   :group 'grep)
 
 (defcustom grep-find-ignored-directories
   vc-directory-exclusion-list
-  "*List of names of sub-directories which `rgrep' shall not recurse into.
+  "List of names of sub-directories which `rgrep' shall not recurse into.
 If an element is a cons cell, the car is called on the search directory
 to determine whether cdr should not be recursed into."
   :type '(choice (repeat :tag "Ignored directories" string)
@@ -221,7 +221,7 @@ to determine whether cdr should not be recursed into."
                                  (unless (string-match-p "/\\'" s)
                                    (concat "*" s)))
                                completion-ignored-extensions)))
-  "*List of file names which `rgrep' and `lgrep' shall exclude.
+  "List of file names which `rgrep' and `lgrep' shall exclude.
 If an element is a cons cell, the car is called on the search directory
 to determine whether cdr should not be excluded."
   :type '(choice (repeat :tag "Ignored file" string)
@@ -229,7 +229,7 @@ to determine whether cdr should not be excluded."
   :group 'grep)
 
 (defcustom grep-error-screen-columns nil
-  "*If non-nil, column numbers in grep hits are screen columns.
+  "If non-nil, column numbers in grep hits are screen columns.
 See `compilation-error-screen-columns'"
   :type '(choice (const :tag "Default" nil)
                 integer)
@@ -245,8 +245,8 @@ See `compilation-error-screen-columns'"
 (defvar grep-mode-map
   (let ((map (make-sparse-keymap)))
     (set-keymap-parent map compilation-minor-mode-map)
-    (define-key map " " 'scroll-up)
-    (define-key map "\^?" 'scroll-down)
+    (define-key map " " 'scroll-up-command)
+    (define-key map "\^?" 'scroll-down-command)
     (define-key map "\C-c\C-f" 'next-error-follow-minor-mode)
 
     (define-key map "\r" 'compile-goto-error)  ;; ?
@@ -339,34 +339,44 @@ See `compilation-error-screen-columns'"
   "The most recent grep buffer.
 A grep buffer becomes most recent when you select Grep mode in it.
 Notice that using \\[next-error] or \\[compile-goto-error] modifies
-`complation-last-buffer' rather than `grep-last-buffer'.")
+`compilation-last-buffer' rather than `grep-last-buffer'.")
 
 ;;;###autoload
 (defconst grep-regexp-alist
-  '(("^\\(.+?\\)\\(:[ \t]*\\)\\([1-9][0-9]*\\)\\2"
-     1 3)
+  '(
     ;; Rule to match column numbers is commented out since no known grep
     ;; produces them
-    ;; ("^\\(.+?\\)\\(:[ \t]*\\)\\([0-9]+\\)\\2\\(?:\\([0-9]+\\)\\(?:-\\([0-9]+\\)\\)?\\2\\)?"
+    ;; ("^\\(.+?\\)\\(:[ \t]*\\)\\([1-9][0-9]*\\)\\2\\(?:\\([1-9][0-9]*\\)\\(?:-\\([1-9][0-9]*\\)\\)?\\2\\)?"
     ;;  1 3 (4 . 5))
     ;; Note that we want to use as tight a regexp as we can to try and
     ;; handle weird file names (with colons in them) as well as possible.
-    ;; E.g. we use [1-9][0-9]* rather than [0-9]+ so as to accept ":034:" in
-    ;; file names.
-    ("^\\(\\(.+?\\):\\([1-9][0-9]*\\):\\).*?\
-\\(\033\\[01;31m\\(?:\033\\[K\\)?\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)"
-     2 3
-     ;; Calculate column positions (beg . end) of first grep match on a line
+    ;; E.g. we use [1-9][0-9]* rather than [0-9]+ so as to accept ":034:"
+    ;; in file names.
+    ("^\\(.+?\\)\\(:[ \t]*\\)\\([1-9][0-9]*\\)\\2"
+     1 3
+     ;; Calculate column positions (col . end-col) of first grep match on a line
      ((lambda ()
-       (setq compilation-error-screen-columns nil)
-        (- (match-beginning 4) (match-end 1)))
+       (when grep-highlight-matches
+         (let* ((beg (match-end 0))
+                (end (save-excursion (goto-char beg) (line-end-position)))
+                (mbeg (text-property-any beg end 'font-lock-face 'match)))
+           (when mbeg
+             (- mbeg beg)))))
       .
-      (lambda () (- (match-end 5) (match-end 1)
-               (- (match-end 4) (match-beginning 4)))))
-     nil 1)
+      (lambda ()
+       (when grep-highlight-matches
+         (let* ((beg (match-end 0))
+                (end (save-excursion (goto-char beg) (line-end-position)))
+                (mbeg (text-property-any beg end 'font-lock-face 'match))
+                (mend (and mbeg (next-single-property-change mbeg 'font-lock-face nil end))))
+           (when mend
+             (- mend beg)))))))
     ("^Binary file \\(.+\\) matches$" 1 nil nil 0 1))
   "Regexp used to match grep hits.  See `compilation-error-regexp-alist'.")
 
+(defvar grep-first-column 0            ; bug#10594
+  "Value to use for `compilation-first-column' in grep buffers.")
+
 (defvar grep-error "grep hit"
   "Message to print when no matches are found.")
 
@@ -431,9 +441,9 @@ This variable's value takes effect when `grep-compute-defaults' is called.")
 
 ;; History of grep commands.
 ;;;###autoload
-(defvar grep-history nil)
+(defvar grep-history nil "History list for grep.")
 ;;;###autoload
-(defvar grep-find-history nil)
+(defvar grep-find-history nil "History list for grep-find.")
 
 ;; History of lgrep and rgrep regexp and files args.
 (defvar grep-regexp-history nil)
@@ -446,9 +456,10 @@ Set up `compilation-exit-message-function' and run `grep-setup-hook'."
   (when (eq grep-highlight-matches 'auto-detect)
     (grep-compute-defaults))
   (unless (or (eq grep-highlight-matches 'auto-detect)
-             ;; Uses font-lock to parse color escapes.  (Bug#8084)
-             (null font-lock-mode)
-             (null grep-highlight-matches))
+             (null grep-highlight-matches)
+             ;; Don't output color escapes if they can't be
+             ;; highlighted with `font-lock-face' by `grep-filter'.
+             (null font-lock-mode))
     ;; `setenv' modifies `process-environment' let-bound in `compilation-start'
     ;; Any TERM except "dumb" allows GNU grep to use `--color=auto'
     (setenv "TERM" "emacs-grep")
@@ -459,13 +470,16 @@ Set up `compilation-exit-message-function' and run `grep-setup-hook'."
     ;; GREP_COLOR is used in GNU grep 2.5.1, but deprecated in later versions
     (setenv "GREP_COLOR" "01;31")
     ;; GREP_COLORS is used in GNU grep 2.5.2 and later versions
-    (setenv "GREP_COLORS" "mt=01;31:fn=:ln=:bn=:se=:ml=:cx=:ne"))
+    (setenv "GREP_COLORS" "mt=01;31:fn=:ln=:bn=:se=:sl=:cx=:ne"))
   (set (make-local-variable 'compilation-exit-message-function)
        (lambda (status code msg)
         (if (eq status 'exit)
-            (cond ((zerop code)
+            ;; This relies on the fact that `compilation-start'
+            ;; sets buffer-modified to nil before running the command,
+            ;; so the buffer is still unmodified if there is no output.
+            (cond ((and (zerop code) (buffer-modified-p))
                    '("finished (matches found)\n" . "matched"))
-                  ((= code 1)
+                  ((not (buffer-modified-p))
                    '("finished with no matches found\n" . "no match"))
                   (t
                    (cons msg code)))
@@ -477,20 +491,21 @@ Set up `compilation-exit-message-function' and run `grep-setup-hook'."
 This function is called from `compilation-filter-hook'."
   (save-excursion
     (forward-line 0)
-    (let ((end (point)))
+    (let ((end (point)) beg)
       (goto-char compilation-filter-start)
       (forward-line 0)
+      (setq beg (point))
       ;; Only operate on whole lines so we don't get caught with part of an
       ;; escape sequence in one chunk and the rest in another.
       (when (< (point) end)
         (setq end (copy-marker end))
         ;; Highlight grep matches and delete marking sequences.
-        (while (re-search-forward "\033\\[01;31m\\(.*?\\)\033\\[[0-9]*m" end 1)
+        (while (re-search-forward "\033\\[0?1;31m\\(.*?\\)\033\\[[0-9]*m" end 1)
           (replace-match (propertize (match-string 1)
                                      'face nil 'font-lock-face grep-match-face)
                          t t))
         ;; Delete all remaining escape sequences
-        (goto-char compilation-filter-start)
+        (goto-char beg)
         (while (re-search-forward "\033\\[[0-9;]*[mK]" end 1)
           (replace-match "" t t))))))
 
@@ -571,7 +586,7 @@ This function is called from `compilation-filter-hook'."
                  'exec-plus)
                 ((and
                   (grep-probe find-program `(nil nil nil ,null-device "-print0"))
-                  (grep-probe xargs-program `(nil nil nil "-0" "-e" "echo")))
+                  (grep-probe xargs-program `(nil nil nil "-0" "echo")))
                  'gnu)
                 (t
                  'exec))))
@@ -581,7 +596,7 @@ This function is called from `compilation-filter-hook'."
                       ;; Windows shells need the program file name
                       ;; after the pipe symbol be quoted if they use
                       ;; forward slashes as directory separators.
-                      (format "%s . -type f -print0 | \"%s\" -0 -e %s"
+                      (format "%s . -type f -print0 | \"%s\" -0 %s"
                               find-program xargs-program grep-command))
                      ((memq grep-find-use-xargs '(exec exec-plus))
                       (let ((cmd0 (format "%s . -type f -exec %s"
@@ -606,7 +621,7 @@ This function is called from `compilation-filter-hook'."
                                (format "%s " null-device)
                              "")))
                  (cond ((eq grep-find-use-xargs 'gnu)
-                        (format "%s . <X> -type f <F> -print0 | \"%s\" -0 -e %s"
+                        (format "%s . <X> -type f <F> -print0 | \"%s\" -0 %s"
                                 find-program xargs-program gcmd))
                        ((eq grep-find-use-xargs 'exec)
                         (format "%s . <X> -type f <F> -exec %s {} %s%s"
@@ -704,6 +719,8 @@ This function is called from `compilation-filter-hook'."
   (set (make-local-variable 'compilation-process-setup-function)
        'grep-process-setup)
   (set (make-local-variable 'compilation-disable-input) t)
+  (set (make-local-variable 'compilation-error-screen-columns)
+       grep-error-screen-columns)
   (add-hook 'compilation-filter-hook 'grep-filter nil t))
 
 
@@ -711,9 +728,9 @@ This function is called from `compilation-filter-hook'."
 (defun grep (command-args)
   "Run grep, with user-specified args, and collect output in a buffer.
 While grep runs asynchronously, you can use \\[next-error] (M-x next-error),
-or \\<grep-mode-map>\\[compile-goto-error] in the grep \
-output buffer, to go to the lines where grep
-found matches.
+or \\<grep-mode-map>\\[compile-goto-error] in the *grep* \
+buffer, to go to the lines where grep found
+matches.  To kill the grep job before it finishes, type \\[kill-compilation].
 
 For doing a recursive `grep', see the `rgrep' command.  For running
 `grep' in a specific directory, see `lgrep'.
@@ -800,11 +817,11 @@ substitution string.  Note dynamic scoping of variables.")
 (defun grep-read-regexp ()
   "Read regexp arg for interactive grep."
   (let ((default (grep-tag-default)))
-    (read-string
+    (read-regexp
      (concat "Search for"
             (if (and default (> (length default) 0))
                 (format " (default \"%s\"): " default) ": "))
-     nil 'grep-regexp-history default)))
+     default 'grep-regexp-history)))
 
 (defun grep-read-files (regexp)
   "Read files arg for interactive grep."
@@ -940,12 +957,16 @@ With \\[universal-argument] prefix, you can edit the constructed shell command l
 before it is executed.
 With two \\[universal-argument] prefixes, directly edit and run `grep-find-command'.
 
-Collect output in a buffer.  While find runs asynchronously, you
-can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error] \
+Collect output in a buffer.  While the recursive grep is running,
+you can use \\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error] \
 in the grep output buffer,
-to go to the lines where grep found matches.
+to visit the lines where matches were found.  To kill the job
+before it finishes, type \\[kill-compilation].
+
+This command shares argument histories with \\[lgrep] and \\[grep-find].
 
-This command shares argument histories with \\[lgrep] and \\[grep-find]."
+When called programmatically and FILES is nil, REGEXP is expected
+to specify a command to run."
   (interactive
    (progn
      (grep-compute-defaults)
@@ -971,20 +992,24 @@ This command shares argument histories with \\[lgrep] and \\[grep-find]."
            (compilation-start regexp 'grep-mode))
       (setq dir (file-name-as-directory (expand-file-name dir)))
       (require 'find-dired)            ; for `find-name-arg'
+      ;; In Tramp, there could be problems if the command line is too
+      ;; long.  We escape it, therefore.
       (let ((command (grep-expand-template
                      grep-find-template
                      regexp
                      (concat (shell-quote-argument "(")
                              " " find-name-arg " "
-                             (mapconcat #'shell-quote-argument
-                                        (split-string files)
-                                        (concat " -o " find-name-arg " "))
+                             (mapconcat
+                              #'shell-quote-argument
+                              (split-string files)
+                              (concat "\\\n" " -o " find-name-arg " "))
                              " "
                              (shell-quote-argument ")"))
                      dir
                      (concat
                       (and grep-find-ignored-directories
-                           (concat (shell-quote-argument "(")
+                           (concat "-type d "
+                                   (shell-quote-argument "(")
                                    ;; we should use shell-quote-argument here
                                    " -path "
                                    (mapconcat
@@ -998,12 +1023,13 @@ This command shares argument histories with \\[lgrep] and \\[grep-find]."
                                                      (concat "*/"
                                                              (cdr ignore)))))))
                                     grep-find-ignored-directories
-                                    " -o -path ")
+                                    "\\\n -o -path ")
                                    " "
                                    (shell-quote-argument ")")
                                    " -prune -o "))
                       (and grep-find-ignored-files
-                           (concat (shell-quote-argument "(")
+                           (concat (shell-quote-argument "!") " -type d "
+                                   (shell-quote-argument "(")
                                    ;; we should use shell-quote-argument here
                                    " -name "
                                    (mapconcat
@@ -1015,7 +1041,7 @@ This command shares argument histories with \\[lgrep] and \\[grep-find]."
                                                     (shell-quote-argument
                                                      (cdr ignore))))))
                                     grep-find-ignored-files
-                                    " -o -name ")
+                                    "\\\n -o -name ")
                                    " "
                                    (shell-quote-argument ")")
                                    " -prune -o "))))))