From: Stefan Kangas Date: Mon, 3 Mar 2025 17:37:43 +0000 (+0100) Subject: Fix fontification outside hunks in Git patches X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=a2370bb043b2236a6fb0e54b5652feeb2992b8e9;p=emacs.git Fix fontification outside hunks in Git patches * lisp/vc/diff-mode.el (diff-font-lock-keywords): Don't fontify lines in Git patches starting with + or - as added/removed, if they are either before the first hunk, or in the email signature. (Bug#75884) (diff-buffer-type): Move definition up. (diff--indicator-added-re, diff--indicator-removed-re): New variables. (diff--git-preamble-end, diff--git-footer-start) (diff--indicator-matcher-helper, diff--indicator-added-matcher) (diff--indicator-removed-matcher): New functions. * test/lisp/vc/diff-mode-tests.el (diff-mode-test-git-patch) (diff-mode-test-git-patch/before-first-hunk) (diff-mode-test-git-patch/signature): New tests. * test/lisp/vc/diff-mode-resources/git.patch: New file. (cherry picked from commit 10abb87f0519b3d53d6b7078703d3a0120e3aaa8) --- diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index 6a38ce3addd..9f45c8eba72 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el @@ -480,6 +480,59 @@ If non-nil, use the face `diff-changed-unspecified'. Otherwise, use the face `diff-removed' for removed lines, and the face `diff-added' for added lines.") +(defvar diff-buffer-type nil) + +(defvar diff--indicator-added-re + (rx bol + (group (any "+>")) + (group (zero-or-more nonl) "\n"))) + +(defvar diff--indicator-removed-re + (rx bol + (group (any "<-")) + (group (zero-or-more nonl) "\n"))) + +(defun diff--git-preamble-end () + (save-excursion + (goto-char (point-min)) + (re-search-forward "^diff --git .+ .+$" nil t) + (forward-line 2) + (point))) + +(defun diff--git-footer-start () + (save-excursion + (goto-char (point-max)) + (re-search-backward "^-- $" nil t) + (point))) + +(defun diff--indicator-matcher-helper (limit regexp) + "Fontify added/removed lines from point to LIMIT using REGEXP. + +If this is a Git patch, don't fontify lines before the first hunk, or in +the email signature at the end." + (catch 'return + (when (eq diff-buffer-type 'git) + (let ((preamble-end (diff--git-preamble-end)) + (footer-start (diff--git-footer-start)) + (beg (point)) + (end limit)) + (cond ((or (<= end preamble-end) + (>= beg footer-start)) + (throw 'return nil)) + ;; end is after preamble, adjust beg: + ((< beg preamble-end) + (goto-char preamble-end)) + ;; beg is before footer, adjust end: + ((> end footer-start) + (setq limit footer-start))))) + (re-search-forward regexp limit t))) + +(defun diff--indicator-added-matcher (limit) + (diff--indicator-matcher-helper limit diff--indicator-added-re)) + +(defun diff--indicator-removed-matcher (limit) + (diff--indicator-matcher-helper limit diff--indicator-removed-re)) + (defvar diff-font-lock-keywords `((,(concat "\\(" diff-hunk-header-re-unified "\\)\\(.*\\)$") (1 'diff-hunk-header) (6 'diff-function)) @@ -494,9 +547,9 @@ use the face `diff-removed' for removed lines, and the face ("^\\(---\\|\\+\\+\\+\\|\\*\\*\\*\\) \\([^\t\n]+?\\)\\(?:\t.*\\| \\(\\*\\*\\*\\*\\|----\\)\\)?\n" (0 'diff-header) (2 (if (not (match-end 3)) 'diff-file-header) prepend)) - ("^\\([-<]\\)\\(.*\n\\)" + (diff--indicator-removed-matcher (1 diff-indicator-removed-face) (2 'diff-removed)) - ("^\\([+>]\\)\\(.*\n\\)" + (diff--indicator-added-matcher (1 diff-indicator-added-face) (2 'diff-added)) ("^\\(!\\)\\(.*\n\\)" (1 (if diff-use-changed-face @@ -561,7 +614,6 @@ See https://lists.gnu.org/r/emacs-devel/2007-11/msg01990.html") (defconst diff-separator-re "^--+ ?$") (defvar diff-narrowed-to nil) -(defvar diff-buffer-type nil) (defun diff-hunk-style (&optional style) (when (looking-at diff-hunk-header-re) diff --git a/test/lisp/vc/diff-mode-resources/git.patch b/test/lisp/vc/diff-mode-resources/git.patch new file mode 100644 index 00000000000..05ec90d105c --- /dev/null +++ b/test/lisp/vc/diff-mode-resources/git.patch @@ -0,0 +1,51 @@ +From 1234567890abcdef1234567890abcdef12345678 Mon Sep 17 00:00:00 2001 +From: Alyssa P. Hacker +Date: Sun, 3 Mar 2025 10:30:00 -0400 +Subject: [PATCH] Subtle bug fixes and slight improvements + +- This is not a removed line ++ This is not an added line + +--- + src/main.py | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/main.py b/src/main.py +index 9f6c5fe43e47eab441232e54456c5c2b06297b65..7b3f91a8b4ed923c8f43183276e3ab36fe04f6c9 100644 +--- a/src/main.py ++++ b/src/main.py +@@ -2,25 +2,24 @@ + + def main(): + # Initialize the magic number generator +- magic_number = 42 +- print("Magic number: ", magic_number) + +- # TODO: Fix the infinite loop +- while True: +- print("This loop will never end") ++ magic_number = 73 # After reconsidering, 73 seems more appropriate ++ print("Updated magic number: ", magic_number) + ++ # The infinite loop was probably not the best approach ++ # while True: ++ # print("This loop will never end.") + + # This part of the code handles other important tasks + print("Processing other tasks...") + + # Error handling has been updated for clarity +- if not fixed_it_yet: +- print("ERROR: Still broken!") ++ if not fixed_it_yet: # This should be fine now ++ print("ERROR: No longer an issue.") + + # Exiting the function on a positive note +- print("Goodbye, cruel world!") ++ print("Goodbye, world!") + + if __name__ == "__main__": + main() + +-- +2.40.0 diff --git a/test/lisp/vc/diff-mode-tests.el b/test/lisp/vc/diff-mode-tests.el index cd3f613f532..bbd66824e48 100644 --- a/test/lisp/vc/diff-mode-tests.el +++ b/test/lisp/vc/diff-mode-tests.el @@ -557,5 +557,45 @@ baz")))) +1 "))))) +(ert-deftest diff-mode-test-git-patch () + (let ((file (ert-resource-file "git.patch"))) + (with-temp-buffer + (insert-file-contents file) + (diff-mode) + (font-lock-ensure) + (goto-char (point-min)) + (re-search-forward "magic_number = 42") + (should (eq (get-text-property (match-beginning 0) 'face) + 'diff-removed)) + (re-search-forward "magic_number = 73") + (should (eq (get-text-property (match-beginning 0) 'face) + 'diff-added))))) + +(ert-deftest diff-mode-test-git-patch/before-first-hunk () + (let ((file (ert-resource-file "git.patch"))) + (with-temp-buffer + (insert-file-contents file) + (diff-mode) + (font-lock-ensure) + (goto-char (point-min)) + (re-search-forward "This is not a removed line") + (should (eq (get-text-property (match-beginning 0) 'face) + 'diff-context)) + (re-search-forward "This is not an added line") + (font-lock-ensure) + (should (eq (get-text-property (match-beginning 0) 'face) + 'diff-context))))) + +(ert-deftest diff-mode-test-git-patch/signature () + (let ((file (ert-resource-file "git.patch"))) + (with-temp-buffer + (insert-file-contents file) + (diff-mode) + (font-lock-ensure) + (goto-char (point-max)) + (re-search-backward "^-- $") + (should (eq (get-text-property (match-beginning 0) 'face) + 'diff-context))))) + (provide 'diff-mode-tests) ;;; diff-mode-tests.el ends here