Created
October 10, 2012 06:26
-
-
Save scottjad/3863483 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defface diff-refine-change-add | |
'((t :background "#aaffaa" :foreground "black")) | |
"Face used for char-based changes shown by `diff-refine-hunk'." | |
:group 'diff-mode) | |
(defface diff-refine-change-del | |
'((t :background "#ffaaaa" :foreground "black")) | |
"Face used for char-based changes shown by `diff-refine-hunk'." | |
:group 'diff-mode) | |
(custom-set-faces | |
'(magit-diff-add ((t (:background "#ddffdd" :foreground "black")))) | |
'(magit-diff-del ((t (:background "#ffdddd" :foreground "black"))))) | |
(defun jsj-smerge-refine-subst (beg1 end1 beg2 end2 props-add props-del &optional preproc) | |
"Show fine differences in the two regions BEG1..END1 and BEG2..END2. | |
PROPS is an alist of properties to put (via overlays) on the changes. | |
If non-nil, PREPROC is called with no argument in a buffer that contains | |
a copy of a region, just before preparing it to for `diff'. It can be | |
used to replace chars to try and eliminate some spurious differences." | |
(let* ((buf (current-buffer)) | |
(pos (point)) | |
deactivate-mark ; The code does not modify any visible buffer. | |
(file1 (make-temp-file "diff1")) | |
(file2 (make-temp-file "diff2"))) | |
;; Chop up regions into smaller elements and save into files. | |
(smerge-refine-chopup-region beg1 end1 file1 preproc) | |
(smerge-refine-chopup-region beg2 end2 file2 preproc) | |
;; Call diff on those files. | |
(unwind-protect | |
(with-temp-buffer | |
(let ((coding-system-for-read 'emacs-mule)) | |
(call-process diff-command nil t nil | |
(if (and smerge-refine-ignore-whitespace | |
(not smerge-refine-weight-hack)) | |
;; Pass -a so diff treats it as a text file even | |
;; if it contains \0 and such. | |
;; Pass -d so as to get the smallest change, but | |
;; also and more importantly because otherwise it | |
;; may happen that diff doesn't behave like | |
;; smerge-refine-weight-hack expects it to. | |
;; See http://thread.gmane.org/gmane.emacs.devel/82685. | |
"-awd" "-ad") | |
file1 file2)) | |
;; Process diff's output. | |
(goto-char (point-min)) | |
(let ((last1 nil) | |
(last2 nil)) | |
(while (not (eobp)) | |
(if (not (looking-at "\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?\\([acd]\\)\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?$")) | |
(error "Unexpected patch hunk header: %s" | |
(buffer-substring (point) (line-end-position)))) | |
(let ((op (char-after (match-beginning 3))) | |
(m1 (match-string 1)) | |
(m2 (match-string 2)) | |
(m4 (match-string 4)) | |
(m5 (match-string 5))) | |
(when (memq op '(?d ?c)) | |
(setq last1 | |
(smerge-refine-highlight-change buf beg1 m1 m2 props-del))) | |
(when (memq op '(?a ?c)) | |
(setq last2 | |
(smerge-refine-highlight-change buf beg2 m4 m5 props-add)))) | |
(forward-line 1) ;Skip hunk header. | |
(and (re-search-forward "^[0-9]" nil 'move) ;Skip hunk body. | |
(goto-char (match-beginning 0)))) | |
;; (assert (or (null last1) (< (overlay-start last1) end1))) | |
;; (assert (or (null last2) (< (overlay-start last2) end2))) | |
(if smerge-refine-weight-hack | |
(progn | |
;; (assert (or (null last1) (<= (overlay-end last1) end1))) | |
;; (assert (or (null last2) (<= (overlay-end last2) end2))) | |
) | |
;; smerge-refine-forward-function when calling in chopup may | |
;; have stopped because it bumped into EOB whereas in | |
;; smerge-refine-weight-hack it may go a bit further. | |
(if (and last1 (> (overlay-end last1) end1)) | |
(move-overlay last1 (overlay-start last1) end1)) | |
(if (and last2 (> (overlay-end last2) end2)) | |
(move-overlay last2 (overlay-start last2) end2)) | |
))) | |
(goto-char pos) | |
(delete-file file1) | |
(delete-file file2)))) | |
(defun diff-refine-hunk () | |
"Highlight changes of hunk at point at a finer granularity." | |
(interactive) | |
(require 'smerge-mode) | |
(save-excursion | |
(diff-beginning-of-hunk 'try-harder) | |
(let* ((start (point)) | |
(style (diff-hunk-style)) ;Skips the hunk header as well. | |
(beg (point)) | |
(props-add '((diff-mode . fine) (face diff-refine-change-add))) | |
(props-del '((diff-mode . fine) (face diff-refine-change-del))) | |
;; Be careful to go back to `start' so diff-end-of-hunk gets | |
;; to read the hunk header's line info. | |
(end (progn (goto-char start) (diff-end-of-hunk) (point)))) | |
(remove-overlays beg end 'diff-mode 'fine) | |
(goto-char beg) | |
(case style | |
(unified | |
(while (re-search-forward "^\\(?:-.*\n\\)+\\(\\)\\(?:\\+.*\n\\)+" | |
end t) | |
(jsj-smerge-refine-subst (match-beginning 0) (match-end 1) | |
(match-end 1) (match-end 0) | |
props-add props-del 'diff-refine-preproc))) | |
(context | |
(let* ((middle (save-excursion (re-search-forward "^---"))) | |
(other middle)) | |
(while (re-search-forward "^\\(?:!.*\n\\)+" middle t) | |
(jsj-smerge-refine-subst (match-beginning 0) (match-end 0) | |
(save-excursion | |
(goto-char other) | |
(re-search-forward "^\\(?:!.*\n\\)+" end) | |
(setq other (match-end 0)) | |
(match-beginning 0)) | |
other | |
props-add props-del 'diff-refine-preproc)))) | |
(t ;; Normal diffs. | |
(let ((beg1 (1+ (point)))) | |
(when (re-search-forward "^---.*\n" end t) | |
;; It's a combined add&remove, so there's something to do. | |
(jsj-smerge-refine-subst beg1 (match-beginning 0) | |
(match-end 0) end | |
props-add props-del 'diff-refine-preproc)))))))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment