diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index b91a2ba45a..175687f184 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el @@ -96,6 +96,11 @@ diff-font-lock-refine :version "27.1" :type 'boolean) +(defcustom diff-font-lock-syntaxify t + "If non-nil, diff hunk font-lock includes syntax highlighting." + :version "27.1" + :type 'boolean) + (defcustom diff-font-lock-prettify nil "If non-nil, font-lock will try and make the format prettier." :version "27.1" @@ -402,7 +411,8 @@ diff-font-lock-keywords (2 font-lock-comment-face)) ("^[^-=+*!<>#].*\n" (0 'diff-context)) (,#'diff--font-lock-prettify) - (,#'diff--font-lock-refined))) + (,#'diff--font-lock-refined) + (,#'diff--font-lock-syntaxify))) (defconst diff-font-lock-defaults '(diff-font-lock-keywords t nil nil nil (font-lock-multiline . nil))) @@ -2230,6 +2246,66 @@ diff--font-lock-prettify 'display ""))))) nil) +;;; Syntactic fontification + +(defun diff--font-lock-syntaxify (max) + "Highlight language syntax in diff hunks." + (when diff-font-lock-syntaxify + (when (get-char-property (point) 'diff--font-lock-syntaxified) + (goto-char (next-single-char-property-change + (point) 'diff--font-lock-syntaxified nil max))) + (let* ((min (point)) + (beg (or (ignore-errors (diff-beginning-of-hunk)) + (ignore-errors (let ((diff-auto-refine-mode nil)) + (diff-hunk-next)) + (point)) + max))) + (while (< beg max) + (let ((file (save-excursion + (diff-beginning-of-file) + (when (looking-at "^\\S-+\\s-+\\(\\S-+\\)") + (match-string 1)))) + (end (save-excursion (goto-char beg) (diff-end-of-hunk) (point)))) + (if (< end min) (setq beg min)) + (unless (or (< end beg) + (get-char-property beg 'diff--font-lock-syntaxified)) + (diff--syntaxify-hunk beg end file) + (let ((ol (make-overlay beg end))) + (overlay-put ol 'diff--font-lock-syntaxified t) + (overlay-put ol 'evaporate t))) + (goto-char (max beg end)) + (setq beg (or (ignore-errors (let ((diff-auto-refine-mode nil)) + (diff-hunk-next)) + (point)) + max)))))) + nil) + +(defun diff--syntaxify-hunk (beg end file-name) + (let (props (buffer (current-buffer))) + (with-temp-buffer + (insert-buffer-substring-no-properties buffer beg end) + (goto-char (point-min)) + (while (re-search-forward "^[-+!]" nil t) + (replace-match " " nil nil)) + (let ((buffer-file-name file-name)) + (set-auto-mode)) + (jit-lock-register #'font-lock-fontify-region) + (jit-lock-fontify-now (point-min) (point-max)) + (goto-char (point-min)) + (let* ((from (point)) to + (val (get-text-property from 'face))) + (while (setq to (next-single-property-change from 'face)) + (when val (push (list from to val) props)) + (setq val (get-text-property to 'face) + from to)))) + (dolist (prop props) + (let ((ol (make-overlay (+ beg (nth 0 prop) -1) + (+ beg (nth 1 prop) -1) + nil 'front-advance nil))) + (overlay-put ol 'evaporate t) + (overlay-put ol 'face (nth 2 prop)) + ol)))) + ;;; Support for converting a diff to diff3 markers via `wiggle'. ;; Wiggle can be found at http://neil.brown.name/wiggle/ or in your nearest