From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED.blaine.gmane.org!not-for-mail From: Noam Postavsky Newsgroups: gmane.emacs.bugs Subject: bug#16301: 24.3.50; [Feature] Split add-change-log-entry to make it reusable Date: Thu, 04 Jul 2019 21:57:44 -0400 Message-ID: <874l415jl3.fsf@gmail.com> References: <87bnzyv7aj.fsf@hati.baby-gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: blaine.gmane.org; posting-host="blaine.gmane.org:195.159.176.226"; logging-data="186084"; mail-complaints-to="usenet@blaine.gmane.org" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.2.90 (gnu/linux) Cc: 16301@debbugs.gnu.org To: Daniel Dehennin Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Fri Jul 05 03:58:27 2019 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([209.51.188.17]) by blaine.gmane.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1hjDUX-000m1U-Tw for geb-bug-gnu-emacs@m.gmane.org; Fri, 05 Jul 2019 03:58:26 +0200 Original-Received: from localhost ([::1]:49488 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hjDUN-0004wJ-5q for geb-bug-gnu-emacs@m.gmane.org; Thu, 04 Jul 2019 21:58:15 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:47981) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hjDUD-0004vx-95 for bug-gnu-emacs@gnu.org; Thu, 04 Jul 2019 21:58:07 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hjDUB-0004zP-7D for bug-gnu-emacs@gnu.org; Thu, 04 Jul 2019 21:58:05 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:43354) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hjDU9-0004z3-SH for bug-gnu-emacs@gnu.org; Thu, 04 Jul 2019 21:58:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1hjDU9-0001MS-Q1 for bug-gnu-emacs@gnu.org; Thu, 04 Jul 2019 21:58:01 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Noam Postavsky Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 05 Jul 2019 01:58:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 16301 X-GNU-PR-Package: emacs Original-Received: via spool by 16301-submit@debbugs.gnu.org id=B16301.15622918755220 (code B ref 16301); Fri, 05 Jul 2019 01:58:01 +0000 Original-Received: (at 16301) by debbugs.gnu.org; 5 Jul 2019 01:57:55 +0000 Original-Received: from localhost ([127.0.0.1]:52175 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1hjDU3-0001M7-31 for submit@debbugs.gnu.org; Thu, 04 Jul 2019 21:57:55 -0400 Original-Received: from mail-io1-f67.google.com ([209.85.166.67]:43253) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1hjDU0-0001Lt-FN for 16301@debbugs.gnu.org; Thu, 04 Jul 2019 21:57:53 -0400 Original-Received: by mail-io1-f67.google.com with SMTP id k20so15954939ios.10 for <16301@debbugs.gnu.org>; Thu, 04 Jul 2019 18:57:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:references:date:in-reply-to:message-id :user-agent:mime-version; bh=5WHhRZr0ELhArrYdiG4fyQ+kEZ43OEVqceubn5cKrpQ=; b=RaFyW7qD4VO2mSV9Vu2Wh/sHj1OhQeBUhk6BGQjfaBB4LvAiTdWkEMNOio3gkjXPpP qJsjb12N4GSHyxT7h8ZNW2pqo2KJ9kk/xF7oY3lpU+Y9m2rw3PpohwNdHHvfARMf1NiC 6b3THsG8dMWvBPFB+qdyVRSD3/I8wIB3PPx/ypO/ZGvAL4J0iyYqn7P9DgdKz+jEvV3r R+Gqc/uH0deAOhgZY/uSUVYMeUjCR844eyiMjt7c15CDPz2xV8g/9hlkvS9d5tUQ0b8I O/wkJRkKuLPAwllo9edWL03cP69vU4b+lEXCM1SJn/ErlGPXnS40lOr8ZEfE9c1wf3XC 9L2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:references:date:in-reply-to :message-id:user-agent:mime-version; bh=5WHhRZr0ELhArrYdiG4fyQ+kEZ43OEVqceubn5cKrpQ=; b=RXz/5ZQPfL8Sxbrt4Sv7Q1aZ1yv0b+1sh6NwIyIXEdH4ZvhlN2CGM5k53gAeZOTuMe V7nIJ5SIJAsTB1SODWtivdyTPouyC2I2NbC+qWeCgs12PaPh0AMprv/fd0HexqA2QmTX vjsUWsojYlhmA7zgR1ZYenkSLZNkFo8nl7FItPgs+f0ATTPNVO5o68AU7ulXvmKJJWaf 3zQ9Wkwd/wmEtDKIPUh6TCqHELAEzFZT9ZQVSz/XZCgXOlvFa802ooEdbaOOu5nOhbwa wZ3Xf/80FyIw6jqHtH1E9XaleX0XeMB5RjIsRHVfg772u51hJgAy14A4yxklCEhF0w+H hDPw== X-Gm-Message-State: APjAAAXcGPNB6/rXnvTm7OTnIzRTOBCnh53xeQ8/4VAeHvLgphUJD1yR w9R8ebg1UJcU3tjA0dVxjo+anE4f X-Google-Smtp-Source: APXvYqx/e+hJ623xE3ufJTu+d657JD9CwnNBfNIjADDUbS6BLXvNVx0bpdx6ZKbzf50vVOMwccXxZQ== X-Received: by 2002:a6b:901:: with SMTP id t1mr1446021ioi.42.1562291866655; Thu, 04 Jul 2019 18:57:46 -0700 (PDT) Original-Received: from minid (cbl-45-2-119-34.yyz.frontiernetworks.ca. [45.2.119.34]) by smtp.gmail.com with ESMTPSA id p3sm6612003iog.70.2019.07.04.18.57.45 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 04 Jul 2019 18:57:45 -0700 (PDT) In-Reply-To: <87bnzyv7aj.fsf@hati.baby-gnu.org> (Daniel Dehennin's message of "Mon, 30 Dec 2013 13:43:00 +0100") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.51.188.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.org gmane.emacs.bugs:162098 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Daniel Dehennin writes: > I just start to use magit in complement[1] of vc-git and was wondering > why there is no feature like =E2=80=9Cadd-change-log-entry(-other-window)= =E2=80=9D that > insert in the commit log instead of creating/adding to a plain > ChangeLog. > > I finally found[2] the =E2=80=9Cmagit-commit-add-log=E2=80=9D which behav= e like what I > had in DVC[3]. > > All these functions do near the same thing but code is duplicated. > > I propose to split the =E2=80=9Cadd-change-log-entry=E2=80=9D function to= make parts > usable by third parties. I've started working on this, patch attached below. It's not entirely baked, but I thought I'd post it now to let people know about it. --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=0001-Improved-ChangeLog-generation-for-vc-log-Bug-16301.patch Content-Description: patch >From 00b2e60143b9ecfbe86e993d2c29b76514368fb4 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Thu, 4 Jul 2019 20:32:39 -0400 Subject: [PATCH] Improved ChangeLog generation for vc log (Bug#16301) * lisp/vc/add-log.el (change-log-unindented-file-names-re) (change-log-read-entries, change-log-read-defuns) (change-log-insert-entries): New functions. * lisp/vc/diff-mode.el (diff-find-source-location): Fix docstring. (diff-add-log-current-defuns): New function. * lisp/vc/log-edit.el (log-edit-generate-changelog): New command. (log-edit-mode-map): Bind it to C-c C-a. (log-edit-fill-entry): New function. (log-edit-mode): Set it as fill-paragraph-function. (log-edit-insert-filled-defuns): (change-log-no-margin-fill-forward-paragraph): New functions. --- lisp/vc/add-log.el | 18 ++++++++ lisp/vc/diff-mode.el | 115 +++++++++++++++++++++++++++++++++++++++++++++++++-- lisp/vc/log-edit.el | 81 +++++++++++++++++++++++++++++++++++- 3 files changed, 209 insertions(+), 5 deletions(-) diff --git a/lisp/vc/add-log.el b/lisp/vc/add-log.el index f9efd44c5c..4810c0dbe9 100644 --- a/lisp/vc/add-log.el +++ b/lisp/vc/add-log.el @@ -309,6 +309,24 @@ change-log-search-file-name (re-search-forward change-log-file-names-re nil t) (match-string-no-properties 2)))))) +(defconst change-log-unindented-file-names-re "^[*] \\([^ ,:([\n]+\\)") + +(defun change-log-read-entries (&optional end) + (cl-loop while (re-search-forward change-log-unindented-file-names-re end t) + collect (cons (match-string-no-properties 1) + (change-log-read-defuns end)))) + +(defun change-log-read-defuns (&optional end) + (cl-loop while (re-search-forward change-log-tag-re end t) + nconc (split-string (match-string-no-properties 1) + ",[[:blank:]]*" t))) + +(defun change-log-insert-entries (changelogs) + (cl-loop for (file . defuns) in changelogs do + (insert "* " file " ") + (cl-loop for def in defuns + do (insert "(" def "):\n")))) + (defun change-log-find-file () "Visit the file for the change under point." (interactive) diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index 0d5dc0e1c0..229e901e0a 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 @@ diff-find-approx-text (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,105 @@ diff-undo (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) + (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) + (diff-find-source-location t)) + (new-buf nil) + (goto-newbuf + ;; If APPLIED, we have NEW-TEXT in BUF, so we + ;; need to a buffer with OLD-TEXT to follow + ;; -lines. + (lambda () + (if new-buf (set-buffer new-buf) + (set-buffer (generate-new-buffer " *diff-new-text*")) + (insert (if applied old-text new-text)) + (funcall (buffer-local-value 'major-mode buf)) + (setq new-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-newbuf gotobuf))) + (`(,+ck-eodefun ,+ck-defun) + (funcall make-defun-context-follower + (if applied gotobuf goto-newbuf)))) + (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 new-buf) + (kill-buffer new-buf))))) + (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." diff --git a/lisp/vc/log-edit.el b/lisp/vc/log-edit.el index 91e18c1ec5..736bcaf0fa 100644 --- a/lisp/vc/log-edit.el +++ b/lisp/vc/log-edit.el @@ -53,7 +53,8 @@ cvs-buffer (easy-mmode-defmap log-edit-mode-map '(("\C-c\C-c" . log-edit-done) - ("\C-c\C-a" . log-edit-insert-changelog) + ;;("\C-c\C-a" . log-edit-insert-changelog) + ("\C-c\C-a" . log-edit-generate-changelog) ("\C-c\C-d" . log-edit-show-diff) ("\C-c\C-f" . log-edit-show-files) ("\C-c\C-k" . log-edit-kill-buffer) @@ -488,10 +489,73 @@ log-edit-mode (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". + (setq-local fill-paragraph-function #'log-edit-fill-entry) (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)) +(defun log-edit-insert-filled-defuns (defuns) + (cl-loop for def in defuns do + (when (> (+ (current-column) (string-width def)) fill-column) + (insert (if (memq (char-before) '(?\n ?\s)) + "\n" ")\n"))) + (insert (if (memq (char-before) '(?\n ?\s)) + "(" ", ") + def)) + (insert "): ")) + +(defun log-edit-fill-entry (&optional _justify _region) + ;; TODO: Use arguments! + (pcase-let ((`(,beg ,entry-end) (log-edit-changelog-subparagraph))) + (if (= beg entry-end) + ;; Not a ChangeLog entry, fill as normal. + nil + (cl-callf copy-marker entry-end) + (cl-loop + do (goto-char beg) + (let ((end (if (re-search-forward "): ?\\(\\).*[^:[:blank:]\n].*$" + entry-end t) + (match-beginning 1) + entry-end))) + (goto-char beg) + (re-search-forward (concat "\\(?1:" change-log-unindented-file-names-re + " \\)\\|^\\(?1:\\)(") + entry-end) + (goto-char (setq beg (match-end 1))) + (log-edit-insert-filled-defuns + (prog1 (change-log-read-defuns end) + (delete-region beg end)))) + while (setq beg (and (re-search-forward "^(" entry-end t) + (match-beginning 0)))) + (set-marker entry-end nil) + t))) + +(defun change-log-no-margin-fill-forward-paragraph (n) + "Move N change log entries forward. +Delete redundant parens along the way." + (let ((end-marker (make-marker)) + (dir (cl-signum n))) + (cl-callf abs n) + (catch 'paragraphs-left + (dotimes (i n) + (pcase-let ((`(,beg ,end) (log-edit-changelog-subparagraph))) + (when (= end beg) + (throw 'paragraphs-left (- n i))) + (goto-char beg) + (set-marker end-marker end) + (cl-loop + do (progn (when (and (re-search-forward "):?$" (line-end-position) 'move) + (eq ?\( (char-after (1+ (match-end 0))))) + (replace-match "," t t)) + (forward-line 1)) + while (< (point) end-marker) + do (when (looking-at "^(") + (replace-match "" t t))) + (goto-char (if (< dir 0) beg (1- end-marker))) + (set-marker end-marker nil) + )) + 0))) + (defun log-edit-hide-buf (&optional buf where) (when (setq buf (get-buffer (or buf log-edit-files-buf))) ;; FIXME: Should use something like `quit-windows-on' here, but @@ -726,6 +790,21 @@ log-edit-add-field (replace-match (concat " " value) t t nil 1) (insert field ": " value "\n" (if (looking-at "\n") "" "\n")))) +(defun log-edit-generate-changelog () + (interactive) + (let* ((diff-buf nil) + ;; Unfortunately, `log-edit-show-diff' doesn't have a NO-SHOW + ;; option, so we try to work around it via display-buffer + ;; machinery. + (display-buffer-overriding-action + `(,(lambda (buf alist) + (setq diff-buf buf) + (display-buffer-no-window buf alist)) + . ((allow-no-window . t))))) + (change-log-insert-entries + (with-current-buffer (progn (log-edit-show-diff) diff-buf) + (diff-add-log-current-defuns))))) + (defun log-edit-insert-changelog (&optional use-first) "Insert a log message by looking at the ChangeLog. The idea is to write your ChangeLog entries first, and then use this -- 2.11.0 --=-=-=--