diff options
| author | Stefan Monnier | 2007-10-09 04:12:24 +0000 |
|---|---|---|
| committer | Stefan Monnier | 2007-10-09 04:12:24 +0000 |
| commit | be36f934da2f42f5fb57cd1a3202a4d402e3e107 (patch) | |
| tree | bb9a0fa3b3972f7b862dba395fc4a5441d14bbfd /lisp/diff-mode.el | |
| parent | 9f2e22a06dfa082f6519ff3c5b1403d66e848a8f (diff) | |
| download | emacs-be36f934da2f42f5fb57cd1a3202a4d402e3e107.tar.gz emacs-be36f934da2f42f5fb57cd1a3202a4d402e3e107.zip | |
(diff-hunk-style): New fun.
(diff-end-of-hunk): Use it.
(diff-context->unified): Use the new `apply' undo element, if applicable,
so as to save undo-log space.
(diff-fine-change): New face.
(diff-fine-highlight-preproc): New function.
(diff-fine-highlight): New command.
Diffstat (limited to 'lisp/diff-mode.el')
| -rw-r--r-- | lisp/diff-mode.el | 211 |
1 files changed, 146 insertions, 65 deletions
diff --git a/lisp/diff-mode.el b/lisp/diff-mode.el index d4244126f23..706a532938d 100644 --- a/lisp/diff-mode.el +++ b/lisp/diff-mode.el | |||
| @@ -386,12 +386,15 @@ when editing big diffs)." | |||
| 386 | (defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* ]\\).+\n" (substring diff-hunk-header-re 1))) | 386 | (defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* ]\\).+\n" (substring diff-hunk-header-re 1))) |
| 387 | (defvar diff-narrowed-to nil) | 387 | (defvar diff-narrowed-to nil) |
| 388 | 388 | ||
| 389 | (defun diff-end-of-hunk (&optional style) | 389 | (defun diff-hunk-style (&optional style) |
| 390 | (when (looking-at diff-hunk-header-re) | 390 | (when (looking-at diff-hunk-header-re) |
| 391 | (unless style | 391 | (setq style (cdr (assq (char-after) '((?@ . unified) (?* . context))))) |
| 392 | ;; Especially important for unified (because headers are ambiguous). | ||
| 393 | (setq style (cdr (assq (char-after) '((?@ . unified) (?* . context)))))) | ||
| 394 | (goto-char (match-end 0))) | 392 | (goto-char (match-end 0))) |
| 393 | style) | ||
| 394 | |||
| 395 | (defun diff-end-of-hunk (&optional style) | ||
| 396 | ;; Especially important for unified (because headers are ambiguous). | ||
| 397 | (setq style (diff-hunk-style style)) | ||
| 395 | (let ((end (and (re-search-forward (case style | 398 | (let ((end (and (re-search-forward (case style |
| 396 | ;; A `unified' header is ambiguous. | 399 | ;; A `unified' header is ambiguous. |
| 397 | (unified (concat "^[^-+# \\]\\|" | 400 | (unified (concat "^[^-+# \\]\\|" |
| @@ -843,68 +846,89 @@ With a prefix argument, convert unified format to context format." | |||
| 843 | (diff-unified->context start end) | 846 | (diff-unified->context start end) |
| 844 | (unless (markerp end) (setq end (copy-marker end t))) | 847 | (unless (markerp end) (setq end (copy-marker end t))) |
| 845 | (let ( ;;(diff-inhibit-after-change t) | 848 | (let ( ;;(diff-inhibit-after-change t) |
| 846 | (inhibit-read-only t)) | 849 | (inhibit-read-only t)) |
| 847 | (save-excursion | 850 | (save-excursion |
| 848 | (goto-char start) | 851 | (goto-char start) |
| 849 | (while (and (re-search-forward "^\\(\\(\\*\\*\\*\\) .+\n\\(---\\) .+\\|\\*\\{15\\}.*\n\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]+\\) \\*\\*\\*\\*\\)$" nil t) | 852 | (while (and (re-search-forward "^\\(\\(\\*\\*\\*\\) .+\n\\(---\\) .+\\|\\*\\{15\\}.*\n\\*\\*\\* \\([0-9]+\\),\\(-?[0-9]+\\) \\*\\*\\*\\*\\)$" nil t) |
| 850 | (< (point) end)) | 853 | (< (point) end)) |
| 851 | (combine-after-change-calls | 854 | (combine-after-change-calls |
| 852 | (if (match-beginning 2) | 855 | (if (match-beginning 2) |
| 853 | ;; we matched a file header | 856 | ;; we matched a file header |
| 854 | (progn | 857 | (progn |
| 855 | ;; use reverse order to make sure the indices are kept valid | 858 | ;; use reverse order to make sure the indices are kept valid |
| 856 | (replace-match "+++" t t nil 3) | 859 | (replace-match "+++" t t nil 3) |
| 857 | (replace-match "---" t t nil 2)) | 860 | (replace-match "---" t t nil 2)) |
| 858 | ;; we matched a hunk header | 861 | ;; we matched a hunk header |
| 859 | (let ((line1s (match-string 4)) | 862 | (let ((line1s (match-string 4)) |
| 860 | (line1e (match-string 5)) | 863 | (line1e (match-string 5)) |
| 861 | (pt1 (match-beginning 0))) | 864 | (pt1 (match-beginning 0)) |
| 862 | (replace-match "") | 865 | ;; Variables to use the special undo function. |
| 863 | (unless (re-search-forward | 866 | (old-undo buffer-undo-list) |
| 864 | "^--- \\([0-9]+\\),\\(-?[0-9]+\\) ----$" nil t) | 867 | (old-end (marker-position end)) |
| 865 | (error "Can't find matching `--- n1,n2 ----' line")) | 868 | (reversible t)) |
| 866 | (let ((line2s (match-string 1)) | 869 | (replace-match "") |
| 867 | (line2e (match-string 2)) | 870 | (unless (re-search-forward |
| 868 | (pt2 (progn | 871 | "^--- \\([0-9]+\\),\\(-?[0-9]+\\) ----$" nil t) |
| 869 | (delete-region (progn (beginning-of-line) (point)) | 872 | (error "Can't find matching `--- n1,n2 ----' line")) |
| 870 | (progn (forward-line 1) (point))) | 873 | (let ((line2s (match-string 1)) |
| 871 | (point-marker)))) | 874 | (line2e (match-string 2)) |
| 872 | (goto-char pt1) | 875 | (pt2 (progn |
| 873 | (forward-line 1) | 876 | (delete-region (progn (beginning-of-line) (point)) |
| 874 | (while (< (point) pt2) | 877 | (progn (forward-line 1) (point))) |
| 875 | (case (char-after) | 878 | (point-marker)))) |
| 876 | ((?! ?-) (delete-char 2) (insert "-") (forward-line 1)) | 879 | (goto-char pt1) |
| 877 | (?\s ;merge with the other half of the chunk | 880 | (forward-line 1) |
| 878 | (let* ((endline2 | 881 | (while (< (point) pt2) |
| 879 | (save-excursion | 882 | (case (char-after) |
| 880 | (goto-char pt2) (forward-line 1) (point))) | 883 | (?! (delete-char 2) (insert "-") (forward-line 1)) |
| 881 | (c (char-after pt2))) | 884 | (?- (forward-char 1) (delete-char 1) (forward-line 1)) |
| 882 | (case c | 885 | (?\s ;merge with the other half of the chunk |
| 883 | ((?! ?+) | 886 | (let* ((endline2 |
| 884 | (insert "+" | 887 | (save-excursion |
| 885 | (prog1 (buffer-substring (+ pt2 2) endline2) | 888 | (goto-char pt2) (forward-line 1) (point)))) |
| 886 | (delete-region pt2 endline2)))) | 889 | (case (char-after pt2) |
| 887 | (?\s ;FIXME: check consistency | 890 | ((?! ?+) |
| 888 | (delete-region pt2 endline2) | 891 | (insert "+" |
| 889 | (delete-char 1) | 892 | (prog1 (buffer-substring (+ pt2 2) endline2) |
| 890 | (forward-line 1)) | 893 | (delete-region pt2 endline2)))) |
| 891 | (?\\ (forward-line 1)) | 894 | (?\s |
| 892 | (t (delete-char 1) (forward-line 1))))) | 895 | (unless (= (- endline2 pt2) |
| 893 | (t (forward-line 1)))) | 896 | (- (line-beginning-position 2) (point))) |
| 894 | (while (looking-at "[+! ] ") | 897 | ;; If the two lines we're merging don't have the |
| 895 | (if (/= (char-after) ?!) (forward-char 1) | 898 | ;; same length (can happen with "diff -b"), then |
| 896 | (delete-char 1) (insert "+")) | 899 | ;; diff-unified->context will not properly undo |
| 897 | (delete-char 1) (forward-line 1)) | 900 | ;; this operation. |
| 898 | (save-excursion | 901 | (setq reversible nil)) |
| 899 | (goto-char pt1) | 902 | (delete-region pt2 endline2) |
| 900 | (insert "@@ -" line1s "," | 903 | (delete-char 1) |
| 901 | (number-to-string (- (string-to-number line1e) | 904 | (forward-line 1)) |
| 902 | (string-to-number line1s) | 905 | (?\\ (forward-line 1)) |
| 903 | -1)) | 906 | (t (setq reversible nil) |
| 904 | " +" line2s "," | 907 | (delete-char 1) (forward-line 1))))) |
| 905 | (number-to-string (- (string-to-number line2e) | 908 | (t (setq reversible nil) (forward-line 1)))) |
| 906 | (string-to-number line2s) | 909 | (while (looking-at "[+! ] ") |
| 907 | -1)) " @@"))))))))))) | 910 | (if (/= (char-after) ?!) (forward-char 1) |
| 911 | (delete-char 1) (insert "+")) | ||
| 912 | (delete-char 1) (forward-line 1)) | ||
| 913 | (save-excursion | ||
| 914 | (goto-char pt1) | ||
| 915 | (insert "@@ -" line1s "," | ||
| 916 | (number-to-string (- (string-to-number line1e) | ||
| 917 | (string-to-number line1s) | ||
| 918 | -1)) | ||
| 919 | " +" line2s "," | ||
| 920 | (number-to-string (- (string-to-number line2e) | ||
| 921 | (string-to-number line2s) | ||
| 922 | -1)) " @@")) | ||
| 923 | (set-marker pt2 nil) | ||
| 924 | ;; The whole procedure succeeded, let's replace the myriad | ||
| 925 | ;; of undo elements with just a single special one. | ||
| 926 | (unless (or (not reversible) (eq buffer-undo-list t)) | ||
| 927 | (setq buffer-undo-list | ||
| 928 | (cons (list 'apply (- old-end end) pt1 (point) | ||
| 929 | 'diff-unified->context pt1 (point)) | ||
| 930 | old-undo))) | ||
| 931 | ))))))))) | ||
| 908 | 932 | ||
| 909 | (defun diff-reverse-direction (start end) | 933 | (defun diff-reverse-direction (start end) |
| 910 | "Reverse the direction of the diffs. | 934 | "Reverse the direction of the diffs. |
| @@ -1610,6 +1634,63 @@ For use in `add-log-current-defun-function'." | |||
| 1610 | (delete-file file1) | 1634 | (delete-file file1) |
| 1611 | (delete-file file2)))) | 1635 | (delete-file file2)))) |
| 1612 | 1636 | ||
| 1637 | ;;; Fine change highlighting. | ||
| 1638 | |||
| 1639 | (defface diff-fine-change | ||
| 1640 | '((t :background "yellow")) | ||
| 1641 | "Face used for char-based changes shown by `diff-fine-highlight'.") | ||
| 1642 | |||
| 1643 | (defun diff-fine-highlight-preproc () | ||
| 1644 | (while (re-search-forward "^." nil t) | ||
| 1645 | ;; Replace the hunk's leading prefix (+, -, !, <, or >) on each line | ||
| 1646 | ;; with something constant, otherwise it'll be flagged as changes | ||
| 1647 | ;; (since it's typically "-" on one side and "+" on the other). | ||
| 1648 | ;; Note that we keep the same number of chars: we treat the prefix | ||
| 1649 | ;; as part of the texts-to-diff, so that finding the right char | ||
| 1650 | ;; afterwards will be easier. This only makes sense because we make | ||
| 1651 | ;; diffs at char-granularity. | ||
| 1652 | (replace-match " "))) | ||
| 1653 | |||
| 1654 | (defun diff-fine-highlight () | ||
| 1655 | "Highlight changes of hunk at point at a finer granularity." | ||
| 1656 | (interactive) | ||
| 1657 | (require 'smerge-mode) | ||
| 1658 | (diff-beginning-of-hunk 'try-harder) | ||
| 1659 | (let* ((style (diff-hunk-style)) ;Skips the hunk header as well. | ||
| 1660 | (beg (point)) | ||
| 1661 | (props '((diff-mode . fine) (face diff-fine-change))) | ||
| 1662 | (end (progn (diff-end-of-hunk) (point)))) | ||
| 1663 | |||
| 1664 | (remove-overlays beg end 'diff-mode 'fine) | ||
| 1665 | |||
| 1666 | (goto-char beg) | ||
| 1667 | (case style | ||
| 1668 | (unified | ||
| 1669 | (while (re-search-forward "^\\(?:-.*\n\\)+\\(\\)\\(?:\\+.*\n\\)+" end t) | ||
| 1670 | (smerge-refine-subst (match-beginning 0) (match-end 1) | ||
| 1671 | (match-end 1) (match-end 0) | ||
| 1672 | props 'diff-fine-highlight-preproc))) | ||
| 1673 | (context | ||
| 1674 | (let* ((middle (save-excursion (re-search-forward "^---"))) | ||
| 1675 | (other middle)) | ||
| 1676 | (while (re-search-forward "^\\(?:!.*\n\\)+" middle t) | ||
| 1677 | (smerge-refine-subst (match-beginning 0) (match-end 0) | ||
| 1678 | (save-excursion | ||
| 1679 | (goto-char other) | ||
| 1680 | (re-search-forward "^\\(?:!.*\n\\)+" end) | ||
| 1681 | (setq other (match-end 0)) | ||
| 1682 | (match-beginning 0)) | ||
| 1683 | other | ||
| 1684 | props 'diff-fine-highlight-preproc)))) | ||
| 1685 | (t ;; Normal diffs. | ||
| 1686 | (let ((beg1 (1+ (point)))) | ||
| 1687 | (when (re-search-forward "^---.*\n" end t) | ||
| 1688 | ;; It's a combined add&remove, so there's something to do. | ||
| 1689 | (smerge-refine-subst beg1 (match-beginning 0) | ||
| 1690 | (match-end 0) end | ||
| 1691 | props 'diff-fine-highlight-preproc))))))) | ||
| 1692 | |||
| 1693 | |||
| 1613 | ;; provide the package | 1694 | ;; provide the package |
| 1614 | (provide 'diff-mode) | 1695 | (provide 'diff-mode) |
| 1615 | 1696 | ||