;;; diff-mode.el --- a mode for viewing/editing context diffs
;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;; 2005, 2006, 2007 Free Software Foundation, Inc.
+;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
;; Keywords: convenience patch diff
;; This file is part of GNU Emacs.
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs 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 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; 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.
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Todo:
+;; - Improve `diff-add-change-log-entries-other-window',
+;; it is very simplistic now.
+;;
;; - Add a `delete-after-apply' so C-c C-a automatically deletes hunks.
;; Also allow C-c C-a to delete already-applied hunks.
;;
`(("\e" . ,diff-mode-shared-map)
;; From compilation-minor-mode.
("\C-c\C-c" . diff-goto-source)
+ ;; By analogy with the global C-x 4 a binding.
+ ("\C-x4A" . diff-add-change-log-entries-other-window)
;; Misc operations.
("\C-c\C-a" . diff-apply-hunk)
("\C-c\C-e" . diff-ediff-patch)
(easy-menu-define diff-mode-menu diff-mode-map
"Menu for `diff-mode'."
'("Diff"
- ["Jump to Source" diff-goto-source t]
- ["Apply hunk" diff-apply-hunk t]
- ["Test applying hunk" diff-test-hunk t]
- ["Apply diff with Ediff" diff-ediff-patch t]
+ ["Jump to Source" diff-goto-source
+ :help "Jump to the corresponding source line"]
+ ["Apply hunk" diff-apply-hunk
+ :help "Apply the current hunk to the source file and go to the next"]
+ ["Test applying hunk" diff-test-hunk
+ :help "See whether it's possible to apply the current hunk"]
+ ["Apply diff with Ediff" diff-ediff-patch
+ :help "Call `ediff-patch-file' on the current buffer"]
+ ["Create Change Log entries" diff-add-change-log-entries-other-window
+ :help "Create ChangeLog entries for the changes in the diff buffer"]
"-----"
- ["Reverse direction" diff-reverse-direction t]
- ["Context -> Unified" diff-context->unified t]
- ["Unified -> Context" diff-unified->context t]
+ ["Reverse direction" diff-reverse-direction
+ :help "Reverse the direction of the diffs"]
+ ["Context -> Unified" diff-context->unified
+ :help "Convert context diffs to unified diffs"]
+ ["Unified -> Context" diff-unified->context
+ :help "Convert unified diffs to context diffs"]
;;["Fixup Headers" diff-fixup-modifs (not buffer-read-only)]
"-----"
- ["Split hunk" diff-split-hunk (diff-splittable-p)]
- ["Ignore whitespace changes" diff-ignore-whitespace-hunk t]
- ["Highlight fine changes" diff-refine-hunk t]
- ["Kill current hunk" diff-hunk-kill t]
- ["Kill current file's hunks" diff-file-kill t]
+ ["Split hunk" diff-split-hunk
+ :active (diff-splittable-p)
+ :help "Split the current (unified diff) hunk at point into two hunks"]
+ ["Ignore whitespace changes" diff-ignore-whitespace-hunk
+ :help "Re-diff the current hunk, ignoring whitespace differences"]
+ ["Highlight fine changes" diff-refine-hunk
+ :help "Highlight changes of hunk at point at a finer granularity"]
+ ["Kill current hunk" diff-hunk-kill
+ :help "Kill current hunk"]
+ ["Kill current file's hunks" diff-file-kill
+ :help "Kill all current file's hunks"]
"-----"
- ["Previous Hunk" diff-hunk-prev t]
- ["Next Hunk" diff-hunk-next t]
- ["Previous File" diff-file-prev t]
- ["Next File" diff-file-next t]
+ ["Previous Hunk" diff-hunk-prev
+ :help "Go to the previous count'th hunk"]
+ ["Next Hunk" diff-hunk-next
+ :help "Go to the next count'th hunk"]
+ ["Previous File" diff-file-prev
+ :help "Go to the previous count'th file"]
+ ["Next File" diff-file-next
+ :help "Go to the next count'th file"]
))
(defcustom diff-minor-mode-prefix "\C-c="
(defface diff-header
'((((class color) (min-colors 88) (background light))
- :background "grey85")
+ :background "grey80")
(((class color) (min-colors 88) (background dark))
:background "grey45")
(((class color) (background light))
(while (re-search-backward re start t)
(replace-match "" t t)))))))
+(defconst diff-hunk-header-re-unified
+ "^@@ -\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? \\+\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? @@")
+(defconst diff-context-mid-hunk-header-re
+ "--- \\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? ----$")
(defvar diff-font-lock-keywords
- `(("^\\(@@ -[0-9,]+ \\+[0-9,]+ @@\\)\\(.*\\)$" ;unified
- (1 diff-hunk-header-face) (2 diff-function-face))
+ `((,(concat "\\(" diff-hunk-header-re-unified "\\)\\(.*\\)$")
+ (1 diff-hunk-header-face) (6 diff-function-face))
("^\\(\\*\\{15\\}\\)\\(.*\\)$" ;context
(1 diff-hunk-header-face) (2 diff-function-face))
("^\\*\\*\\* .+ \\*\\*\\*\\*". diff-hunk-header-face) ;context
- ("^--- .+ ----$" . diff-hunk-header-face) ;context
+ (,diff-context-mid-hunk-header-re . diff-hunk-header-face) ;context
("^[0-9,]+[acd][0-9,]+$" . diff-hunk-header-face) ;normal
("^---$" . diff-hunk-header-face) ;normal
;; For file headers, accept files with spaces, but be careful to rule
;;;; Movement
;;;;
-(defconst diff-hunk-header-re "^\\(@@ -[0-9,]+ \\+[0-9,]+ @@.*\\|\\*\\{15\\}.*\n\\*\\*\\* .+ \\*\\*\\*\\*\\|[0-9]+\\(,[0-9]+\\)?[acd][0-9]+\\(,[0-9]+\\)?\\)$")
-(defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* ]\\).+\n" (substring diff-hunk-header-re 1)))
+(defvar diff-valid-unified-empty-line t
+ "If non-nil, empty lines are valid in unified diffs.
+Some versions of diff replace all-blank context lines in unified format with
+empty lines. This makes the format less robust, but is tolerated.
+See http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01990.html")
+
+(defconst diff-hunk-header-re
+ (concat "^\\(?:" diff-hunk-header-re-unified ".*\\|\\*\\{15\\}.*\n\\*\\*\\* .+ \\*\\*\\*\\*\\|[0-9]+\\(,[0-9]+\\)?[acd][0-9]+\\(,[0-9]+\\)?\\)$"))
+(defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* \n]\\).+\n" (substring diff-hunk-header-re 1)))
(defvar diff-narrowed-to nil)
(defun diff-hunk-style (&optional style)
(goto-char (match-end 0)))
style)
-(defun diff-end-of-hunk (&optional style)
- ;; Especially important for unified (because headers are ambiguous).
- (setq style (diff-hunk-style style))
- (let ((end (and (re-search-forward (case style
- ;; A `unified' header is ambiguous.
- (unified (concat "^[^-+# \\]\\|"
- diff-file-header-re))
- (context "^[^-+#! \\]")
- (normal "^[^<>#\\]")
- (t "^[^-+#!<> \\]"))
- nil t)
- (match-beginning 0))))
+(defun diff-end-of-hunk (&optional style donttrustheader)
+ (let (end)
+ (when (looking-at diff-hunk-header-re)
+ ;; Especially important for unified (because headers are ambiguous).
+ (setq style (diff-hunk-style style))
+ (goto-char (match-end 0))
+ (when (and (not donttrustheader) (match-end 2))
+ (let* ((nold (string-to-number (or (match-string 2) "1")))
+ (nnew (string-to-number (or (match-string 4) "1")))
+ (endold
+ (save-excursion
+ (re-search-forward (if diff-valid-unified-empty-line
+ "^[- \n]" "^[- ]")
+ nil t nold)
+ (line-beginning-position 2)))
+ (endnew
+ ;; The hunk may end with a bunch of "+" lines, so the `end' is
+ ;; then further than computed above.
+ (save-excursion
+ (re-search-forward (if diff-valid-unified-empty-line
+ "^[+ \n]" "^[+ ]")
+ nil t nnew)
+ (line-beginning-position 2))))
+ (setq end (max endold endnew)))))
+ ;; We may have a first evaluation of `end' thanks to the hunk header.
+ (unless end
+ (setq end (and (re-search-forward
+ (case style
+ (unified (concat (if diff-valid-unified-empty-line
+ "^[^-+# \\\n]\\|" "^[^-+# \\]\\|")
+ ;; A `unified' header is ambiguous.
+ diff-file-header-re))
+ (context "^[^-+#! \\]")
+ (normal "^[^<>#\\]")
+ (t "^[^-+#!<> \\]"))
+ nil t)
+ (match-beginning 0)))
+ (when diff-valid-unified-empty-line
+ ;; While empty lines may be valid inside hunks, they are also likely
+ ;; to be unrelated to the hunk.
+ (goto-char (or end (point-max)))
+ (while (eq ?\n (char-before (1- (point))))
+ (forward-char -1)
+ (setq end (point)))))
;; The return value is used by easy-mmode-define-navigation.
(goto-char (or end (point-max)))))
(diff-end-of-hunk)
(kill-region start (point)))))
+;; "index ", "old mode", "new mode", "new file mode" and
+;; "deleted file mode" are output by git-diff.
+(defconst diff-file-junk-re
+ "diff \\|index \\|\\(?:deleted file\\|new\\(?: file\\)?\\|old\\) mode")
+
(defun diff-beginning-of-file-and-junk ()
"Go to the beginning of file-related diff-info.
This is like `diff-beginning-of-file' except it tries to skip back over leading
data such as \"Index: ...\" and such."
- (let* ((start (point))
+ (let* ((orig (point))
+ ;; Skip forward over what might be "leading junk" so as to get
+ ;; closer to the actual diff.
+ (_ (progn (beginning-of-line)
+ (while (looking-at diff-file-junk-re)
+ (forward-line 1))))
+ (start (point))
(prevfile (condition-case err
(save-excursion (diff-beginning-of-file) (point))
(error err)))
(if (numberp prevfile) (goto-char prevfile))
(diff-hunk-prev) (point))))
(previndex (save-excursion
+ (forward-line 1) ;In case we're looking at "Index:".
(re-search-backward "^Index: " prevhunk t))))
;; If we're in the junk, we should use nextfile instead of prevfile.
(if (and (numberp nextfile)
(if (and previndex (numberp prevfile) (< previndex prevfile))
(setq prevfile previndex))
(if (and (numberp prevfile) (<= prevfile start))
- (goto-char prevfile)
+ (progn
+ (goto-char prevfile)
+ ;; Now skip backward over the leading junk we may have before the
+ ;; diff itself.
+ (while (save-excursion
+ (and (zerop (forward-line -1))
+ (looking-at diff-file-junk-re)))
+ (forward-line -1)))
;; File starts *after* the starting point: we really weren't in
;; a file diff but elsewhere.
- (goto-char start)
+ (goto-char orig)
(signal (car err) (cdr err)))))
(defun diff-file-kill ()
"Kill current file's hunks."
(interactive)
- (diff-beginning-of-file-and-junk)
- (let* ((start (point))
+ (let ((orig (point))
+ (start (progn (diff-beginning-of-file-and-junk) (point)))
(inhibit-read-only t))
(diff-end-of-file)
(if (looking-at "^\n") (forward-char 1)) ;`tla' generates such diffs.
+ (if (> orig (point)) (error "Not inside a file diff"))
(kill-region start (point))))
(defun diff-kill-junk ()
(beginning-of-line)
(let ((pos (point))
(start (progn (diff-beginning-of-hunk) (point))))
- (unless (looking-at "@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@")
+ (unless (looking-at diff-hunk-header-re-unified)
(error "diff-split-hunk only works on unified context diffs"))
(forward-line 1)
(let* ((start1 (string-to-number (match-string 1)))
- (start2 (string-to-number (match-string 2)))
+ (start2 (string-to-number (match-string 3)))
(newstart1 (+ start1 (diff-count-matches "^[- \t]" (point) pos)))
(newstart2 (+ start2 (diff-count-matches "^[+ \t]" (point) pos)))
(inhibit-read-only t))
;;;;
(defvar diff-remembered-files-alist nil)
+(defvar diff-remembered-defdir nil)
(defun diff-filename-drop-dir (file)
(when (string-match "/" file) (substring file (match-end 0))))
Non-nil BATCH means to prefer returning an incorrect answer than to prompt
the user.
PREFIX is only used internally: don't use it."
+ (unless (equal diff-remembered-defdir default-directory)
+ ;; Flush diff-remembered-files-alist if the default-directory is changed.
+ (set (make-local-variable 'diff-remembered-defdir) default-directory)
+ (set (make-local-variable 'diff-remembered-files-alist) nil))
(save-excursion
(unless (looking-at diff-file-header-re)
(or (ignore-errors (diff-beginning-of-file))
(inhibit-read-only t))
(save-excursion
(goto-char start)
- (while (and (re-search-forward "^\\(\\(---\\) .+\n\\(\\+\\+\\+\\) .+\\|@@ -\\([0-9]+\\),\\([0-9]+\\) \\+\\([0-9]+\\),\\([0-9]+\\) @@.*\\)$" nil t)
+ (while (and (re-search-forward
+ (concat "^\\(\\(---\\) .+\n\\(\\+\\+\\+\\) .+\\|"
+ diff-hunk-header-re-unified ".*\\)$")
+ nil t)
(< (point) end))
(combine-after-change-calls
(if (match-beginning 2)
(replace-match "***" t t nil 2))
;; we matched a hunk header
(let ((line1 (match-string 4))
- (lines1 (match-string 5))
+ (lines1 (or (match-string 5) "1"))
(line2 (match-string 6))
- (lines2 (match-string 7))
+ (lines2 (or (match-string 7) "1"))
;; Variables to use the special undo function.
(old-undo buffer-undo-list)
(old-end (marker-position end))
(concat "***************\n*** " line1 ","
(number-to-string (+ (string-to-number line1)
(string-to-number lines1)
- -1)) " ****"))
- (forward-line 1)
+ -1))
+ " ****"))
(save-restriction
- (narrow-to-region (point)
+ (narrow-to-region (line-beginning-position 2)
+ ;; Call diff-end-of-hunk from just before
+ ;; the hunk header so it can use the hunk
+ ;; header info.
(progn (diff-end-of-hunk 'unified) (point)))
(let ((hunk (buffer-string)))
(goto-char (point-min))
(?\\ (when (save-excursion (forward-line -1)
(= (char-after) ?+))
(delete-region (point) last-pt) (setq modif t)))
+ ;; diff-valid-unified-empty-line.
+ (?\n (insert " ") (setq modif nil) (backward-char 2))
(t (setq modif nil))))))
(goto-char (point-max))
(save-excursion
(insert "--- " line2 ","
(number-to-string (+ (string-to-number line2)
(string-to-number lines2)
- -1)) " ----\n" hunk))
+ -1))
+ " ----\n" hunk))
;;(goto-char (point-min))
(forward-line 1)
(if (not (save-excursion (re-search-forward "^+" nil t)))
(?\\ (when (save-excursion (forward-line 1)
(not (eobp)))
(setq delete t) (setq modif t)))
+ ;; diff-valid-unified-empty-line.
+ (?\n (insert " ") (setq modif nil) (backward-char 2)
+ (setq reversible nil))
(t (setq modif nil)))
(let ((last-pt (point)))
(forward-line 1)
(reversible t))
(replace-match "")
(unless (re-search-forward
- "^--- \\([0-9]+\\),\\(-?[0-9]+\\) ----$" nil t)
+ diff-context-mid-hunk-header-re nil t)
(error "Can't find matching `--- n1,n2 ----' line"))
(let ((line2s (match-string 1))
(line2e (match-string 2))
(when (= (char-after) ?-) (delete-char 1) (insert "+"))
(forward-line 1))
(let ((half1 (delete-and-extract-region half1s (point))))
- (unless (looking-at "^--- \\([0-9]+,-?[0-9]+\\) ----$")
+ (unless (looking-at diff-context-mid-hunk-header-re)
(insert half1)
(error "Can't find matching `--- n1,n2 ----' line"))
- (let ((str1 (match-string 1)))
- (replace-match lines1 nil nil nil 1)
+ (let* ((str1end (or (match-end 2) (match-end 1)))
+ (str1 (buffer-substring (match-beginning 1) str1end)))
+ (goto-char str1end)
+ (insert lines1)
+ (delete-region (match-beginning 1) str1end)
(forward-line 1)
(let ((half2s (point)))
(while (looking-at "[!+ \\][ \t]\\|#")
(t (when (and first last (< first last))
(insert (delete-and-extract-region first last)))
(setq first nil last nil)
- (equal ?\s c)))
+ (memq c (if diff-valid-unified-empty-line
+ '(?\s ?\n) '(?\s)))))
(forward-line 1))))))))))
(defun diff-fixup-modifs (start end)
(list (point-min) (point-max))))
(let ((inhibit-read-only t))
(save-excursion
- (goto-char end) (diff-end-of-hunk)
+ (goto-char end) (diff-end-of-hunk nil 'donttrustheader)
(let ((plus 0) (minus 0) (space 0) (bang 0))
(while (and (= (forward-line -1) 0) (<= start (point)))
(if (not (looking-at
- (concat "@@ -[0-9,]+ \\+[0-9,]+ @@"
+ (concat diff-hunk-header-re-unified
"\\|[-*][-*][-*] [0-9,]+ [-*][-*][-*][-*]$"
"\\|--- .+\n\\+\\+\\+ ")))
(case (char-after)
((?\\ ?#) nil)
(t (setq space 0 plus 0 minus 0 bang 0)))
(cond
- ((looking-at "@@ -[0-9]+,\\([0-9]*\\) \\+[0-9]+,\\([0-9]*\\) @@.*$")
- (let* ((old1 (match-string 1))
- (old2 (match-string 2))
+ ((looking-at diff-hunk-header-re-unified)
+ (let* ((old1 (match-string 2))
+ (old2 (match-string 4))
(new1 (number-to-string (+ space minus)))
(new2 (number-to-string (+ space plus))))
- (unless (string= new2 old2) (replace-match new2 t t nil 2))
- (unless (string= new1 old1) (replace-match new1 t t nil 1))))
- ((looking-at "--- \\([0-9]+\\),\\([0-9]*\\) ----$")
+ (if old2
+ (unless (string= new2 old2) (replace-match new2 t t nil 4))
+ (goto-char (match-end 4)) (insert "," new2))
+ (if old1
+ (unless (string= new1 old1) (replace-match new1 t t nil 2))
+ (goto-char (match-end 2)) (insert "," new1))))
+ ((looking-at diff-context-mid-hunk-header-re)
(when (> (+ space bang plus) 0)
(let* ((old1 (match-string 1))
(old2 (match-string 2))
(goto-char (car diff-unhandled-changes))
;; Maybe we've cut the end of the hunk before point.
(if (and (bolp) (not (bobp))) (backward-char 1))
- ;; We used to fixup modifs on all the changes, but it turns out
- ;; that it's safer not to do it on big changes, for example
- ;; when yanking a big diff, since we might then screw up perfectly
- ;; correct values. -stef
- ;; (unless (ignore-errors
- ;; (diff-beginning-of-hunk)
- ;; (save-excursion
- ;; (diff-end-of-hunk)
- ;; (> (point) (car diff-unhandled-changes))))
- ;; (goto-char (car diff-unhandled-changes))
- ;; (re-search-forward diff-hunk-header-re (cdr diff-unhandled-changes))
- ;; (diff-beginning-of-hunk))
- ;; (diff-fixup-modifs (point) (cdr diff-unhandled-changes))
+ ;; We used to fixup modifs on all the changes, but it turns out that
+ ;; it's safer not to do it on big changes, e.g. when yanking a big
+ ;; diff, or when the user edits the header, since we might then
+ ;; screw up perfectly correct values. --Stef
(diff-beginning-of-hunk)
- (when (save-excursion
- (diff-end-of-hunk)
- (>= (point) (cdr diff-unhandled-changes)))
+ (let* ((style (if (looking-at "\\*\\*\\*") 'context))
+ (start (line-beginning-position (if (eq style 'context) 3 2)))
+ (mid (if (eq style 'context)
+ (save-excursion
+ (re-search-forward diff-context-mid-hunk-header-re
+ nil t)))))
+ (when (and ;; Don't try to fixup changes in the hunk header.
+ (> (car diff-unhandled-changes) start)
+ ;; Don't try to fixup changes in the mid-hunk header either.
+ (or (not mid)
+ (< (cdr diff-unhandled-changes) (match-beginning 0))
+ (> (car diff-unhandled-changes) (match-end 0)))
+ (save-excursion
+ (diff-end-of-hunk nil 'donttrustheader)
+ ;; Don't try to fixup changes past the end of the hunk.
+ (>= (point) (cdr diff-unhandled-changes))))
(diff-fixup-modifs (point) (cdr diff-unhandled-changes)))))
- (setq diff-unhandled-changes nil)))
+ (setq diff-unhandled-changes nil))))
(defun diff-next-error (arg reset)
;; Select a window that displays the current buffer so that point
;; compile support
(set (make-local-variable 'next-error-function) 'diff-next-error)
+ (set (make-local-variable 'beginning-of-defun-function)
+ 'diff-beginning-of-file-and-junk)
+ (set (make-local-variable 'end-of-defun-function)
+ 'diff-end-of-file)
+
(setq buffer-read-only diff-default-read-only)
;; setup change hooks
(if (not diff-update-on-the-fly)
Only works for unified diffs."
(interactive)
(while
- (and (re-search-forward "^@@ [-0-9]+,\\([0-9]+\\) [+0-9]+,\\([0-9]+\\) @@"
- nil t)
- (equal (match-string 1) (match-string 2)))))
+ (and (re-search-forward diff-hunk-header-re-unified nil t)
+ (equal (match-string 2) (match-string 4)))))
(defun diff-sanity-check-context-hunk-half (lines)
(let ((count lines))
(error "Unrecognized context diff first hunk header format")
(forward-line 2)
(diff-sanity-check-context-hunk-half
- (if (match-string 2)
+ (if (match-end 2)
(1+ (- (string-to-number (match-string 2))
(string-to-number (match-string 1))))
1))
- (if (not (looking-at "--- \\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? ----$"))
+ (if (not (looking-at diff-context-mid-hunk-header-re))
(error "Unrecognized context diff second hunk header format")
(forward-line)
(diff-sanity-check-context-hunk-half
- (if (match-string 2)
+ (if (match-end 2)
(1+ (- (string-to-number (match-string 2))
(string-to-number (match-string 1))))
1)))))
;; A unified diff.
((eq (char-after) ?@)
- (if (not (looking-at
- "@@ -[0-9]+\\(?:,\\([0-9]+\\)\\)? \\+[0-9]+\\(?:,\\([0-9]+\\)\\)? @@"))
+ (if (not (looking-at diff-hunk-header-re-unified))
(error "Unrecognized unified diff hunk header format")
- (let ((before (if (match-string 1) (string-to-number (match-string 1)) 1))
- (after (if (match-string 2) (string-to-number (match-string 2)) 1)))
+ (let ((before (string-to-number (or (match-string 2) "1")))
+ (after (string-to-number (or (match-string 4) "1"))))
(forward-line)
(while
(case (char-after)
(?+ (decf after) t)
(t
(cond
+ ((and diff-valid-unified-empty-line
+ ;; Not just (eolp) so we don't infloop at eob.
+ (eq (char-after) ?\n)
+ (> before 0) (> after 0))
+ (decf before) (decf after) t)
((and (zerop before) (zerop after)) nil)
((or (< before 0) (< after 0))
(error (if (or (zerop before) (zerop after))
"End of hunk ambiguously marked"
"Hunk seriously messed up")))
- ((not (y-or-n-p "Try to auto-fix whitespace loss and word-wrap damage? "))
+ ((not (y-or-n-p (concat "Try to auto-fix " (if (eolp) "whitespace loss" "word-wrap damage") "? ")))
(error "Abort!"))
((eolp) (insert " ") (forward-line -1) t)
(t (insert " ")
;; context diff
(forward-line 2)
(setq src-pos (point))
- (re-search-forward "^--- " nil t)
+ (re-search-forward diff-context-mid-hunk-header-re nil t)
(forward-line 0)
(setq divider-pos (point))
(forward-line 1)
(error "Can't find the hunk header")
(if other (match-string 1)
(if (match-end 3) (match-string 3)
- (unless (re-search-forward "^--- \\([0-9,]+\\)" nil t)
+ (unless (re-search-forward
+ diff-context-mid-hunk-header-re nil t)
(error "Can't find the hunk separator"))
(match-string 1)))))
(file (or (diff-find-file-name other) (error "Can't find the file")))
(let ((old (if switched dst src)))
(with-temp-buffer
(insert (car old))
- (funcall (with-current-buffer buf major-mode))
+ (funcall (buffer-local-value 'major-mode buf))
(goto-char (+ (point-min) (cdr old)))
(add-log-current-defun))))
(with-current-buffer buf
(line-nb (and (or (looking-at "[^0-9]+\\([0-9]+\\)")
(error "Can't find line number"))
(string-to-number (match-string 1))))
+ (inhibit-read-only t)
(hunk (delete-and-extract-region
(point) (save-excursion (diff-end-of-hunk) (point))))
(lead (make-string (1- line-nb) ?\n)) ;Line nums start at 1.
(file1 (make-temp-file "diff1"))
(file2 (make-temp-file "diff2"))
(coding-system-for-read buffer-file-coding-system)
- (inhibit-read-only t)
old new)
(unwind-protect
(save-excursion
(defface diff-refine-change
'((((class color) (min-colors 88) (background light))
- :background "grey90")
+ :background "grey85")
(((class color) (min-colors 88) (background dark))
- :background "grey40")
+ :background "grey60")
(((class color) (background light))
:background "yellow")
(((class color) (background dark))
props 'diff-refine-preproc))))))))
+(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."
+ (interactive)
+ ;; XXX: Currently add-change-log-entry-other-window is only called
+ ;; once per hunk. Some hunks have multiple changes, it would be
+ ;; good to call it for each change.
+ (save-excursion
+ (goto-char (point-min))
+ (let ((orig-buffer (current-buffer)))
+ (condition-case nil
+ ;; Call add-change-log-entry-other-window for each hunk in
+ ;; the diff buffer.
+ (while (progn
+ (diff-hunk-next)
+ ;; Move to where the changes are,
+ ;; `add-change-log-entry-other-window' works better in
+ ;; that case.
+ (re-search-forward
+ (concat "\n[!+-<>]"
+ ;; If the hunk is a context hunk with an empty first
+ ;; half, recognize the "--- NNN,MMM ----" line
+ "\\(-- [0-9]+\\(,[0-9]+\\)? ----\n"
+ ;; and skip to the next non-context line.
+ "\\( .*\n\\)*[+]\\)?")
+ nil t))
+ (save-excursion
+ (add-change-log-entry nil nil t nil t)))
+ ;; When there's no more hunks, diff-hunk-next signals an error.
+ (error nil)))))
+
;; provide the package
(provide 'diff-mode)