Mercurial > emacs
changeset 96640:970b19b546c0
(change-log-search-file-name): Use match-string-no-properties.
(change-log-search-tag-name-1, change-log-search-tag-name)
(change-log-goto-source-1, change-log-goto-source): New functions.
(change-log-tag-re, change-log-find-head, change-log-find-tail):
New variables.
(change-log-mode-map): Bind C-c C-c to change-log-goto-source.
author | Martin Rudalics <rudalics@gmx.at> |
---|---|
date | Sun, 13 Jul 2008 07:30:48 +0000 |
parents | 5cf733ca8fbc |
children | 484d1e716329 |
files | lisp/add-log.el |
diffstat | 1 files changed, 191 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/add-log.el Sun Jul 13 05:42:31 2008 +0000 +++ b/lisp/add-log.el Sun Jul 13 07:30:48 2008 +0000 @@ -298,10 +298,10 @@ ;; name. (progn (re-search-forward change-log-file-names-re nil t) - (match-string 2)) + (match-string-no-properties 2)) (if (looking-at change-log-file-names-re) ;; We found a file name. - (match-string 2) + (match-string-no-properties 2) ;; Look backwards for either a file name or the log entry start. (if (re-search-backward (concat "\\(" change-log-start-entry-re @@ -312,11 +312,11 @@ ;; file name. (progn (re-search-forward change-log-file-names-re nil t) - (match-string 2)) - (match-string 4)) + (match-string-no-properties 2)) + (match-string-no-properties 4)) ;; We must be before any file name, look forward. (re-search-forward change-log-file-names-re nil t) - (match-string 2)))))) + (match-string-no-properties 2)))))) (defun change-log-find-file () "Visit the file for the change under point." @@ -326,11 +326,197 @@ (find-file file) (message "No such file or directory: %s" file)))) +(defun change-log-search-tag-name-1 (&optional from) + "Search for a tag name within subexpression 1 of last match. +Optional argument FROM specifies a buffer position where the tag +name should be located. Return value is a cons whose car is the +string representing the tag and whose cdr is the position where +the tag was found." + (save-restriction + (narrow-to-region (match-beginning 1) (match-end 1)) + (when from (goto-char from)) + ;; The regexp below skips any symbol near `point' (FROM) followed by + ;; whitespace and another symbol. This should skip, for example, + ;; "struct" in a specification like "(struct buffer)" and move to + ;; "buffer". A leading paren is ignored. + (when (looking-at + "[(]?\\(?:\\(?:\\sw\\|\\s_\\)+\\(?:[ \t]+\\(\\sw\\|\\s_\\)+\\)\\)") + (goto-char (match-beginning 1))) + (cons (find-tag-default) (point)))) + +(defconst change-log-tag-re + "(\\(\\(?:\\sw\\|\\s_\\)+\\(?:[, \t]+\\(?:\\sw\\|\\s_\\)+\\)*\\))" + "Regexp matching a tag name in change log entries.") + +(defun change-log-search-tag-name (&optional at) + "Search for a tag name near `point'. +Optional argument AT non-nil means search near buffer position +AT. Return value is a cons whose car is the string representing +the tag and whose cdr is the position where the tag was found." + (save-excursion + (goto-char (setq at (or at (point)))) + (save-restriction + (widen) + (or (condition-case nil + ;; Within parenthesized list? + (save-excursion + (backward-up-list) + (when (looking-at change-log-tag-re) + (change-log-search-tag-name-1 at))) + (error nil)) + (condition-case nil + ;; Before parenthesized list? + (save-excursion + (when (and (skip-chars-forward " \t") + (looking-at change-log-tag-re)) + (change-log-search-tag-name-1))) + (error nil)) + (condition-case nil + ;; Near filename? + (save-excursion + (when (and (progn + (beginning-of-line) + (looking-at change-log-file-names-re)) + (goto-char (match-end 0)) + (skip-syntax-forward " ") + (looking-at change-log-tag-re)) + (change-log-search-tag-name-1))) + (error nil)) + (condition-case nil + ;; Before filename? + (save-excursion + (when (and (progn + (skip-syntax-backward " ") + (beginning-of-line) + (looking-at change-log-file-names-re)) + (goto-char (match-end 0)) + (skip-syntax-forward " ") + (looking-at change-log-tag-re)) + (change-log-search-tag-name-1))) + (error nil)) + (condition-case nil + ;; Near start entry? + (save-excursion + (when (and (progn + (beginning-of-line) + (looking-at change-log-start-entry-re)) + (forward-line) ; Won't work for multiple + ; names, etc. + (skip-syntax-forward " ") + (progn + (beginning-of-line) + (looking-at change-log-file-names-re)) + (goto-char (match-end 0)) + (re-search-forward change-log-tag-re)) + (change-log-search-tag-name-1))) + (error nil)) + (condition-case nil + ;; After parenthesized list?. + (when (re-search-backward change-log-tag-re) + (save-restriction + (narrow-to-region (match-beginning 1) (match-end 1)) + (goto-char (point-max)) + (cons (find-tag-default) (point-max)))) + (error nil)))))) + +(defvar change-log-find-head nil) +(defvar change-log-find-tail nil) + +(defun change-log-goto-source-1 (tag regexp file buffer + &optional window first last) + "Search for tag TAG in buffer BUFFER visiting file FILE. +REGEXP is a regular expression for TAG. The remaining arguments +are optional: WINDOW denotes the window to display the results of +the search. FIRST is a position in BUFFER denoting the first +match from previous searches for TAG. LAST is the position in +BUFFER denoting the last match for TAG in the last search." + (with-current-buffer buffer + (save-excursion + (save-restriction + (widen) + (if last + (progn + ;; When LAST is set make sure we continue from the next + ;; line end to not find the same tag again. + (goto-char last) + (end-of-line) + (condition-case nil + ;; Try to go to the end of the current defun to avoid + ;; false positives within the current defun's body + ;; since these would match `add-log-current-defun'. + (end-of-defun) + ;; Don't fall behind when `end-of-defun' fails. + (error (progn (goto-char last) (end-of-line)))) + (setq last nil)) + ;; When LAST was not set start at beginning of BUFFER. + (goto-char (point-min))) + (let (current-defun) + (while (and (not last) (re-search-forward regexp nil t)) + ;; Verify that `add-log-current-defun' invoked at the end + ;; of the match returns TAG. This heuristic works well + ;; whenever the name of the defun occurs within the first + ;; line of the defun. + (setq current-defun (add-log-current-defun)) + (when (and current-defun (string-equal current-defun tag)) + ;; Record this as last match. + (setq last (line-beginning-position)) + ;; Record this as first match when there's none. + (unless first (setq first last))))))) + (if (or last first) + (with-selected-window (or window (display-buffer buffer)) + (if last + (progn + (when (or (< last (point-min)) (> last (point-max))) + ;; Widen to show TAG. + (widen)) + (push-mark) + (goto-char last)) + ;; When there are no more matches go (back) to FIRST. + (message "No more matches for tag `%s' in file `%s'" tag file) + (setq last first) + (goto-char first)) + ;; Return new "tail". + (list (selected-window) first last)) + (message "Source location of tag `%s' not found in file `%s'" tag file) + nil))) + +(defun change-log-goto-source () + "Go to source location of change log tag near `point'. +A change log tag is a symbol within a parenthesized, +comma-separated list." + (interactive) + (if (and (eq last-command 'change-log-goto-source) + change-log-find-tail) + (setq change-log-find-tail + (condition-case nil + (apply 'change-log-goto-source-1 + (append change-log-find-head change-log-find-tail)) + (error + (format "Cannot find more matches for tag `%s' in file `%s'" + (car change-log-find-head) + (nth 2 change-log-find-head))))) + (save-excursion + (let* ((tag-at (change-log-search-tag-name)) + (tag (car tag-at)) + (file (when tag-at + (change-log-search-file-name (cdr tag-at))))) + (if (not tag) + (error "No suitable tag near `point'") + (setq change-log-find-head + (list tag (concat "\\_<" (regexp-quote tag) "\\_>") + file (find-file-noselect file))) + (condition-case nil + (setq change-log-find-tail + (apply 'change-log-goto-source-1 change-log-find-head)) + (error (format "Cannot find matches for tag `%s' in `%s'" + tag file)))))))) + (defvar change-log-mode-map (let ((map (make-sparse-keymap))) (define-key map [?\C-c ?\C-p] 'add-log-edit-prev-comment) (define-key map [?\C-c ?\C-n] 'add-log-edit-next-comment) (define-key map [?\C-c ?\C-f] 'change-log-find-file) + (define-key map [?\C-c ?\C-c] 'change-log-goto-source) map) "Keymap for Change Log major mode.")