;;; 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>
'((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
(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
(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.
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
"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)
(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."
;; 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))
))))