aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/diff-mode.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/diff-mode.el')
-rw-r--r--lisp/diff-mode.el116
1 files changed, 97 insertions, 19 deletions
diff --git a/lisp/diff-mode.el b/lisp/diff-mode.el
index 64199147c21..a1bd0afa126 100644
--- a/lisp/diff-mode.el
+++ b/lisp/diff-mode.el
@@ -155,7 +155,7 @@ when editing big diffs)."
155 ("\C-c\C-u" . diff-context->unified) 155 ("\C-c\C-u" . diff-context->unified)
156 ;; `d' because it duplicates the context :-( --Stef 156 ;; `d' because it duplicates the context :-( --Stef
157 ("\C-c\C-d" . diff-unified->context) 157 ("\C-c\C-d" . diff-unified->context)
158 ("\C-c\C-w" . diff-refine-hunk) 158 ("\C-c\C-w" . diff-refine-ignore-spaces-hunk)
159 ("\C-c\C-f" . next-error-follow-minor-mode)) 159 ("\C-c\C-f" . next-error-follow-minor-mode))
160 "Keymap for `diff-mode'. See also `diff-mode-shared-map'.") 160 "Keymap for `diff-mode'. See also `diff-mode-shared-map'.")
161 161
@@ -164,12 +164,23 @@ when editing big diffs)."
164 '("Diff" 164 '("Diff"
165 ["Jump to Source" diff-goto-source t] 165 ["Jump to Source" diff-goto-source t]
166 ["Apply hunk" diff-apply-hunk t] 166 ["Apply hunk" diff-apply-hunk t]
167 ["Test applying hunk" diff-test-hunk t]
167 ["Apply diff with Ediff" diff-ediff-patch t] 168 ["Apply diff with Ediff" diff-ediff-patch t]
168 ["-----" nil nil] 169 "-----"
169 ["Reverse direction" diff-reverse-direction t] 170 ["Reverse direction" diff-reverse-direction t]
170 ["Context -> Unified" diff-context->unified t] 171 ["Context -> Unified" diff-context->unified t]
171 ["Unified -> Context" diff-unified->context t] 172 ["Unified -> Context" diff-unified->context t]
172 ;;["Fixup Headers" diff-fixup-modifs (not buffer-read-only)] 173 ;;["Fixup Headers" diff-fixup-modifs (not buffer-read-only)]
174 "-----"
175 ["Split hunk" diff-split-hunk (diff-splittable-p)]
176 ["Refine hunk" diff-refine-ignore-spaces-hunk t]
177 ["Kill current hunk" diff-hunk-kill t]
178 ["Kill current file's hunks" diff-file-kill t]
179 "-----"
180 ["Previous Hunk" diff-hunk-prev t]
181 ["Next Hunk" diff-hunk-next t]
182 ["Previous File" diff-file-prev t]
183 ["Next File" diff-file-next t]
173 )) 184 ))
174 185
175(defcustom diff-minor-mode-prefix "\C-c=" 186(defcustom diff-minor-mode-prefix "\C-c="
@@ -390,13 +401,26 @@ when editing big diffs)."
390 ;; The return value is used by easy-mmode-define-navigation. 401 ;; The return value is used by easy-mmode-define-navigation.
391 (goto-char (or end (point-max))))) 402 (goto-char (or end (point-max)))))
392 403
393(defun diff-beginning-of-hunk () 404(defun diff-beginning-of-hunk (&optional try-harder)
405 "Move back to beginning of hunk.
406If TRY-HARDER is non-nil, try to cater to the case where we're not in a hunk
407but in the file header instead, in which case move forward to the first hunk."
394 (beginning-of-line) 408 (beginning-of-line)
395 (unless (looking-at diff-hunk-header-re) 409 (unless (looking-at diff-hunk-header-re)
396 (forward-line 1) 410 (forward-line 1)
397 (condition-case () 411 (condition-case ()
398 (re-search-backward diff-hunk-header-re) 412 (re-search-backward diff-hunk-header-re)
399 (error (error "Can't find the beginning of the hunk"))))) 413 (error
414 (if (not try-harder)
415 (error "Can't find the beginning of the hunk")
416 (diff-beginning-of-file-and-junk)
417 (diff-hunk-next))))))
418
419(defun diff-unified-hunk-p ()
420 (save-excursion
421 (ignore-errors
422 (diff-beginning-of-hunk)
423 (looking-at "^@@"))))
400 424
401(defun diff-beginning-of-file () 425(defun diff-beginning-of-file ()
402 (beginning-of-line) 426 (beginning-of-line)
@@ -425,7 +449,7 @@ when editing big diffs)."
425If the prefix ARG is given, restrict the view to the current file instead." 449If the prefix ARG is given, restrict the view to the current file instead."
426 (interactive "P") 450 (interactive "P")
427 (save-excursion 451 (save-excursion
428 (if arg (diff-beginning-of-file) (diff-beginning-of-hunk)) 452 (if arg (diff-beginning-of-file) (diff-beginning-of-hunk 'try-harder))
429 (narrow-to-region (point) 453 (narrow-to-region (point)
430 (progn (if arg (diff-end-of-file) (diff-end-of-hunk)) 454 (progn (if arg (diff-end-of-file) (diff-end-of-hunk))
431 (point))) 455 (point)))
@@ -453,18 +477,37 @@ If the prefix ARG is given, restrict the view to the current file instead."
453 (diff-end-of-hunk) 477 (diff-end-of-hunk)
454 (kill-region start (point))))) 478 (kill-region start (point)))))
455 479
480(defun diff-beginning-of-file-and-junk ()
481 "Go to the beginning of file-related diff-info.
482This is like `diff-beginning-of-file' except it tries to skip back over leading
483data such as \"Index: ...\" and such."
484 (let ((start (point))
485 (file (condition-case err (progn (diff-beginning-of-file) (point))
486 (error err)))
487 ;; prevhunk is one of the limits.
488 (prevhunk (save-excursion (ignore-errors (diff-hunk-prev) (point))))
489 err)
490 (when (consp file)
491 ;; Presumably, we started before the file header, in the leading junk.
492 (setq err file)
493 (diff-file-next)
494 (setq file (point)))
495 (let ((index (save-excursion
496 (re-search-backward "^Index: " prevhunk t))))
497 (when index (setq file index))
498 (if (<= file start)
499 (goto-char file)
500 ;; File starts *after* the starting point: we really weren't in
501 ;; a file diff but elsewhere.
502 (goto-char start)
503 (signal (car err) (cdr err))))))
504
456(defun diff-file-kill () 505(defun diff-file-kill ()
457 "Kill current file's hunks." 506 "Kill current file's hunks."
458 (interactive) 507 (interactive)
459 (diff-beginning-of-file) 508 (diff-beginning-of-file-and-junk)
460 (let* ((start (point)) 509 (let* ((start (point))
461 (prevhunk (save-excursion
462 (ignore-errors
463 (diff-hunk-prev) (point))))
464 (index (save-excursion
465 (re-search-backward "^Index: " prevhunk t)))
466 (inhibit-read-only t)) 510 (inhibit-read-only t))
467 (when index (setq start index))
468 (diff-end-of-file) 511 (diff-end-of-file)
469 (if (looking-at "^\n") (forward-char 1)) ;`tla' generates such diffs. 512 (if (looking-at "^\n") (forward-char 1)) ;`tla' generates such diffs.
470 (kill-region start (point)))) 513 (kill-region start (point))))
@@ -491,6 +534,13 @@ If the prefix ARG is given, restrict the view to the current file instead."
491 (while (re-search-forward re end t) (incf n)) 534 (while (re-search-forward re end t) (incf n))
492 n))) 535 n)))
493 536
537(defun diff-splittable-p ()
538 (save-excursion
539 (beginning-of-line)
540 (and (looking-at "^[-+ ]")
541 (progn (forward-line -1) (looking-at "^[-+ ]"))
542 (diff-unified-hunk-p))))
543
494(defun diff-split-hunk () 544(defun diff-split-hunk ()
495 "Split the current (unified diff) hunk at point into two hunks." 545 "Split the current (unified diff) hunk at point into two hunks."
496 (interactive) 546 (interactive)
@@ -585,9 +635,11 @@ If the OLD prefix arg is passed, tell the file NAME of the old file."
585 (list (if old (match-string 2) (match-string 4)) 635 (list (if old (match-string 2) (match-string 4))
586 (if old (match-string 4) (match-string 2))))))))) 636 (if old (match-string 4) (match-string 2)))))))))
587 637
588(defun diff-find-file-name (&optional old prefix) 638(defun diff-find-file-name (&optional old batch prefix)
589 "Return the file corresponding to the current patch. 639 "Return the file corresponding to the current patch.
590Non-nil OLD means that we want the old file. 640Non-nil OLD means that we want the old file.
641Non-nil BATCH means to prefer returning an incorrect answer than to prompt
642the user.
591PREFIX is only used internally: don't use it." 643PREFIX is only used internally: don't use it."
592 (save-excursion 644 (save-excursion
593 (unless (looking-at diff-file-header-re) 645 (unless (looking-at diff-file-header-re)
@@ -622,7 +674,10 @@ PREFIX is only used internally: don't use it."
622 (boundp 'cvs-pcl-cvs-dirchange-re) 674 (boundp 'cvs-pcl-cvs-dirchange-re)
623 (save-excursion 675 (save-excursion
624 (re-search-backward cvs-pcl-cvs-dirchange-re nil t)) 676 (re-search-backward cvs-pcl-cvs-dirchange-re nil t))
625 (diff-find-file-name old (match-string 1))) 677 (diff-find-file-name old batch (match-string 1)))
678 ;; Invent something, if necessary.
679 (when batch
680 (or (car fs) default-directory))
626 ;; if all else fails, ask the user 681 ;; if all else fails, ask the user
627 (let ((file (read-file-name (format "Use file %s: " (or (first fs) "")) 682 (let ((file (read-file-name (format "Use file %s: " (or (first fs) ""))
628 nil (first fs) t (first fs)))) 683 nil (first fs) t (first fs))))
@@ -670,7 +725,12 @@ else cover the whole bufer."
670 (let ((line1 (match-string 4)) 725 (let ((line1 (match-string 4))
671 (lines1 (match-string 5)) 726 (lines1 (match-string 5))
672 (line2 (match-string 6)) 727 (line2 (match-string 6))
673 (lines2 (match-string 7))) 728 (lines2 (match-string 7))
729 ;; Variables to use the special undo function.
730 (old-undo buffer-undo-list)
731 (old-end (marker-position end))
732 (start (match-beginning 0))
733 (reversible t))
674 (replace-match 734 (replace-match
675 (concat "***************\n*** " line1 "," 735 (concat "***************\n*** " line1 ","
676 (number-to-string (+ (string-to-number line1) 736 (number-to-string (+ (string-to-number line1)
@@ -712,6 +772,14 @@ else cover the whole bufer."
712 (if (not (save-excursion (re-search-forward "^+" nil t))) 772 (if (not (save-excursion (re-search-forward "^+" nil t)))
713 (delete-region (point) (point-max)) 773 (delete-region (point) (point-max))
714 (let ((modif nil) (delete nil)) 774 (let ((modif nil) (delete nil))
775 (if (save-excursion (re-search-forward "^\\+.*\n-" nil t))
776 ;; Normally, lines in a substitution come with
777 ;; first the removals and then the additions, and
778 ;; the context->unified function follows this
779 ;; convention, of course. Yet, other alternatives
780 ;; are valid as well, but they preclude the use of
781 ;; context->unified as an undo command.
782 (setq reversible nil))
715 (while (not (eobp)) 783 (while (not (eobp))
716 (case (char-after) 784 (case (char-after)
717 (?\s (insert " ") (setq modif nil) (backward-char 1)) 785 (?\s (insert " ") (setq modif nil) (backward-char 1))
@@ -730,7 +798,15 @@ else cover the whole bufer."
730 (forward-line 1) 798 (forward-line 1)
731 (when delete 799 (when delete
732 (delete-region last-pt (point)) 800 (delete-region last-pt (point))
733 (setq delete nil))))))))))))))) 801 (setq delete nil)))))))
802 (unless (or (not reversible) (eq buffer-undo-list t))
803 ;; Drop the many undo entries and replace them with
804 ;; a single entry that uses diff-context->unified to do
805 ;; the work.
806 (setq buffer-undo-list
807 (cons (list 'apply (- old-end end) start (point-max)
808 'diff-context->unified start (point-max))
809 old-undo)))))))))))
734 810
735(defun diff-context->unified (start end &optional to-context) 811(defun diff-context->unified (start end &optional to-context)
736 "Convert context diffs to unified diffs. 812 "Convert context diffs to unified diffs.
@@ -1289,7 +1365,8 @@ SRC and DST are the two variants of text as returned by `diff-hunk-text'.
1289SWITCHED is non-nil if the patch is already applied." 1365SWITCHED is non-nil if the patch is already applied."
1290 (save-excursion 1366 (save-excursion
1291 (let* ((other (diff-xor other-file diff-jump-to-old-file)) 1367 (let* ((other (diff-xor other-file diff-jump-to-old-file))
1292 (char-offset (- (point) (progn (diff-beginning-of-hunk) (point)))) 1368 (char-offset (- (point) (progn (diff-beginning-of-hunk 'try-harder)
1369 (point))))
1293 ;; Check that the hunk is well-formed. Otherwise diff-mode and 1370 ;; Check that the hunk is well-formed. Otherwise diff-mode and
1294 ;; the user may disagree on what constitutes the hunk 1371 ;; the user may disagree on what constitutes the hunk
1295 ;; (e.g. because an empty line truncates the hunk mid-course), 1372 ;; (e.g. because an empty line truncates the hunk mid-course),
@@ -1461,10 +1538,11 @@ For use in `add-log-current-defun-function'."
1461 (goto-char (+ (car pos) (cdr src))) 1538 (goto-char (+ (car pos) (cdr src)))
1462 (add-log-current-defun)))))) 1539 (add-log-current-defun))))))
1463 1540
1464(defun diff-refine-hunk () 1541(defun diff-refine-ignore-spaces-hunk ()
1465 "Refine the current hunk by ignoring space differences." 1542 "Refine the current hunk by ignoring space differences."
1466 (interactive) 1543 (interactive)
1467 (let* ((char-offset (- (point) (progn (diff-beginning-of-hunk) (point)))) 1544 (let* ((char-offset (- (point) (progn (diff-beginning-of-hunk 'try-harder)
1545 (point))))
1468 (opts (case (char-after) (?@ "-bu") (?* "-bc") (t "-b"))) 1546 (opts (case (char-after) (?@ "-bu") (?* "-bc") (t "-b")))
1469 (line-nb (and (or (looking-at "[^0-9]+\\([0-9]+\\)") 1547 (line-nb (and (or (looking-at "[^0-9]+\\([0-9]+\\)")
1470 (error "Can't find line number")) 1548 (error "Can't find line number"))