]> code.delx.au - gnu-emacs/blobdiff - lisp/cedet/semantic/symref/grep.el
Perform xref searches without visiting unopened files
[gnu-emacs] / lisp / cedet / semantic / symref / grep.el
index 981dab8a8b52cde79d9d02d81e0c862977e20700..868e6c3f726c0265aeb1f1c792ec43e4fafc7e4b 100644 (file)
@@ -1,6 +1,6 @@
 ;;; semantic/symref/grep.el --- Symref implementation using find/grep
 
-;; Copyright (C) 2008-2015 Free Software Foundation, Inc.
+;; Copyright (C) 2008-2016 Free Software Foundation, Inc.
 
 ;; Author: Eric M. Ludlam <eric@siege-engine.com>
 
@@ -46,11 +46,17 @@ and those hits returned.")
   '((c-mode "*.[ch]")
     (c++-mode "*.[chCH]" "*.[ch]pp" "*.cc" "*.hh")
     (html-mode "*.s?html" "*.php")
+    (ruby-mode "*.r[bu]" "*.rake" "*.gemspec" "*.erb" "*.haml"
+               "Rakefile" "Thorfile" "Capfile" "Guardfile" "Vagrantfile")
+    (perl-mode "*.pl" "*.PL")
+    (cperl-mode "*.pl" "*.PL")
     )
-  "List of major modes and file extension pattern regexp.
-See find -regex man page for format.")
+  "List of major modes and file extension pattern.
+See find -name man page for format.")
 
 (defun semantic-symref-derive-find-filepatterns (&optional mode)
+  ;; FIXME: This should be moved to grep.el, where it could be used
+  ;; for "C-u M-x grep" as well.
   "Derive a list of file patterns for the current buffer.
 Looks first in `semantic-symref-filepattern-alist'.  If it is not
 there, it then looks in `auto-mode-alist', and attempts to derive something
@@ -62,28 +68,23 @@ Optional argument MODE specifies the `major-mode' to test."
     (when (not pat)
       ;; No hit, try auto-mode-alist.
       (dolist (X auto-mode-alist)
-       (when (eq (cdr X) mode)
-         ;; Only take in simple patterns, so try to convert this one.
-         (let ((Xp
-                (cond ((string-match "\\\\\\.\\([^\\'>]+\\)\\\\'" (car X))
-                       (concat "*." (match-string 1 (car X))))
-                      (t nil))))
-           (when Xp
-             (setq pat (cons Xp pat))))
-         )))
+       (when (and (eq (cdr X) mode)
+                   ;; Only take in simple patterns, so try to convert this one.
+                   (string-match "\\\\\\.\\([^\\'>]+\\)\\\\'" (car X)))
+          (push (concat "*." (match-string 1 (car X))) pat))))
     ;; Convert the list into some find-flags.
-    (cond ((= (length pat) 1)
-          (concat "-name \"" (car pat) "\""))
-         ((consp pat)
-          (concat "\\( "
-                  (mapconcat (lambda (s)
-                               (concat "-name \"" s "\""))
-                             pat
-                             " -o ")
-                  " \\)"))
-         (t
-          (error "Customize `semantic-symref-filepattern-alist' for %s" major-mode))
-         )))
+    (if (null pat)
+        (error "Customize `semantic-symref-filepattern-alist' for %S"
+               major-mode)
+      (let ((args `("-name" ,(car pat))))
+        (if (null (cdr args))
+            args
+          `("(" ,@args
+            ,@(apply #'nconc (mapcar (lambda (s) `("-o" "-name" ,s)) pat))
+            ")"))))))
+
+(defvar grepflags)
+(defvar greppattern)
 
 (defvar semantic-symref-grep-expand-keywords
   (condition-case nil
@@ -96,7 +97,7 @@ Optional argument MODE specifies the `major-mode' to test."
     (error nil))
   "Grep expand keywords used when expanding templates for symref.")
 
-(defun semantic-symref-grep-use-template (rootdir filepattern grepflags greppattern)
+(defun semantic-symref-grep-use-template (rootdir filepattern flags pattern)
   "Use the grep template expand feature to create a grep command.
 ROOTDIR is the root location to run the `find' from.
 FILEPATTERN is a string representing find flags for searching file patterns.
@@ -104,18 +105,29 @@ GREPFLAGS are flags passed to grep, such as -n or -l.
 GREPPATTERN is the pattern used by grep."
   ;; We have grep-compute-defaults.  Let's use it.
   (grep-compute-defaults)
-  (let* ((grep-expand-keywords semantic-symref-grep-expand-keywords)
-        (cmd (grep-expand-template grep-find-template
-                                   greppattern
-                                   filepattern
-                                   rootdir)))
-    ;; For some reason, my default has no <D> in it.
+  (let* ((grepflags flags)
+         (greppattern pattern)
+         (grep-expand-keywords semantic-symref-grep-expand-keywords)
+        (cmd (grep-expand-template
+               (if (memq system-type '(windows-nt ms-dos))
+                   ;; grep-find uses '--color=always' on MS-Windows
+                   ;; because it wants the colorized output, to show
+                   ;; it to the user.  By contrast, here we don't show
+                   ;; the output, and the SGR escapes get in the way
+                   ;; of parsing the output.
+                   (replace-regexp-in-string "--color=always" ""
+                                             grep-find-template t t)
+                 grep-find-template)
+               greppattern
+               filepattern
+               rootdir)))
+    ;; http://debbugs.gnu.org/20719
     (when (string-match "find \\(\\.\\)" cmd)
       (setq cmd (replace-match rootdir t t cmd 1)))
     ;;(message "New command: %s" cmd)
     cmd))
 
-(defcustom semantic-symref-grep-shell "sh"
+(defcustom semantic-symref-grep-shell shell-file-name
   "The shell command to use for executing find/grep.
 This shell should support pipe redirect syntax."
   :group 'semantic
@@ -125,22 +137,29 @@ This shell should support pipe redirect syntax."
   "Perform a search with Grep."
   ;; Grep doesn't support some types of searches.
   (let ((st (oref tool :searchtype)))
-    (when (not (eq st 'symbol))
+    (when (not (memq st '(symbol regexp)))
       (error "Symref impl GREP does not support searchtype of %s" st))
     )
   ;; Find the root of the project, and do a find-grep...
   (let* (;; Find the file patterns to use.
-        (pat (cdr (assoc major-mode semantic-symref-filepattern-alist)))
         (rootdir (semantic-symref-calculate-rootdir))
-        (filepattern (semantic-symref-derive-find-filepatterns))
+        (filepatterns (semantic-symref-derive-find-filepatterns))
+         (filepattern (mapconcat #'shell-quote-argument filepatterns " "))
         ;; Grep based flags.
         (grepflags (cond ((eq (oref tool :resulttype) 'file)
-                         "-l ")
-                        (t "-n ")))
-        (greppat (cond ((eq (oref tool :searchtype) 'regexp)
-                        (oref tool searchfor))
-                       (t
-                        (concat "'\\<" (oref tool searchfor) "\\>'"))))
+                           "-l ")
+                          ((eq (oref tool :searchtype) 'regexp)
+                           "-nE ")
+                          (t "-n ")))
+        (greppat (shell-quote-argument
+                   (cond ((eq (oref tool :searchtype) 'regexp)
+                          (oref tool searchfor))
+                         (t
+                          ;; Can't use the word boundaries: Grep
+                          ;; doesn't always agrees with the language
+                          ;; syntax on those.
+                          (format "\\(^\\|\\W\\)%s\\(\\W\\|$\\)"
+                                  (oref tool searchfor))))))
         ;; Misc
         (b (get-buffer-create "*Semantic SymRef*"))
         (ans nil)
@@ -158,15 +177,20 @@ This shell should support pipe redirect syntax."
          (let ((cmd (concat "find " default-directory " -type f " filepattern " -print0 "
                             "| xargs -0 grep -H " grepflags "-e " greppat)))
            ;;(message "Old command: %s" cmd)
-           (call-process semantic-symref-grep-shell nil b nil "-c" cmd)
+           (call-process semantic-symref-grep-shell nil b nil
+                          shell-command-switch cmd)
            )
        (let ((cmd (semantic-symref-grep-use-template rootdir filepattern grepflags greppat)))
-         (call-process semantic-symref-grep-shell nil b nil "-c" cmd))
+         (call-process semantic-symref-grep-shell nil b nil
+                        shell-command-switch cmd))
        ))
     (setq ans (semantic-symref-parse-tool-output tool b))
     ;; Return the answer
     ans))
 
+(defconst semantic-symref-grep--line-re
+  "^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):")
+
 (cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-grep))
   "Parse one line of grep output, and return it as a match list.
 Moves cursor to end of the match."
@@ -174,8 +198,13 @@ Moves cursor to end of the match."
         ;; Search for files
         (when (re-search-forward "^\\([^\n]+\\)$" nil t)
           (match-string 1)))
+        ((eq (oref tool :resulttype) 'line-and-text)
+         (when (re-search-forward semantic-symref-grep--line-re nil t)
+           (list (string-to-number (match-string 2))
+                 (match-string 1)
+                 (buffer-substring-no-properties (point) (line-end-position)))))
        (t
-        (when (re-search-forward  "^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):" nil t)
+        (when (re-search-forward semantic-symref-grep--line-re nil t)
           (cons (string-to-number (match-string 2))
                 (match-string 1))
           ))))