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.
This commit is contained in:
Stefan Kangas 2025-03-03 18:37:43 +01:00
parent eeda2a8ab1
commit 10abb87f05
3 changed files with 146 additions and 3 deletions

View file

@ -481,6 +481,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))
@ -495,9 +548,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
@ -562,7 +615,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)

View file

@ -0,0 +1,51 @@
From 1234567890abcdef1234567890abcdef12345678 Mon Sep 17 00:00:00 2001
From: Alyssa P. Hacker <alyssa.p.hacker@example.com>
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

View file

@ -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