[DRAFT PATCH] emacs: Eliminate buffer invisibility specs from show and wash
Austin Clements
amdragon at MIT.EDU
Tue Dec 18 10:48:25 PST 2012
Quoth Mark Walters on Dec 18 at 6:14 pm:
> On Tue, 18 Dec 2012, Austin Clements <amdragon at MIT.EDU> wrote:
> > Previously, all visibility in show buffers for headers, message
> > bodies, and washed text was specified by generating one or more
> > symbols for each region and creating overlays with their 'invisible
> > property set to carefully crafted combinations of these symbols.
> > Visibility was controlled not by modifying the overlays directly, but
> > by adding and removing the generated symbols from a gigantic buffer
> > invisibilty spec.
> >
> > This has myriad negative consequences. It's slow because Emacs'
> > display engine has to traverse the buffer invisibility list for every
> > overlay and, since every overlay has its own symbol, this makes
> > rendering O(N^2) in the number of overlays. It composes poorly
> > because symbol-type 'invisible properties are taken from the highest
> > priority overlay over a given character (which is often ambiguous!),
> > rather than being gathered from all overlays over a character. As a
> > result, we have to include symbols related to message hiding in the
> > wash code lest the wash overlays un-hide parts of hidden messages. It
> > also requires various workarounds for isearch to properly open
> > overlays, to set up buffer-invisibility-spec for
> > remove-from-invisibility-spec to work right, and to explicitly refresh
> > the display after updating the buffer invisibility spec.
> >
> > None of this is necessary.
> >
> > This patch converts show and wash to use simple boolean 'invisible
> > properties and to not use the buffer invisibility spec. Rather than
> > adding and removing generated symbols from the invisibility spec, the
> > code now directly toggles the 'invisible property of the appropriate
> > overlay. This speeds up rendering because the display engine only has
> > to check the boolean values of the overlays over a character. It
> > composes nicely because text will be invisible if *any* overlay over
> > it has 'invisible t, which means we can overlap invisibility overlays
> > with abandon. We no longer need any of the workarounds mentioned
> > above. And it fixes a minor bug for free: now, when isearch opens a
> > washed region, the button text will update to say "Click/Enter to
> > hide" rather than remaining unchanged.
> > ---
> >
> > Mark's part toggling code got me thinking about how needlessly
> > complicated our other show-mode invisibility code was. This patch is
> > a shot at fixing that. It will require a bit of reworking after part
> > toggling goes in (owning to the aforementioned non-composability, part
> > toggling needs to be intimately aware of wash and message hiding).
> > However, I think this patch should wait until after the release
> > freeze; this code is fragile (though much less so after this patch),
> > so I'd rather it soak for a release than cause user-visible bugs for
> > no user-visible gain.
> >
> > emacs/notmuch-show.el | 42 +++------------------
> > emacs/notmuch-wash.el | 97 ++++++-------------------------------------------
> > 2 files changed, 17 insertions(+), 122 deletions(-)
> >
> > diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
> > index 7d9f8a9..4bdd5af 100644
> > --- a/emacs/notmuch-show.el
> > +++ b/emacs/notmuch-show.el
> > @@ -872,27 +872,8 @@ message at DEPTH in the current thread."
> > message-start message-end
> > content-start content-end
> > headers-start headers-end
> > - body-start body-end
> > - (headers-invis-spec (notmuch-show-make-symbol "header"))
> > - (message-invis-spec (notmuch-show-make-symbol "message"))
> > (bare-subject (notmuch-show-strip-re (plist-get headers :Subject))))
> >
> > - ;; Set `buffer-invisibility-spec' to `nil' (a list), otherwise
> > - ;; removing items from `buffer-invisibility-spec' (which is what
> > - ;; `notmuch-show-headers-visible' and
> > - ;; `notmuch-show-message-visible' do) is a no-op and has no
> > - ;; effect. This caused threads with only matching messages to have
> > - ;; those messages hidden initially because
> > - ;; `buffer-invisibility-spec' stayed `t'.
> > - ;;
> > - ;; This needs to be set here (rather than just above the call to
> > - ;; `notmuch-show-headers-visible') because some of the part
> > - ;; rendering or body washing functions
> > - ;; (e.g. `notmuch-wash-text/plain-citations') manipulate
> > - ;; `buffer-invisibility-spec').
> > - (when (eq buffer-invisibility-spec t)
> > - (setq buffer-invisibility-spec nil))
> > -
> > (setq message-start (point-marker))
> >
> > (notmuch-show-insert-headerline headers
> > @@ -904,9 +885,6 @@ message at DEPTH in the current thread."
> >
> > (setq content-start (point-marker))
> >
> > - (plist-put msg :headers-invis-spec headers-invis-spec)
> > - (plist-put msg :message-invis-spec message-invis-spec)
> > -
> > ;; Set `headers-start' to point after the 'Subject:' header to be
> > ;; compatible with the existing implementation. This just sets it
> > ;; to after the first header.
> > @@ -924,7 +902,6 @@ message at DEPTH in the current thread."
> >
> > (setq notmuch-show-previous-subject bare-subject)
> >
> > - (setq body-start (point-marker))
> > ;; A blank line between the headers and the body.
> > (insert "\n")
> > (notmuch-show-insert-body msg (plist-get msg :body)
> > @@ -932,7 +909,6 @@ message at DEPTH in the current thread."
> > ;; Ensure that the body ends with a newline.
> > (unless (bolp)
> > (insert "\n"))
> > - (setq body-end (point-marker))
> > (setq content-end (point-marker))
> >
> > ;; Indent according to the depth in the thread.
> > @@ -945,11 +921,9 @@ message at DEPTH in the current thread."
> > ;; message.
> > (put-text-property message-start message-end :notmuch-message-extent (cons message-start message-end))
> >
> > - (let ((headers-overlay (make-overlay headers-start headers-end))
> > - (invis-specs (list headers-invis-spec message-invis-spec)))
> > - (overlay-put headers-overlay 'invisible invis-specs)
> > - (overlay-put headers-overlay 'priority 10))
> > - (overlay-put (make-overlay body-start body-end) 'invisible message-invis-spec)
> > + ;; Create overlays used to control visibility
> > + (plist-put msg :headers-overlay (make-overlay headers-start headers-end))
> > + (plist-put msg :message-overlay (make-overlay headers-start content-end))
>
> I am puzzled by this (though it was essentially the same before):
> http://www.gnu.org/software/emacs/manual/html_node/elisp/Other-Plists.html
> says that "This stores value as the value of the property property in
> the property list plist. It may modify plist destructively, or it may
> construct a new list structure without altering the old." Does this mean
> that this might work but it might not?
Yeah, I know. The show code does this all over the place, so I was
being consistent. Code like this depends on unspecified behavior, but
it is reliable given the actual implementation of plist-put, which
will always add new properties to the end of the plist by mutation.
Hence the only time the setq is *actually* necessary is when
plist-put-ing to an empty plist, and msg will never be an empty plist.
> Otherwise, though, this looks good to me.
>
> My inclination is to get this in and then do the part invisibility on
> top. One reason is that I think we definitely want to go this route
> soon and if we do it just after the next release we may end up with bugs
> being reported (bugs in my invisibility stuff) that there's not much
> point in fixing as mainline has diverged.
>
> Best wishes
>
> Mark
More information about the notmuch
mailing list