;; Copyright (C) 1985, 86, 88, 93, 94, 97, 98, 2000 Free Software Foundation, Inc.
+;; Maintainer: FSF
;; Keywords: tools
;; This file is part of GNU Emacs.
"*If non-nil, function to guess name of surrounding function.
It is used by `add-log-current-defun' in preference to built-in rules.
Returns function's name as a string, or nil if outside a function."
- :type 'function
+ :type '(choice (const nil) function)
:group 'change-log)
;;;###autoload
;;;###autoload
(defcustom add-log-mailing-address nil
- "*Electronic mail address of user, for inclusion in ChangeLog daily headers.
-This defaults to the value of `user-mail-address'."
+ "*Electronic mail addresses of user, for inclusion in ChangeLog headers.
+This defaults to the value of `user-mail-address'. In addition to
+being a simple string, this value can also be a list. All elements
+will be recognized as referring to the same user; when creating a new
+ChangeLog entry, one element will be chosen at random."
:type '(choice (const :tag "Default" nil)
- string)
+ (string :tag "String")
+ (repeat :tag "List of Strings" string))
:group 'change-log)
(defcustom add-log-time-format 'add-log-iso8601-time-string
:type 'boolean
:group 'change-log)
+(defcustom add-log-always-start-new-record nil
+ "*If non-nil, `add-change-log-entry' will always start a new record."
+ :version "21.4"
+ :type 'boolean
+ :group 'change-log)
+
+(defcustom add-log-buffer-file-name-function nil
+ "*If non-nil, function to call to identify the full filename of a buffer.
+This function is called with no argument. If this is nil, the default is to
+use `buffer-file-name'."
+ :type '(choice (const nil) function)
+ :group 'change-log)
+
(defcustom add-log-file-name-function nil
"*If non-nil, function to call to identify the filename for a ChangeLog entry.
This function is called with one argument, the value of variable
`buffer-file-name' in that buffer. If this is nil, the default is to
use the file's name relative to the directory of the change log file."
- :type 'function
+ :type '(choice (const nil) function)
:group 'change-log)
:type '(repeat regexp)
:group 'change-log)
+(defface change-log-date-face
+ '((t (:inherit font-lock-string-face)))
+ "Face used to highlight dates in date lines."
+ :version "21.1"
+ :group 'change-log)
+
+(defface change-log-name-face
+ '((t (:inherit font-lock-constant-face)))
+ "Face for highlighting author names."
+ :version "21.1"
+ :group 'change-log)
+
+(defface change-log-email-face
+ '((t (:inherit font-lock-variable-name-face)))
+ "Face for highlighting author email addresses."
+ :version "21.1"
+ :group 'change-log)
+
+(defface change-log-file-face
+ '((t (:inherit font-lock-function-name-face)))
+ "Face for highlighting file names."
+ :version "21.1"
+ :group 'change-log)
+
+(defface change-log-list-face
+ '((t (:inherit font-lock-keyword-face)))
+ "Face for highlighting parenthesized lists of functions or variables."
+ :version "21.1"
+ :group 'change-log)
+
+(defface change-log-conditionals-face
+ '((t (:inherit font-lock-variable-name-face)))
+ "Face for highlighting conditionals of the form `[...]'."
+ :version "21.1"
+ :group 'change-log)
+
+(defface change-log-function-face
+ '((t (:inherit font-lock-variable-name-face)))
+ "Face for highlighting items of the form `<....>'."
+ :version "21.1"
+ :group 'change-log)
+
+(defface change-log-acknowledgement-face
+ '((t (:inherit font-lock-comment-face)))
+ "Face for highlighting acknowledgments."
+ :version "21.1"
+ :group 'change-log)
(defvar change-log-font-lock-keywords
'(;;
;; Date lines, new and old styles.
("^\\sw.........[0-9:+ ]*"
- (0 font-lock-string-face)
+ (0 'change-log-date-face)
;; Name and e-mail; some people put e-mail in parens, not angles.
- ("\\([^<(]+\\)[(<]\\([A-Za-z0-9_.-]+@[A-Za-z0-9_.-]+\\)[>)]" nil nil
- (1 font-lock-constant-face)
- (2 font-lock-variable-name-face)))
+ ("\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.-]+@[A-Za-z0-9_.-]+\\)[>)]" nil nil
+ (1 'change-log-name-face)
+ (2 'change-log-email-face)))
;;
;; File names.
("^\t\\* \\([^ ,:([\n]+\\)"
- (1 font-lock-function-name-face)
+ (1 'change-log-file-face)
;; Possibly further names in a list:
- ("\\=, \\([^ ,:([\n]+\\)" nil nil (1 font-lock-function-name-face))
+ ("\\=, \\([^ ,:([\n]+\\)" nil nil (1 'change-log-file-face))
;; Possibly a parenthesized list of names:
- ("\\= (\\([^) ,:\n]+\\)" nil nil (1 font-lock-keyword-face))
- ("\\=, *\\([^) ,:\n]+\\)" nil nil (1 font-lock-keyword-face)))
+ ("\\= (\\([^) ,:\n]+\\)" nil nil (1 'change-log-list-face))
+ ("\\=, *\\([^) ,:\n]+\\)" nil nil (1 'change-log-list-face)))
;;
;; Function or variable names.
("^\t(\\([^) ,:\n]+\\)"
- (1 font-lock-keyword-face)
- ("\\=, *\\([^) ,:\n]+\\)" nil nil (1 font-lock-keyword-face)))
+ (1 'change-log-list-face)
+ ("\\=, *\\([^) ,:\n]+\\)" nil nil (1 'change-log-list-face)))
;;
;; Conditionals.
- ("\\[!?\\([^]\n]+\\)\\]\\(:\\| (\\)" (1 font-lock-variable-name-face))
+ ("\\[!?\\([^]\n]+\\)\\]\\(:\\| (\\)" (1 'change-log-conditionals-face))
;;
;; Function of change.
- ("<\\([^>\n]+\\)>\\(:\\| (\\)" (1 font-lock-variable-name-face))
+ ("<\\([^>\n]+\\)>\\(:\\| (\\)" (1 'change-log-function-face))
;;
;; Acknowledgements.
("\\(^\t\\| \\)\\(From\\|Patch\\(es\\)? by\\|Report\\(ed by\\| from\\)\\|Suggest\\(ed by\\|ion from\\)\\)"
- 2 font-lock-comment-face))
+ 2 'change-log-acknowledgement-face))
"Additional expressions to highlight in Change Log mode.")
(defvar change-log-mode-map (make-sparse-keymap)
(/ size 10)
size))
version)
- (or (and buffer-file-name
- (vc-workfile-version buffer-file-name))
+ (or (and buffer-file-name (vc-workfile-version buffer-file-name))
(save-restriction
(widen)
(let ((regexps change-log-version-number-regexp-list))
;;;###autoload
-(defun find-change-log (&optional file-name)
+(defun find-change-log (&optional file-name buffer-file)
"Find a change log file for \\[add-change-log-entry] and return the name.
Optional arg FILE-NAME specifies the file to use.
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."
+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
(progn
;; Chase links in the source file
;; and use the change log in the dir where it points.
- (setq file-name (or (and buffer-file-name
+ (setq file-name (or (and (or buffer-file buffer-file-name)
(file-name-directory
- (file-chase-links buffer-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)))
(set (make-local-variable 'change-log-default-name) file-name)
file-name)
+(defun add-log-file-name (buffer-file log-file)
+ ;; Never want to add a change log entry for the ChangeLog file itself.
+ (unless (or (null buffer-file) (string= buffer-file log-file))
+ (if add-log-file-name-function
+ (funcall add-log-file-name-function buffer-file)
+ (setq buffer-file
+ (if (string-match
+ (concat "^" (regexp-quote (file-name-directory log-file)))
+ buffer-file)
+ (substring buffer-file (match-end 0))
+ (file-name-nondirectory buffer-file)))
+ ;; If we have a backup file, it's presumably because we're
+ ;; comparing old and new versions (e.g. for deleted
+ ;; functions) and we'll want to use the original name.
+ (if (backup-file-name-p buffer-file)
+ (file-name-sans-versions buffer-file)
+ buffer-file))))
+
;;;###autoload
(defun add-change-log-entry (&optional whoami file-name other-window new-entry)
- "Find change log file and add an entry for today.
+ "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 site.
-Second arg is FILE-NAME of change log. If nil, uses `change-log-default-name'.
+Second arg FILE-NAME is file name of the change log.
+If nil, use the value of `change-log-default-name'.
+
Third arg OTHER-WINDOW non-nil means visit in other window.
+
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.
+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.
+
+The change log file can start with a copyright notice and a copying
+permission notice. The first blank line indicates the end of these
+notices.
+
Today's date is calculated according to `change-log-time-zone-rule' if
non-nil, otherwise in local time."
(interactive (list current-prefix-arg
;; s/he can edit the full name field in prompter if s/he wants.
(setq add-log-mailing-address
(read-input "Mailing address: " add-log-mailing-address))))
- (let ((defun (add-log-current-defun))
- (version (and change-log-version-info-enabled
- (change-log-version-number-search)))
- bound entry)
-
- (setq file-name (expand-file-name (find-change-log file-name)))
-
- ;; Set ENTRY to the file name to use in the new entry.
- (and buffer-file-name
- ;; Never want to add a change log entry for the ChangeLog file itself.
- (not (string= buffer-file-name file-name))
- (if add-log-file-name-function
- (setq entry
- (funcall add-log-file-name-function buffer-file-name))
- (setq entry
- (if (string-match
- (concat "^" (regexp-quote (file-name-directory
- file-name)))
- buffer-file-name)
- (substring buffer-file-name (match-end 0))
- (file-name-nondirectory buffer-file-name)))
- ;; If we have a backup file, it's presumably because we're
- ;; comparing old and new versions (e.g. for deleted
- ;; functions) and we'll want to use the original name.
- (if (backup-file-name-p entry)
- (setq entry (file-name-sans-versions entry)))))
-
- (if (and other-window (not (equal file-name buffer-file-name)))
+
+ (let* ((defun (add-log-current-defun))
+ (version (and change-log-version-info-enabled
+ (change-log-version-number-search)))
+ (buf-file-name (if add-log-buffer-file-name-function
+ (funcall add-log-buffer-file-name-function)
+ buffer-file-name))
+ (buffer-file (if buf-file-name (expand-file-name buf-file-name)))
+ (file-name (expand-file-name
+ (or file-name (find-change-log file-name buffer-file))))
+ ;; Set ITEM to the file name to use in the new item.
+ (item (add-log-file-name buffer-file file-name))
+ bound)
+
+ (if (or (and other-window (not (equal file-name buffer-file-name)))
+ (window-dedicated-p (selected-window)))
(find-file-other-window file-name)
(find-file file-name))
(or (eq major-mode 'change-log-mode)
(change-log-mode))
(undo-boundary)
(goto-char (point-min))
- (let ((new-entry (concat (funcall add-log-time-format)
- " " add-log-full-name
- " <" add-log-mailing-address ">")))
- (if (looking-at (regexp-quote new-entry))
+
+ ;; If file starts with a copyright and permission notice, skip them.
+ ;; Assume they end at first blank line.
+ (when (looking-at "Copyright")
+ (search-forward "\n\n")
+ (skip-chars-forward "\n"))
+
+ ;; Advance into first entry if it is usable; else make new one.
+ (let ((new-entries (mapcar (lambda (addr)
+ (concat (funcall add-log-time-format)
+ " " add-log-full-name
+ " <" addr ">"))
+ (if (consp add-log-mailing-address)
+ add-log-mailing-address
+ (list add-log-mailing-address)))))
+ (if (and (not add-log-always-start-new-record)
+ (let ((hit nil))
+ (dolist (entry new-entries hit)
+ (when (looking-at (regexp-quote entry))
+ (setq hit t)))))
(forward-line 1)
- (insert new-entry "\n\n")))
+ (insert (nth (random (length new-entries))
+ new-entries)
+ "\n\n")
+ (forward-line -1)))
+ ;; Determine where we should stop searching for a usable
+ ;; item to add to, within this entry.
(setq bound
- (progn
+ (save-excursion
(if (looking-at "\n*[^\n* \t]")
(skip-chars-forward "\n")
(if add-log-keep-changes-together
(forward-page) ; page delimits entries for date
(forward-paragraph))) ; paragraph delimits entries for file
(point)))
- (goto-char (point-min))
- ;; Now insert the new line for this entry.
+
+ ;; Now insert the new line for this item.
(cond ((re-search-forward "^\\s *\\*\\s *$" bound t)
- ;; Put this file name into the existing empty entry.
- (if entry
- (insert entry)))
+ ;; Put this file name into the existing empty item.
+ (if item
+ (insert item)))
((and (not new-entry)
(let (case-fold-search)
(re-search-forward
- (concat (regexp-quote (concat "* " entry))
+ (concat (regexp-quote (concat "* " item))
;; Don't accept `foo.bar' when
;; looking for `foo':
"\\(\\s \\|[(),:]\\)")
bound t)))
- ;; Add to the existing entry for the same file.
+ ;; Add to the existing item for the same file.
(re-search-forward "^\\s *$\\|^\\s \\*")
(goto-char (match-beginning 0))
;; Delete excess empty lines; make just 2.
(forward-line -2)
(indent-relative-maybe))
(t
- ;; Make a new entry.
- (forward-line 1)
+ ;; Make a new item.
(while (looking-at "\\sW")
(forward-line 1))
(while (and (not (eobp)) (looking-at "^\\s *$"))
(forward-line -2)
(indent-to left-margin)
(insert "* ")
- (if entry (insert entry))))
+ (if item (insert item))))
;; Now insert the function name, if we have one.
- ;; Point is at the entry for this file,
+ ;; Point is at the item for this file,
;; either at the end of the line or at the first blank line.
(if defun
(progn
(beginning-of-line 1)
(looking-at "\\s *$"))
(insert ?\ ))
- ;; See if the prev function name has a message yet or not
- ;; If not, merge the two entries.
+ ;; See if the prev function name has a message yet or not.
+ ;; If not, merge the two items.
(let ((pos (point-marker)))
(if (and (skip-syntax-backward " ")
(skip-chars-backward "):")
;;;###autoload
(defun add-change-log-entry-other-window (&optional whoami file-name)
- "Find change log file in other window and add an entry for today.
-Optional arg WHOAMI (interactive prefix) non-nil means prompt for user
-name and site.
-Second optional arg FILE-NAME is file name of change log.
-If nil, use `change-log-default-name'.
-
-Affected by the same options as `add-change-log-entry'."
+ "Find change log file in other window and add entry and item.
+This is just like `add-change-log-entry' except that it displays
+the change log file in another window."
(interactive (if current-prefix-arg
(list current-prefix-arg
(prompt-for-change-log-name))))
(set (make-local-variable 'version-control) 'never)
(set (make-local-variable 'adaptive-fill-regexp) "\\s *")
(set (make-local-variable 'font-lock-defaults)
- '(change-log-font-lock-keywords t))
+ '(change-log-font-lock-keywords t nil nil backward-paragraph))
(run-hooks 'change-log-mode-hook))
;; It might be nice to have a general feature to replace this. The idea I
point for uppercase headings starting in the first column or
identifiers followed by `:' or `='. See variables
`add-log-current-defun-header-regexp' and
-`add-log-current-defun-function'
+`add-log-current-defun-function'.
Has a preference of looking backwards."
(condition-case nil
((eq major-mode 'texinfo-mode)
(if (re-search-backward "^@node[ \t]+\\([^,\n]+\\)" nil t)
(match-string-no-properties 1)))
- ((eq major-mode 'perl-mode)
+ ((memq major-mode '(perl-mode cperl-mode))
(if (re-search-backward "^sub[ \t]+\\([^ \t\n]+\\)" nil t)
(match-string-no-properties 1)))
;; Emacs's autoconf-mode installs its own