;;; log-edit.el --- Major mode for editing CVS commit messages -*- lexical-binding: t -*-
-;; Copyright (C) 1999-2013 Free Software Foundation, Inc.
+;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
;; Keywords: pcl-cvs cvs commit log vc
(defcustom log-edit-confirm 'changed
"If non-nil, `log-edit-done' will request confirmation.
-If 'changed, only request confirmation if the list of files has
+If `changed', only request confirmation if the list of files has
changed since the beginning of the log-edit session."
:group 'log-edit
:type '(choice (const changed) (const t) (const nil)))
(defcustom log-edit-setup-invert nil
"Non-nil means `log-edit' should invert the meaning of its SETUP arg.
-If SETUP is 'force, this variable has no effect."
+If SETUP is `force', this variable has no effect."
:group 'log-edit
:type 'boolean)
log-edit-insert-changelog
log-edit-show-files)
"Hook run at the end of `log-edit'."
+ ;; Added log-edit-insert-message-template, moved log-edit-show-files.
+ :version "24.4"
:group 'log-edit
:type '(hook :options (log-edit-insert-message-template
log-edit-insert-cvs-rcstemplate
(defvar log-edit-changelog-full-paragraphs t
"If non-nil, include full ChangeLog paragraphs in the log.
-This may be set in the ``local variables'' section of a ChangeLog, to
+This may be set in the \"local variables\" section of a ChangeLog, to
indicate the policy for that ChangeLog.
A ChangeLog paragraph is a bunch of log text containing no blank lines;
and site. FILE-NAME is the name of the change log; if nil, use
`change-log-default-name'.
-This may be useful as a `log-edit-checkin-hook' to update change logs
+This may be useful as a `vc-checkin-hook' to update change logs
automatically."
(interactive (if current-prefix-arg
(list current-prefix-arg
(set-match-data (list start (point)))
(point))))
+(defun log-edit-goto-eoh () ;FIXME: Almost rfc822-goto-eoh!
+ (goto-char (point-min))
+ (when (re-search-forward
+ "^\\([^[:alpha:]]\\|[[:alnum:]-]+[^[:alnum:]-:]\\)" nil 'move)
+ (goto-char (match-beginning 0))))
+
+(defun log-edit--match-first-line (limit)
+ (let ((start (point)))
+ (log-edit-goto-eoh)
+ (skip-chars-forward "\n")
+ (and (< start (line-end-position))
+ (< (point) limit)
+ (save-excursion
+ (not (re-search-backward "^Summary:[ \t]*[^ \t\n]" nil t)))
+ (looking-at ".+")
+ (progn
+ (goto-char (match-end 0))
+ (put-text-property (point-min) (point)
+ 'jit-lock-defer-multiline t)
+ (point)))))
+
(defvar log-edit-font-lock-keywords
;; Copied/inspired by message-font-lock-keywords.
`((log-edit-match-to-eoh
nil lax))
("^\n"
(progn (goto-char (match-end 0)) (1+ (match-end 0))) nil
- (0 '(:height 0.1 :inverse-video t))))))
+ (0 '(:height 0.1 :inverse-video t))))
+ (log-edit--match-first-line (0 'log-edit-summary))))
(defvar log-edit-font-lock-gnu-style nil
"If non-nil, highlight common failures to follow the GNU coding standards.")
\\{log-edit-mode-map}"
(set (make-local-variable 'font-lock-defaults)
'(log-edit-font-lock-keywords t))
+ (setq-local jit-lock-contextually t) ;For the "first line is summary".
(make-local-variable 'log-edit-comment-ring-index)
(add-hook 'kill-buffer-hook 'log-edit-remember-comment nil t)
(hack-dir-local-variables-non-file-buffer))
(defvar log-edit-changelog-use-first nil)
+(defvar log-edit-rewrite-tiny-change t
+ "Non-nil means rewrite (tiny change).")
+
(defvar log-edit-rewrite-fixes nil
"Rule to rewrite bug numbers into Fixes: headers.
The value should be of the form (REGEXP . REPLACEMENT)
(log-edit-insert-changelog-entries (log-edit-files)))))
(log-edit-set-common-indentation)
;; Add an Author: field if appropriate.
- (when author (log-edit-add-field "Author" author))
+ (when author (log-edit-add-field "Author" (car author)))
;; Add a Fixes: field if applicable.
(when (consp log-edit-rewrite-fixes)
(rfc822-goto-eoh)
(goto-char start)
(skip-chars-forward "^():")
(skip-chars-forward ": ")
- (delete-region start (point))))))))
+ (delete-region start (point)))))
+ ;; FIXME also add "Co-authored-by" when appropriate.
+ ;; Bzr accepts multiple --author arguments, others (?) don't.
+ (and log-edit-rewrite-tiny-change
+ (eq 'tiny (cdr author))
+ (goto-char (point-max))
+ (insert "\nCopyright-paperwork-exempt: yes\n")))))
;;;;
;;;; functions for getting commit message from ChangeLog a file...
(and (boundp 'user-mail-address) user-mail-address)))
(time (or (and (boundp 'add-log-time-format)
(functionp add-log-time-format)
- (funcall add-log-time-format))
+ (funcall add-log-time-format
+ nil add-log-time-zone-rule))
(format-time-string "%Y-%m-%d"))))
(if (null log-edit-changelog-use-first)
(looking-at (regexp-quote (format "%s %s <%s>" time name mail)))
;; Check the author, to potentially add it as a "Author: " header.
+ ;; FIXME This accumulates multiple authors, but only when there
+ ;; are multiple ChangeLog files. It should also check for
+ ;; multiple authors in each individual entry.
(when (looking-at "[^ \t]")
(when (and (boundp 'log-edit-author)
(not (looking-at (format ".+ .+ <%s>"
(regexp-quote mail))))
- (looking-at ".+ \\(.+ <.+>\\)"))
+ (looking-at ".+ \\(.+ <.+>\\) *\\((tiny change)\\)?"))
(let ((author (replace-regexp-in-string " " " "
(match-string 1))))
(unless (and log-edit-author
- (string-match (regexp-quote author) log-edit-author))
- (setq log-edit-author
- (if log-edit-author
- (concat log-edit-author ", " author)
- author)))))
+ (string-match (regexp-quote author)
+ (car log-edit-author)))
+ (if (not log-edit-author)
+ (setq log-edit-author
+ (cons author (if (match-string 2) 'tiny)))
+ (setcar log-edit-author
+ (concat (car log-edit-author) ", " author))
+ (and (match-string 2) (not (cdr log-edit-author))
+ (setcdr log-edit-author 'tiny))))))
t))))
(defun log-edit-changelog-entries (file)
The return value looks like this:
(LOGBUFFER (ENTRYSTART ENTRYEND) ...)
where LOGBUFFER is the name of the ChangeLog buffer, and each
-\(ENTRYSTART . ENTRYEND\) pair is a buffer region."
+\(ENTRYSTART . ENTRYEND) pair is a buffer region."
(let ((changelog-file-name
(let ((default-directory
(file-name-directory (expand-file-name file)))
;; that memoizing which is undesired here.
(setq change-log-default-name nil)
(find-change-log)))))
- (with-current-buffer (find-file-noselect changelog-file-name)
- (unless (eq major-mode 'change-log-mode) (change-log-mode))
- (goto-char (point-min))
- (if (looking-at "\\s-*\n") (goto-char (match-end 0)))
- (if (not (log-edit-changelog-ours-p))
- (list (current-buffer))
- (save-restriction
- (log-edit-narrow-changelog)
- (goto-char (point-min))
-
- ;; Search for the name of FILE relative to the ChangeLog. If that
- ;; doesn't occur anywhere, they're not using full relative
- ;; filenames in the ChangeLog, so just look for FILE; we'll accept
- ;; some false positives.
- (let ((pattern (file-relative-name
- file (file-name-directory changelog-file-name))))
- (if (or (string= pattern "")
- (not (save-excursion
- (search-forward pattern nil t))))
- (setq pattern (file-name-nondirectory file)))
-
- (setq pattern (concat "\\(^\\|[^[:alnum:]]\\)"
- (regexp-quote pattern)
- "\\($\\|[^[:alnum:]]\\)"))
-
- (let (texts
- (pos (point)))
- (while (and (not (eobp)) (re-search-forward pattern nil t))
- (let ((entry (log-edit-changelog-entry)))
- (if (< (elt entry 1) (max (1+ pos) (point)))
- ;; This is not relevant, actually.
- nil
- (push entry texts))
- ;; Make sure we make progress.
- (setq pos (max (1+ pos) (elt entry 1)))
- (goto-char pos)))
-
- (cons (current-buffer) texts))))))))
+ (when (or (find-buffer-visiting changelog-file-name)
+ (file-exists-p changelog-file-name))
+ (with-current-buffer (find-file-noselect changelog-file-name)
+ (unless (eq major-mode 'change-log-mode) (change-log-mode))
+ (goto-char (point-min))
+ (if (looking-at "\\s-*\n") (goto-char (match-end 0)))
+ (if (not (log-edit-changelog-ours-p))
+ (list (current-buffer))
+ (save-restriction
+ (log-edit-narrow-changelog)
+ (goto-char (point-min))
+
+ (let ((pattern (log-edit-changelog--pattern file
+ changelog-file-name)))
+ (let (texts
+ (pos (point)))
+ (while (and (not (eobp)) (re-search-forward pattern nil t))
+ (let ((entry (log-edit-changelog-entry)))
+ (if (< (elt entry 1) (max (1+ pos) (point)))
+ ;; This is not relevant, actually.
+ nil
+ (push entry texts))
+ ;; Make sure we make progress.
+ (setq pos (max (1+ pos) (elt entry 1)))
+ (goto-char pos)))
+
+ (cons (current-buffer) texts)))))))))
+
+(defun log-edit-changelog--pattern (file changelog-file-name)
+ (if (eq (aref file (1- (length file))) ?/)
+ ;; Match any files inside this directory.
+ (concat "^\t\\* " (unless (string= file "./") file))
+ ;; Search for the name of FILE relative to the ChangeLog. If that
+ ;; doesn't occur anywhere, they're not using full relative
+ ;; filenames in the ChangeLog, so just look for FILE; we'll accept
+ ;; some false positives.
+ (let ((pattern (file-relative-name
+ file (file-name-directory changelog-file-name))))
+ ;; FIXME: When can the above return an empty string?
+ (if (or (string= pattern "")
+ (not (save-excursion
+ (search-forward pattern nil t))))
+ (setq pattern (file-name-nondirectory file)))
+ (setq pattern (concat "\\(^\\|[^[:alnum:]]\\)"
+ (regexp-quote pattern)
+ "\\($\\|[^[:alnum:]]\\)")))))
(defun log-edit-changelog-insert-entries (buffer beg end &rest files)
"Insert the text from BUFFER between BEG and END.
(setq bound (point-marker))
(when log-name
(dolist (f files)
+ ;; FIXME: f can be a directory, a (possibly indirect) parent
+ ;; of the ChangeLog file.
(save-excursion
(goto-char opoint)
(when (re-search-forward
(apply 'log-edit-changelog-insert-entries
(append (car log-entry) (cdr log-entry)))
(insert "\n"))
+ ;; No newline after the last entry.
+ (when log-entries
+ (delete-char -1))
log-edit-author))
(defun log-edit-toggle-header (header value)