Subject: bug#51809: 29.0.50; [PATCH] Support for outline default state in Diff buffers

From: Juri Linkov <[email-masked]>
Date: Mon, 29 Nov 2021 19:06:40 +0200

Hi Matthias,

> Updated patch that takes Juri comments into account.

I didn't forget about your patch.  Actually, I have been using it all the time,
and it saved me in many cases when long lines in diffs make Emacs unresponsive.
With your patch, there are only slight delays for 2-3 seconds while scrolling
a lot of hidden outlines, but this is not a problem, thank you very much.

I have the same problem of too long outline-body lines in xref buffers,
so need to use a similar feature with:

#+begin_src emacs-lisp
(add-hook 'xref-after-update-hook
          (lambda ()
            (setq-local outline-regexp (if (eq xref-file-name-display 'abs)
                                           "/" "[^ 0-9]"))
            (outline-minor-mode +1)
            (outline-map-region
             (lambda ()
               (when (string-match-p "ChangeLog\\|test/manual/etags"
                                     (buffer-substring
                                      (line-beginning-position)
                                      (line-end-position)))
                 (outline-hide-entry)))
             (point-min) (point-max))))
#+end_src

It would be nice to move this feature completely to outline.el,
so it could be used by other modes, not only in diff-mode.
Please see bug#49731 that will be closed after this feature
will be supported generally by outline-minor-mode.

Then there are two variants: to add customizable variables
to outline-minor-mode like outline-default-state or
outline-hide-initial, or allow hiding initial heading
with a hook like

  (add-hook 'outline-minor-mode-hook 'outline-hide-file-headings)

that could use a regexp from e.g. outline-hide-heading-regexp
or some better name.

> +(defun outline-map-sublevel-overlay (level fun)

Instead of adding a new function, you can use outline-map-region
after adding a new argument, e.g.

  (defun outline-map-region (fun beg end &optional next-heading-fun)

By default, outline-map-region uses outline-next-heading to move to the
next heading, but a new argument could allow to use
outline-next-visible-heading or outline-forward-same-level, etc.
with (or (and next-heading-fun (funcall next-heading-fun)) (outline-next-heading))

> +(defcustom diff-outline-file-heading-regexp "ChangeLog\\|package-lock\\.json"

There is no need to add arbitrary default values.
The users know better what values are needed.
For example, I customized it to "public/packs",
so it hides the outline headings for compiled assets like:
"public/packs-pro/js/application-fa9d8202220130e40f46.js"

> +(defun diff-outline-apply-default-state ()
> +  (when diff-outline-default-state
> +    (when (not outline-minor-mode)
> +      (outline-minor-mode))

Actually, the above lines are not needed because the same can be achieved by:

  (add-hook 'diff-mode-hook 'outline-minor-mode)

> +    (cond
> +     ((eq diff-outline-default-state 'outline-hunks)
> +      (outline-hide-body))

These lines are not needed too, because the same can be achieved by:

  (add-hook 'outline-minor-mode-hook 'outline-hide-body)

> +     ((when (functionp diff-outline-default-state)
> +        (funcall diff-outline-default-state))))))

And this can be achieved by:

  (add-hook 'outline-minor-mode-hook 'custom-function)

> +(defun diff--outline-set-file-heading-visibility (overlay)
> +  (cond
> +   ((and
> +     (memq 'file-heading-regexp
> +           diff-outline-default-state)
> +     (string-match-p
> +      diff-outline-file-heading-regexp
> +      (match-string 0)))

Here (match-string 0) is unusable in most values of outline-regexp
that don't contain the whole heading line.  A better way
to get the whole heading line usable in modes other than diff-mode
would be:

  (buffer-substring (line-beginning-position) (line-end-position))