Fix bug #9221 with memory leak in bidi display.
[bpt/emacs.git] / lisp / progmodes / grep.el
index 3f5fa54..5561575 100644 (file)
@@ -1,7 +1,6 @@
-;;; grep.el --- run Grep as inferior of Emacs, parse match messages
+;;; grep.el --- run `grep' and display the results
 
-;; Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-;;   2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+;; Copyright (C) 1985-1987, 1993-1999, 2001-2011
 ;;   Free Software Foundation, Inc.
 
 ;; Author: Roland McGrath <roland@gnu.org>
@@ -34,7 +33,7 @@
 
 
 (defgroup grep nil
-  "Run grep as inferior of Emacs, parse error messages."
+  "Run `grep' and display the results."
   :group 'tools
   :group 'processes)
 
@@ -73,7 +72,9 @@ SYMBOL should be one of `grep-command', `grep-template',
 
 Some grep programs are able to surround matches with special
 markers in grep output.  Such markers can be used to highlight
-matches in grep mode.
+matches in grep mode.  This requires `font-lock-mode' to be active
+in grep buffers, so if you have globally disabled font-lock-mode,
+you will not get highlighting.
 
 This option sets the environment variable GREP_COLORS to specify
 markers for highlighting and GREP_OPTIONS to add the --color
@@ -342,13 +343,17 @@ Notice that using \\[next-error] or \\[compile-goto-error] modifies
 
 ;;;###autoload
 (defconst grep-regexp-alist
-  '(("^\\(.+?\\)\\(:[ \t]*\\)\\([0-9]+\\)\\2"
+  '(("^\\(.+?\\)\\(:[ \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\\)?"
     ;;  1 3 (4 . 5))
-    ("^\\(\\(.+?\\):\\([0-9]+\\):\\).*?\
+    ;; 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
@@ -357,7 +362,7 @@ Notice that using \\[next-error] or \\[compile-goto-error] modifies
         (- (match-beginning 4) (match-end 1)))
       .
       (lambda () (- (match-end 5) (match-end 1)
-                   (- (match-end 4) (match-beginning 4)))))
+               (- (match-end 4) (match-beginning 4)))))
      nil 1)
     ("^Binary file \\(.+\\) matches$" 1 nil nil 0 1))
   "Regexp used to match grep hits.  See `compilation-error-regexp-alist'.")
@@ -381,37 +386,20 @@ Notice that using \\[next-error] or \\[compile-goto-error] modifies
 
 (defvar grep-mode-font-lock-keywords
    '(;; Command output lines.
-     ("^\\([A-Za-z_0-9/\.+-]+\\)[ \t]*:" 1 font-lock-function-name-face)
      (": \\(.+\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or address\\)\\)$"
       1 grep-error-face)
      ;; remove match from grep-regexp-alist before fontifying
      ("^Grep[/a-zA-z]* started.*"
-      (0 '(face nil message nil help-echo nil mouse-face nil) t))
+      (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t))
      ("^Grep[/a-zA-z]* finished \\(?:(\\(matches found\\))\\|with \\(no matches found\\)\\).*"
-      (0 '(face nil message nil help-echo nil mouse-face nil) t)
+      (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t)
       (1 compilation-info-face nil t)
       (2 compilation-warning-face nil t))
      ("^Grep[/a-zA-z]* \\(exited abnormally\\|interrupt\\|killed\\|terminated\\)\\(?:.*with code \\([0-9]+\\)\\)?.*"
-      (0 '(face nil message nil help-echo nil mouse-face nil) t)
+      (0 '(face nil compilation-message nil help-echo nil mouse-face nil) t)
       (1 grep-error-face)
       (2 grep-error-face nil t))
-     ("^.+?-[0-9]+-.*\n" (0 grep-context-face))
-     ;; Highlight grep matches and delete markers
-     ("\\(\033\\[01;31m\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)"
-      ;; Refontification does not work after the markers have been
-      ;; deleted.  So we use the font-lock-face property here as Font
-      ;; Lock does not clear that.
-      (2 (list 'face nil 'font-lock-face grep-match-face))
-      ((lambda (bound))
-       (progn
-        ;; Delete markers with `replace-match' because it updates
-        ;; the match-data, whereas `delete-region' would render it obsolete.
-        (replace-match "" t t nil 3)
-        (replace-match "" t t nil 1))))
-     ("\\(\033\\[[0-9;]*[mK]\\)"
-      ;; Delete all remaining escape sequences
-      ((lambda (bound))
-       (replace-match "" t t nil 1))))
+     ("^.+?-[0-9]+-.*\n" (0 grep-context-face)))
    "Additional things to highlight in grep output.
 This gets tacked on the end of the generated expressions.")
 
@@ -433,10 +421,11 @@ This variable's value takes effect when `grep-compute-defaults' is called.")
 
 ;;;###autoload
 (defvar grep-find-use-xargs nil
-  "Non-nil means that `grep-find' uses the `xargs' utility by default.
-If `exec', use `find -exec'.
+  "How to invoke find and grep.
+If `exec', use `find -exec {} ;'.
+If `exec-plus' use `find -exec {} +'.
 If `gnu', use `find -print0' and `xargs -0'.
-Any other non-nil value means to use `find -print' and `xargs'.
+Any other value means to use `find -print' and `xargs'.
 
 This variable's value takes effect when `grep-compute-defaults' is called.")
 
@@ -457,6 +446,8 @@ 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))
     ;; `setenv' modifies `process-environment' let-bound in `compilation-start'
     ;; Any TERM except "dumb" allows GNU grep to use `--color=auto'
@@ -481,6 +472,28 @@ Set up `compilation-exit-message-function' and run `grep-setup-hook'."
           (cons msg code))))
   (run-hooks 'grep-setup-hook))
 
+(defun grep-filter ()
+  "Handle match highlighting escape sequences inserted by the grep process.
+This function is called from `compilation-filter-hook'."
+  (save-excursion
+    (forward-line 0)
+    (let ((end (point)))
+      (goto-char compilation-filter-start)
+      (forward-line 0)
+      ;; 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)
+          (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)
+        (while (re-search-forward "\033\\[[0-9;]*[mK]" end 1)
+          (replace-match "" t t))))))
+
 (defun grep-probe (command args &optional func result)
   (let (process-file-side-effects)
     (equal (condition-case nil
@@ -552,6 +565,10 @@ Set up `compilation-exit-message-function' and run `grep-setup-hook'."
        (unless grep-find-use-xargs
          (setq grep-find-use-xargs
                (cond
+                ((grep-probe find-program
+                             `(nil nil nil ,null-device "-exec" "echo"
+                                   "{}" "+"))
+                 'exec-plus)
                 ((and
                   (grep-probe find-program `(nil nil nil ,null-device "-print0"))
                   (grep-probe xargs-program `(nil nil nil "-0" "-e" "echo")))
@@ -561,32 +578,45 @@ Set up `compilation-exit-message-function' and run `grep-setup-hook'."
        (unless grep-find-command
          (setq grep-find-command
                (cond ((eq grep-find-use-xargs 'gnu)
-                      (format "%s . -type f -print0 | %s -0 -e %s"
+                      ;; 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"
                               find-program xargs-program grep-command))
-                     ((eq grep-find-use-xargs 'exec)
+                     ((memq grep-find-use-xargs '(exec exec-plus))
                       (let ((cmd0 (format "%s . -type f -exec %s"
-                                          find-program grep-command)))
+                                          find-program grep-command))
+                            (null (if grep-use-null-device
+                                      (format "%s " null-device)
+                                    "")))
                         (cons
-                         (format "%s {} %s %s"
-                                 cmd0 null-device
-                                 (shell-quote-argument ";"))
+                         (if (eq grep-find-use-xargs 'exec-plus)
+                             (format "%s %s{} +" cmd0 null)
+                           (format "%s {} %s%s" cmd0 null
+                                   (shell-quote-argument ";")))
                          (1+ (length cmd0)))))
                      (t
-                      (format "%s . -type f -print | %s %s"
+                      (format "%s . -type f -print | \"%s\" %s"
                               find-program xargs-program grep-command)))))
        (unless grep-find-template
          (setq grep-find-template
                (let ((gcmd (format "%s <C> %s <R>"
-                                   grep-program grep-options)))
+                                   grep-program grep-options))
+                     (null (if grep-use-null-device
+                               (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 -e %s"
                                 find-program xargs-program gcmd))
                        ((eq grep-find-use-xargs 'exec)
-                        (format "%s . <X> -type f <F> -exec %s {} %s %s"
-                                find-program gcmd null-device
+                        (format "%s . <X> -type f <F> -exec %s {} %s%s"
+                                find-program gcmd null
                                 (shell-quote-argument ";")))
+                       ((eq grep-find-use-xargs 'exec-plus)
+                        (format "%s . <X> -type f <F> -exec %s %s{} +"
+                                find-program gcmd null))
                        (t
-                        (format "%s . <X> -type f <F> -print | %s %s"
+                        (format "%s . <X> -type f <F> -print | \"%s\" %s"
                                 find-program xargs-program gcmd))))))))
     (when (eq grep-highlight-matches 'auto-detect)
       (setq grep-highlight-matches
@@ -668,9 +698,13 @@ Set up `compilation-exit-message-function' and run `grep-setup-hook'."
        grep-hit-face)
   (set (make-local-variable 'compilation-error-regexp-alist)
        grep-regexp-alist)
+  ;; compilation-directory-matcher can't be nil, so we set it to a regexp that
+  ;; can never match.
+  (set (make-local-variable 'compilation-directory-matcher) '("\\`a\\`"))
   (set (make-local-variable 'compilation-process-setup-function)
        'grep-process-setup)
-  (set (make-local-variable 'compilation-disable-input) t))
+  (set (make-local-variable 'compilation-disable-input) t)
+  (add-hook 'compilation-filter-hook 'grep-filter nil t))
 
 
 ;;;###autoload
@@ -989,7 +1023,8 @@ This command shares argument histories with \\[lgrep] and \\[grep-find]."
                    (read-from-minibuffer "Confirm: "
                                          command nil nil 'grep-find-history))
            (add-to-history 'grep-find-history command))
-         (let ((default-directory dir))
+         (let ((default-directory dir)
+               (process-connection-type nil))
            (compilation-start command 'grep-mode))
          ;; Set default-directory if we started rgrep in the *grep* buffer.
          (if (eq next-error-last-buffer (current-buffer))
@@ -1042,5 +1077,4 @@ file name to `*.gz', and sets `grep-highlight-matches' to `always'."
 
 (provide 'grep)
 
-;; arch-tag: 5a5b9169-a79d-4f38-9c38-f69615f39c4d
 ;;; grep.el ends here