aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2018-07-10 22:52:21 -0400
committerStefan Monnier2018-07-10 22:52:21 -0400
commitf8b1e40fb63b0a6bc6692cc0bc84e5f5e65c2644 (patch)
tree4aaafe66b5cbfebf4415cbe4d35a10dc44e1ec05
parent1d7151e98e9da5eeb4e341cfdb7d1f4462dc5b70 (diff)
downloademacs-f8b1e40fb63b0a6bc6692cc0bc84e5f5e65c2644.tar.gz
emacs-f8b1e40fb63b0a6bc6692cc0bc84e5f5e65c2644.zip
* lisp/vc/diff-mode.el: Perform hunk refinement from font-lock
Remove redundant :group arguments. (diff-font-lock-refine): New var. (diff--refine-hunk): New function, extracted from diff-refine-hunk. (diff-refine-hunk): Use it. (diff--font-lock-refine--refresh): New function. (diff--font-lock-refined): New function. (diff-font-lock-keywords): Use it.
-rw-r--r--lisp/vc/diff-mode.el206
1 files changed, 115 insertions, 91 deletions
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index e88ccece415..ffbd9e5479a 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -66,14 +66,12 @@
66 66
67(defcustom diff-default-read-only nil 67(defcustom diff-default-read-only nil
68 "If non-nil, `diff-mode' buffers default to being read-only." 68 "If non-nil, `diff-mode' buffers default to being read-only."
69 :type 'boolean 69 :type 'boolean)
70 :group 'diff-mode)
71 70
72(defcustom diff-jump-to-old-file nil 71(defcustom diff-jump-to-old-file nil
73 "Non-nil means `diff-goto-source' jumps to the old file. 72 "Non-nil means `diff-goto-source' jumps to the old file.
74Else, it jumps to the new file." 73Else, it jumps to the new file."
75 :type 'boolean 74 :type 'boolean)
76 :group 'diff-mode)
77 75
78(defcustom diff-update-on-the-fly t 76(defcustom diff-update-on-the-fly t
79 "Non-nil means hunk headers are kept up-to-date on-the-fly. 77 "Non-nil means hunk headers are kept up-to-date on-the-fly.
@@ -82,19 +80,21 @@ need to be kept consistent with the actual diff. This can
82either be done on the fly (but this sometimes interacts poorly with the 80either be done on the fly (but this sometimes interacts poorly with the
83undo mechanism) or whenever the file is written (can be slow 81undo mechanism) or whenever the file is written (can be slow
84when editing big diffs)." 82when editing big diffs)."
85 :type 'boolean 83 :type 'boolean)
86 :group 'diff-mode)
87 84
88(defcustom diff-advance-after-apply-hunk t 85(defcustom diff-advance-after-apply-hunk t
89 "Non-nil means `diff-apply-hunk' will move to the next hunk after applying." 86 "Non-nil means `diff-apply-hunk' will move to the next hunk after applying."
90 :type 'boolean 87 :type 'boolean)
91 :group 'diff-mode)
92 88
93(defcustom diff-mode-hook nil 89(defcustom diff-mode-hook nil
94 "Run after setting up the `diff-mode' major mode." 90 "Run after setting up the `diff-mode' major mode."
95 :type 'hook 91 :type 'hook
96 :options '(diff-delete-empty-files diff-make-unified) 92 :options '(diff-delete-empty-files diff-make-unified))
97 :group 'diff-mode) 93
94(defcustom diff-font-lock-refine t
95 "If non-nil, font-lock highlighting includes hunk refinement."
96 :version "27.1"
97 :type 'boolean)
98 98
99(defvar diff-vc-backend nil 99(defvar diff-vc-backend nil
100 "The VC backend that created the current Diff buffer, if any.") 100 "The VC backend that created the current Diff buffer, if any.")
@@ -207,8 +207,7 @@ when editing big diffs)."
207 207
208(defcustom diff-minor-mode-prefix "\C-c=" 208(defcustom diff-minor-mode-prefix "\C-c="
209 "Prefix key for `diff-minor-mode' commands." 209 "Prefix key for `diff-minor-mode' commands."
210 :type '(choice (string "\e") (string "C-c=") string) 210 :type '(choice (string "\e") (string "C-c=") string))
211 :group 'diff-mode)
212 211
213(easy-mmode-defmap diff-minor-mode-map 212(easy-mmode-defmap diff-minor-mode-map
214 `((,diff-minor-mode-prefix . ,diff-mode-shared-map)) 213 `((,diff-minor-mode-prefix . ,diff-mode-shared-map))
@@ -238,8 +237,7 @@ well."
238 (((class color)) 237 (((class color))
239 :foreground "blue1" :weight bold) 238 :foreground "blue1" :weight bold)
240 (t :weight bold)) 239 (t :weight bold))
241 "`diff-mode' face inherited by hunk and index header faces." 240 "`diff-mode' face inherited by hunk and index header faces.")
242 :group 'diff-mode)
243 241
244(defface diff-file-header 242(defface diff-file-header
245 '((((class color) (min-colors 88) (background light)) 243 '((((class color) (min-colors 88) (background light))
@@ -249,18 +247,15 @@ well."
249 (((class color)) 247 (((class color))
250 :foreground "cyan" :weight bold) 248 :foreground "cyan" :weight bold)
251 (t :weight bold)) ; :height 1.3 249 (t :weight bold)) ; :height 1.3
252 "`diff-mode' face used to highlight file header lines." 250 "`diff-mode' face used to highlight file header lines.")
253 :group 'diff-mode)
254 251
255(defface diff-index 252(defface diff-index
256 '((t :inherit diff-file-header)) 253 '((t :inherit diff-file-header))
257 "`diff-mode' face used to highlight index header lines." 254 "`diff-mode' face used to highlight index header lines.")
258 :group 'diff-mode)
259 255
260(defface diff-hunk-header 256(defface diff-hunk-header
261 '((t :inherit diff-header)) 257 '((t :inherit diff-header))
262 "`diff-mode' face used to highlight hunk header lines." 258 "`diff-mode' face used to highlight hunk header lines.")
263 :group 'diff-mode)
264 259
265(defface diff-removed 260(defface diff-removed
266 '((default 261 '((default
@@ -271,8 +266,7 @@ well."
271 :background "#553333") 266 :background "#553333")
272 (((class color)) 267 (((class color))
273 :foreground "red")) 268 :foreground "red"))
274 "`diff-mode' face used to highlight removed lines." 269 "`diff-mode' face used to highlight removed lines.")
275 :group 'diff-mode)
276 270
277(defface diff-added 271(defface diff-added
278 '((default 272 '((default
@@ -283,40 +277,34 @@ well."
283 :background "#335533") 277 :background "#335533")
284 (((class color)) 278 (((class color))
285 :foreground "green")) 279 :foreground "green"))
286 "`diff-mode' face used to highlight added lines." 280 "`diff-mode' face used to highlight added lines.")
287 :group 'diff-mode)
288 281
289(defface diff-changed 282(defface diff-changed
290 '((t nil)) 283 '((t nil))
291 "`diff-mode' face used to highlight changed lines." 284 "`diff-mode' face used to highlight changed lines."
292 :version "25.1" 285 :version "25.1")
293 :group 'diff-mode)
294 286
295(defface diff-indicator-removed 287(defface diff-indicator-removed
296 '((t :inherit diff-removed)) 288 '((t :inherit diff-removed))
297 "`diff-mode' face used to highlight indicator of removed lines (-, <)." 289 "`diff-mode' face used to highlight indicator of removed lines (-, <)."
298 :group 'diff-mode
299 :version "22.1") 290 :version "22.1")
300(defvar diff-indicator-removed-face 'diff-indicator-removed) 291(defvar diff-indicator-removed-face 'diff-indicator-removed)
301 292
302(defface diff-indicator-added 293(defface diff-indicator-added
303 '((t :inherit diff-added)) 294 '((t :inherit diff-added))
304 "`diff-mode' face used to highlight indicator of added lines (+, >)." 295 "`diff-mode' face used to highlight indicator of added lines (+, >)."
305 :group 'diff-mode
306 :version "22.1") 296 :version "22.1")
307(defvar diff-indicator-added-face 'diff-indicator-added) 297(defvar diff-indicator-added-face 'diff-indicator-added)
308 298
309(defface diff-indicator-changed 299(defface diff-indicator-changed
310 '((t :inherit diff-changed)) 300 '((t :inherit diff-changed))
311 "`diff-mode' face used to highlight indicator of changed lines." 301 "`diff-mode' face used to highlight indicator of changed lines."
312 :group 'diff-mode
313 :version "22.1") 302 :version "22.1")
314(defvar diff-indicator-changed-face 'diff-indicator-changed) 303(defvar diff-indicator-changed-face 'diff-indicator-changed)
315 304
316(defface diff-function 305(defface diff-function
317 '((t :inherit diff-header)) 306 '((t :inherit diff-header))
318 "`diff-mode' face used to highlight function names produced by \"diff -p\"." 307 "`diff-mode' face used to highlight function names produced by \"diff -p\".")
319 :group 'diff-mode)
320 308
321(defface diff-context 309(defface diff-context
322 '((((class color grayscale) (min-colors 88) (background light)) 310 '((((class color grayscale) (min-colors 88) (background light))
@@ -324,13 +312,11 @@ well."
324 (((class color grayscale) (min-colors 88) (background dark)) 312 (((class color grayscale) (min-colors 88) (background dark))
325 :foreground "#dddddd")) 313 :foreground "#dddddd"))
326 "`diff-mode' face used to highlight context and other side-information." 314 "`diff-mode' face used to highlight context and other side-information."
327 :version "25.1" 315 :version "25.1")
328 :group 'diff-mode)
329 316
330(defface diff-nonexistent 317(defface diff-nonexistent
331 '((t :inherit diff-file-header)) 318 '((t :inherit diff-file-header))
332 "`diff-mode' face used to highlight nonexistent files in recursive diffs." 319 "`diff-mode' face used to highlight nonexistent files in recursive diffs.")
333 :group 'diff-mode)
334 320
335(defconst diff-yank-handler '(diff-yank-function)) 321(defconst diff-yank-handler '(diff-yank-function))
336(defun diff-yank-function (text) 322(defun diff-yank-function (text)
@@ -409,7 +395,8 @@ and the face `diff-added' for added lines.")
409 ("^\\(#\\)\\(.*\\)" 395 ("^\\(#\\)\\(.*\\)"
410 (1 font-lock-comment-delimiter-face) 396 (1 font-lock-comment-delimiter-face)
411 (2 font-lock-comment-face)) 397 (2 font-lock-comment-face))
412 ("^[^-=+*!<>#].*\n" (0 'diff-context)))) 398 ("^[^-=+*!<>#].*\n" (0 'diff-context))
399 (,#'diff--font-lock-refined)))
413 400
414(defconst diff-font-lock-defaults 401(defconst diff-font-lock-defaults
415 '(diff-font-lock-keywords t nil nil nil (font-lock-multiline . nil))) 402 '(diff-font-lock-keywords t nil nil nil (font-lock-multiline . nil)))
@@ -1964,8 +1951,7 @@ For use in `add-log-current-defun-function'."
1964 (((class color) (min-colors 88) (background dark)) 1951 (((class color) (min-colors 88) (background dark))
1965 :background "#aaaa22") 1952 :background "#aaaa22")
1966 (t :inverse-video t)) 1953 (t :inverse-video t))
1967 "Face used for char-based changes shown by `diff-refine-hunk'." 1954 "Face used for char-based changes shown by `diff-refine-hunk'.")
1968 :group 'diff-mode)
1969 1955
1970(defface diff-refine-removed 1956(defface diff-refine-removed
1971 '((default 1957 '((default
@@ -1975,7 +1961,6 @@ For use in `add-log-current-defun-function'."
1975 (((class color) (min-colors 88) (background dark)) 1961 (((class color) (min-colors 88) (background dark))
1976 :background "#aa2222")) 1962 :background "#aa2222"))
1977 "Face used for removed characters shown by `diff-refine-hunk'." 1963 "Face used for removed characters shown by `diff-refine-hunk'."
1978 :group 'diff-mode
1979 :version "24.3") 1964 :version "24.3")
1980 1965
1981(defface diff-refine-added 1966(defface diff-refine-added
@@ -1986,7 +1971,6 @@ For use in `add-log-current-defun-function'."
1986 (((class color) (min-colors 88) (background dark)) 1971 (((class color) (min-colors 88) (background dark))
1987 :background "#22aa22")) 1972 :background "#22aa22"))
1988 "Face used for added characters shown by `diff-refine-hunk'." 1973 "Face used for added characters shown by `diff-refine-hunk'."
1989 :group 'diff-mode
1990 :version "24.3") 1974 :version "24.3")
1991 1975
1992(defun diff-refine-preproc () 1976(defun diff-refine-preproc ()
@@ -2013,59 +1997,99 @@ Return new point, if it was moved."
2013(defun diff-refine-hunk () 1997(defun diff-refine-hunk ()
2014 "Highlight changes of hunk at point at a finer granularity." 1998 "Highlight changes of hunk at point at a finer granularity."
2015 (interactive) 1999 (interactive)
2016 (require 'smerge-mode)
2017 (when (diff--some-hunks-p) 2000 (when (diff--some-hunks-p)
2018 (save-excursion 2001 (save-excursion
2019 (diff-beginning-of-hunk t) 2002 (let ((beg (diff-beginning-of-hunk t))
2020 (let* ((start (point)) 2003 ;; Be careful to start from the hunk header so diff-end-of-hunk
2021 (style (diff-hunk-style)) ;Skips the hunk header as well. 2004 ;; gets to read the hunk header's line info.
2022 (beg (point)) 2005 (end (progn (diff-end-of-hunk) (point))))
2023 (props-c '((diff-mode . fine) (face diff-refine-changed))) 2006 (diff--refine-hunk beg end)))))
2024 (props-r '((diff-mode . fine) (face diff-refine-removed))) 2007
2025 (props-a '((diff-mode . fine) (face diff-refine-added))) 2008(defun diff--refine-hunk (start end)
2026 ;; Be careful to go back to `start' so diff-end-of-hunk gets 2009 (require 'smerge-mode)
2027 ;; to read the hunk header's line info. 2010 (goto-char start)
2028 (end (progn (goto-char start) (diff-end-of-hunk) (point)))) 2011 (let* ((style (diff-hunk-style)) ;Skips the hunk header as well.
2029 2012 (beg (point))
2030 (remove-overlays beg end 'diff-mode 'fine) 2013 (props-c '((diff-mode . fine) (face . diff-refine-changed)))
2031 2014 (props-r '((diff-mode . fine) (face . diff-refine-removed)))
2032 (goto-char beg) 2015 (props-a '((diff-mode . fine) (face . diff-refine-added))))
2033 (pcase style 2016
2034 (`unified 2017 (remove-overlays beg end 'diff-mode 'fine)
2035 (while (re-search-forward "^-" end t) 2018
2036 (let ((beg-del (progn (beginning-of-line) (point))) 2019 (goto-char beg)
2037 beg-add end-add) 2020 (pcase style
2038 (when (and (diff--forward-while-leading-char ?- end) 2021 (`unified
2039 ;; Allow for "\ No newline at end of file". 2022 (while (re-search-forward "^-" end t)
2040 (progn (diff--forward-while-leading-char ?\\ end) 2023 (let ((beg-del (progn (beginning-of-line) (point)))
2041 (setq beg-add (point))) 2024 beg-add end-add)
2042 (diff--forward-while-leading-char ?+ end) 2025 (when (and (diff--forward-while-leading-char ?- end)
2043 (progn (diff--forward-while-leading-char ?\\ end) 2026 ;; Allow for "\ No newline at end of file".
2044 (setq end-add (point)))) 2027 (progn (diff--forward-while-leading-char ?\\ end)
2045 (smerge-refine-regions beg-del beg-add beg-add end-add 2028 (setq beg-add (point)))
2046 nil #'diff-refine-preproc props-r props-a))))) 2029 (diff--forward-while-leading-char ?+ end)
2047 (`context 2030 (progn (diff--forward-while-leading-char ?\\ end)
2048 (let* ((middle (save-excursion (re-search-forward "^---"))) 2031 (setq end-add (point))))
2049 (other middle)) 2032 (smerge-refine-regions beg-del beg-add beg-add end-add
2050 (while (re-search-forward "^\\(?:!.*\n\\)+" middle t) 2033 nil #'diff-refine-preproc props-r props-a)))))
2051 (smerge-refine-regions (match-beginning 0) (match-end 0) 2034 (`context
2052 (save-excursion 2035 (let* ((middle (save-excursion (re-search-forward "^---")))
2053 (goto-char other) 2036 (other middle))
2054 (re-search-forward "^\\(?:!.*\n\\)+" end) 2037 (while (re-search-forward "^\\(?:!.*\n\\)+" middle t)
2055 (setq other (match-end 0)) 2038 (smerge-refine-regions (match-beginning 0) (match-end 0)
2056 (match-beginning 0)) 2039 (save-excursion
2057 other 2040 (goto-char other)
2058 (if diff-use-changed-face props-c) 2041 (re-search-forward "^\\(?:!.*\n\\)+" end)
2059 #'diff-refine-preproc 2042 (setq other (match-end 0))
2060 (unless diff-use-changed-face props-r) 2043 (match-beginning 0))
2061 (unless diff-use-changed-face props-a))))) 2044 other
2062 (_ ;; Normal diffs. 2045 (if diff-use-changed-face props-c)
2063 (let ((beg1 (1+ (point)))) 2046 #'diff-refine-preproc
2064 (when (re-search-forward "^---.*\n" end t) 2047 (unless diff-use-changed-face props-r)
2065 ;; It's a combined add&remove, so there's something to do. 2048 (unless diff-use-changed-face props-a)))))
2066 (smerge-refine-regions beg1 (match-beginning 0) 2049 (_ ;; Normal diffs.
2067 (match-end 0) end 2050 (let ((beg1 (1+ (point))))
2068 nil #'diff-refine-preproc props-r props-a))))))))) 2051 (when (re-search-forward "^---.*\n" end t)
2052 ;; It's a combined add&remove, so there's something to do.
2053 (smerge-refine-regions beg1 (match-beginning 0)
2054 (match-end 0) end
2055 nil #'diff-refine-preproc props-r props-a)))))))
2056
2057(defun diff--font-lock-refined (max)
2058 "Apply hunk refinement from font-lock."
2059 (when diff-font-lock-refine
2060 (when (get-char-property (point) 'diff--font-lock-refined)
2061 ;; Refinement works over a complete hunk, whereas font-lock limits itself
2062 ;; to highlighting smallish chunks between point..max, so we may be
2063 ;; called N times for a large hunk in which case we don't want to
2064 ;; rehighlight that hunk N times (especially since each highlighting
2065 ;; of a large hunk can itself take a long time, adding insult to injury).
2066 ;; So, after refining a hunk (including a failed attempt), we place an
2067 ;; overlay over the whole hunk to mark it as refined, to avoid redoing
2068 ;; the job redundantly when asked to highlight subsequent parts of the
2069 ;; same hunk.
2070 (goto-char (next-single-char-property-change
2071 (point) 'diff--font-lock-refined nil max)))
2072 (let* ((min (point))
2073 (beg (or (ignore-errors (diff-beginning-of-hunk))
2074 (ignore-errors (diff-hunk-next) (point))
2075 max)))
2076 (while (< beg max)
2077 (let ((end
2078 (save-excursion (goto-char beg) (diff-end-of-hunk) (point))))
2079 (if (< end min) (setq beg min))
2080 (unless (or (< end beg)
2081 (get-char-property beg 'diff--font-lock-refined))
2082 (diff--refine-hunk beg end)
2083 (let ((ol (make-overlay beg end)))
2084 (overlay-put ol 'diff--font-lock-refined t)
2085 (overlay-put ol 'evaporate t)
2086 (overlay-put ol 'modification-hooks
2087 '(diff--font-lock-refine--refresh))))
2088 (goto-char (max beg end))
2089 (setq beg (or (ignore-errors (diff-hunk-next) (point)) max)))))))
2090
2091(defun diff--font-lock-refine--refresh (ol _after _beg _end &optional _len)
2092 (delete-overlay ol))
2069 2093
2070(defun diff-undo (&optional arg) 2094(defun diff-undo (&optional arg)
2071 "Perform `undo', ignoring the buffer's read-only status." 2095 "Perform `undo', ignoring the buffer's read-only status."