X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/1af74d06e5bdafad9d629d2ed729c5d743cfaf0f..3a0f6aac0db3b1961c759a278d2bc67b501ddd0a:/lisp/add-log.el diff --git a/lisp/add-log.el b/lisp/add-log.el index 546f87b4e4..f6d53a4a6d 100644 --- a/lisp/add-log.el +++ b/lisp/add-log.el @@ -1,17 +1,17 @@ ;;; add-log.el --- change log maintenance commands for Emacs ;; Copyright (C) 1985, 1986, 1988, 1993, 1994, 1997, 1998, 2000, 2001, -;; 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. +;; 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. ;; Maintainer: FSF ;; Keywords: tools ;; This file is part of GNU Emacs. -;; GNU Emacs is free software; you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,9 +19,7 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -54,6 +52,7 @@ :type '(choice (const :tag "default" nil) string) :group 'change-log) +;;;###autoload (put 'change-log-default-name 'safe-local-variable 'string-or-null-p) (defcustom change-log-mode-hook nil @@ -240,8 +239,11 @@ Note: The search is conducted only within 10%, at the beginning of the file." ;; backward-compatibility alias (put 'change-log-acknowledgement-face 'face-alias 'change-log-acknowledgement) +(defconst change-log-file-names-re "^\\( +\\|\t\\)\\* \\([^ ,:([\n]+\\)") +(defconst change-log-start-entry-re "^\\sw.........[0-9:+ ]*") + (defvar change-log-font-lock-keywords - '(;; + `(;; ;; Date lines, new (2000-01-01) and old (Sat Jan 1 00:00:00 2000) styles. ;; Fixme: this regepx is just an approximate one and may match ;; wrongly with a non-date line existing as a random note. In @@ -255,7 +257,7 @@ Note: The search is conducted only within 10%, at the beginning of the file." (2 'change-log-email))) ;; ;; File names. - ("^\\( +\\|\t\\)\\* \\([^ ,:([\n]+\\)" + (,change-log-file-names-re (2 'change-log-file) ;; Possibly further names in a list: ("\\=, \\([^ ,:([\n]+\\)" nil nil (1 'change-log-file)) @@ -287,10 +289,276 @@ Note: The search is conducted only within 10%, at the beginning of the file." 3 'change-log-acknowledgement)) "Additional expressions to highlight in Change Log mode.") +(defun change-log-search-file-name (where) + "Return the file-name for the change under point." + (save-excursion + (goto-char where) + (beginning-of-line 1) + (if (looking-at change-log-start-entry-re) + ;; We are at the start of an entry, search forward for a file + ;; name. + (progn + (re-search-forward change-log-file-names-re nil t) + (match-string-no-properties 2)) + (if (looking-at change-log-file-names-re) + ;; We found a file name. + (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 + "\\)\\|\\(" + change-log-file-names-re "\\)") nil t) + (if (match-beginning 1) + ;; We got the start of the entry, look forward for a + ;; file name. + (progn + (re-search-forward change-log-file-names-re nil t) + (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-no-properties 2)))))) + +(defun change-log-find-file () + "Visit the file for the change under point." + (interactive) + (let ((file (change-log-search-file-name (point)))) + (if (and file (file-exists-p file)) + (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 on same line? + (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 file name? + (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 + ;; Anywhere else within current entry? + (let ((from + (save-excursion + (end-of-line) + (if (re-search-backward change-log-start-entry-re nil t) + (match-beginning 0) + (point-min)))) + (to + (save-excursion + (end-of-line) + (if (re-search-forward change-log-start-entry-re nil t) + (match-beginning 0) + (point-max))))) + (when (and (< from to) (<= from at) (<= at to)) + (save-restriction + ;; Narrow to current change log entry. + (narrow-to-region from to) + (cond + ((re-search-backward change-log-tag-re nil t) + (narrow-to-region (match-beginning 1) (match-end 1)) + (goto-char (point-max)) + (cons (find-tag-default) (point-max))) + ((re-search-forward change-log-tag-re nil t) + (narrow-to-region (match-beginning 1) (match-end 1)) + (goto-char (point-min)) + (cons (find-tag-default) (point-min))))))) + (error nil)))))) + +(defvar change-log-find-head nil) +(defvar change-log-find-tail nil) +(defvar change-log-find-window 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 + (setq change-log-find-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. If no suitable tag can be found nearby, +try to visit the file for the change under `point' instead." + (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* ((at (point)) + (tag-at (change-log-search-tag-name)) + (tag (car tag-at)) + (file (when tag-at (change-log-search-file-name (cdr tag-at)))) + (file-at (when file (match-beginning 2))) + ;; `file-2' is the file `change-log-search-file-name' finds + ;; at `point'. We use `file-2' as a fallback when `tag' or + ;; `file' are not suitable for some reason. + (file-2 (change-log-search-file-name at)) + (file-2-at (when file-2 (match-beginning 2)))) + (cond + ((and (or (not tag) (not file) (not (file-exists-p file))) + (or (not file-2) (not (file-exists-p file-2)))) + (error "Cannot find tag or file near `point'")) + ((and file-2 (file-exists-p file-2) + (or (not tag) (not file) (not (file-exists-p file)) + (and (or (and (< file-at file-2-at) (<= file-2-at at)) + (and (<= at file-2-at) (< file-2-at file-at)))))) + ;; We either have not found a suitable file name or `file-2' + ;; provides a "better" file name wrt `point'. Go to the + ;; buffer of `file-2' instead. + (setq change-log-find-window + (display-buffer (find-file-noselect file-2)))) + (t + (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 file `%s'" + tag file))))))))) + +(defun change-log-next-error (&optional argp reset) + "Move to the Nth (default 1) next match in a ChangeLog buffer. +Compatibility function for \\[next-error] invocations." + (interactive "p") + (let* ((argp (or argp 0)) + (count (abs argp)) ; how many cycles + (down (< argp 0)) ; are we going down? (is argp negative?) + (up (not down)) + (search-function (if up 're-search-forward 're-search-backward))) + + ;; set the starting position + (goto-char (cond (reset (point-min)) + (down (line-beginning-position)) + (up (line-end-position)) + ((point)))) + + (funcall search-function change-log-file-names-re nil t count)) + + (beginning-of-line) + ;; if we found a place to visit... + (when (looking-at change-log-file-names-re) + (let (change-log-find-window) + (change-log-goto-source) + (when change-log-find-window + ;; Select window displaying source file. + (select-window change-log-find-window))))) + (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.") @@ -330,9 +598,7 @@ If t, use universal time.") (defun change-log-name () "Return (system-dependent) default name for a change log file." (or change-log-default-name - (if (eq system-type 'vax-vms) - "$CHANGE_LOG$.TXT" - "ChangeLog"))) + "ChangeLog")) (defun add-log-edit-prev-comment (arg) "Cycle backward through Log-Edit mode comment history. @@ -379,7 +645,7 @@ With a numeric prefix ARG, go back ARG comments." (defun change-log-version-number-search () "Return version number of current buffer's file. -This is the value returned by `vc-workfile-version' or, if that is +This is the value returned by `vc-working-revision' or, if that is nil, by matching `change-log-version-number-regexp-list'." (let* ((size (buffer-size)) (limit @@ -390,7 +656,7 @@ nil, by matching `change-log-version-number-regexp-list'." ;; Apply percentage only if buffer size is bigger than ;; approx 100 lines. (if (> size (* 100 80)) (+ (point) (/ size 10))))) - (or (and buffer-file-name (vc-workfile-version buffer-file-name)) + (or (and buffer-file-name (vc-working-revision buffer-file-name)) (save-restriction (widen) (let ((regexps change-log-version-number-regexp-list) @@ -403,6 +669,8 @@ nil, by matching `change-log-version-number-regexp-list'." regexps nil)))) version))))) +(declare-function diff-find-source-location "diff-mode" + (&optional other-file reverse noprompt)) ;;;###autoload (defun find-change-log (&optional file-name buffer-file) @@ -420,47 +688,54 @@ directory and its successive parents for a file so named. Once a file is found, `change-log-default-name' is set locally in the current buffer to the complete file name. Optional arg BUFFER-FILE overrides `buffer-file-name'." - ;; If user specified a file name or if this buffer knows which one to use, - ;; just use that. - (or file-name - (setq file-name (and change-log-default-name - (file-name-directory change-log-default-name) - change-log-default-name)) - (progn - ;; Chase links in the source file - ;; and use the change log in the dir where it points. - (setq file-name (or (and (or buffer-file buffer-file-name) - (file-name-directory - (file-chase-links - (or buffer-file buffer-file-name)))) - default-directory)) - (if (file-directory-p file-name) - (setq file-name (expand-file-name (change-log-name) file-name))) - ;; Chase links before visiting the file. - ;; This makes it easier to use a single change log file - ;; for several related directories. - (setq file-name (file-chase-links file-name)) - (setq file-name (expand-file-name file-name)) - ;; Move up in the dir hierarchy till we find a change log file. - (let ((file1 file-name) - parent-dir) - (while (and (not (or (get-file-buffer file1) (file-exists-p file1))) - (progn (setq parent-dir + ;; If we are called from a diff, first switch to the source buffer; + ;; in order to respect buffer-local settings of change-log-default-name, etc. + (with-current-buffer (let ((buff (if (eq major-mode 'diff-mode) + (car (ignore-errors + (diff-find-source-location)))))) + (if (buffer-live-p buff) buff + (current-buffer))) + ;; If user specified a file name or if this buffer knows which one to use, + ;; just use that. + (or file-name + (setq file-name (and change-log-default-name + (file-name-directory change-log-default-name) + change-log-default-name)) + (progn + ;; Chase links in the source file + ;; and use the change log in the dir where it points. + (setq file-name (or (and (or buffer-file buffer-file-name) (file-name-directory - (directory-file-name - (file-name-directory file1)))) - ;; Give up if we are already at the root dir. - (not (string= (file-name-directory file1) - parent-dir)))) - ;; Move up to the parent dir and try again. - (setq file1 (expand-file-name - (file-name-nondirectory (change-log-name)) - parent-dir))) - ;; If we found a change log in a parent, use that. - (if (or (get-file-buffer file1) (file-exists-p file1)) - (setq file-name file1))))) - ;; Make a local variable in this buffer so we needn't search again. - (set (make-local-variable 'change-log-default-name) file-name) + (file-chase-links + (or buffer-file buffer-file-name)))) + default-directory)) + (if (file-directory-p file-name) + (setq file-name (expand-file-name (change-log-name) file-name))) + ;; Chase links before visiting the file. + ;; This makes it easier to use a single change log file + ;; for several related directories. + (setq file-name (file-chase-links file-name)) + (setq file-name (expand-file-name file-name)) + ;; Move up in the dir hierarchy till we find a change log file. + (let ((file1 file-name) + parent-dir) + (while (and (not (or (get-file-buffer file1) (file-exists-p file1))) + (progn (setq parent-dir + (file-name-directory + (directory-file-name + (file-name-directory file1)))) + ;; Give up if we are already at the root dir. + (not (string= (file-name-directory file1) + parent-dir)))) + ;; Move up to the parent dir and try again. + (setq file1 (expand-file-name + (file-name-nondirectory (change-log-name)) + parent-dir))) + ;; If we found a change log in a parent, use that. + (if (or (get-file-buffer file1) (file-exists-p file1)) + (setq file-name file1))))) + ;; Make a local variable in this buffer so we needn't search again. + (set (make-local-variable 'change-log-default-name) file-name)) file-name) (defun add-log-file-name (buffer-file log-file) @@ -478,7 +753,8 @@ Optional arg BUFFER-FILE overrides `buffer-file-name'." buffer-file)))) ;;;###autoload -(defun add-change-log-entry (&optional whoami file-name other-window new-entry) +(defun add-change-log-entry (&optional whoami file-name other-window new-entry + put-new-entry-on-new-line) "Find change log file, and add an entry for today and an item for this file. Optional arg WHOAMI (interactive prefix) non-nil means prompt for user name and email (stored in `add-log-full-name' and `add-log-mailing-address'). @@ -492,6 +768,10 @@ Fourth arg NEW-ENTRY non-nil means always create a new entry at the front; never append to an existing entry. Option `add-log-keep-changes-together' otherwise affects whether a new entry is created. +Fifth arg PUT-NEW-ENTRY-ON-NEW-LINE non-nil means that if a new +entry is created, put it on a new line by itself, do not put it +after a comma on an existing line. + Option `add-log-always-start-new-record' non-nil means always create a new record, even when the last record was made on the same date and by the same person. @@ -639,7 +919,8 @@ non-nil, otherwise in local time." (let ((pos (point-marker))) (skip-syntax-backward " ") (skip-chars-backward "):") - (if (and (looking-at "):") + (if (and (not put-new-entry-on-new-line) + (looking-at "):") (let ((pos (save-excursion (backward-sexp 1) (point)))) (when (equal (buffer-substring pos (point)) defun) (delete-region pos (point))) @@ -647,8 +928,8 @@ non-nil, otherwise in local time." (progn (skip-chars-backward ", ") (delete-region (point) pos) (unless (memq (char-before) '(?\()) (insert ", "))) - (if (looking-at "):") - (delete-region (+ 1 (point)) (line-end-position))) + (when (and (not put-new-entry-on-new-line) (looking-at "):")) + (delete-region (+ 1 (point)) (line-end-position))) (goto-char pos) (insert "(")) (set-marker pos nil)) @@ -664,7 +945,6 @@ the change log file in another window." (list current-prefix-arg (prompt-for-change-log-name)))) (add-change-log-entry whoami file-name t)) -;;;###autoload (define-key ctl-x-4-map "a" 'add-change-log-entry-other-window) (defvar change-log-indent-text 0) @@ -723,6 +1003,7 @@ the change log file in another window." (defvar smerge-resolve-function) +(defvar copyright-at-end-flag) ;;;###autoload (define-derived-mode change-log-mode text-mode "Change Log" @@ -742,10 +1023,11 @@ Runs `change-log-mode-hook'. ;; Avoid that filling leaves behind a single "*" on a line. (add-hook 'fill-nobreak-predicate '(lambda () - (looking-back "^\\s *\\*\\s *" (line-beginning-position))) + (looking-back "^\\s *\\*\\s *" (line-beginning-position))) nil t) (set (make-local-variable 'indent-line-function) 'change-log-indent) (set (make-local-variable 'tab-always-indent) nil) + (set (make-local-variable 'copyright-at-end-flag) t) ;; We really do want "^" in paragraph-start below: it is only the ;; lines that begin at column 0 (despite the left-margin of 8) that ;; we are looking for. Adding `* ' allows eliding the blank line @@ -760,7 +1042,38 @@ Runs `change-log-mode-hook'. 'change-log-resolve-conflict) (set (make-local-variable 'adaptive-fill-regexp) "\\s *") (set (make-local-variable 'font-lock-defaults) - '(change-log-font-lock-keywords t nil nil backward-paragraph))) + '(change-log-font-lock-keywords t nil nil backward-paragraph)) + (set (make-local-variable 'multi-isearch-next-buffer-function) + 'change-log-next-buffer) + (set (make-local-variable 'beginning-of-defun-function) + 'change-log-beginning-of-defun) + (set (make-local-variable 'end-of-defun-function) + 'change-log-end-of-defun) + ;; next-error function glue + (setq next-error-function 'change-log-next-error) + (setq next-error-last-buffer (current-buffer))) + +(defun change-log-next-buffer (&optional buffer wrap) + "Return the next buffer in the series of ChangeLog file buffers. +This function is used for multiple buffers isearch. +A sequence of buffers is formed by ChangeLog files with decreasing +numeric file name suffixes in the directory of the initial ChangeLog +file were isearch was started." + (let* ((name (change-log-name)) + (files (cons name (sort (file-expand-wildcards + (concat name "[-.][0-9]*")) + (lambda (a b) + ;; The file's extension may not have a valid + ;; version form (e.g. VC backup revisions). + (ignore-errors + (version< (substring b (length name)) + (substring a (length name)))))))) + (files (if isearch-forward files (reverse files)))) + (find-file-noselect + (if wrap + (car files) + (cadr (member (file-name-nondirectory (buffer-file-name buffer)) + files)))))) ;; It might be nice to have a general feature to replace this. The idea I ;; have is a variable giving a regexp matching text which should not be @@ -801,6 +1114,9 @@ Prefix arg means justify as well." '(TeX-mode plain-TeX-mode LaTeX-mode tex-mode) "*Modes that look like TeX to `add-log-current-defun'.") +(declare-function c-cpp-define-name "cc-cmds" ()) +(declare-function c-defun-name "cc-cmds" ()) + ;;;###autoload (defun add-log-current-defun () "Return name of function definition point is in, or nil. @@ -844,167 +1160,10 @@ Has a preference of looking backwards." (buffer-substring-no-properties (point) (progn (forward-sexp 1) (point))))) - ((and (apply 'derived-mode-p add-log-c-like-modes) - (save-excursion - (beginning-of-line) - ;; Use eq instead of = here to avoid - ;; error when at bob and char-after - ;; returns nil. - (while (eq (char-after (- (point) 2)) ?\\) - (forward-line -1)) - (looking-at "[ \t]*#[ \t]*define[ \t]"))) - ;; Handle a C macro definition. - (beginning-of-line) - (while (eq (char-after (- (point) 2)) ?\\) ;not =; note above - (forward-line -1)) - (search-forward "define") - (skip-chars-forward " \t") - (buffer-substring-no-properties (point) - (progn (forward-sexp 1) - (point)))) ((apply 'derived-mode-p add-log-c-like-modes) - ;; See whether the point is inside a defun. - (let (having-previous-defun - having-next-defun - previous-defun-end - next-defun-beginning) - - (save-excursion - (setq having-previous-defun - (c-beginning-of-defun)) - (c-end-of-defun) - ;; `c-end-of-defun' moves point to the line after - ;; the function close, but the position we prefer - ;; here is the position after the final }. - (backward-sexp 1) - (forward-sexp 1) - ;; Skip the semicolon ``;'' for - ;; enum/union/struct/class definition. - (if (= (char-after (point)) ?\;) - (forward-char 1)) - (setq previous-defun-end (point))) - - (save-excursion - (setq having-next-defun - (c-end-of-defun)) - (c-beginning-of-defun) - (setq next-defun-beginning (point))) - - (if (and having-next-defun - (< location next-defun-beginning)) - (skip-syntax-forward " ")) - (if (and having-previous-defun - (> location previous-defun-end)) - (skip-syntax-backward " ")) - (unless (or - ;; When there is no previous defun, the - ;; point is not in a defun if it is not at - ;; the beginning of the next defun. - (and (not having-previous-defun) - (not (= (point) - next-defun-beginning))) - ;; When there is no next defun, the point - ;; is not in a defun if it is not at the - ;; end of the previous defun. - (and (not having-next-defun) - (not (= (point) - previous-defun-end))) - ;; If the point is between two defuns, it - ;; is not in a defun. - (and (> (point) previous-defun-end) - (< (point) next-defun-beginning))) - ;; If the point is already at the beginning of a - ;; defun, there is no need to move point again. - (if (not (= (point) next-defun-beginning)) - (c-beginning-of-defun)) - ;; Is this a DEFUN construct? And is LOCATION in it? - (if (and (looking-at "DEFUN\\b") - (>= location (point))) - ;; DEFUN ("file-name-directory", Ffile_name_directory, Sfile_name_directory, ...) ==> Ffile_name_directory - ;; DEFUN(POSIX::STREAM-LOCK, stream lockp &key BLOCK SHARED START LENGTH) ==> POSIX::STREAM-LOCK - (progn - (down-list 1) - (when (= (char-after (point)) ?\") - (forward-sexp 1) - (search-forward ",")) - (skip-syntax-forward " ") - (buffer-substring-no-properties - (point) - (progn (search-forward ",") - (forward-char -1) - (skip-syntax-backward " ") - (point)))) - (if (looking-at "^[+-]") - ;; Objective-C - (change-log-get-method-definition) - ;; Ordinary C function syntax. - (let ((beg (point))) - (if (and - ;; Protect against "Unbalanced parens" error. - (condition-case nil - (progn - (down-list 1) ; into arglist - (backward-up-list 1) - (skip-chars-backward " \t") - t) - (error nil)) - ;; Verify initial pos was after - ;; real start of function. - (save-excursion - (goto-char beg) - ;; For this purpose, include the line - ;; that has the decl keywords. This - ;; may also include some of the - ;; comments before the function. - (while (and (not (bobp)) - (save-excursion - (forward-line -1) - (looking-at "[^\n\f]"))) - (forward-line -1)) - (>= location (point))) - ;; Consistency check: going down and up - ;; shouldn't take us back before BEG. - (> (point) beg)) - (let (end middle) - ;; Don't include any final whitespace - ;; in the name we use. - (skip-chars-backward " \t\n") - (setq end (point)) - (backward-sexp 1) - ;; Now find the right beginning of the name. - ;; Include certain keywords if they - ;; precede the name. - (setq middle (point)) - ;; We tried calling `forward-sexp' in a loop - ;; but it causes inconsistency for C names. - (forward-sexp -1) - ;; Is this C++ method? - (when (and (< 2 middle) - (string= (buffer-substring (- middle 2) - middle) - "::")) - ;; Include "classname::". - (setq middle (point))) - ;; Ignore these subparts of a class decl - ;; and move back to the class name itself. - (while (looking-at "public \\|private ") - (skip-chars-backward " \t:") - (setq end (point)) - (backward-sexp 1) - (setq middle (point)) - (forward-word -1)) - (and (bolp) - (looking-at - "enum \\|struct \\|union \\|class ") - (setq middle (point))) - (goto-char end) - (when (eq (preceding-char) ?=) - (forward-char -1) - (skip-chars-backward " \t") - (setq end (point))) - (buffer-substring-no-properties - middle end))))))))) - ((apply 'derived-mode-p add-log-tex-like-modes) + (or (c-cpp-define-name) + (c-defun-name))) + ((memq major-mode add-log-tex-like-modes) (if (re-search-backward "\\\\\\(sub\\)*\\(section\\|paragraph\\|chapter\\)" nil t) @@ -1077,7 +1236,7 @@ Has a preference of looking backwards." "Return date of log entry in a consistent form for sorting. Point is assumed to be at the start of the entry." (require 'timezone) - (if (looking-at "^\\sw.........[0-9:+ ]*") + (if (looking-at change-log-start-entry-re) (let ((date (match-string-no-properties 0))) (if date (if (string-match "\\(....\\)-\\(..\\)-\\(..\\)\\s-+" date) @@ -1164,6 +1323,32 @@ old-style time formats for entries are supported." (goto-char (point-max))) (insert-buffer-substring other-buf start))))))) +(defun change-log-beginning-of-defun () + (re-search-backward change-log-start-entry-re nil 'move)) + +(defun change-log-end-of-defun () + ;; Look back and if there is no entry there it means we are before + ;; the first ChangeLog entry, so go forward until finding one. + (unless (save-excursion (re-search-backward change-log-start-entry-re nil t)) + (re-search-forward change-log-start-entry-re nil t)) + + ;; In case we are at the end of log entry going forward a line will + ;; make us find the next entry when searching. If we are inside of + ;; an entry going forward a line will still keep the point inside + ;; the same entry. + (forward-line 1) + + ;; In case we are at the beginning of an entry, move past it. + (when (looking-at change-log-start-entry-re) + (goto-char (match-end 0)) + (forward-line 1)) + + ;; Search for the start of the next log entry. Go to the end of the + ;; buffer if we could not find a next entry. + (when (re-search-forward change-log-start-entry-re nil 'move) + (goto-char (match-beginning 0)) + (forward-line -1))) + (provide 'add-log) ;; arch-tag: 81eee6fc-088f-4372-a37f-80ad9620e762