;;; diff-mode.el --- a mode for viewing/editing context diffs
-;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-;; Free Software Foundation, Inc.
+;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;; 2005, 2006 Free Software Foundation, Inc.
-;; Author: Stefan Monnier <monnier@cs.yale.edu>
+;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
;; Keywords: convenience patch diff
;; This file is part of GNU Emacs.
;; 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., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
;;; Commentary:
(eval-when-compile (require 'cl))
+(defvar add-log-buffer-file-name-function)
+
(defgroup diff-mode ()
"Major mode for viewing/editing diffs."
("\C-m" . diff-goto-source)
([mouse-2] . diff-goto-source)
;; From XEmacs' diff-mode.
- ("W" . widen)
+;; Standard M-w is useful, so don't change M-W.
+;; ("W" . widen)
;;("." . diff-goto-source) ;display-buffer
;;("f" . diff-goto-source) ;find-file
("o" . diff-goto-source) ;other-window
;;("h" . diff-show-header)
;;("j" . diff-show-difference) ;jump to Nth diff
;;("q" . diff-quit)
- (" " . scroll-up)
- ("\177" . scroll-down)
- ;; Our very own bindings.
- ("A" . diff-ediff-patch)
- ("r" . diff-restrict-view)
- ("R" . diff-reverse-direction)
- ("U" . diff-context->unified)
- ("C" . diff-unified->context)
+;; Not useful if you have to metafy them.
+;; (" " . scroll-up)
+;; ("\177" . scroll-down)
+;; Standard M-a is useful, so don't change M-A.
+;; ("A" . diff-ediff-patch)
+;; Standard M-r is useful, so don't change M-r or M-R.
+;; ("r" . diff-restrict-view)
+;; ("R" . diff-reverse-direction)
("q" . quit-window))
"Basic keymap for `diff-mode', bound to various prefix keys.")
;; From compilation-minor-mode.
("\C-c\C-c" . diff-goto-source)
;; Misc operations.
- ("\C-c\C-r" . diff-refine-hunk)
- ("\C-c\C-s" . diff-split-hunk)
("\C-c\C-a" . diff-apply-hunk)
+ ("\C-c\C-e" . diff-ediff-patch)
+ ("\C-c\C-n" . diff-restrict-view)
+ ("\C-c\C-r" . diff-reverse-direction)
+ ("\C-c\C-s" . diff-split-hunk)
("\C-c\C-t" . diff-test-hunk)
+ ("\C-c\C-u" . diff-context->unified)
+ ("\C-c\C-w" . diff-refine-hunk)
("\C-c\C-f" . next-error-follow-minor-mode))
"Keymap for `diff-mode'. See also `diff-mode-shared-map'.")
(((class color) (min-colors 88) (background dark))
:background "grey60" :weight bold)
(((class color) (background light))
- :foreground "yellow" :weight bold)
+ :foreground "green" :weight bold)
(((class color) (background dark))
:foreground "cyan" :weight bold)
(t :weight bold)) ; :height 1.3
(put 'diff-changed-face 'face-alias 'diff-changed)
(defvar diff-changed-face 'diff-changed)
+(defface diff-indicator-removed
+ '((t :inherit diff-removed))
+ "`diff-mode' face used to highlight indicator of removed lines (-, <)."
+ :group 'diff-mode
+ :version "22.1")
+(defvar diff-indicator-removed-face 'diff-indicator-removed)
+
+(defface diff-indicator-added
+ '((t :inherit diff-added))
+ "`diff-mode' face used to highlight indicator of added lines (+, >)."
+ :group 'diff-mode
+ :version "22.1")
+(defvar diff-indicator-added-face 'diff-indicator-added)
+
+(defface diff-indicator-changed
+ '((t :inherit diff-changed))
+ "`diff-mode' face used to highlight indicator of changed lines."
+ :group 'diff-mode
+ :version "22.1")
+(defvar diff-indicator-changed-face 'diff-indicator-changed)
+
(defface diff-function
- '((t :inherit diff-context))
+ '((t :inherit diff-header))
"`diff-mode' face used to highlight function names produced by \"diff -p\"."
:group 'diff-mode)
;; backward-compatibility alias
(defvar diff-function-face 'diff-function)
(defface diff-context
- '((t :inherit shadow))
+ '((((class color grayscale) (min-colors 88)) :inherit shadow))
"`diff-mode' face used to highlight context and other side-information."
:group 'diff-mode)
;; backward-compatibility alias
(defvar diff-font-lock-keywords
- `(("^\\(@@ -[0-9,]+ \\+[0-9,]+ @@\\)\\(.*\\)$" ;unified
- (1 diff-hunk-header-face)
- (2 diff-function-face))
- ("^--- .+ ----$" . diff-hunk-header-face) ;context
- ("^\\(\\*\\{15\\}\\)\\(.*\\)$" ;context
- (1 diff-hunk-header-face)
- (2 diff-function-face))
+ `(("^\\(@@ -[0-9,]+ \\+[0-9,]+ @@\\)\\(.*\\)$" ;unified
+ (1 diff-hunk-header-face) (2 diff-function-face))
+ ("^\\(\\*\\{15\\}\\)\\(.*\\)$" ;context
+ (1 diff-hunk-header-face) (2 diff-function-face))
("^\\*\\*\\* .+ \\*\\*\\*\\*". diff-hunk-header-face) ;context
+ ("^--- .+ ----$" . diff-hunk-header-face) ;context
+ ("^[0-9,]+[acd][0-9,]+$" . diff-hunk-header-face) ;normal
+ ("^---$" . diff-hunk-header-face) ;normal
("^\\(---\\|\\+\\+\\+\\|\\*\\*\\*\\) \\(\\S-+\\)\\(.*[^*-]\\)?\n"
(0 diff-header-face) (2 diff-file-header-face prepend))
- ("^[0-9,]+[acd][0-9,]+$" . diff-hunk-header-face)
- ("^!.*\n" (0 diff-changed-face))
- ("^[+>].*\n" (0 diff-added-face))
- ("^[-<].*\n" (0 diff-removed-face))
- ("^Index: \\(.+\\).*\n" (0 diff-header-face) (1 diff-index-face prepend))
+ ("^\\([-<]\\)\\(.*\n\\)"
+ (1 diff-indicator-removed-face) (2 diff-removed-face))
+ ("^\\([+>]\\)\\(.*\n\\)"
+ (1 diff-indicator-added-face) (2 diff-added-face))
+ ("^\\(!\\)\\(.*\n\\)"
+ (1 diff-indicator-changed-face) (2 diff-changed-face))
+ ("^Index: \\(.+\\).*\n"
+ (0 diff-header-face) (1 diff-index-face prepend))
("^Only in .*\n" . diff-nonexistent-face)
- ("^#.*" . font-lock-string-face)
- ("^[^-=+*!<>].*\n" (0 diff-context-face))))
+ ("^\\(#\\)\\(.*\\)"
+ (1 font-lock-comment-delimiter-face)
+ (2 font-lock-comment-face))
+ ("^[^-=+*!<>#].*\n" (0 diff-context-face))))
(defconst diff-font-lock-defaults
'(diff-font-lock-keywords t nil nil nil (font-lock-multiline . nil)))
(delete-region last-pt (point))
(setq delete nil)))))))))))))))
-(defun diff-context->unified (start end)
+(defun diff-context->unified (start end &optional to-context)
"Convert context diffs to unified diffs.
-START and END are either taken from the region (if a prefix arg is given) or
-else cover the whole bufer."
- (interactive (if current-prefix-arg
- (list (mark) (point))
- (list (point-min) (point-max))))
- (unless (markerp end) (setq end (copy-marker end)))
- (let (;;(diff-inhibit-after-change t)
- (inhibit-read-only t))
- (save-excursion
- (goto-char start)
- (while (and (re-search-forward "^\\(\\(\\*\\*\\*\\) .+\n\\(---\\) .+\\|\\*\\{15\\}.*\n\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]+\\) \\*\\*\\*\\*\\)$" nil t)
- (< (point) end))
- (combine-after-change-calls
- (if (match-beginning 2)
- ;; we matched a file header
- (progn
- ;; use reverse order to make sure the indices are kept valid
- (replace-match "+++" t t nil 3)
- (replace-match "---" t t nil 2))
- ;; we matched a hunk header
- (let ((line1s (match-string 4))
- (line1e (match-string 5))
- (pt1 (match-beginning 0)))
- (replace-match "")
- (unless (re-search-forward
- "^--- \\([0-9]+\\),\\(-?[0-9]+\\) ----$" nil t)
- (error "Can't find matching `--- n1,n2 ----' line"))
- (let ((line2s (match-string 1))
- (line2e (match-string 2))
- (pt2 (progn
- (delete-region (progn (beginning-of-line) (point))
- (progn (forward-line 1) (point)))
- (point-marker))))
- (goto-char pt1)
- (forward-line 1)
- (while (< (point) pt2)
- (case (char-after)
- ((?! ?-) (delete-char 2) (insert "-") (forward-line 1))
- (?\s ;merge with the other half of the chunk
- (let* ((endline2
- (save-excursion
- (goto-char pt2) (forward-line 1) (point)))
- (c (char-after pt2)))
- (case c
- ((?! ?+)
- (insert "+"
- (prog1 (buffer-substring (+ pt2 2) endline2)
- (delete-region pt2 endline2))))
- (?\s ;FIXME: check consistency
- (delete-region pt2 endline2)
- (delete-char 1)
- (forward-line 1))
- (?\\ (forward-line 1))
- (t (delete-char 1) (forward-line 1)))))
- (t (forward-line 1))))
- (while (looking-at "[+! ] ")
- (if (/= (char-after) ?!) (forward-char 1)
- (delete-char 1) (insert "+"))
- (delete-char 1) (forward-line 1))
- (save-excursion
+START and END are either taken from the region
+\(when it is highlighted) or else cover the whole buffer.
+With a prefix argument, convert unified format to context format."
+ (interactive (if (and transient-mark-mode mark-active)
+ (list (mark) (point) current-prefix-arg)
+ (list (point-min) (point-max) current-prefix-arg)))
+ (if to-context
+ (diff-unified->context start end)
+ (unless (markerp end) (setq end (copy-marker end)))
+ (let ( ;;(diff-inhibit-after-change t)
+ (inhibit-read-only t))
+ (save-excursion
+ (goto-char start)
+ (while (and (re-search-forward "^\\(\\(\\*\\*\\*\\) .+\n\\(---\\) .+\\|\\*\\{15\\}.*\n\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]+\\) \\*\\*\\*\\*\\)$" nil t)
+ (< (point) end))
+ (combine-after-change-calls
+ (if (match-beginning 2)
+ ;; we matched a file header
+ (progn
+ ;; use reverse order to make sure the indices are kept valid
+ (replace-match "+++" t t nil 3)
+ (replace-match "---" t t nil 2))
+ ;; we matched a hunk header
+ (let ((line1s (match-string 4))
+ (line1e (match-string 5))
+ (pt1 (match-beginning 0)))
+ (replace-match "")
+ (unless (re-search-forward
+ "^--- \\([0-9]+\\),\\(-?[0-9]+\\) ----$" nil t)
+ (error "Can't find matching `--- n1,n2 ----' line"))
+ (let ((line2s (match-string 1))
+ (line2e (match-string 2))
+ (pt2 (progn
+ (delete-region (progn (beginning-of-line) (point))
+ (progn (forward-line 1) (point)))
+ (point-marker))))
(goto-char pt1)
- (insert "@@ -" line1s ","
- (number-to-string (- (string-to-number line1e)
- (string-to-number line1s)
- -1))
- " +" line2s ","
- (number-to-string (- (string-to-number line2e)
- (string-to-number line2s)
- -1)) " @@"))))))))))
+ (forward-line 1)
+ (while (< (point) pt2)
+ (case (char-after)
+ ((?! ?-) (delete-char 2) (insert "-") (forward-line 1))
+ (?\s ;merge with the other half of the chunk
+ (let* ((endline2
+ (save-excursion
+ (goto-char pt2) (forward-line 1) (point)))
+ (c (char-after pt2)))
+ (case c
+ ((?! ?+)
+ (insert "+"
+ (prog1 (buffer-substring (+ pt2 2) endline2)
+ (delete-region pt2 endline2))))
+ (?\s ;FIXME: check consistency
+ (delete-region pt2 endline2)
+ (delete-char 1)
+ (forward-line 1))
+ (?\\ (forward-line 1))
+ (t (delete-char 1) (forward-line 1)))))
+ (t (forward-line 1))))
+ (while (looking-at "[+! ] ")
+ (if (/= (char-after) ?!) (forward-char 1)
+ (delete-char 1) (insert "+"))
+ (delete-char 1) (forward-line 1))
+ (save-excursion
+ (goto-char pt1)
+ (insert "@@ -" line1s ","
+ (number-to-string (- (string-to-number line1e)
+ (string-to-number line1s)
+ -1))
+ " +" line2s ","
+ (number-to-string (- (string-to-number line2e)
+ (string-to-number line2s)
+ -1)) " @@")))))))))))
(defun diff-reverse-direction (start end)
"Reverse the direction of the diffs.
headers for you on-the-fly.
You can also switch between context diff and unified diff with \\[diff-context->unified],
-or vice versa with \\[diff-unified->context] and you can also revert the direction of
+or vice versa with \\[diff-unified->context] and you can also reverse the direction of
a diff with \\[diff-reverse-direction]."
(set (make-local-variable 'font-lock-defaults) diff-font-lock-defaults)
(set (make-local-variable 'outline-regexp) diff-outline-regexp)