2

diff-mode provides diff-refine-hunk function to refine the highlighting of the current hunk. However, by default, only diff-hunk-next or explicit diff-refine-hunk calls will do this job -- hunks will not be refined by default. I wrote the following snippet to refine all hunks when I enter diff-mode:

(defun my-diff-hunks-highlight-all ()
  "Highlight all hunks in diff-mode."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((last-point))
      (while (not (equal (point) last-point))
        (setq last-point (point))
        (diff-refine-hunk)
        (diff-hunk-next)))))
(add-hook 'diff-mode-hook 'my-diff-hunks-highlight-all)

However, it turns out that only the last hunk will be refined. Is there any reason that this approach fails? What should be the proper way to do it?

Related Emacs bug: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=18128

2

The actual problem is the following form in the definition of diff-mode-hook. It removes all the refinement overlays that you created with your function my-diff-hunks-highlight-all immediately after starting up diff-mode and initializing font-lock-mode.

(declare-function 'diff-hunk-next "diff-mode")

(defun my-diff-refine-all ()
  "Refine all diffs."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (condition-case nil
        (diff-beginning-of-hunk t)
      (error (diff-hunk-next)))
    (condition-case nil
        (while (not (eobp))
          (diff-refine-hunk)
          (diff-hunk-next))
      (user-error) ;; catches "No next hunk." from `diff-hunk-next' if there is garbage at the end of the file.
      )))

(defun my-diff-hunks-highlight-all ()
  "Highlight all hunks in diff-mode."
  (add-hook 'font-lock-mode-hook #'my-diff-refine-all t t))

(add-hook 'diff-mode-hook 'my-diff-hunks-highlight-all)
  • The call of (diff-beginning-of-hunk t) does not seem necessary (as diff-refine-hunk calls it), and it causes problems when reading git format patches: diff-beginning-of-file-and-junk: Can’t find the beginning of the file – xuhdev Nov 1 '16 at 18:44
  • Seems the other parts also cause error for git format patches... But running diff-hunk-next after loading a git format patch works as expected. Don't know why. – xuhdev Nov 1 '16 at 18:54
  • @xuhdev This is a problem of diff --git .... This form of diff appends and prepends stuff and includes binary diffs encoded as ascii. I tried to make the lisp code more roboust for these cases. – Tobias Nov 1 '16 at 19:48
  • This actually still cause some issues with diff-hl. It works well after replacing all the condition-case with ignore-errors. – xuhdev Nov 4 '16 at 7:53
2

The following works for me when I test on two small files (i.e., there are not many diffs).

(defun my-diff-hunks-highlight-all ()
  "Highlight all hunks in diff-mode."
  (interactive)
  (run-at-time 0.1 nil
               (lambda ()
                 (save-excursion
                   (goto-char (point-min))
                   (while (not (eobp))
                     (diff-hunk-next 1)
                     (sit-for 0.01))))))

(add-hook 'diff-mode-hook 'my-diff-hunks-highlight-all)
  • I didn't call diff-refine-hunk explicitly since it will be called from diff-hunk-next if diff-auto-refine-mode is on (it is by default).
  • (run-at-time 0.1 ...): gives 0.1 seconds to diff-mode to be ready
  • (sit-for 0.01): gives 0.01 seconds to diff-refine-hunk to make effect

The above is not a very good solution, I think. You might need to adjust the times. There should be other element way. I don't know diff-mode very much.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.