[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
char-granularity fine diff highlight in diff-mode
From: |
Stefan Monnier |
Subject: |
char-granularity fine diff highlight in diff-mode |
Date: |
Mon, 17 Jul 2006 01:18:38 -0400 |
User-agent: |
Gnus/5.11 (Gnus v5.11) Emacs/22.0.50 (gnu/linux) |
Sorry to use this list for info not related to Emacs-22, but I can't
remember who asked for this feature.
Anyway, a while back, someone asked if diff-mode could highlight
fine-grained changes in the patch, kind of like the word-based fine
highlight in ediff.
At that time I pointed out that such a feature could probably be implemented
by reusing the code in smerge-mode which does just that for 2-way conflicts.
I just had the need for this feature, so here is the port of the smerge-mode
code to diff-mode. It's crude and not meant for Emacs-22 (probably post-22,
tho it still needs more work), but I believe someone on this list might
be interested.
Stefan
* auto-adding address@hidden/emacs--monnier--0--patch-372 to greedy revision
library /home/monnier/tmp/arch-lib
* found immediate ancestor revision in library
(address@hidden/emacs--monnier--0--patch-371)
* patching for this revision (address@hidden/emacs--monnier--0--patch-372)
--- orig/lisp/diff-mode.el
+++ mod/lisp/diff-mode.el
@@ -1498,6 +1498,108 @@
(delete-file file1)
(delete-file file2))))
+;;; Fine change highlighting.
+
+(defface diff-fine-change
+ '((t :background "yellow"))
+ "Face used for char-based changes shown by `diff-fine-highlight'.")
+
+(defun diff-fine-chopup-region (beg end file)
+ "Chopup the region into small elements, one per line."
+ ;; FIXME: see smerge-refine-chopup-region which duplicates most of this.
+ ;;
+ ;; ediff chops up into words, where the definition of a word is
+ ;; customizable. Instead we here keep only one char per line.
+ ;; The advantages are that there's nothing to configure, that we get very
+ ;; fine results, and that it's trivial to map the line numbers in the
+ ;; output of diff back into buffer positions. The disadvantage is that it
+ ;; can take more time to compute the diff and that the result is sometimes
+ ;; too fine. I'm not too concerned about the slowdown because conflicts
+ ;; are usually significantly smaller than the whole file. As for the
+ ;; problem of too-fine-refinement, I have found it to be unimportant
+ ;; especially when you consider the cases where the fine-grain is just
+ ;; what you want.
+ (let ((buf (current-buffer)))
+ (with-temp-buffer
+ (insert-buffer-substring buf beg end)
+ (goto-char (point-min))
+ (while (re-search-forward "^." nil t) (replace-match " "))
+ (goto-char (point-min))
+ (while (not (eobp))
+ (forward-char 1)
+ (unless (eq (char-before) ?\n) (insert ?\n)))
+ (let ((coding-system-for-write 'emacs-mule))
+ (write-region (point-min) (point-max) file nil 'nomessage)))))
+
+(defun diff-fine-highlight-change (buf beg match-num1 match-num2)
+ (let* ((startline (string-to-number (match-string match-num1)))
+ (ol (make-overlay
+ (+ beg startline -1)
+ (+ beg (if (match-end match-num2)
+ (string-to-number (match-string match-num2))
+ startline))
+ buf
+ 'front-advance nil)))
+ (overlay-put ol 'diff-mode 'fine)
+ (overlay-put ol 'evaporate t)
+ (overlay-put ol 'face 'diff-fine-change)))
+
+
+(defun diff-fine-highlight ()
+ "Blabla."
+ ;; TODO:
+ ;; - Share code with smerge-refine
+ ;; - extend to context diffs (only the ! lines)
+ ;; - clean up
+ ;; - make more robust
+ ;; - maybe two different faces should be used here
+ ;; - provide a reasonable UI
+ ;; - do it hunk-wide rather than on a single substitution change
+ (interactive)
+ (if (re-search-backward "^[^+-]" nil 'move) (forward-line 1))
+ (let* ((buf (current-buffer))
+ (beg1 (point))
+ (end1 (if (re-search-forward "^[^-]" nil 'move)
+ (match-beginning 0) (point-max)))
+ (beg2 end1)
+ (end2 (if (re-search-forward "^[^+]" nil 'move)
+ (match-beginning 0) (point-max)))
+ (file1 (make-temp-file "diff1"))
+ (file2 (make-temp-file "diff2")))
+
+ ;; If the user makes edits, this may not be enough because some
+ ;; highlights may now be located outside of the change (e.g. the first
+ ;; char has been turned into a SPC). Maybe we should remove overlays on
+ ;; the whole hunk?
+ (remove-overlays beg1 end1 'diff-mode 'fine)
+ (remove-overlays beg2 end2 'diff-mode 'fine)
+
+ ;; Chop up regions into smaller elements and save into files.
+ (diff-fine-chopup-region beg1 end1 file1)
+ (diff-fine-chopup-region beg2 end2 file2)
+
+ ;; Call diff on those files.
+ (unwind-protect
+ (with-temp-buffer
+ (let ((coding-system-for-read 'emacs-mule))
+ (call-process diff-command nil t nil file1 file2))
+ ;; Process diff's output.
+ (goto-char (point-min))
+ (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))))
+ (when (memq op '(?d ?c))
+ (diff-fine-highlight-change buf beg1 1 2))
+ (when (memq op '(?a ?c))
+ (diff-fine-highlight-change buf beg2 4 5)))
+ (forward-line 1) ;Skip hunk header.
+ (and (re-search-forward "^[0-9]" nil 'move) ;Skip hunk body.
+ (goto-char (match-beginning 0))))))
+ (delete-file file1)
+ (delete-file file2))))
+
;; provide the package
(provide 'diff-mode)
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- char-granularity fine diff highlight in diff-mode,
Stefan Monnier <=