;;; xhg-log.el --- Mercurial interface for dvc: mode for hg log style output ;; Copyright (C) 2005-2008 by all contributors ;; Author: Stefan Reichoer, ;; 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