From f698c22bffd8cd5dd5c98a86f0e143c4e184b4dc Mon Sep 17 00:00:00 2001 From: Matthias Meulien Date: Sat, 13 Nov 2021 12:08:58 +0100 Subject: [PATCH] Support for outline default state in Diff buffers * lisp/vc/diff-mode.el (diff-outline-default-state): Add custom variable that defines an outline state and apply that state in Diff buffers. --- lisp/vc/diff-mode.el | 92 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index e68aa2257d..01ea1c3994 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el @@ -50,7 +50,11 @@ ;; ;; - in diff-apply-hunk, strip context in replace-match to better ;; preserve markers and spacing. +;; ;; - Handle `diff -b' output in context->unified. +;; +;; - Support outlining files by name (eg to skip automatically +;; generated files like package-lock.json in Javascript projects). ;;; Code: (eval-when-compile (require 'cl-lib)) @@ -147,6 +151,27 @@ diff-font-lock-syntax (const :tag "Highlight syntax" t) (const :tag "Allow hunk-based fallback" hunk-also))) +(defcustom diff-outline-default-state nil + "If non-nil, some files or hunk are outlined. +Outlining is performed by Outline minor mode. + +If `hide-body', only file and hunk headings are visible. + +If `size-threshold', files whose hunks cover more than +`diff-file-outline-threshold' lines are outlined." + :version "29.1" + :type '(choice (const :tag "Don't outline " nil) + (const :tag "Outline hunks" hide-body) + (const :tag "Outline files with long hunks" size-threshold) + (function :tag "Custom function"))) + +(defcustom diff-file-outline-threshold 50 + "Number of lines of hunks for a file to be outlined. + +Used by `diff-outline-file-according-to-size'." + :version "29.1" + :type '(natnum :tag "Number of lines")) + (defvar diff-vc-backend nil "The VC backend that created the current Diff buffer, if any.") @@ -1578,7 +1603,8 @@ diff-setup-whitespace (defun diff-setup-buffer-type () "Try to guess the `diff-buffer-type' from content of current Diff mode buffer. -`outline-regexp' is updated accordingly." +`outline-regexp' is updated accordingly and outline default state +applied." (save-excursion (goto-char (point-min)) (setq-local diff-buffer-type @@ -1589,7 +1615,8 @@ diff-setup-buffer-type (setq diff-outline-regexp (concat "\\(^diff --git.*\n\\|" diff-hunk-header-re "\\)")) (setq-local outline-level #'diff--outline-level)) - (setq-local outline-regexp diff-outline-regexp)) + (setq-local outline-regexp diff-outline-regexp) + (diff-outline-apply-default-state)) (defun diff-delete-if-empty () ;; An empty diff file means there's no more diffs to integrate, so we @@ -2143,6 +2170,67 @@ diff-refresh-hunk (delete-file file1) (delete-file file2)))) +(defun diff-outline-apply-default-state () + "Apply the outline state defined by `diff-outline-default-state'. + +When `diff-outline-default-state' is non-nil, Outline minor mode +is enabled." + (when diff-outline-default-state + (when (not outline-minor-mode) + (outline-minor-mode)) + (cond + ((eq diff-outline-default-state 'size-threshold) + (diff-outline-file-according-to-size)) + ((eq diff-outline-default-state 'hide-body) + (outline-hide-body)) + ((when (functionp diff-outline-default-state) + (funcall diff-outline-default-state)))))) + +(defun diff-outline-file-according-to-size () + "Outline file with long hunks. + +A file is outlined when its hunks cover more than +`diff-file-outline-threshold' lines. Does nothing when Outline +minor mode is not enabled or `diff-file-outline-threshold'. + +Inspired by `outline-hide-sublevels'." + (interactive) + (when (and outline-minor-mode diff-file-outline-threshold) + (save-excursion + (let* (outline-view-change-hook + (beg (progn + (goto-char (point-min)) + ;; Skip the prelude, if any. + (unless (outline-on-heading-p t) (outline-next-heading)) + (point))) + (end (progn + (goto-char (point-max)) + ;; Keep empty last line, if available. + (if (bolp) (1- (point)) (point))))) + (if (< end beg) + (setq beg (prog1 end (setq end beg)))) + ;; First hide sublevels + (outline-hide-sublevels 1) + ;; Then unhide short subtrees + (outline-map-region + (lambda () + (when (= (funcall outline-level) 1) + (goto-char (match-end 0)) + (let ((overlays (overlays-at (point)))) + (while overlays + (let ((overlay (car overlays))) + (progn + (when (eq (overlay-get overlay 'invisible) 'outline) + (let ((size (count-lines + (overlay-end overlay) + (overlay-start overlay)))) + (goto-char (match-beginning 0)) + (if (< size diff-file-outline-threshold) + (outline-show-subtree) + (outline-show-branches)))) + (setq overlays (cdr overlays)))))))) + beg end))))) + ;;; Fine change highlighting. (defface diff-refine-changed -- 2.30.2