diff options
| author | Stefan Monnier | 2008-02-19 19:44:48 +0000 |
|---|---|---|
| committer | Stefan Monnier | 2008-02-19 19:44:48 +0000 |
| commit | 95dfb89d87037faadbe95226472d06af56432a2c (patch) | |
| tree | 97bc723857223cfd7ec169618c20e85f40d0a890 | |
| parent | ece58427be11f0f96b3f43f6452598fd354f2cb9 (diff) | |
| download | emacs-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/ChangeLog | 13 | ||||
| -rw-r--r-- | lisp/diff-mode.el | 120 |
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 @@ | |||
| 1 | 2008-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 | |||
| 1 | 2008-02-19 Nick Roberts <nickrob@snap.net.nz> | 14 | 2008-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. | ||
| 388 | Some versions of diff replace all-blank context lines in unified format with | ||
| 389 | empty lines. This makes the format less robust, but is tolerated. | ||
| 390 | See 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]. | |||
| 1124 | Only works for unified diffs." | 1162 | Only 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 " ") |