Download
(require 'compile+ nil t)
(require 'grep)
(when (and (require 'thingatpt+ nil t)
(fboundp 'tap-put-thing-at-point-props))
(tap-define-aliases-wo-prefix)
(tap-put-thing-at-point-props))
(defcustom grepp-default-comment-line-regexp ":[0-9]+: *;"
"*Default regexp for a comment line, for use in `grepp-remove-comments'.
The default value matches lines that begin with a Lisp comment."
:type 'string :group 'grep)
(defcustom grepp-default-regexp-fn (if (fboundp 'non-nil-symbol-name-nearest-point)
'non-nil-symbol-name-nearest-point
'word-at-point)
"*Function of 0 args called to provide default search regexp to \\[grep].
Some reasonable choices are defined in `thingatpt+.el':
`word-nearest-point', `non-nil-symbol-name-nearest-point',
`region-or-non-nil-symbol-name-nearest-point', `sexp-nearest-point'.
This is ignored if Transient Mark mode is on and the region is active
and non-empty. In that case, the quoted (\") region text is used as
the default regexp.
If `grepp-default-regexp-fn' is nil and no prefix arg is given to
`grep', then no defaulting is done.
Otherwise, if the value is not a function, then function
`grepp-default-regexp-fn' does the defaulting."
:type '(choice
(const :tag "No default search regexp (unless you use `C-u')" nil)
(function :tag "Function of zero args to provide default search regexp"))
:group 'grep)
(defun grepp-default-regexp-fn ()
"*Function of 0 args called to provide default search regexp to \\[grep].
This is used only if both of the following are true:
- Transient Mark mode is off or the region is inactive or empty.
- The value of option `grepp-default-regexp-fn' is
`grepp-default-regexp-fn'.
When this is used, the default regexp is provided by calling the
first of these that references a defined function:
- variable `grepp-default-regexp-fn'
- variable `find-tag-default-function'
- the `find-tag-default-function' property of the `major-mode'
- function `non-nil-symbol-name-nearest-point', if bound
- function `grep-tag-default'"
(cond ((functionp grepp-default-regexp-fn) grepp-default-regexp-fn)
(find-tag-default-function)
((get major-mode 'find-tag-default-function))
((fboundp 'non-nil-symbol-name-nearest-point) 'non-nil-symbol-name-nearest-point)
(t 'find-tag-default)))
(defun grep-default-command ()
(let ((tag-default (shell-quote-argument (or (funcall (grepp-default-regexp-fn)) "")))
(sh-arg-re "\\(\\(?:\"\\(?:[^\"]\\|\\\\\"\\)+\"\\|'[^']+'\\|[^\"' \t\n]\\)+\\)")
(grep-default (or (car grep-history) grep-command)))
(when (or (string-match
(concat "[^ ]+\\s +\\(?:-[^ ]+\\s +\\)*" sh-arg-re "\\(\\s +\\(\\S +\\)\\)?")
grep-default)
(string-match "\\(\\)\\'" grep-default))
(condition-case nil
(unless (or (not (stringp buffer-file-name))
(when (match-beginning 2)
(save-match-data
(string-match (wildcard-to-regexp (file-name-nondirectory
(match-string 3 grep-default)))
(file-name-nondirectory buffer-file-name)))))
(setq grep-default (concat (substring grep-default 0 (match-beginning 2)) " *."
(file-name-extension buffer-file-name))))
(error nil))
(replace-match tag-default t t grep-default 1))))
(defun grep (command-args &optional highlight-regexp)
"Run `grep', with user-specified args, and collect output in a buffer.
COMMAND-ARGS are the user-specified arguments.
While `grep' runs asynchronously, you can use
\\[next-error] (M-x next-error), or \\<grep-mode-map>\\[compile-goto-error]
in output buffer `*grep*', to go to the lines where `grep' found matches.
This command uses a special history list for its COMMAND-ARGS, so you can
easily repeat a grep command.
The text (regexp) to find is defaulted as follows:
- If Transient Mark mode is on and the region is active and nonempty,
then the double-quoted region text is used. (If the region contains
double-quotes (\"), then you will need to escape them by hand.)
- If option `grepp-default-regexp-fn' is a function, then it is called
to return the default regexp.
- If `grepp-default-regexp-fn' is nil and no prefix arg is provided,
then no default regexp is used.
If a prefix arg is provided, the default text is substituted into the
last grep command in the grep command history (or into `grep-command'
if that history list is empty). That is, the same command options and
files to search are used as the last time.
If specified, optional second arg HIGHLIGHT-REGEXP is the regexp to
temporarily highlight in visited source lines."
(interactive
(progn
(unless (and grep-command (or (not grep-use-null-device) (eq grep-use-null-device t)))
(grep-compute-defaults))
(let ((default (grep-default-command)))
(list
(if nil
(read-shell-command "grep <pattern> <files> : "
(if current-prefix-arg
default
(concat
grep-command
(if (and transient-mark-mode mark-active
(not (eq (region-beginning) (region-end))))
(concat "\"" (buffer-substring (region-beginning) (region-end)) "\"")
(and (grepp-default-regexp-fn) (funcall (grepp-default-regexp-fn))))
" "))
'grep-history
(if current-prefix-arg nil default))
(read-from-minibuffer
"grep <pattern> <files> : "
(if current-prefix-arg
default
(concat grep-command
(if (and transient-mark-mode mark-active
(not (eq (region-beginning) (region-end))))
(concat "\"" (buffer-substring (region-beginning) (region-end)) "\"")
(and (grepp-default-regexp-fn) (funcall (grepp-default-regexp-fn))))
" "))
nil nil 'grep-history
(if current-prefix-arg nil default)))))))
(compilation-start (if (and grep-use-null-device null-device)
(concat command-args " " null-device)
command-args)
'grep-mode nil highlight-regexp))
(defalias 'new-grep-buffer 'grepp-new-grep-buffer)
(defun grepp-new-grep-buffer ()
"Rename current grep buffer and switch to new buffer *grep*.
Current buffer must be a grep buffer. It is renamed to *grep*<N>."
(interactive)
(unless (string-match "\\*grep\\*" (buffer-name (current-buffer)))
(error "Not in a grep buffer"))
(rename-uniquely)
(switch-to-buffer "*grep*")
(grep-mode))
(defalias 'choose-grep-buffer 'grepp-choose-grep-buffer)
(defun grepp-choose-grep-buffer (buf)
"Switch to a grep buffer."
(interactive
(let ((bufs (grepp-buffers)))
(unless bufs (error "No grep buffers"))
(list (completing-read "Grep buffer: " bufs nil t nil nil
(and grep-last-buffer (buffer-name grep-last-buffer))))))
(switch-to-buffer buf)
(select-frame-set-input-focus (selected-frame))
(grep-mode))
(defun grepp-buffers ()
"List of names of grep buffers."
(let ((bufs ()))
(dolist (buf (buffer-list))
(when (string-match "\\*grep\\*" (buffer-name buf))
(push (list (buffer-name buf)) bufs)))
(nreverse bufs)))
(defalias 'remove-grep-comments 'grepp-remove-comments)
(defun grepp-remove-comments (&optional read-regexp-p)
"Remove lines that are completely commented out.
With a prefix argument, you are prompted for the regexp used to match
commented lines. The default value is
`grepp-default-comment-line-regexp'.
With no prefix argument, this default value is used as the regexp.
You can use command `grep-toggle-comments' to toggle automatic removal
of commented lines.
Note: This simply removes lines that begin with the regexp you
provide. It does not, in general, remove multi-line comments. Use it
to remove C++ comments that start with //, but not multi-line comments
between /* and */."
(interactive "P")
(when (eq major-mode 'grep-mode)
(let ((inhibit-read-only t)
(regexp (if read-regexp-p
(read-from-minibuffer "Comment regexp: " nil nil nil 'regexp-history
grepp-default-comment-line-regexp)
grepp-default-comment-line-regexp)))
(save-excursion (flush-lines regexp (point-min) (point-max))))))
(defalias 'toggle-grep-comments 'grepp-toggle-comments)
(defun grepp-toggle-comments ()
"Toggle removal of commented lines in grep output."
(interactive)
(cond ((and (boundp 'compilation-filter-hook)
(memq 'remove-grep-comments compilation-filter-hook))
(remove-hook 'compilation-filter-hook 'remove-grep-comments)
(when (consp grep-history) (grep (car grep-history)))
(message "Automatic removal of commented lines is now OFF"))
(t
(add-hook 'compilation-filter-hook 'remove-grep-comments)
(when (consp grep-history) (grep (car grep-history)))
(message "Automatic removal of commented lines is now ON"))))
(define-key grep-mode-map "g" 'grep)
(define-key grep-mode-map "G" 'grep)
(define-key grep-mode-map "n" 'next-error-no-select)
(define-key grep-mode-map "N" 'next-error-no-select)
(define-key grep-mode-map "p" 'previous-error-no-select)
(define-key grep-mode-map "P" 'previous-error-no-select)
(define-key grep-mode-map "+" 'grepp-new-grep-buffer)
(define-key grep-mode-map "b" 'grepp-choose-grep-buffer)
(define-key grep-mode-map "B" 'grepp-choose-grep-buffer)
(define-key grep-mode-map ";" 'grepp-remove-comments)
(define-key grep-mode-map [(meta ?\
(unless (featurep 'grep+)
(setq grep-regexp-alist
'(
("^\\(.+?\\)\\(:[ \t]*\\)\\([1-9][0-9]*\\)\\2.*"
1 3
((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)))
(and mbeg (- mbeg beg)))))
.
(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))))
(and mend (- mend beg))))))
nil
nil)
("^Binary file \\(.+\\) matches$" 1 nil nil 0 1))))
(unless (featurep 'grep+)
(setq grep-mode-font-lock-keywords
(if (> emacs-major-version 23)
'(
("^\\(.+?\\):\\([0-9]+\\):.*" (0 '(face nil mouse-face compilation-mouseover)))
(": \\(.+\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or \
address\\)\\)$"
1 grep-error-face)
("^Grep[/a-zA-z]* started.*"
(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 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 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)))
'(
("^\\(.+?\\):\\([0-9]+\\):.*" (0 '(face nil mouse-face compilation-mouseover)))
(": \\(.+\\): \\(?:Permission denied\\|No such \\(?:file or directory\\|device or \
address\\)\\)$"
1 grep-error-face)
("^Grep[/a-zA-z]* started.*"
(0 '(face nil compilation-message nil message nil help-echo nil mouse-face nil) t))
("^Grep[/a-zA-z]* finished \\(?:(\\(matches found\\))\\|with \\(no matches found\\)\\).*"
(0 '(face nil compilation-message nil 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 compilation-message nil 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))
("\\(\033\\[01;31m\\)\\(.*?\\)\\(\033\\[[0-9]*m\\)"
(2 (list 'face nil 'font-lock-face grep-match-face))
((lambda (bound))
(progn
(when (> emacs-major-version 23) (syntax-ppss-flush-cache (match-beginning 0)))
(replace-match "" t t nil 3)
(replace-match "" t t nil 1))))
("\033\\[[0-9;]*[mK]"
((lambda (bound))
(when (> emacs-major-version 23) (syntax-ppss-flush-cache (match-beginning 0)))
(replace-match "" t t)))))))
(define-compilation-mode grep-mode "Grep"
"Sets `grep-last-buffer' and `compilation-window-height'."
(setq grep-last-buffer (current-buffer))
(when (boundp 'grep-mode-tool-bar-map)
(set (make-local-variable 'tool-bar-map) grep-mode-tool-bar-map))
(set (make-local-variable 'compilation-error-face) grep-hit-face)
(set (make-local-variable 'compilation-error-regexp-alist) grep-regexp-alist)
(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-error-screen-columns)
grep-error-screen-columns)
(when (fboundp 'grep-filter) (add-hook 'compilation-filter-hook 'grep-filter nil t)))
(provide 'grep+)