[RFC PATCH 1/4] emacs: simplify point placement in notmuch-hello

Jani Nikula jani at nikula.org
Tue Apr 17 02:58:11 PDT 2012


On Tue, 17 Apr 2012 13:04:39 +0400, Dmitry Kurochkin <dmitry.kurochkin at gmail.com> wrote:
> Hi Jani.
> 
> Jani Nikula <jani at nikula.org> writes:
> 
> > notmuch-hello (called also through notmuch-hello-update, bound to '='
> > by default) tries to find the widget under or following point before
> > refresh, and put the point back to the widget afterwards. The code has
> > gotten a bit complicated, and has at least the following issues:
> >
> > 1) All the individual section functions have to include code to
> >    support point placement. If there is no such support, point is
> >    dropped to the search box. Only saved searches and all tags
> >    sections support point placement.
> >
> > 2) Point placement is based on widget-value. If there are two widgets
> >    with the same widget-value (for example a saved search with the
> >    same name as a tag) the point is moved to the earlier one.
> >
> > 3) When first entering notmuch-hello notmuch-hello-target is nil, and
> >    point is dropped to the search box.
> >
> > This patch simplifies the code by removing all point placement based
> > on widgets. Point is simply saved before refresh, and put back to
> > where it was. Sometimes, but not very often, this would have the
> > appearance of moving the point relative to the nearest widgets. IMHO
> > this is a minor problem compared to the issues listed above.
> >
> > A downside is that there's no visual cue (point movement) to indicate
> > that refresh has finished. Then again, neither was there before, if
> > point was at the beginning of a widget.
> 
> Thanks for looking into this.  This is an annoying issue indeed.  And I
> was thinking about fixing it myself.
> 
> I am not sure I like the approach of moving the cursor to the same
> position.  It is common that buffer content would change significantly
> after a refresh (e.g. after I archived all new mail).  That would mean
> the cursor would just randomly jump somewhere.  IMO we should allow
> smart cursor positioning which means that logic should go to individual
> sections.
>  I would propose the following plan:
> 
> 1. Remove special case for search box.  No section should be special.
>    Moreover it is possible to remove it (I did it) and in that case the
>    cursor would be left at the end of the buffer.  By default, the
>    cursor should be moved to the beginning of the buffer.
> 
> 2. Replace current cursor positioning logic with section specific code.
>    I.e. `notmuch-hello' would not do any cursor positioning (except for
>    item 1) but queries and tags section would save required state when a
>    button is clicked and the same section would use this state to
>    restore cursor position on refresh.  What state should be saved would
>    depend on the section but we should at least save the section
>    name/ID.  If during refresh no section set the cursor position, then
>    the cursor is moved to the beginning of the buffer.
> 
> 3. Provide a custom variable to set the default section to move the
>    cursor to.  I.e. set the section name/ID part of the state from item
>    2.  Again, details on what the default position inside the section is
>    would depend on the section.  For search box, it would be the input
>    field.  For queries/tags it would be the first tag.
> 
> Item 1 is pretty simple.  The rest may be more tricky.  What do you
> think?

My approach was this: keep it extremely simple, and catch the low
hanging fruit. It places the point where I want, say, 90% of the
time. And when it's wrong, it's not far off. In contrast, the current
implementation places the cursor exactly where I do *not* want it about
half the time.

What you describe sounds smart, but complicated. Maybe it just sounds
complicated from my limited lisp skills perspective. Personally I don't
think sections should have to implement their own logic for point
placement. And as a fallback, I prefer keeping the point where it is
instead of moving it to the beginning of buffer.

I don't oppose to your plan, but I don't think I'm up to implementing it
either. I just cooked up something together to fix the #1 annoyance I've
lately had with notmuch, and Tomi persuaded me to share the patches as
RFC. I still think it's pretty good, considering the simplicity of it
all, but certainly not perfect.


BR,
Jani.


> 
> Regards,
>   Dmitry
> 
> > ---
> >  emacs/notmuch-hello.el |   70 +++++++++++------------------------------------
> >  1 files changed, 17 insertions(+), 53 deletions(-)
> >
> > diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
> > index 71d37b8..9cd907a 100644
> > --- a/emacs/notmuch-hello.el
> > +++ b/emacs/notmuch-hello.el
> > @@ -154,11 +154,6 @@ International Bureau of Weights and Measures."
> >  (defvar notmuch-hello-url "http://notmuchmail.org"
> >    "The `notmuch' web site.")
> >  
> > -(defvar notmuch-hello-search-pos nil
> > -  "Position of search widget, if any.
> > -
> > -This should only be set by `notmuch-hello-insert-search'.")
> > -
> >  (defvar notmuch-hello-custom-section-options
> >    '((:filter (string :tag "Filter for each tag"))
> >      (:filter-count (string :tag "Different filter to generate message counts"))
> > @@ -209,11 +204,8 @@ function produces a section simply by adding content to the current
> >  buffer.  A section should not end with an empty line, because a
> >  newline will be inserted after each section by `notmuch-hello'.
> >  
> > -Each function should take no arguments.  If the produced section
> > -includes `notmuch-hello-target' (i.e. cursor should be positioned
> > -inside this section), the function should return this element's
> > -position.
> > -Otherwise, it should return nil.
> > +Each function should take no arguments. The return value is
> > +ignored.
> >  
> >  For convenience an element can also be a list of the form (FUNC ARG1
> >  ARG2 .. ARGN) in which case FUNC will be applied to the rest of the
> > @@ -240,15 +232,6 @@ supported for \"Customized queries section\" items."
> >  	    notmuch-hello-query-section
> >  	    (function :tag "Custom section"))))
> >  
> > -(defvar notmuch-hello-target nil
> > -  "Button text at position of point before rebuilding the notmuch-buffer.
> > -
> > -This variable contains the text of the button, if any, the
> > -point was positioned at before the notmuch-hello buffer was
> > -rebuilt. This should never actually be global and is defined as a
> > -defvar only for documentation purposes and to avoid a compiler
> > -warning about it occurring as a free variable.")
> > -
> >  (defvar notmuch-hello-hidden-sections nil
> >    "List of sections titles whose contents are hidden")
> >  
> > @@ -449,8 +432,6 @@ Such a list can be computed with `notmuch-hello-query-counts'."
> >  		     (msg-count (third elem)))
> >  		(widget-insert (format "%8s "
> >  				       (notmuch-hello-nice-number msg-count)))
> > -		(if (string= name notmuch-hello-target)
> > -		    (setq found-target-pos (point-marker)))
> >  		(widget-create 'push-button
> >  			       :notify #'notmuch-hello-widget-search
> >  			       :notmuch-search-terms query
> > @@ -589,7 +570,6 @@ Complete list of currently available key bindings:
> >  (defun notmuch-hello-insert-search ()
> >    "Insert a search widget."
> >    (widget-insert "Search: ")
> > -  (setq notmuch-hello-search-pos (point-marker))
> >    (widget-create 'editable-field
> >  		 ;; Leave some space at the start and end of the
> >  		 ;; search boxes.
> > @@ -763,13 +743,7 @@ following:
> >        (set-buffer "*notmuch-hello*")
> >      (switch-to-buffer "*notmuch-hello*"))
> >  
> > -  (let ((notmuch-hello-target (if (widget-at)
> > -				  (widget-value (widget-at))
> > -				(condition-case nil
> > -				    (progn
> > -				      (widget-forward 1)
> > -				      (widget-value (widget-at)))
> > -				  (error nil))))
> > +  (let ((final-target-pos (point))
> >  	(inhibit-read-only t))
> >  
> >      ;; Delete all editable widget fields.  Editable widget fields are
> > @@ -788,30 +762,20 @@ following:
> >        (mapc 'delete-overlay (car all))
> >        (mapc 'delete-overlay (cdr all)))
> >  
> > -    (let (final-target-pos)
> > -      (mapc
> > -       (lambda (section)
> > -	 (let ((point-before (point))
> > -	       (result (if (functionp section)
> > -			   (funcall section)
> > -			 (apply (car section) (cdr section)))))
> > -	   (if (and (not final-target-pos) (integer-or-marker-p result))
> > -	       (setq final-target-pos result))
> > -	   ;; don't insert a newline when the previous section didn't show
> > -	   ;; anything.
> > -	   (unless (eq (point) point-before)
> > -	     (widget-insert "\n"))))
> > -       notmuch-hello-sections)
> > -      (widget-setup)
> > -
> > -      (when final-target-pos
> > -	(goto-char final-target-pos)
> > -	(unless (widget-at)
> > -	  (widget-forward 1)))
> > -
> > -      (unless (widget-at)
> > -	(when notmuch-hello-search-pos
> > -	  (goto-char notmuch-hello-search-pos)))))
> > +    (mapc
> > +     (lambda (section)
> > +       (let ((point-before (point)))
> > +	 (if (functionp section)
> > +	     (funcall section)
> > +	   (apply (car section) (cdr section)))
> > +	 ;; don't insert a newline when the previous section didn't
> > +	 ;; show anything.
> > +	 (unless (eq (point) point-before)
> > +	   (widget-insert "\n"))))
> > +     notmuch-hello-sections)
> > +    (widget-setup)
> > +
> > +    (goto-char final-target-pos))
> >    (run-hooks 'notmuch-hello-refresh-hook)
> >    (setq notmuch-hello-first-run nil))
> >  
> > -- 
> > 1.7.1
> >
> > _______________________________________________
> > notmuch mailing list
> > notmuch at notmuchmail.org
> > http://notmuchmail.org/mailman/listinfo/notmuch


More information about the notmuch mailing list