elisp-vcs/dvc/lisp/xhg-log.el
2009-10-10 08:02:43 +02:00

238 lines
8.9 KiB
EmacsLisp

;;; xhg-log.el --- Mercurial interface for dvc: mode for hg log style output
;; Copyright (C) 2005-2008 by all contributors
;; Author: Stefan Reichoer, <stefan@xsteve.at>
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; The mercurial interface for dvc: a mode to handle xhg log style output
;;; Commands:
;;
;; Below is a complete command list:
;;
;; `xhg-log-mode'
;; Major mode to display hg log output with embedded diffs. Derives from `diff-mode'.
;; `xhg-log-next'
;; Move to the next changeset header of the next diff hunk
;; `xhg-log-previous'
;; Move to the previous changeset header of the previous diff hunk
;; `xhg-log-dwim-next'
;; Either move to the next changeset via `xhg-log-next' or call `scroll-up'.
;; `xhg-log-toggle-diff-for-changeset'
;; Toggle displaying the diff for the current changeset.
;; `xhg-log-review-next-diff'
;; Close the previous viewed inline diff and open the next one for reviewing.
;; `xhg-log-review-previous-diff'
;; Close the previous viewed inline diff and open the previous one for reviewing.
;;
;;; History:
;;
;;; Code:
(require 'diff-mode)
(defvar xhg-log-mode-map
(let ((map (copy-keymap diff-mode-shared-map)))
(define-key map dvc-keyvec-help 'describe-mode)
(define-key map [?g] 'xhg-log)
(define-key map [?R] 'xhg-rollback)
(define-key map [?T] 'xhg-log-toggle-verbose)
(define-key map [?G] 'xhg-search-regexp-in-log)
(define-key map [?h] 'dvc-buffer-pop-to-partner-buffer)
(define-key map [?e] 'xhg-export)
(define-key map [?E] 'xhg-export-via-mail)
(define-key map [?s] 'xhg-status)
(define-key map [?=] 'xhg-log-toggle-diff-for-changeset)
(define-key map [?v] 'xhg-log-review-next-diff)
(define-key map [?V] 'xhg-log-review-previous-diff)
(define-key map dvc-keyvec-next 'xhg-log-next)
(define-key map dvc-keyvec-previous 'xhg-log-previous)
(define-key map [?\ ] 'xhg-log-dwim-next)
(define-key map dvc-keyvec-quit 'dvc-buffer-quit)
;; the merge group
(define-key map (dvc-prefix-merge ?u) 'dvc-update)
(define-key map (dvc-prefix-merge ?f) 'dvc-pull) ;; hint: fetch, p is reserved for push
(define-key map (dvc-prefix-merge ?m) '(lambda () (interactive) (dvc-missing nil default-directory)))
map)
"Keymap used in `xhg-log-mode'.")
(easy-menu-define xhg-log-mode-menu xhg-log-mode-map
"`xhg-log-mode' menu"
`("hg-log"
["Show status" dvc-status t] ;; `xhg-status' is not defined at compile time.
["Toggle embedded diff" xhg-log-toggle-diff-for-changeset t]
["Start Commiting" dvc-log-edit t]
["Export Changeset" xhg-export t]
["Export Changeset via Email" xhg-export-via-mail t]
))
(defvar xhg-log-font-lock-keywords
(append
'(("^changeset:" . font-lock-function-name-face)
("^branch:" . font-lock-function-name-face)
("^tag:" . font-lock-function-name-face)
("^user:" . font-lock-function-name-face)
("^date:" . font-lock-function-name-face)
("^summary:" . font-lock-function-name-face)
("^parent:" . font-lock-function-name-face))
diff-font-lock-keywords)
"Keywords in `xhg-log-mode' mode.")
(defvar xhg-log-review-current-diff-revision nil)
(defvar xhg-log-review-recenter-position-on-next-diff 5)
(define-derived-mode xhg-log-mode fundamental-mode "xhg-log"
"Major mode to display hg log output with embedded diffs. Derives from `diff-mode'.
Commands:
\\{xhg-log-mode-map}
"
(let ((diff-mode-shared-map (copy-keymap xhg-log-mode-map))
major-mode mode-name)
(diff-mode))
(set (make-local-variable 'font-lock-defaults)
(list 'xhg-log-font-lock-keywords t nil nil))
(set (make-local-variable 'xhg-log-review-current-diff-revision) nil))
(defconst xhg-log-start-regexp "^ *changeset: +\\([0-9]+:[0-9a-f]+\\)")
(defun xhg-log-next (n)
"Move to the next changeset header of the next diff hunk"
(interactive "p")
(end-of-line)
(re-search-forward xhg-log-start-regexp nil t n)
(beginning-of-line)
(when xhg-log-review-recenter-position-on-next-diff
(recenter xhg-log-review-recenter-position-on-next-diff)))
;; TODO: add (diff-hunk-next)
(defun xhg-log-previous (n)
"Move to the previous changeset header of the previous diff hunk"
(interactive "p")
(end-of-line)
(when (save-excursion
(beginning-of-line)
(looking-at xhg-log-start-regexp))
(re-search-backward xhg-log-start-regexp))
(re-search-backward xhg-log-start-regexp nil t n)
(when xhg-log-review-recenter-position-on-next-diff
(recenter xhg-log-review-recenter-position-on-next-diff)))
;; TODO: add (diff-hunk-prev)
(defun xhg-log-dwim-next ()
"Either move to the next changeset via `xhg-log-next' or call `scroll-up'.
When the beginning of the next changeset is already visible, call `xhg-log-next',
otherwise call `scroll-up'."
(interactive)
(let* ((start-pos (point))
(window-line (count-lines (window-start) start-pos))
(window-height (dvc-window-body-height))
(distance-to-next-changeset (save-window-excursion (xhg-log-next 1) (count-lines start-pos (point)))))
(goto-char start-pos)
(when (eq distance-to-next-changeset 0) ; last changeset
(setq distance-to-next-changeset (count-lines start-pos (point-max))))
(if (< (- window-height window-line) distance-to-next-changeset)
(scroll-up)
(xhg-log-next 1))))
(defun xhg-log-revision-at-point ()
(save-excursion
(end-of-line)
(re-search-backward xhg-log-start-regexp)
(match-string-no-properties 1)))
(defun xhg-log-inline-diff-opened-here ()
(save-excursion
(end-of-line)
(re-search-backward xhg-log-start-regexp)
(re-search-forward "^$")
(forward-line 1)
(looking-at "diff")))
(defun xhg-log-toggle-diff-for-changeset ()
"Toggle displaying the diff for the current changeset."
(interactive)
(let ((rev (xhg-log-revision-at-point))
(insert-diff))
(dvc-trace "xhg-log-toggle-diff-for-changeset %s" rev)
(save-excursion
(end-of-line)
(re-search-backward xhg-log-start-regexp)
(re-search-forward "^$")
(forward-line 1)
(setq insert-diff (not (looking-at "diff")))
(let ((buffer-read-only nil))
(save-excursion
(if insert-diff
(progn (save-excursion
(insert
(dvc-run-dvc-sync 'xhg (list "log" "-r" rev "-p")
:finished 'dvc-output-buffer-handler)))
(delete-region (point) (- (re-search-forward "^diff") 4)))
(delete-region (point)
(or (and (re-search-forward xhg-log-start-regexp nil t) (line-beginning-position))
(goto-char (point-max))))))))))
(defun xhg-log-goto-revision (rev)
"Move point to the revision REV. If REV is not found in the log buffer, do nothing."
(let ((rev-pos))
(save-excursion
(when
(re-search-forward (concat "^changeset: +" rev) nil t)
(setq rev-pos (point))))
(when rev-pos
(goto-char rev-pos))))
(defun xhg-log-review-next-diff (n)
"Close the previous viewed inline diff and open the next one for reviewing.
When invoked the first time, just open the diff at point via `xhg-log-toggle-diff-for-changeset'.
For every further invocation close the previously opened diff and open the next one.
N is the number of revisions to skip. Per default advance 1 revision."
(interactive "p")
(when (and (numberp n) (< n 0))
(setq n (- n 1)))
(let ((cur-pos (point)))
(when xhg-log-review-current-diff-revision
;; close the previous diff
(xhg-log-goto-revision xhg-log-review-current-diff-revision)
(when (xhg-log-inline-diff-opened-here)
(xhg-log-toggle-diff-for-changeset))
(if (eq n 0)
(goto-char cur-pos)
(xhg-log-next n)))
(setq xhg-log-review-current-diff-revision (xhg-log-revision-at-point))
(unless (xhg-log-inline-diff-opened-here)
(xhg-log-toggle-diff-for-changeset))
(when xhg-log-review-recenter-position-on-next-diff
(recenter xhg-log-review-recenter-position-on-next-diff))))
(defun xhg-log-review-previous-diff (n)
"Close the previous viewed inline diff and open the previous one for reviewing.
See `xhg-log-review-next-diff' for details."
(interactive "p")
(xhg-log-review-next-diff (- n)))
(provide 'xhg-log)
;;; xhg-log.el ends here