diff options
| author | Noam Postavsky <npostavs@gmail.com> | 2019-07-04 20:32:39 -0400 |
|---|---|---|
| committer | Noam Postavsky <npostavs@gmail.com> | 2019-08-03 20:14:52 -0400 |
| commit | 01661f33c11654d1fe5fe1013332db2500b7f449 (patch) | |
| tree | d46bd831f134d495373c8eb389c11f95d7c6028a /lisp/vc/diff-mode.el | |
| parent | 2d47483e815979d0ae0edd6f878b7fbb85fd72fa (diff) | |
| download | emacs-01661f33c11654d1fe5fe1013332db2500b7f449.tar.gz | |
Improved ChangeLog generation for vc log (Bug#16301)
* lisp/vc/diff-mode.el (diff-find-source-location): Fix docstring.
* lisp/vc/add-log.el (change-log-unindented-file-names-re)
(change-log-read-entries, change-log-read-defuns)
(change-log-insert-entries):
* lisp/vc/diff-mode.el (diff-add-log-current-defuns):
* lisp/vc/log-edit.el (log-edit--insert-filled-defuns)
(log-edit-fill-entry): New functions.
(log-edit-mode): Set `log-edit-fill-entry' as
`fill-paragraph-function'.
(log-edit-generate-changelog-from-diff): New command.
(log-edit-mode-map): Bind it to C-c C-w.
* doc/emacs/maintaining.texi (Types of Log File, Log Buffer):
* CONTRIBUTE: Document it.
* etc/NEWS: Announce it.
* test/lisp/vc/log-edit-tests.el (log-edit-fill-entry)
(log-edit-fill-entry-joining): New tests.
Diffstat (limited to 'lisp/vc/diff-mode.el')
| -rw-r--r-- | lisp/vc/diff-mode.el | 131 |
1 files changed, 127 insertions, 4 deletions
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index 0d5dc0e1c0c..81662cafeda 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el @@ -54,6 +54,7 @@ ;;; Code: (eval-when-compile (require 'cl-lib)) +(eval-when-compile (require 'subr-x)) (autoload 'vc-find-revision "vc") (autoload 'vc-find-revision-no-save "vc") @@ -1773,15 +1774,22 @@ Whitespace differences are ignored." (defsubst diff-xor (a b) (if a (if (not b) a) b)) (defun diff-find-source-location (&optional other-file reverse noprompt) - "Find out (BUF LINE-OFFSET POS SRC DST SWITCHED). + "Find current diff location within the source file. +OTHER-FILE, if non-nil, means to look at the diff's name and line + numbers for the old file. Furthermore, use `diff-vc-revisions' + if it's available. If `diff-jump-to-old-file' is non-nil, the + sense of this parameter is reversed. If the prefix argument is + 8 or more, `diff-jump-to-old-file' is set to OTHER-FILE. +REVERSE, if non-nil, switches the sense of SRC and DST (see below). +NOPROMPT, if non-nil, means not to prompt the user. +Return a list (BUF LINE-OFFSET (BEG . END) SRC DST SWITCHED). BUF is the buffer corresponding to the source file. LINE-OFFSET is the offset between the expected and actual positions of the text of the hunk or nil if the text was not found. -POS is a pair (BEG . END) indicating the position of the text in the buffer. +\(BEG . END) is a pair indicating the position of the text in the buffer. SRC and DST are the two variants of text as returned by `diff-hunk-text'. SRC is the variant that was found in the buffer. -SWITCHED is non-nil if the patch is already applied. -NOPROMPT, if non-nil, means not to prompt the user." +SWITCHED is non-nil if the patch is already applied." (save-excursion (let* ((other (diff-xor other-file diff-jump-to-old-file)) (char-offset (- (point) (diff-beginning-of-hunk t))) @@ -2210,6 +2218,121 @@ Call FUN with two args (BEG and END) for each hunk." (let ((inhibit-read-only t)) (undo arg))) +(defun diff-add-log-current-defuns () + "Return an alist of defun names for the current diff. +The elements of the alist are of the form (FILE . (DEFUN...)), +where DEFUN... is a list of function names found in FILE." + (save-excursion + (goto-char (point-min)) + (let ((defuns nil) + (hunk-end nil) + (hunk-mismatch-files nil) + (make-defun-context-follower + (lambda (goline) + (let ((eodefun nil) + (defname nil)) + (list + (lambda () ;; Check for end of current defun. + (when (and eodefun + (funcall goline) + (>= (point) eodefun)) + (setq defname nil) + (setq eodefun nil))) + (lambda (&optional get-current) ;; Check for new defun. + (if get-current + defname + (when-let* ((def (and (not eodefun) + (funcall goline) + (add-log-current-defun))) + (eof (save-excursion (end-of-defun) (point)))) + (setq eodefun eof) + (setq defname def))))))))) + (while + ;; Might need to skip over file headers between diff + ;; hunks (e.g., "diff --git ..." etc). + (re-search-forward diff-hunk-header-re nil t) + (setq hunk-end (save-excursion (diff-end-of-hunk))) + (pcase-let* ((filename (substring-no-properties (diff-find-file-name))) + (=lines 0) + (+lines 0) + (-lines 0) + (`(,buf ,line-offset (,beg . ,_end) + (,old-text . ,_old-offset) + (,new-text . ,_new-offset) + ,applied) + ;; Try to use the vc integration of + ;; `diff-find-source-location', unless it + ;; would look for non-existent files like + ;; /dev/null. + (diff-find-source-location + (not (equal "/dev/null" + (car (diff-hunk-file-names t)))))) + (other-buf nil) + (goto-otherbuf + ;; If APPLIED, we have NEW-TEXT in BUF, so we + ;; need to a buffer with OLD-TEXT to follow + ;; -lines. + (lambda () + (if other-buf (set-buffer other-buf) + (set-buffer (generate-new-buffer " *diff-other-text*")) + (insert (if applied old-text new-text)) + (funcall (buffer-local-value 'major-mode buf)) + (setq other-buf (current-buffer))) + (goto-char (point-min)) + (forward-line (+ =lines -1 + (if applied -lines +lines))))) + (gotobuf (lambda () + (set-buffer buf) + (goto-char beg) + (forward-line (+ =lines -1 + (if applied +lines -lines))))) + (`(,=ck-eodefun ,=ck-defun) + (funcall make-defun-context-follower gotobuf)) + (`(,-ck-eodefun ,-ck-defun) + (funcall make-defun-context-follower + (if applied goto-otherbuf gotobuf))) + (`(,+ck-eodefun ,+ck-defun) + (funcall make-defun-context-follower + (if applied gotobuf goto-otherbuf)))) + (unless (eql line-offset 0) + (cl-pushnew filename hunk-mismatch-files :test #'equal)) + ;; Some modes always return nil for `add-log-current-defun', + ;; make sure at least the filename is included. + (unless (assoc filename defuns) + (push (cons filename nil) defuns)) + (unwind-protect + (while (progn (forward-line) + (< (point) hunk-end)) + (let ((patch-char (char-after))) + (pcase patch-char + (?+ (cl-incf +lines)) + (?- (cl-incf -lines)) + (?\s (cl-incf =lines))) + (save-current-buffer + (funcall =ck-eodefun) + (funcall +ck-eodefun) + (funcall -ck-eodefun) + (when-let* ((def (cond + ((eq patch-char ?\s) + ;; Just updating context defun. + (ignore (funcall =ck-defun))) + ;; + or - in existing defun. + ((funcall =ck-defun t)) + ;; Check added or removed defun. + (t (funcall (if (eq ?+ patch-char) + +ck-defun -ck-defun)))))) + (cl-pushnew def (alist-get filename defuns + nil nil #'equal) + :test #'equal))))) + (when (buffer-live-p other-buf) + (kill-buffer other-buf))))) + (when hunk-mismatch-files + (message "Diff didn't match for %s." + (mapconcat #'identity hunk-mismatch-files ", "))) + (dolist (file-defuns defuns) + (cl-callf nreverse (cdr file-defuns))) + (nreverse defuns)))) + (defun diff-add-change-log-entries-other-window () "Iterate through the current diff and create ChangeLog entries. I.e. like `add-change-log-entry-other-window' but applied to all hunks." |
