emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [patch] add interactive browse of revisions from vc *Annotate* buffe


From: Benjamin Rutt
Subject: Re: [patch] add interactive browse of revisions from vc *Annotate* buffers
Date: Mon, 19 Jan 2004 16:13:56 -0500
User-agent: Gnus/5.1003 (Gnus v5.10.3) Emacs/21.3.50 (usg-unix-v)

Thanks for the feedback, I've implemented your changes (I think :).

Andre Spiegel <address@hidden> writes:

> A few minor points: We already have a backend-specific function
> vc-BACKEND-previous-version, which currently has a single default
> implementation, vc-default-previous-version in vc.el.  It handles
> RCS/CVS revision numbers (it's used to provide the defaults for C-u C-x
> v =).
>
> Could you please take a look at that function and resolve the redundancy
> with vc-cvs-increment/decrement-revision in your code?  The reconciled
> implementation should combine the behaviour of both previous functions. 
> Please also put these functions into vc.el as defaults for all backends,
> because the RCS-style revision numbers are still common for most
> backends.

Done, I've eliminated my function vc-cvs-increment/decrement-revision,
and added a vc-default-next-version.

> I'm a bit puzzled by your function vc-current-line.  I must admit that
> I'm totally clueless in this area, but is there really no simpler way to
> determine this?  Surely, this problem must have occured elsewhere
> already.  Anybody got a hint for us?

If someone could patch what-line in the manner I've suggested (just
now) on this thread, I'll definitely eliminate my function
vc-current-line; it does seem redundant.

> Last point: The doc strings in your functions do not comply with the
> Emacs guidelines yet.  Every function must have a doc string, and the
> first line must be a complete sentence of its own (in the imperative
> voice).  Please see the corresponding sections in the Elisp manual.

Done, I think.  Let me know if there are still any problems.

I've also added docs to etc/NEWS, man/emacs.texi and lisp/ChangeLog.
In addition, the function vc-annotate-workfile-version is new with
this patch, useful when you've used 'P' to go back in time and want to
go back to the latest version.

Let me know of any feedback you may have.  Here begins the patch (this
time, I used 'cvs diff' to do the diffs):

Index: etc/NEWS
===================================================================
RCS file: /cvsroot/emacs/emacs/etc/NEWS,v
retrieving revision 1.891
diff -u -r1.891 NEWS
--- etc/NEWS    18 Jan 2004 20:26:39 -0000      1.891
+++ etc/NEWS    19 Jan 2004 20:57:40 -0000
@@ -1549,6 +1549,20 @@
 Meta and Alt:
     (setq x-alt-keysym 'meta)
     (setq x-meta-keysym 'alt)
+
+** vc-annotate-mode enhancements
+
+In vc-annotate mode, you can now use the following key bindings for
+enhanced functionality to browse the annotations of past revisions, or
+to view diffs or log entries directly from vc-annotate-mode:
+
+    P:  annotates the previous revision
+    N:  annotates the next revision
+    J:  annotates the revision at line
+    A:  annotates the revision previous to line
+    D:  shows the diff of the revision at line with its previous revision
+    L:  shows the log of the revision at line
+    W:  annotates the workfile (most up to date) version
 
 * New modes and packages in 21.4
 
Index: lisp/ChangeLog
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/ChangeLog,v
retrieving revision 1.5605
diff -u -r1.5605 ChangeLog
--- lisp/ChangeLog      18 Jan 2004 14:11:11 -0000      1.5605
+++ lisp/ChangeLog      19 Jan 2004 20:57:40 -0000
@@ -1,3 +1,25 @@
+2004-01-19  Benjamin Rutt  <address@hidden>
+
+       * vc.el (vc-default-previous-version): Doc enhancement.
+       (vc-default-next-version): New function.
+       (vc-print-log): New arg FOCUS-REV.
+       (vc-annotate-mode):  Derives from view-mode now.
+       (vc-annotate):  New args REVISION, DISPLAY-MODE.
+       (vc-annotate-prev-version): New function.
+       (vc-annotate-prev-version): New function.
+       (vc-annotate-next-version): New function.
+       (vc-annotate-workfile-version): New function.
+       (vc-annotate-extract-revision-at-line): New function.
+       (vc-annotate-revision-at-line): New function.
+       (vc-annotate-revision-previous-to-line): New function.
+       (vc-annotate-show-log-revision-at-line): New function.
+       (vc-annotate-show-diff-revision-at-line): New function.
+       (vc-current-line): New function.
+       (vc-annotate-warp-version): New function.
+        
+       * vc-cvs.el (vc-cvs-annotate-extract-revision-at-line): New
+       function.
+        
 2004-01-18  Jesper Harder  <address@hidden>
 
        * mail/smtpmail.el (smtpmail-send-data): Don't append spurious
Index: lisp/vc-cvs.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/vc-cvs.el,v
retrieving revision 1.66
diff -u -r1.66 vc-cvs.el
--- lisp/vc-cvs.el      1 Oct 2003 13:22:53 -0000       1.66
+++ lisp/vc-cvs.el      19 Jan 2004 20:57:41 -0000
@@ -625,6 +625,14 @@
          (beginning-of-line nil)
            (vc-cvs-annotate-time))))))
 
+(defun vc-cvs-annotate-extract-revision-at-line ()
+  (save-excursion
+    (beginning-of-line)
+    (if (re-search-forward "^\\([0-9]+\\.[0-9]+\\(\\.[0-9]+\\)*\\) +("
+                          (line-end-position) t)
+       (match-string-no-properties 1)
+      nil)))
+
 ;;;
 ;;; Snapshot system
 ;;;
Index: lisp/vc.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/vc.el,v
retrieving revision 1.361
diff -u -r1.361 vc.el
--- lisp/vc.el  24 Dec 2003 23:18:10 -0000      1.361
+++ lisp/vc.el  19 Jan 2004 20:57:41 -0000
@@ -347,6 +347,13 @@
 ;;   time with hours, minutes, and seconds included.  Probably safe to
 ;;   ignore.  Return the current-time, in units of fractional days.
 ;;
+;; - annotate-extract-revision-at-line ()
+;;
+;;   Only required if `annotate-command' is defined for the backend.
+;;   Invoked from a buffer in vc-annotate-mode, return the revision
+;;   corresponding to the current line, or nil if there is no revision
+;;   corresponding to the current line.
+;;
 ;; SNAPSHOT SYSTEM
 ;;
 ;; - create-snapshot (dir name branchp)
@@ -392,7 +399,13 @@
 ;;
 ;; - previous-version (file rev)
 ;;
-;;   Return the version number that precedes REV for FILE.
+;;   Return the version number that precedes REV for FILE, or nil if no such
+;;   version exists.
+;;
+;; - next-version (file rev)
+;;
+;;   Return the version number that follows REV for FILE, or nil if no such
+;;   version exists.
 ;;
 ;; - check-headers ()
 ;;
@@ -631,6 +644,14 @@
     m)
   "Local keymap used for VC-Annotate mode.")
 
+(define-key vc-annotate-mode-map "A" 'vc-annotate-revision-previous-to-line)
+(define-key vc-annotate-mode-map "D" 'vc-annotate-show-diff-revision-at-line)
+(define-key vc-annotate-mode-map "J" 'vc-annotate-revision-at-line)
+(define-key vc-annotate-mode-map "L" 'vc-annotate-show-log-revision-at-line)
+(define-key vc-annotate-mode-map "N" 'vc-annotate-next-version)
+(define-key vc-annotate-mode-map "P" 'vc-annotate-prev-version)
+(define-key vc-annotate-mode-map "W" 'vc-annotate-workfile-version)
+
 (defvar vc-annotate-mode-menu nil
   "Local keymap used for VC-Annotate mode's menu bar menu.")
 
@@ -714,9 +735,10 @@
   (substring rev (match-beginning 0) (match-end 0)))
 
 (defun vc-default-previous-version (backend file rev)
-  "Guess the version number immediately preceding REV for FILE.
-This default implementation works for <major>.<minor>-style version numbers
-as used by RCS and CVS."
+  "Return the version number immediately preceding REV for FILE,
+or nil if there is no previous version.  This default
+implementation works for <major>.<minor>-style version numbers as
+used by RCS and CVS."
   (let ((branch (vc-branch-part rev))
         (minor-num (string-to-number (vc-minor-part rev))))
     (when branch
@@ -731,6 +753,16 @@
           ;; return version of starting point
           (vc-branch-part branch))))))
 
+(defun vc-default-next-version (backend file rev)
+  "Return the version number immediately following REV for FILE,
+or nil if there is no next version.  This default implementation
+works for <major>.<minor>-style version numbers as used by RCS
+and CVS."
+  (when (not (string= rev (vc-workfile-version file)))
+    (let ((branch (vc-branch-part rev))
+         (minor-num (string-to-number (vc-minor-part rev))))
+      (concat branch "." (number-to-string (1+ minor-num))))))
+
 ;; File property caching
 
 (defun vc-clear-context ()
@@ -2285,11 +2317,13 @@
 ;; Miscellaneous other entry points
 
 ;;;###autoload
-(defun vc-print-log ()
-  "List the change log of the current buffer in a window."
+(defun vc-print-log (&optional focus-rev)
+  "List the change log of the current buffer in a window.  If
+FOCUS-REV is non-nil, leave the point at that revision."
   (interactive)
   (vc-ensure-vc-buffer)
   (let ((file buffer-file-name))
+    (or focus-rev (setq focus-rev (vc-workfile-version file)))
     (vc-call print-log file)
     (set-buffer "*vc*")
     (pop-to-buffer (current-buffer))
@@ -2307,7 +2341,7 @@
        ;; move point to the log entry for the current version
        (vc-call-backend ',(vc-backend file)
                         'show-log-entry
-                        ',(vc-workfile-version file))
+                        ',focus-rev)
         (set-buffer-modified-p nil)))))
 
 (defun vc-default-show-log-entry (backend rev)
@@ -2778,6 +2812,14 @@
 (defvar vc-annotate-ratio nil "Global variable.")
 (defvar vc-annotate-backend nil "Global variable.")
 
+;; internal buffer-local variables
+(defvar vc-annotate-parent-file nil)
+(defvar vc-annotate-parent-rev nil)
+(defvar vc-annotate-parent-display-mode nil)
+(make-local-variable 'vc-annotate-parent-file)
+(make-local-variable 'vc-annotate-parent-rev)
+(make-local-variable 'vc-annotate-parent-display-mode)
+
 (defconst vc-annotate-font-lock-keywords
   ;; The fontification is done by vc-annotate-lines instead of font-lock.
   '((vc-annotate-lines)))
@@ -2788,7 +2830,7 @@
 `vc-annotate-buffers'."
   (cdr (assoc buffer vc-annotate-buffers)))
 
-(define-derived-mode vc-annotate-mode fundamental-mode "Annotate"
+(define-derived-mode vc-annotate-mode view-mode "Annotate"
   "Major mode for output buffers of the `vc-annotate' command.
 
 You can use the mode-specific menu to alter the time-span of the used
@@ -2885,7 +2927,23 @@
                    (unless (eq vc-annotate-display-mode 'fullscale)
                      (vc-annotate-display-select nil 'fullscale))
                    :style toggle :selected
-                   (eq vc-annotate-display-mode 'fullscale)])))
+                   (eq vc-annotate-display-mode 'fullscale)])
+                 (list "--")
+                 (list ["Annotate previous revision"
+                        (call-interactively 'vc-annotate-prev-version)])
+                 (list ["Annotate next revision"
+                        (call-interactively 'vc-annotate-next-version)])
+                 (list ["Annotate revision at line"
+                        (vc-annotate-revision-at-line)])
+                 (list ["Annotate revision previous to line"
+                        (vc-annotate-revision-previous-to-line)])
+                 (list ["Annotate latest revision"
+                        (vc-annotate-workfile-version)])
+                 (list ["Show log of revision at line"
+                        (vc-annotate-show-log-revision-at-line)])
+                 (list ["Show diff of revision at line"
+                        (vc-annotate-show-diff-revision-at-line)])))
+
     ;; Define the menu
     (if (or (featurep 'easymenu) (load "easymenu" t))
        (easy-menu-define vc-annotate-mode-menu vc-annotate-mode-map
@@ -2922,7 +2980,7 @@
 ;;;;  the contents in BUFFER.
 
 ;;;###autoload
-(defun vc-annotate (prefix)
+(defun vc-annotate (prefix &optional revision display-mode)
   "Display the edit history of the current file using colours.
 
 This command creates a buffer that shows, for each line of the current
@@ -2949,19 +3007,24 @@
 colors. `vc-annotate-background' specifies the background color."
   (interactive "P")
   (vc-ensure-vc-buffer)
-  (let* ((temp-buffer-name (concat "*Annotate " (buffer-name) "*"))
+  (let* ((temp-buffer-name nil)
          (temp-buffer-show-function 'vc-annotate-display-select)
-         (rev (vc-workfile-version buffer-file-name))
+        (rev (or revision (vc-workfile-version buffer-file-name)))
+        (bfn buffer-file-name)
          (vc-annotate-version
-          (if prefix (read-string
-                      (format "Annotate from version: (default %s) " rev)
-                      nil nil rev)
-            rev)))
-    (if prefix
-        (setq vc-annotate-display-mode
-              (float (string-to-number
-                      (read-string "Annotate span days: (default 20) "
-                                   nil nil "20")))))
+         (if prefix (read-string
+                     (format "Annotate from version: (default %s) " rev)
+                     nil nil rev)
+           rev)))
+    (if display-mode
+       (setq vc-annotate-display-mode display-mode)
+      (if prefix
+         (setq vc-annotate-display-mode
+               (float (string-to-number
+                       (read-string "Annotate span days: (default 20) "
+                                    nil nil "20"))))))
+    (setq temp-buffer-name (format "*Annotate %s (rev %s)*"
+                                  (buffer-name) vc-annotate-version))
     (setq vc-annotate-backend (vc-backend buffer-file-name))
     (message "Annotating...")
     (if (not (vc-find-backend-function vc-annotate-backend 'annotate-command))
@@ -2972,12 +3035,162 @@
                       buffer-file-name
                       (get-buffer temp-buffer-name)
                        vc-annotate-version))
+    (save-excursion
+      (set-buffer temp-buffer-name)
+      (setq vc-annotate-parent-file bfn)
+      (setq vc-annotate-parent-rev vc-annotate-version)
+      (setq vc-annotate-parent-display-mode vc-annotate-display-mode))
+          
     ;; Don't use the temp-buffer-name until the buffer is created
     ;; (only after `with-output-to-temp-buffer'.)
     (setq vc-annotate-buffers
          (append vc-annotate-buffers
                  (list (cons (get-buffer temp-buffer-name) 
vc-annotate-backend))))
   (message "Annotating... done")))
+
+(defun vc-annotate-prev-version (prefix)
+  "Visit the annotation of the version previous to this one.
+
+With a numeric prefix argument, annotate the version that many
+versions previous."
+  (interactive "p")
+  (vc-annotate-warp-version (- 0 prefix)))
+
+(defun vc-annotate-next-version (prefix)
+  "Visit the annotation of the version after this one.
+
+With a numeric prefix argument, annotate the version that many
+versions after."
+  (interactive "p")
+  (vc-annotate-warp-version prefix))
+
+(defun vc-annotate-workfile-version ()
+  "Visit the annotation of the workfile version of this file."
+  (interactive)
+  (if (not (equal major-mode 'vc-annotate-mode))
+      (message "Cannot be invoked outside of a vc annotate buffer")
+    (let ((warp-rev (vc-workfile-version vc-annotate-parent-file)))
+      (if (equal warp-rev vc-annotate-parent-rev)
+         (message "Already at version %s" warp-rev)
+       (vc-annotate-warp-version warp-rev)))))
+
+(defun vc-annotate-extract-revision-at-line ()
+  "Extract the revision number of the current line."
+  ;; This function must be invoked from a buffer in vc-annotate-mode
+  (save-window-excursion
+    (vc-ensure-vc-buffer)
+    (setq vc-annotate-backend (vc-backend buffer-file-name)))
+  (vc-call-backend vc-annotate-backend 'annotate-extract-revision-at-line))
+
+(defun vc-annotate-revision-at-line ()
+  "Visit the annotation of the version identified in the current line."
+  (interactive)
+  (if (not (equal major-mode 'vc-annotate-mode))
+      (message "Cannot be invoked outside of a vc annotate buffer")
+    (let ((rev-at-line (vc-annotate-extract-revision-at-line)))
+      (if (not rev-at-line)
+         (message "Cannot extract revision number from the current line")
+       (if (equal rev-at-line vc-annotate-parent-rev)
+           (message "Already at version %s" rev-at-line)
+         (vc-annotate-warp-version rev-at-line))))))
+
+(defun vc-annotate-revision-previous-to-line ()
+  "Visit the annotation of the version before the version at line."  
+  (interactive)
+  (if (not (equal major-mode 'vc-annotate-mode))
+      (message "Cannot be invoked outside of a vc annotate buffer")
+    (let ((rev-at-line (vc-annotate-extract-revision-at-line))
+         (prev-rev nil))
+      (if (not rev-at-line)
+         (message "Cannot extract revision number from the current line")
+       (setq prev-rev
+             (vc-call previous-version vc-annotate-parent-file rev-at-line))
+       (vc-annotate-warp-version prev-rev)))))
+
+(defun vc-annotate-show-log-revision-at-line ()
+  "Visit the log of the version at line."
+  (interactive)
+  (if (not (equal major-mode 'vc-annotate-mode))
+      (message "Cannot be invoked outside of a vc annotate buffer")
+    (let ((rev-at-line (vc-annotate-extract-revision-at-line)))
+      (if (not rev-at-line)
+         (message "Cannot extract revision number from the current line")
+       (vc-print-log rev-at-line)))))
+
+(defun vc-annotate-show-diff-revision-at-line ()
+  "Visit the diff of the version at line from its previous version."
+  (interactive)
+  (if (not (equal major-mode 'vc-annotate-mode))
+      (message "Cannot be invoked outside of a vc annotate buffer")
+    (let ((rev-at-line (vc-annotate-extract-revision-at-line))
+         (prev-rev nil))
+      (if (not rev-at-line)
+         (message "Cannot extract revision number from the current line")
+       (setq prev-rev
+             (vc-call previous-version vc-annotate-parent-file rev-at-line))
+       (if (not prev-rev)
+           (message "Cannot diff from any version prior to %s" rev-at-line)
+         (save-window-excursion
+           (vc-version-diff vc-annotate-parent-file prev-rev rev-at-line))
+         (switch-to-buffer "*vc-diff*"))))))
+
+(defun vc-current-line ()
+  "Return the current buffer's line number."
+  (let ((oldpoint (point)) start)
+    (save-excursion
+      (save-restriction
+       (goto-char (point-min))
+       (widen)
+       (forward-line 0)
+       (setq start (point))
+       (goto-char oldpoint)
+       (forward-line 0)
+       (1+ (count-lines (point-min) (point)))))))
+
+(defun vc-annotate-warp-version (revspec)
+  "Annotate the version described by REVSPEC.
+
+If REVSPEC is a positive integer, warp that many versions
+forward, if possible, otherwise echo a warning message.  If
+REVSPEC is a negative integer, warp that many versions backward,
+if possible, otherwise echo a warning message.  If REVSPEC is a
+string, then it describes a revision number, so warp to that
+revision."
+  (if (not (equal major-mode 'vc-annotate-mode))
+      (message "Cannot be invoked outside of a vc annotate buffer")
+    (let* ((oldline (vc-current-line))
+          (revspeccopy revspec)
+          (newrev nil))
+      (cond
+       ((and (integerp revspec) (> revspec 0))
+       (setq newrev vc-annotate-parent-rev)
+       (while (and (> revspec 0) newrev)
+              (setq newrev (vc-call next-version
+                                    vc-annotate-parent-file newrev))
+              (setq revspec (1- revspec)))
+       (if (not newrev)
+           (message "Cannot increment %d versions from version %s"
+                    revspeccopy vc-annotate-parent-rev)))
+       ((and (integerp revspec) (< revspec 0))
+       (setq newrev vc-annotate-parent-rev)
+       (while (and (< revspec 0) newrev)
+              (setq newrev (vc-call previous-version
+                                    vc-annotate-parent-file newrev))
+              (setq revspec (1+ revspec)))
+       (if (not newrev)
+           (message "Cannot decrement %d versions from version %s"
+                    (- 0 revspeccopy) vc-annotate-parent-rev)))
+       ((stringp revspec) (setq newrev revspec))
+       (t (error "Invalid argument to vc-annotate-warp-version")))
+      (when newrev
+       (save-window-excursion
+         (find-file vc-annotate-parent-file)
+         (vc-annotate nil newrev vc-annotate-parent-display-mode))
+       (kill-buffer (current-buffer)) ;; kill the buffer we started from
+       (switch-to-buffer (car (car (last vc-annotate-buffers))))
+       (goto-line (min oldline (progn (goto-char (point-max))
+                                      (previous-line)
+                                      (vc-current-line))))))))
 
 (defun vc-annotate-car-last-cons (a-list)
   "Return car of last cons in association list A-LIST."
Index: man/files.texi
===================================================================
RCS file: /cvsroot/emacs/emacs/man/files.texi,v
retrieving revision 1.86
diff -u -r1.86 files.texi
--- man/files.texi      3 Dec 2003 21:04:49 -0000       1.86
+++ man/files.texi      19 Jan 2004 20:57:42 -0000
@@ -1536,6 +1536,46 @@
 stretch factor greater than 1 means the color range spans more than a
 year.
 
+From the annotate buffer, you can use the following keys to browse the
+annotations of past revisions, view diffs, or view log entries:
+
address@hidden @bullet
+
address@hidden
+Pressing @kbd{P} annotates the previous revision.  It also takes a
+numeric prefix argument, so for example @kbd{C-u 10 P} would take you
+back 10 revisions.
+
address@hidden
+Pressing @kbd{N} annotates the next revision.  It also takes a numeric
+prefix argument, so for example @kbd{C-u 10 N} would take you forward
+10 revisions.
+
address@hidden
+Pressing @kbd{J} annotates the revision at line (as denoted by the
+version number on the same line).
+
address@hidden
+Pressing @kbd{A} annotates the revision previous to line (as denoted
+by the version number on the same line).  This is useful to see the
+state the file was in before the change on the current line was made.
+
address@hidden
+Pressing @kbd{D} shows the diff of the revision at line with its
+previous revision.  This is useful to see what actually changed when
+the revision denoted on the current line was committed.
+
address@hidden
+Pressing @kbd{L} shows the log of the revision at line.  This is
+useful to see the author's description of the changes that occured
+when the revision denoted on the current line was committed.
+
address@hidden
+Pressing @kbd{W} annotates the workfile (most up to date) version.  If
+you used @kbd{P} and @kbd{N} to browse to other revisions, use this
+key to return to the latest version.
address@hidden itemize
+
 @node Secondary VC Commands
 @subsection The Secondary Commands of VC




reply via email to

[Prev in Thread] Current Thread [Next in Thread]