1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-03 02:31:03 -08:00

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.
This commit is contained in:
Stefan Monnier 2008-02-19 19:44:48 +00:00
parent ece58427be
commit 95dfb89d87
2 changed files with 93 additions and 40 deletions

View file

@ -341,10 +341,12 @@ when editing big diffs)."
(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]+\\) @@")
(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
@ -381,25 +383,51 @@ when editing big diffs)."
;;;; Movement
;;;;
(defconst diff-hunk-header-re "^\\(@@ -[0-9,]+ \\+[0-9,]+ @@.*\\|\\*\\{15\\}.*\n\\*\\*\\* .+ \\*\\*\\*\\*\\|[0-9]+\\(,[0-9]+\\)?[acd][0-9]+\\(,[0-9]+\\)?\\)$")
(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" (substring diff-hunk-header-re 1)))
(defvar diff-narrowed-to nil)
(defun diff-end-of-hunk (&optional style)
(when (looking-at diff-hunk-header-re)
(unless style
;; Especially important for unified (because headers are ambiguous).
(setq style (cdr (assq (char-after) '((?@ . unified) (?* . context))))))
(goto-char (match-end 0)))
(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)
(unless style
;; Especially important for unified (because headers are ambiguous).
(setq style (cdr (assq (char-after) '((?@ . unified) (?* . context))))))
(goto-char (match-end 0))
(when (and (not donttrustheader) (match-end 2))
(save-excursion
(re-search-forward (if diff-valid-unified-empty-line
"^[- \n]" "^[- ]")
nil t
(string-to-number (match-string 2)))
(setq end (line-beginning-position 2)))))
;; 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)))))
@ -537,11 +565,11 @@ data such as \"Index: ...\" and such."
(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))
@ -699,7 +727,10 @@ else cover the whole buffer."
(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)
@ -718,9 +749,11 @@ else cover the whole buffer."
(number-to-string (+ (string-to-number line1)
(string-to-number lines1)
-1)) " ****"))
(forward-line 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))
@ -742,6 +775,8 @@ else cover the whole buffer."
(?\\ (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
@ -767,6 +802,8 @@ else cover the whole buffer."
(?\\ (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))
(t (setq modif nil)))
(let ((last-pt (point)))
(forward-line 1)
@ -908,7 +945,8 @@ else cover the whole buffer."
(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)
@ -920,11 +958,11 @@ else cover the whole buffer."
(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)
@ -935,13 +973,13 @@ else cover the whole buffer."
((?\\ ?#) 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))))
(unless (string= new2 old2) (replace-match new2 t t nil 4))
(unless (string= new1 old1) (replace-match new1 t t nil 2))))
((looking-at "--- \\([0-9]+\\),\\([0-9]*\\) ----$")
(when (> (+ space bang plus) 0)
(let* ((old1 (match-string 1))
@ -1009,7 +1047,7 @@ See `after-change-functions' for the meaning of BEG, END and LEN."
;; (diff-fixup-modifs (point) (cdr diff-unhandled-changes))
(diff-beginning-of-hunk)
(when (save-excursion
(diff-end-of-hunk)
(diff-end-of-hunk nil 'donttrustheader)
(>= (point) (cdr diff-unhandled-changes)))
(diff-fixup-modifs (point) (cdr diff-unhandled-changes)))))
(setq diff-unhandled-changes nil)))
@ -1124,9 +1162,8 @@ a diff with \\[diff-reverse-direction].
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))
@ -1175,11 +1212,10 @@ Only works for unified diffs."
;; 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 (string-to-number (match-string 1)))
(after (string-to-number (match-string 2))))
(let ((before (string-to-number (match-string 2)))
(after (string-to-number (match-string 4))))
(forward-line)
(while
(case (char-after)
@ -1197,12 +1233,16 @@ Only works for unified diffs."
(?+ (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))
(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 " ")