]> code.delx.au - gnu-emacs/blobdiff - lisp/add-log.el
(set-register): Use push.
[gnu-emacs] / lisp / add-log.el
index f2b93b8c0472d38e08a2b246609b61db289ccacc..1c3f70a9b7714cd3f873a549dae69c4ba9f1eacd 100644 (file)
@@ -2,6 +2,7 @@
 
 ;; 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.
@@ -53,7 +54,7 @@
   "*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
@@ -66,10 +67,14 @@ This defaults to the value returned by the function `user-full-name'."
 
 ;;;###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
@@ -116,12 +121,25 @@ this variable."
   :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)
 
 
@@ -145,40 +163,87 @@ Note: The search is conducted only within 10%, at the beginning of the file."
   :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)
@@ -256,8 +321,7 @@ nil, by matching `change-log-version-number-regexp-list'."
              (/ 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))
@@ -270,7 +334,7 @@ nil, by matching `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.
@@ -283,7 +347,8 @@ simply find it in the current directory.  Otherwise, search in the current
 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
@@ -293,9 +358,10 @@ current buffer to the complete 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)))
@@ -326,18 +392,47 @@ current buffer to the complete 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
@@ -355,70 +450,79 @@ non-nil, otherwise in local time."
         ;; 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.
@@ -428,8 +532,7 @@ non-nil, otherwise in local time."
           (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 *$"))
@@ -438,9 +541,9 @@ non-nil, otherwise in local time."
           (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
@@ -450,8 +553,8 @@ non-nil, otherwise in local time."
                    (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 "):")
@@ -475,13 +578,9 @@ non-nil, otherwise in local time."
 
 ;;;###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))))
@@ -519,7 +618,7 @@ Runs `change-log-mode-hook'."
   (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
@@ -568,7 +667,7 @@ Other modes are handled by a heuristic that looks in the 10K before
 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
@@ -733,7 +832,7 @@ Has a preference of looking backwards."
                ((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