aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2008-02-19 19:44:48 +0000
committerStefan Monnier2008-02-19 19:44:48 +0000
commit95dfb89d87037faadbe95226472d06af56432a2c (patch)
tree97bc723857223cfd7ec169618c20e85f40d0a890
parentece58427be11f0f96b3f43f6452598fd354f2cb9 (diff)
downloademacs-95dfb89d87037faadbe95226472d06af56432a2c.tar.gz
emacs-95dfb89d87037faadbe95226472d06af56432a2c.zip
Make it more robust in the presence of empty context lines in unified hunks.
(diff-valid-unified-empty-line): New var. (diff-unified->context, diff-sanity-check-hunk): Obey it. (diff-end-of-hunk): Obey it. New arg `donttrustheader'. (diff-fixup-modifs, diff-post-command-hook): Use this new arg. (diff-hunk-header-re-unified): New const. (diff-font-lock-keywords, diff-hunk-header-re, diff-split-hunk) (diff-fixup-modifs, diff-unified->context, diff-next-complex-hunk) (diff-sanity-check-hunk): Use it.
-rw-r--r--lisp/ChangeLog13
-rw-r--r--lisp/diff-mode.el120
2 files changed, 93 insertions, 40 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 25a085c5496..5ea2b6be5d6 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,16 @@
12008-02-19 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * diff-mode.el: Make it more robust in the presence of empty context
4 lines in unified hunks.
5 (diff-valid-unified-empty-line): New var.
6 (diff-unified->context, diff-sanity-check-hunk): Obey it.
7 (diff-end-of-hunk): Obey it. New arg `donttrustheader'.
8 (diff-fixup-modifs, diff-post-command-hook): Use this new arg.
9 (diff-hunk-header-re-unified): New const.
10 (diff-font-lock-keywords, diff-hunk-header-re, diff-split-hunk)
11 (diff-fixup-modifs, diff-unified->context, diff-next-complex-hunk)
12 (diff-sanity-check-hunk): Use it.
13
12008-02-19 Nick Roberts <nickrob@snap.net.nz> 142008-02-19 Nick Roberts <nickrob@snap.net.nz>
2 15
3 * progmodes/gdb-ui.el (gdba): Recreate as an alias for gdb. 16 * progmodes/gdb-ui.el (gdba): Recreate as an alias for gdb.
diff --git a/lisp/diff-mode.el b/lisp/diff-mode.el
index 6794fab11f8..e011ce17e1e 100644
--- a/lisp/diff-mode.el
+++ b/lisp/diff-mode.el
@@ -341,10 +341,12 @@ when editing big diffs)."
341 (while (re-search-backward re start t) 341 (while (re-search-backward re start t)
342 (replace-match "" t t))))))) 342 (replace-match "" t t)))))))
343 343
344(defconst diff-hunk-header-re-unified
345 "^@@ -\\([0-9]+\\),\\([0-9]+\\) \\+\\([0-9]+\\),\\([0-9]+\\) @@")
344 346
345(defvar diff-font-lock-keywords 347(defvar diff-font-lock-keywords
346 `(("^\\(@@ -[0-9,]+ \\+[0-9,]+ @@\\)\\(.*\\)$" ;unified 348 `((,(concat "\\(" diff-hunk-header-re-unified "\\)\\(.*\\)$")
347 (1 diff-hunk-header-face) (2 diff-function-face)) 349 (1 diff-hunk-header-face) (6 diff-function-face))
348 ("^\\(\\*\\{15\\}\\)\\(.*\\)$" ;context 350 ("^\\(\\*\\{15\\}\\)\\(.*\\)$" ;context
349 (1 diff-hunk-header-face) (2 diff-function-face)) 351 (1 diff-hunk-header-face) (2 diff-function-face))
350 ("^\\*\\*\\* .+ \\*\\*\\*\\*". diff-hunk-header-face) ;context 352 ("^\\*\\*\\* .+ \\*\\*\\*\\*". diff-hunk-header-face) ;context
@@ -381,25 +383,51 @@ when editing big diffs)."
381;;;; Movement 383;;;; Movement
382;;;; 384;;;;
383 385
384(defconst diff-hunk-header-re "^\\(@@ -[0-9,]+ \\+[0-9,]+ @@.*\\|\\*\\{15\\}.*\n\\*\\*\\* .+ \\*\\*\\*\\*\\|[0-9]+\\(,[0-9]+\\)?[acd][0-9]+\\(,[0-9]+\\)?\\)$") 386(defvar diff-valid-unified-empty-line t
387 "If non-nil, empty lines are valid in unified diffs.
388Some versions of diff replace all-blank context lines in unified format with
389empty lines. This makes the format less robust, but is tolerated.
390See http://lists.gnu.org/archive/html/emacs-devel/2007-11/msg01990.html")
391
392(defconst diff-hunk-header-re
393 (concat "^\\(?:" diff-hunk-header-re-unified ".*\\|\\*\\{15\\}.*\n\\*\\*\\* .+ \\*\\*\\*\\*\\|[0-9]+\\(,[0-9]+\\)?[acd][0-9]+\\(,[0-9]+\\)?\\)$"))
385(defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* ]\\).+\n" (substring diff-hunk-header-re 1))) 394(defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* ]\\).+\n" (substring diff-hunk-header-re 1)))
386(defvar diff-narrowed-to nil) 395(defvar diff-narrowed-to nil)
387 396
388(defun diff-end-of-hunk (&optional style) 397(defun diff-end-of-hunk (&optional style donttrustheader)
389 (when (looking-at diff-hunk-header-re) 398 (let (end)
390 (unless style 399 (when (looking-at diff-hunk-header-re)
391 ;; Especially important for unified (because headers are ambiguous). 400 (unless style
392 (setq style (cdr (assq (char-after) '((?@ . unified) (?* . context)))))) 401 ;; Especially important for unified (because headers are ambiguous).
393 (goto-char (match-end 0))) 402 (setq style (cdr (assq (char-after) '((?@ . unified) (?* . context))))))
394 (let ((end (and (re-search-forward (case style 403 (goto-char (match-end 0))
395 ;; A `unified' header is ambiguous. 404 (when (and (not donttrustheader) (match-end 2))
396 (unified (concat "^[^-+# \\]\\|" 405 (save-excursion
397 diff-file-header-re)) 406 (re-search-forward (if diff-valid-unified-empty-line
398 (context "^[^-+#! \\]") 407 "^[- \n]" "^[- ]")
399 (normal "^[^<>#\\]") 408 nil t
400 (t "^[^-+#!<> \\]")) 409 (string-to-number (match-string 2)))
401 nil t) 410 (setq end (line-beginning-position 2)))))
402 (match-beginning 0)))) 411 ;; We may have a first evaluation of `end' thanks to the hunk header.
412 (unless end
413 (setq end (and (re-search-forward
414 (case style
415 (unified (concat (if diff-valid-unified-empty-line
416 "^[^-+# \\\n]\\|" "^[^-+# \\]\\|")
417 ;; A `unified' header is ambiguous.
418 diff-file-header-re))
419 (context "^[^-+#! \\]")
420 (normal "^[^<>#\\]")
421 (t "^[^-+#!<> \\]"))
422 nil t)
423 (match-beginning 0)))
424 (when diff-valid-unified-empty-line
425 ;; While empty lines may be valid inside hunks, they are also likely
426 ;; to be unrelated to the hunk.
427 (goto-char (or end (point-max)))
428 (while (eq ?\n (char-before (1- (point))))
429 (forward-char -1)
430 (setq end (point)))))
403 ;; The return value is used by easy-mmode-define-navigation. 431 ;; The return value is used by easy-mmode-define-navigation.
404 (goto-char (or end (point-max))))) 432 (goto-char (or end (point-max)))))
405 433
@@ -537,11 +565,11 @@ data such as \"Index: ...\" and such."
537 (beginning-of-line) 565 (beginning-of-line)
538 (let ((pos (point)) 566 (let ((pos (point))
539 (start (progn (diff-beginning-of-hunk) (point)))) 567 (start (progn (diff-beginning-of-hunk) (point))))
540 (unless (looking-at "@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@") 568 (unless (looking-at diff-hunk-header-re-unified)
541 (error "diff-split-hunk only works on unified context diffs")) 569 (error "diff-split-hunk only works on unified context diffs"))
542 (forward-line 1) 570 (forward-line 1)
543 (let* ((start1 (string-to-number (match-string 1))) 571 (let* ((start1 (string-to-number (match-string 1)))
544 (start2 (string-to-number (match-string 2))) 572 (start2 (string-to-number (match-string 3)))
545 (newstart1 (+ start1 (diff-count-matches "^[- \t]" (point) pos))) 573 (newstart1 (+ start1 (diff-count-matches "^[- \t]" (point) pos)))
546 (newstart2 (+ start2 (diff-count-matches "^[+ \t]" (point) pos))) 574 (newstart2 (+ start2 (diff-count-matches "^[+ \t]" (point) pos)))
547 (inhibit-read-only t)) 575 (inhibit-read-only t))
@@ -699,7 +727,10 @@ else cover the whole buffer."
699 (inhibit-read-only t)) 727 (inhibit-read-only t))
700 (save-excursion 728 (save-excursion
701 (goto-char start) 729 (goto-char start)
702 (while (and (re-search-forward "^\\(\\(---\\) .+\n\\(\\+\\+\\+\\) .+\\|@@ -\\([0-9]+\\),\\([0-9]+\\) \\+\\([0-9]+\\),\\([0-9]+\\) @@.*\\)$" nil t) 730 (while (and (re-search-forward
731 (concat "^\\(\\(---\\) .+\n\\(\\+\\+\\+\\) .+\\|"
732 diff-hunk-header-re-unified ".*\\)$")
733 nil t)
703 (< (point) end)) 734 (< (point) end))
704 (combine-after-change-calls 735 (combine-after-change-calls
705 (if (match-beginning 2) 736 (if (match-beginning 2)
@@ -718,9 +749,11 @@ else cover the whole buffer."
718 (number-to-string (+ (string-to-number line1) 749 (number-to-string (+ (string-to-number line1)
719 (string-to-number lines1) 750 (string-to-number lines1)
720 -1)) " ****")) 751 -1)) " ****"))
721 (forward-line 1)
722 (save-restriction 752 (save-restriction
723 (narrow-to-region (point) 753 (narrow-to-region (line-beginning-position 2)
754 ;; Call diff-end-of-hunk from just before
755 ;; the hunk header so it can use the hunk
756 ;; header info.
724 (progn (diff-end-of-hunk 'unified) (point))) 757 (progn (diff-end-of-hunk 'unified) (point)))
725 (let ((hunk (buffer-string))) 758 (let ((hunk (buffer-string)))
726 (goto-char (point-min)) 759 (goto-char (point-min))
@@ -742,6 +775,8 @@ else cover the whole buffer."
742 (?\\ (when (save-excursion (forward-line -1) 775 (?\\ (when (save-excursion (forward-line -1)
743 (= (char-after) ?+)) 776 (= (char-after) ?+))
744 (delete-region (point) last-pt) (setq modif t))) 777 (delete-region (point) last-pt) (setq modif t)))
778 ;; diff-valid-unified-empty-line.
779 (?\n (insert " ") (setq modif nil) (backward-char 2))
745 (t (setq modif nil)))))) 780 (t (setq modif nil))))))
746 (goto-char (point-max)) 781 (goto-char (point-max))
747 (save-excursion 782 (save-excursion
@@ -767,6 +802,8 @@ else cover the whole buffer."
767 (?\\ (when (save-excursion (forward-line 1) 802 (?\\ (when (save-excursion (forward-line 1)
768 (not (eobp))) 803 (not (eobp)))
769 (setq delete t) (setq modif t))) 804 (setq delete t) (setq modif t)))
805 ;; diff-valid-unified-empty-line.
806 (?\n (insert " ") (setq modif nil) (backward-char 2))
770 (t (setq modif nil))) 807 (t (setq modif nil)))
771 (let ((last-pt (point))) 808 (let ((last-pt (point)))
772 (forward-line 1) 809 (forward-line 1)
@@ -908,7 +945,8 @@ else cover the whole buffer."
908 (t (when (and first last (< first last)) 945 (t (when (and first last (< first last))
909 (insert (delete-and-extract-region first last))) 946 (insert (delete-and-extract-region first last)))
910 (setq first nil last nil) 947 (setq first nil last nil)
911 (equal ?\s c))) 948 (memq c (if diff-valid-unified-empty-line
949 '(?\s ?\n) '(?\s)))))
912 (forward-line 1)))))))))) 950 (forward-line 1))))))))))
913 951
914(defun diff-fixup-modifs (start end) 952(defun diff-fixup-modifs (start end)
@@ -920,11 +958,11 @@ else cover the whole buffer."
920 (list (point-min) (point-max)))) 958 (list (point-min) (point-max))))
921 (let ((inhibit-read-only t)) 959 (let ((inhibit-read-only t))
922 (save-excursion 960 (save-excursion
923 (goto-char end) (diff-end-of-hunk) 961 (goto-char end) (diff-end-of-hunk nil 'donttrustheader)
924 (let ((plus 0) (minus 0) (space 0) (bang 0)) 962 (let ((plus 0) (minus 0) (space 0) (bang 0))
925 (while (and (= (forward-line -1) 0) (<= start (point))) 963 (while (and (= (forward-line -1) 0) (<= start (point)))
926 (if (not (looking-at 964 (if (not (looking-at
927 (concat "@@ -[0-9,]+ \\+[0-9,]+ @@" 965 (concat diff-hunk-header-re-unified
928 "\\|[-*][-*][-*] [0-9,]+ [-*][-*][-*][-*]$" 966 "\\|[-*][-*][-*] [0-9,]+ [-*][-*][-*][-*]$"
929 "\\|--- .+\n\\+\\+\\+ "))) 967 "\\|--- .+\n\\+\\+\\+ ")))
930 (case (char-after) 968 (case (char-after)
@@ -935,13 +973,13 @@ else cover the whole buffer."
935 ((?\\ ?#) nil) 973 ((?\\ ?#) nil)
936 (t (setq space 0 plus 0 minus 0 bang 0))) 974 (t (setq space 0 plus 0 minus 0 bang 0)))
937 (cond 975 (cond
938 ((looking-at "@@ -[0-9]+,\\([0-9]*\\) \\+[0-9]+,\\([0-9]*\\) @@.*$") 976 ((looking-at diff-hunk-header-re-unified)
939 (let* ((old1 (match-string 1)) 977 (let* ((old1 (match-string 2))
940 (old2 (match-string 2)) 978 (old2 (match-string 4))
941 (new1 (number-to-string (+ space minus))) 979 (new1 (number-to-string (+ space minus)))
942 (new2 (number-to-string (+ space plus)))) 980 (new2 (number-to-string (+ space plus))))
943 (unless (string= new2 old2) (replace-match new2 t t nil 2)) 981 (unless (string= new2 old2) (replace-match new2 t t nil 4))
944 (unless (string= new1 old1) (replace-match new1 t t nil 1)))) 982 (unless (string= new1 old1) (replace-match new1 t t nil 2))))
945 ((looking-at "--- \\([0-9]+\\),\\([0-9]*\\) ----$") 983 ((looking-at "--- \\([0-9]+\\),\\([0-9]*\\) ----$")
946 (when (> (+ space bang plus) 0) 984 (when (> (+ space bang plus) 0)
947 (let* ((old1 (match-string 1)) 985 (let* ((old1 (match-string 1))
@@ -1009,7 +1047,7 @@ See `after-change-functions' for the meaning of BEG, END and LEN."
1009 ;; (diff-fixup-modifs (point) (cdr diff-unhandled-changes)) 1047 ;; (diff-fixup-modifs (point) (cdr diff-unhandled-changes))
1010 (diff-beginning-of-hunk) 1048 (diff-beginning-of-hunk)
1011 (when (save-excursion 1049 (when (save-excursion
1012 (diff-end-of-hunk) 1050 (diff-end-of-hunk nil 'donttrustheader)
1013 (>= (point) (cdr diff-unhandled-changes))) 1051 (>= (point) (cdr diff-unhandled-changes)))
1014 (diff-fixup-modifs (point) (cdr diff-unhandled-changes))))) 1052 (diff-fixup-modifs (point) (cdr diff-unhandled-changes)))))
1015 (setq diff-unhandled-changes nil))) 1053 (setq diff-unhandled-changes nil)))
@@ -1124,9 +1162,8 @@ a diff with \\[diff-reverse-direction].
1124Only works for unified diffs." 1162Only works for unified diffs."
1125 (interactive) 1163 (interactive)
1126 (while 1164 (while
1127 (and (re-search-forward "^@@ [-0-9]+,\\([0-9]+\\) [+0-9]+,\\([0-9]+\\) @@" 1165 (and (re-search-forward diff-hunk-header-re-unified nil t)
1128 nil t) 1166 (equal (match-string 2) (match-string 4)))))
1129 (equal (match-string 1) (match-string 2)))))
1130 1167
1131(defun diff-sanity-check-context-hunk-half (lines) 1168(defun diff-sanity-check-context-hunk-half (lines)
1132 (let ((count lines)) 1169 (let ((count lines))
@@ -1175,11 +1212,10 @@ Only works for unified diffs."
1175 1212
1176 ;; A unified diff. 1213 ;; A unified diff.
1177 ((eq (char-after) ?@) 1214 ((eq (char-after) ?@)
1178 (if (not (looking-at 1215 (if (not (looking-at diff-hunk-header-re-unified))
1179 "@@ -[0-9]+,\\([0-9]+\\) \\+[0-9]+,\\([0-9]+\\) @@"))
1180 (error "Unrecognized unified diff hunk header format") 1216 (error "Unrecognized unified diff hunk header format")
1181 (let ((before (string-to-number (match-string 1))) 1217 (let ((before (string-to-number (match-string 2)))
1182 (after (string-to-number (match-string 2)))) 1218 (after (string-to-number (match-string 4))))
1183 (forward-line) 1219 (forward-line)
1184 (while 1220 (while
1185 (case (char-after) 1221 (case (char-after)
@@ -1197,12 +1233,16 @@ Only works for unified diffs."
1197 (?+ (decf after) t) 1233 (?+ (decf after) t)
1198 (t 1234 (t
1199 (cond 1235 (cond
1236 ((and diff-valid-unified-empty-line
1237 ;; Not just (eolp) so we don't infloop at eob.
1238 (eq (char-after) ?\n))
1239 (decf before) (decf after) t)
1200 ((and (zerop before) (zerop after)) nil) 1240 ((and (zerop before) (zerop after)) nil)
1201 ((or (< before 0) (< after 0)) 1241 ((or (< before 0) (< after 0))
1202 (error (if (or (zerop before) (zerop after)) 1242 (error (if (or (zerop before) (zerop after))
1203 "End of hunk ambiguously marked" 1243 "End of hunk ambiguously marked"
1204 "Hunk seriously messed up"))) 1244 "Hunk seriously messed up")))
1205 ((not (y-or-n-p "Try to auto-fix whitespace loss and word-wrap damage? ")) 1245 ((not (y-or-n-p (concat "Try to auto-fix " (if (eolp) "whitespace loss" "word-wrap damage") "? ")))
1206 (error "Abort!")) 1246 (error "Abort!"))
1207 ((eolp) (insert " ") (forward-line -1) t) 1247 ((eolp) (insert " ") (forward-line -1) t)
1208 (t (insert " ") 1248 (t (insert " ")