unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [PATCH v4 0/2] emacs: User-defined sections in notmuch-hello
       [not found] <id:"1309379221-5617-1-git-send-email-daniel.schoepe@googlemail.com">
@ 2011-07-07 22:53 ` Daniel Schoepe
  2011-07-07 22:53   ` [PATCH v4 1/2] " Daniel Schoepe
                     ` (4 more replies)
  0 siblings, 5 replies; 57+ messages in thread
From: Daniel Schoepe @ 2011-07-07 22:53 UTC (permalink / raw)
  To: notmuch

Fourth time's the charm. Some more improvements by Michal Sojka, thanks.

A few more opinions about whether or not to leave in the different
filters for queries and counts, would also be very much appreciated.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v4 1/2] emacs: User-defined sections in notmuch-hello
  2011-07-07 22:53 ` [PATCH v4 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
@ 2011-07-07 22:53   ` Daniel Schoepe
  2011-07-08 23:00     ` Michal Sojka
  2011-07-07 22:53   ` [PATCH v4 2/2] emacs: Tests for user-defined sections Daniel Schoepe
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 57+ messages in thread
From: Daniel Schoepe @ 2011-07-07 22:53 UTC (permalink / raw)
  To: notmuch

This patch makes the notmuch-hello screen fully customizable
by allowing the user to add and remove arbitrary sections. It
also provides some convenience functions for constructing sections,
e.g. showing the unread message count for each tag.

This is done by specifying a list of functions that will be run
when notmuch-hello is invoked.
---
 emacs/notmuch-hello.el |  617 ++++++++++++++++++++++++++++++++----------------
 1 files changed, 416 insertions(+), 201 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index 65fde75..434eded 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -70,11 +70,6 @@ should return a filter for that tag, or nil to hide the tag."
 		 (const :tag "Custom filter function" function))
   :group 'notmuch)
 
-(defcustom notmuch-hello-hide-tags nil
-  "List of tags to be hidden in the \"all tags\"-section."
-  :type '(repeat string)
-  :group 'notmuch)
-
 (defface notmuch-hello-logo-background
   '((((class color)
       (background dark))
@@ -123,6 +118,94 @@ Typically \",\" in the US and UK and \".\" in Europe."
 
 (defvar notmuch-hello-recent-searches nil)
 
+(defvar notmuch-custom-section-options
+  '((:make-query (string :tag "Filter for each tag"))
+    (:make-count (string :tag "Different filter message counts"))
+    (:initially-hidden (boolean :tag "Hide this on startup?"))
+    (:show-empty-searches (boolean :tag "Show queries with no matching messages"))
+    (:hide-if-empty (boolean :tag "Hide if empty")))
+  "Various customization-options for notmuch-hello-tags/query-section.")
+
+(define-widget 'notmuch-hello-tags-section 'lazy
+  "Customize-type for notmuch-hello tag-list sections."
+  :tag "Customized tag-list (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-tags-section)
+	 (string :tag "Title for this section")
+	 (plist
+	  :inline t
+	  :options
+	  ,(append notmuch-custom-section-options
+		   '((:hide-tags (repeat :tag "Tags that will be hidden"
+					 string)))))))
+
+(define-widget 'notmuch-hello-query-section 'lazy
+  "Customize-type for custom saved-search-like sections"
+  :tag "Customized queries section (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-query-list)
+	 (string :tag "Title for this section")
+	 (repeat :tag "Queries"
+		 (cons (string :tag "Name") (string :tag "Query")))
+	 (plist :inline t :options ,notmuch-custom-section-options)))
+
+(defcustom notmuch-hello-sections
+  (list #'notmuch-hello-insert-header
+	#'notmuch-hello-insert-saved-searches
+	#'notmuch-hello-insert-search
+	#'notmuch-hello-insert-recent-searches
+	#'notmuch-hello-insert-alltags
+	#'notmuch-hello-insert-footer)
+  "Sections for notmuch-hello.
+
+Each entry of this list should be a function of no arguments that
+should return if `notmuch-hello-target' is produced as part of its
+output and nil otherwise. 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 list.
+
+The functions will be run to construct the content of the
+notmuch-hello buffer in the order they appear in this list. A
+section should not end with an empty line, because a newline will
+be inserted after each section by `notmuch-hello'.
+
+A \"Customized tag-list\" item in the customize-interface
+displays a list of all tags, optionally hiding some of them. It
+is also possible to filter the list of messages matching each tag
+by an additional filter query. Similarly, the count of messages
+displayed next to the buttons can be generated by applying a
+different filter to the tag query. These filters are also
+supported for \"Customized queries section\" items."
+  :group 'notmuch
+  :type 
+  '(repeat
+    (choice (function-item notmuch-hello-insert-header)
+	    (function-item notmuch-hello-insert-saved-searches)
+	    (function-item notmuch-hello-insert-search)
+	    (function-item notmuch-hello-insert-recent-searches)
+	    (function-item notmuch-hello-insert-alltags)
+	    (function-item notmuch-hello-insert-footer)
+	    (function-item notmuch-hello-insert-inbox)
+	    notmuch-hello-tags-section
+	    notmuch-hello-query-section
+	    (function :tag "Custom function"))))
+
+(defvar notmuch-hello-target nil
+  "Button at position of point before rebuilding the notmuch-buffer
+
+This variable contains the string of the button, if any, the
+point was positioned at before the notmuch-hello buffer was
+rebuilt. This is never actually set globally and 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 query section titles whose contents are hidden")
+
+(defvar notmuch-hello-first-run t)
+
 (defun notmuch-hello-remember-search (search)
   (if (not (member search notmuch-hello-recent-searches))
       (push search notmuch-hello-recent-searches))
@@ -173,8 +256,8 @@ Typically \",\" in the US and UK and \".\" in Europe."
     (message "Saved '%s' as '%s'." search name)
     (notmuch-hello-update)))
 
-(defun notmuch-hello-longest-label (tag-alist)
-  (or (loop for elem in tag-alist
+(defun notmuch-hello-longest-label (searches-alist)
+  (or (loop for elem in searches-alist
 	    maximize (length (car elem)))
       0))
 
@@ -238,12 +321,71 @@ should be. Returns a cons cell `(tags-per-line width)'."
 				   (* tags-per-line (+ 9 1))))
 			   tags-per-line))))
 
-(defun notmuch-hello-insert-tags (tag-alist widest target)
-  (let* ((tags-and-width (notmuch-hello-tags-per-line widest))
+(defun notmuch-hello-filtered-query (query filter)
+  "Constructs a query to search all messages matching QUERY and FILTER.
+
+If FILTER is a string, it is directly used in the returned query.
+
+If FILTER is a function, it is called with QUERY as a parameter and
+the string it returns is used as the filter.
+
+Otherwise, FILTER is ignored.
+"
+  (cond
+   ((functionp filter)
+    (let ((result (funcall filter query)))
+      (and result (concat query " and (" result ")"))))
+   ((stringp filter)
+    (concat query " and (" filter ")"))
+   (t (concat query))))
+
+(defun notmuch-hello-query-counts (query-alist &rest options)
+  "Compute list of counts of matched messages from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated query.
+
+The result is the list of elements of the form (NAME QUERY COUNT).
+
+The values :show-empty-searches, :make-query and :make-count from
+options will be handled as specified for
+`notmuch-hello-insert-searches'."
+  (notmuch-remove-if-not
+   #'identity
+   (mapcar
+    (lambda (elem)
+      (let* ((name (car elem))
+	     (query-and-count (if (consp (cdr elem))
+				  ;; do we have a different query for the message count?
+				  (cons (second elem) (third elem))
+				(cons (cdr elem) (cdr elem))))
+	     (message-count
+	      (string-to-number
+	       (notmuch-saved-search-count
+		(notmuch-hello-filtered-query (cdr query-and-count)
+					      (or (plist-get options :make-count)
+						 (plist-get options :make-query)))))))
+	(and (or (plist-get options :show-empty-searches) (> message-count 0))
+	   (list name (notmuch-hello-filtered-query
+		       (car query-and-count) (plist-get options :make-query))
+		 message-count))))
+    query-alist)))
+
+(defun notmuch-hello-insert-buttons (searches)
+  "Insert buttons for SEARCHES.
+
+SEARCHES must be a list containing lists of the form (NAME QUERY COUNT), where
+QUERY is the query to start when the button for the corresponding entry is
+activated. COUNT should be the number of messages matching the query.
+Such a list can be computed with `notmuch-hello-query-counts'."
+  (let* ((widest (notmuch-hello-longest-label searches))
+	 (tags-and-width (notmuch-hello-tags-per-line widest))
 	 (tags-per-line (car tags-and-width))
 	 (widest (cdr tags-and-width))
 	 (count 0)
-	 (reordered-list (notmuch-hello-reflect tag-alist tags-per-line))
+	 (reordered-list (notmuch-hello-reflect searches tags-per-line))
 	 ;; Hack the display of the buttons used.
 	 (widget-push-button-prefix "")
 	 (widget-push-button-suffix "")
@@ -253,13 +395,13 @@ should be. Returns a cons cell `(tags-per-line width)'."
     (mapc (lambda (elem)
 	    ;; (not elem) indicates an empty slot in the matrix.
 	    (when elem
-	      (let* ((name (car elem))
-		     (query (cdr elem))
+	      (let* ((name (first elem))
+		     (query (second elem))
+		     (count (third elem))
 		     (formatted-name (format "%s " name)))
 		(widget-insert (format "%8s "
-				       (notmuch-hello-nice-number
-					(string-to-number (notmuch-saved-search-count query)))))
-		(if (string= formatted-name target)
+				       (notmuch-hello-nice-number count)))
+		(if (string= formatted-name notmuch-hello-target)
 		    (setq found-target-pos (point-marker)))
 		(widget-create 'push-button
 			       :notify #'notmuch-hello-widget-search
@@ -277,7 +419,7 @@ should be. Returns a cons cell `(tags-per-line width)'."
 	    (setq count (1+ count))
 	    (if (eq (% count tags-per-line) 0)
 		(widget-insert "\n")))
-	  reordered-list)
+	  searches)
 
     ;; If the last line was not full (and hence did not include a
     ;; carriage return), insert one now.
@@ -325,59 +467,272 @@ should be. Returns a cons cell `(tags-per-line width)'."
 (fset 'notmuch-hello-mode-map notmuch-hello-mode-map)
 
 (defun notmuch-hello-mode ()
- "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
+  "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
 
 Complete list of currently available key bindings:
 
 \\{notmuch-hello-mode-map}"
- (interactive)
- (kill-all-local-variables)
- (use-local-map notmuch-hello-mode-map)
- (setq major-mode 'notmuch-hello-mode
-       mode-name "notmuch-hello")
- ;;(setq buffer-read-only t)
-)
-
-(defun notmuch-hello-generate-tag-alist ()
+  (interactive)
+  (kill-all-local-variables)
+  (use-local-map notmuch-hello-mode-map)
+  (setq major-mode 'notmuch-hello-mode
+	mode-name "notmuch-hello"))
+
+(defun notmuch-hello-generate-tag-alist (&optional hide-tags filter-query filter-count)
   "Return an alist from tags to queries to display in the all-tags section."
   (notmuch-remove-if-not
-   #'cdr
+   #'identity
    (mapcar (lambda (tag)
-	     (cons tag
-		   (cond
-		    ((functionp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     (funcall notmuch-hello-tag-list-make-query tag) ")"))
-		    ((stringp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     notmuch-hello-tag-list-make-query ")"))
-		    (t (concat "tag:" tag)))))
+	     (let ((query (notmuch-hello-filtered-query (concat "tag:" tag)
+							filter-query)))
+	       (when query
+		 (if filter-count
+		     (list tag (notmuch-hello-filtered-query tag filter-query)
+			   (notmuch-hello-filtered-query (concat "tag:" tag)
+							 filter-count))
+		   (cons tag (notmuch-hello-filtered-query
+			      (concat "tag:" tag) filter-query))))))
 	   (notmuch-remove-if-not
 	    (lambda (tag)
-	      (not (member tag notmuch-hello-hide-tags)))
+	      (not (member tag hide-tags)))
 	    (process-lines notmuch-command "search-tags")))))
 
+(defun notmuch-hello-insert-header ()
+  "Insert the default notmuch-hello header."
+  (when notmuch-show-logo
+    (let ((image notmuch-hello-logo))
+      ;; The notmuch logo uses transparency. That can display poorly
+      ;; when inserting the image into an emacs buffer (black logo on
+      ;; a black background), so force the background colour of the
+      ;; image. We use a face to represent the colour so that
+      ;; `defface' can be used to declare the different possible
+      ;; colours, which depend on whether the frame has a light or
+      ;; dark background.
+      (setq image (cons 'image
+			(append (cdr image)
+				(list :background (face-background 'notmuch-hello-logo-background)))))
+      (insert-image image))
+    (widget-insert "  "))
+
+  (widget-insert "Welcome to ")
+  ;; Hack the display of the links used.
+  (let ((widget-link-prefix "")
+	(widget-link-suffix ""))
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (browse-url notmuch-hello-url))
+		   :help-echo "Visit the notmuch website."
+		   "notmuch")
+    (widget-insert ". ")
+    (widget-insert "You have ")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (notmuch-hello-update))
+		   :help-echo "Refresh"
+		   (notmuch-hello-nice-number
+		    (string-to-number (car (process-lines notmuch-command "count")))))
+    (widget-insert " messages.\n")))
+
+
+(defun notmuch-hello-insert-saved-searches ()
+  "Insert the saved-searches section."
+  (let ((searches (notmuch-hello-query-counts
+		  notmuch-saved-searches
+		  :show-empty-searches notmuch-show-empty-saved-searches))
+	found-target-pos final-target-pos)
+    (when searches
+      (widget-insert "Saved searches: ")
+      (widget-create 'push-button
+		     :notify (lambda (&rest ignore)
+			       (customize-variable 'notmuch-saved-searches))
+		     "edit")
+      (widget-insert "\n\n")
+      (setq final-target-pos (point-marker))
+      (let ((start (point)))
+	(setq found-target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(if found-target-pos
+	    (setq final-target-pos found-target-pos))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	final-target-pos))))
+
+(defun notmuch-hello-insert-search ()
+  "Insert a search widget."
+  (widget-insert "Search: ")
+  (setq notmuch-hello-search-bar-marker (point-marker))
+  (widget-create 'editable-field
+		 ;; Leave some space at the start and end of the
+		 ;; search boxes.
+		 :size (max 8 (- (window-width) notmuch-hello-indent
+				 (length "Search: ")))
+		 :action (lambda (widget &rest ignore)
+			   (notmuch-hello-search (widget-value widget)))))
+
+(defun notmuch-hello-insert-recent-searches ()
+  "Insert recent searches."
+  (when notmuch-hello-recent-searches
+    (widget-insert "\nRecent searches: ")
+    (widget-create 'push-button
+		   :notify (lambda (&rest ignore)
+			     (setq notmuch-hello-recent-searches nil)
+			     (notmuch-hello-update))
+		   "clear")
+    (widget-insert "\n\n")
+    (let ((start (point))
+	  (nth 0))
+      (mapc '(lambda (search)
+	       (let ((widget-symbol (intern (format "notmuch-hello-search-%d" nth))))
+		 (set widget-symbol
+		      (widget-create 'editable-field
+				     ;; Don't let the search boxes be
+				     ;; less than 8 characters wide.
+				     :size (max 8
+						(- (window-width)
+						   ;; Leave some space
+						   ;; at the start and
+						   ;; end of the
+						   ;; boxes.
+						   (* 2 notmuch-hello-indent)
+						   ;; 1 for the space
+						   ;; before the
+						   ;; `[save]' button. 6
+						   ;; for the `[save]'
+						   ;; button.
+						   1 6))
+				     :action (lambda (widget &rest ignore)
+					       (notmuch-hello-search (widget-value widget)))
+				     search))
+		 (widget-insert " ")
+		 (widget-create 'push-button
+				:notify (lambda (widget &rest ignore)
+					  (notmuch-hello-add-saved-search widget))
+				:notmuch-saved-search-widget widget-symbol
+				"save"))
+	       (widget-insert "\n")
+	       (setq nth (1+ nth)))
+	    notmuch-hello-recent-searches)
+      (indent-rigidly start (point) notmuch-hello-indent))))
+
+(defun notmuch-hello-insert-searches (title query-alist &rest options)
+  "Insert a section with TITLE showing a list of buttons made from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated item.
+
+Supports the following entries in OPTIONS as a plist:
+:initially-hidden - if non-nil, section will be hidden on startup
+:show-empty-searches - show buttons with no matching messages
+:hide-if-empty - hide if no buttons would be shown
+   (only makes sense without :show-empty-searches)
+:make-query - This can be a function that takes a tag as its argument and
+    returns a filter to be used for the query for that tag or nil to hide the
+    tag. This can also be a string that is used as a filter for messages with that tag.
+:make-count - Seperate query to generate the count that should be displayed for each
+    tag. Accepts the same values as :make-query"
+  (widget-insert title)
+  (if (and notmuch-hello-first-run (plist-get options :initially-hidden))
+      (add-to-list 'notmuch-hello-hidden-sections title))
+  (let ((is-hidden (member title notmuch-hello-hidden-sections))
+	(start (point)))
+    (if is-hidden
+	(widget-create 'push-button
+		       :notify `(lambda (widget &rest ignore)
+				  (setq notmuch-hello-hidden-sections
+					(delete ,title notmuch-hello-hidden-sections))
+				  (notmuch-hello-update))
+		       "show")
+      (widget-create 'push-button
+		     :notify `(lambda (widget &rest ignore)
+				(add-to-list 'notmuch-hello-hidden-sections
+					     ,title)
+				(notmuch-hello-update))
+		     "hide"))
+    (widget-insert "\n")
+    (let (target-pos
+	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
+      (when (and (not is-hidden)
+	       (or (not (plist-get options :hide-if-empty))
+		  searches))
+	(widget-insert "\n")
+	(setq target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	target-pos))))
+
+(defun notmuch-hello-insert-tags-section (&optional title &rest options)
+  "Insert a section displaying all tags and message counts for each.
+
+TITLE defaults to \"All tags: \".
+Allowed options are those accepted by `notmuch-hello-insert-searches' and the
+following:
+
+:hide-tags - List of tags that should be excluded."
+  (apply 'notmuch-hello-insert-searches
+	 (or title "All tags: ")
+	 (notmuch-hello-generate-tag-alist
+	  (plist-get options :hide-tags)
+	  (plist-get options :make-query)
+	  (plist-get options :make-count))
+	 options))
+
+(defun notmuch-hello-insert-inbox ()
+  "Show an entry for each saved search and inboxed messages for each tag"
+  (notmuch-hello-insert-searches "What's in your inbox: "
+				 (append
+				  (notmuch-saved-searches)
+				  (notmuch-hello-generate-tag-alist))
+				 :make-query "tag:inbox"))
+
+(defun notmuch-hello-insert-alltags ()
+  "Insert a section displaying all tags and associated message counts"
+  (notmuch-hello-insert-tags-section
+   nil
+   :initially-hidden (not notmuch-show-all-tags-list)
+   :make-query notmuch-hello-tag-list-make-query))
+
+(defun notmuch-hello-insert-footer ()
+  "Insert the notmuch-hello footer."
+  (let ((start (point)))
+    (widget-insert "Type a search query and hit RET to view matching threads.\n")
+    (when notmuch-hello-recent-searches
+      (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
+      (widget-insert "Save recent searches with the `save' button.\n"))
+    (when notmuch-saved-searches
+      (widget-insert "Edit saved searches with the `edit' button.\n"))
+    (widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
+    (widget-insert "`=' refreshes this screen. `s' jumps to the search box. `q' to quit.\n")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (customize-variable 'notmuch-hello-sections))
+		   :button-prefix "" :button-suffix ""
+		   "Customize")
+    (widget-insert " this page.\n")
+    (let ((fill-column (- (window-width) notmuch-hello-indent)))
+      (center-region start (point)))))
+
 ;;;###autoload
 (defun notmuch-hello (&optional no-display)
   "Run notmuch and display saved searches, known tags, etc."
   (interactive)
 
-  ; Jump through a hoop to get this value from the deprecated variable
-  ; name (`notmuch-folders') or from the default value.
+					; Jump through a hoop to get this value from the deprecated variable
+					; name (`notmuch-folders') or from the default value.
   (if (not notmuch-saved-searches)
-    (setq notmuch-saved-searches (notmuch-saved-searches)))
+      (setq notmuch-saved-searches (notmuch-saved-searches)))
 
   (if no-display
       (set-buffer "*notmuch-hello*")
     (switch-to-buffer "*notmuch-hello*"))
 
-  (let ((target (if (widget-at)
-		   (widget-value (widget-at))
-		 (condition-case nil
-		     (progn
-		       (widget-forward 1)
-		       (widget-value (widget-at)))
-		   (error nil)))))
+  (let ((notmuch-hello-target (if (widget-at)
+				  (widget-value (widget-at))
+				(condition-case nil
+				    (progn
+				      (widget-forward 1)
+				      (widget-value (widget-at)))
+				  (error nil)))))
 
     (kill-all-local-variables)
     (let ((inhibit-read-only t))
@@ -391,167 +746,27 @@ Complete list of currently available key bindings:
       (mapc 'delete-overlay (car all))
       (mapc 'delete-overlay (cdr all)))
 
-    (when notmuch-show-logo
-      (let ((image notmuch-hello-logo))
-	;; The notmuch logo uses transparency. That can display poorly
-	;; when inserting the image into an emacs buffer (black logo on
-	;; a black background), so force the background colour of the
-	;; image. We use a face to represent the colour so that
-	;; `defface' can be used to declare the different possible
-	;; colours, which depend on whether the frame has a light or
-	;; dark background.
-	(setq image (cons 'image
-			  (append (cdr image)
-				  (list :background (face-background 'notmuch-hello-logo-background)))))
-	(insert-image image))
-      (widget-insert "  "))
-
-    (widget-insert "Welcome to ")
-    ;; Hack the display of the links used.
-    (let ((widget-link-prefix "")
-	  (widget-link-suffix ""))
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (browse-url notmuch-hello-url))
-		     :help-echo "Visit the notmuch website."
-		     "notmuch")
-      (widget-insert ". ")
-      (widget-insert "You have ")
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (notmuch-hello-update))
-		     :help-echo "Refresh"
-		     (notmuch-hello-nice-number
-		      (string-to-number (car (process-lines notmuch-command "count")))))
-      (widget-insert " messages.\n"))
-
-    (let ((found-target-pos nil)
-	  (final-target-pos nil))
-      (let* ((saved-alist
-	      ;; Filter out empty saved searches if required.
-	      (if notmuch-show-empty-saved-searches
-		  notmuch-saved-searches
-		(loop for elem in notmuch-saved-searches
-		      if (> (string-to-number (notmuch-saved-search-count (cdr elem))) 0)
-		      collect elem)))
-	     (saved-widest (notmuch-hello-longest-label saved-alist))
-	     (alltags-alist (if notmuch-show-all-tags-list (notmuch-hello-generate-tag-alist)))
-	     (alltags-widest (notmuch-hello-longest-label alltags-alist))
-	     (widest (max saved-widest alltags-widest)))
-
-	(when saved-alist
-	  (widget-insert "\nSaved searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (customize-variable 'notmuch-saved-searches))
-			 "edit")
-	  (widget-insert "\n\n")
-	  (setq final-target-pos (point-marker))
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags saved-alist widest target))
-	    (if found-target-pos
-		(setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\nSearch: ")
-	(setq notmuch-hello-search-bar-marker (point-marker))
-	(widget-create 'editable-field
-		       ;; Leave some space at the start and end of the
-		       ;; search boxes.
-		       :size (max 8 (- (window-width) notmuch-hello-indent
-				       (length "Search: ")))
-		       :action (lambda (widget &rest ignore)
-				 (notmuch-hello-search (widget-value widget))))
-	(widget-insert "\n")
-
-	(when notmuch-hello-recent-searches
-	  (widget-insert "\nRecent searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (setq notmuch-hello-recent-searches nil)
-				   (notmuch-hello-update))
-			 "clear")
-	  (widget-insert "\n\n")
-	  (let ((start (point))
-		(nth 0))
-	    (mapc '(lambda (search)
-		     (let ((widget-symbol (intern (format "notmuch-hello-search-%d" nth))))
-		       (set widget-symbol
-			    (widget-create 'editable-field
-				       ;; Don't let the search boxes be
-				       ;; less than 8 characters wide.
-				       :size (max 8
-						  (- (window-width)
-						     ;; Leave some space
-						     ;; at the start and
-						     ;; end of the
-						     ;; boxes.
-						     (* 2 notmuch-hello-indent)
-						     ;; 1 for the space
-						     ;; before the
-						     ;; `[save]' button. 6
-						     ;; for the `[save]'
-						     ;; button.
-						     1 6))
-				       :action (lambda (widget &rest ignore)
-						 (notmuch-hello-search (widget-value widget)))
-				       search))
-		       (widget-insert " ")
-		       (widget-create 'push-button
-				      :notify (lambda (widget &rest ignore)
-						(notmuch-hello-add-saved-search widget))
-				      :notmuch-saved-search-widget widget-symbol
-				      "save"))
-		     (widget-insert "\n")
-		     (setq nth (1+ nth)))
-		  notmuch-hello-recent-searches)
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(when alltags-alist
-	  (widget-insert "\nAll tags: ")
-	  (widget-create 'push-button
-			 :notify (lambda (widget &rest ignore)
-				   (setq notmuch-show-all-tags-list nil)
-				   (notmuch-hello-update))
-			 "hide")
-	  (widget-insert "\n\n")
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags alltags-alist widest target))
-	    (if (not final-target-pos)
-		(setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\n")
-
-	(if (not notmuch-show-all-tags-list)
-	    (widget-create 'push-button
-			   :notify (lambda (widget &rest ignore)
-				     (setq notmuch-show-all-tags-list t)
-				     (notmuch-hello-update))
-			   "Show all tags")))
-
-      (let ((start (point)))
-	(widget-insert "\n\n")
-	(widget-insert "Type a search query and hit RET to view matching threads.\n")
-	(when notmuch-hello-recent-searches
-	  (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
-	  (widget-insert "Save recent searches with the `save' button.\n"))
-	(when notmuch-saved-searches
-	  (widget-insert "Edit saved searches with the `edit' button.\n"))
-	(widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
-	(widget-insert "`=' refreshes this screen. `s' jumps to the search box. `q' to quit.\n")
-	(let ((fill-column (- (window-width) notmuch-hello-indent)))
-	  (center-region start (point))))
+    (let (final-target-pos)
+      (mapc
+       (lambda (section)
+	 (let ((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))
+	     (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)
-	(notmuch-hello-goto-search)))))
+	(notmuch-hello-goto-search)))
+    (setq notmuch-hello-first-run nil)))
 
 (defun notmuch-folder ()
   "Deprecated function for invoking notmuch---calling `notmuch' is preferred now."
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v4 2/2] emacs: Tests for user-defined sections
  2011-07-07 22:53 ` [PATCH v4 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
  2011-07-07 22:53   ` [PATCH v4 1/2] " Daniel Schoepe
@ 2011-07-07 22:53   ` Daniel Schoepe
  2011-07-09 18:03   ` [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2011-07-07 22:53 UTC (permalink / raw)
  To: notmuch

---
 test/emacs                                         |   37 ++++++++++++++++++++
 test/emacs.expected-output/notmuch-hello           |    5 ++-
 .../notmuch-hello-new-section                      |    4 ++
 .../notmuch-hello-no-saved-searches                |    4 ++-
 .../notmuch-hello-section-before                   |   18 +++++++++
 .../notmuch-hello-section-counts                   |    5 +++
 .../notmuch-hello-section-hidden-tag               |    4 ++
 .../notmuch-hello-section-with-empty               |    4 ++
 .../emacs.expected-output/notmuch-hello-with-empty |    5 ++-
 9 files changed, 81 insertions(+), 5 deletions(-)
 create mode 100644 test/emacs.expected-output/notmuch-hello-new-section
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-before
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-counts
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-hidden-tag
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-with-empty

diff --git a/test/emacs b/test/emacs
index 53f455a..40e2563 100755
--- a/test/emacs
+++ b/test/emacs
@@ -34,6 +34,43 @@ test_emacs '(let ((notmuch-saved-searches
 	      (test-output))'
 test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-no-saved-searches
 
+test_begin_subtest "User defined section with inbox tag"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test: \" '((\"inbox\" . \"tag:inbox\")))))))
+           (notmuch-hello)
+           (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-new-section
+
+test_begin_subtest "User defined section with empty, hidden entry"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test-with-empty:\" 
+                                     '((\"inbox\" . \"tag:inbox\")
+                                       (\"doesnotexist\" . \"tag:doesnotexist\"))
+                                     :hide-empty-searches t)))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-with-empty
+
+test_begin_subtest "User defined section, unread tag filtered out"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-filtered: \"
+                                     :hide-tags '(\"unread\"))))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-hidden-tag
+
+test_begin_subtest "User defined section, different query for counts"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-counts: \"
+                                     :make-count \"tag:signed\")))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-counts
+
 test_begin_subtest "Basic notmuch-search view in emacs"
 test_emacs '(notmuch-search "tag:inbox")
 	    (notmuch-test-wait)
diff --git a/test/emacs.expected-output/notmuch-hello b/test/emacs.expected-output/notmuch-hello
index 64b7e42..f981650 100644
--- a/test/emacs.expected-output/notmuch-hello
+++ b/test/emacs.expected-output/notmuch-hello
@@ -1,14 +1,15 @@
    Welcome to notmuch. You have 50 messages.
-
 Saved searches: [edit]
 
 	  50 inbox           50 unread    
 
 Search:                                                                     
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
diff --git a/test/emacs.expected-output/notmuch-hello-new-section b/test/emacs.expected-output/notmuch-hello-new-section
new file mode 100644
index 0000000..be7b26a
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-new-section
@@ -0,0 +1,4 @@
+Test: [hide]
+
+	  50 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-no-saved-searches b/test/emacs.expected-output/notmuch-hello-no-saved-searches
index 7f8206a..37b2303 100644
--- a/test/emacs.expected-output/notmuch-hello-no-saved-searches
+++ b/test/emacs.expected-output/notmuch-hello-no-saved-searches
@@ -2,9 +2,11 @@
 
 Search:                                                                     
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-before b/test/emacs.expected-output/notmuch-hello-section-before
new file mode 100644
index 0000000..a5781ce
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-before
@@ -0,0 +1,18 @@
+   Welcome to notmuch. You have 50 messages.
+
+Saved searches: [edit]
+
+	  50 inbox                 50 unread          
+
+Test-before [hide]
+
+	   4 attachment             7 signed          
+	  50 inbox                 50 unread          
+
+Search:                                                                     
+
+
+	 Type a search query and hit RET to view matching threads.
+		Edit saved searches with the `edit' button.
+  Hit RET or click on a saved search or tag name to view matching threads.
+    `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
diff --git a/test/emacs.expected-output/notmuch-hello-section-counts b/test/emacs.expected-output/notmuch-hello-section-counts
new file mode 100644
index 0000000..12d19ed
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-counts
@@ -0,0 +1,5 @@
+Test-with-counts: [hide]
+
+	   2 attachment             7 inbox                  7 signed          
+	   7 unread          
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-hidden-tag b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
new file mode 100644
index 0000000..eb21c07
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
@@ -0,0 +1,4 @@
+Test-with-filtered: [hide]
+
+	   4 attachment            50 inbox                  7 signed          
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-with-empty b/test/emacs.expected-output/notmuch-hello-section-with-empty
new file mode 100644
index 0000000..c5b6623
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-with-empty
@@ -0,0 +1,4 @@
+Test-with-empty:[hide]
+
+	  50 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-with-empty b/test/emacs.expected-output/notmuch-hello-with-empty
index a9ed630..7d8416d 100644
--- a/test/emacs.expected-output/notmuch-hello-with-empty
+++ b/test/emacs.expected-output/notmuch-hello-with-empty
@@ -1,14 +1,15 @@
    Welcome to notmuch. You have 50 messages.
-
 Saved searches: [edit]
 
 	  50 inbox           50 unread           0 empty     
 
 Search:                                                                     
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [PATCH v4 1/2] emacs: User-defined sections in notmuch-hello
  2011-07-07 22:53   ` [PATCH v4 1/2] " Daniel Schoepe
@ 2011-07-08 23:00     ` Michal Sojka
  2011-07-08 23:13       ` Daniel Schoepe
  2011-07-09 17:07       ` Daniel Schoepe
  0 siblings, 2 replies; 57+ messages in thread
From: Michal Sojka @ 2011-07-08 23:00 UTC (permalink / raw)
  To: Daniel Schoepe, notmuch

On Fri, 08 Jul 2011, Daniel Schoepe wrote:
> This has been done in v4 of the patch, for which I screwed up the
> In-Reply-To header and hence is listed as a separate thread:
> 
> id:"1310079227-19120-1-git-send-email-daniel.schoepe@googlemail.com"

Oh, you always send new versions faster than I can investigate the older
ones :-)

> > - The title of custom tags section was not passed correctly to the
> >   functions. This is also fixed in the patch below.
> 
> I changed title to a mandatory argument for consistency with
> notmuch-insert-searches and because a title-less section wouldn't make
> much sense anyway.

Yes, this is definitely a good idea. My last comment to the patch is
that I do not like the use of plists in customization interface. It is
especially weird in the case of boolean options like hide-if-empty,
because they actually have three states: disabled, off and on. And this
make not a lot of sense.

I think that the customization interface is much better arranged when it
is modified like in the patch below (it is against v3).

-Michal

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index d3b146e..3e883ff 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -106,17 +106,14 @@ Typically \",\" in the US and UK and \".\" in Europe."
 (define-widget 'notmuch-hello-tags-section 'lazy
   "Customize-type for notmuch-hello tag-list sections."
   :tag "Customized tag-list"
-  :type
-  (let ((opts
-        '((:title (string :tag "Title for this section"))
-          (:make-query (string :tag "Filter for each tag"))
-          (:make-count (string :tag "Different query to generate counts"))
-          (:hide-tags (repeat :tag "Tags that will be hidden" string))
-          (:initially-hidden (boolean :tag "Hide this on startup?"))
-          (:hide-empty-tags (boolean :tag "Hide tags with no matching messages"))
-          (:hide-if-empty (boolean :tag "Hide if empty")))))
-    `(list (const :tag "" notmuch-hello-insert-tags-section)
-          (plist :inline t :options ,opts))))
+  :type '(list (const :tag "" notmuch-hello-insert-tags-section)
+              (string :tag "Title for this section")
+              (string :tag "Filter for each tag")
+              (string :tag "Different filter to generate counts")
+              (repeat :tag "Tags that will be hidden" string)
+              (boolean :tag "Hide this on startup?")
+              (boolean :tag "Hide tags with no matching messages")
+              (boolean :tag "Hide if empty")))
 
 (define-widget 'notmuch-hello-query-section 'lazy
   "Customize-type for custom saved-search-like sections"
@@ -629,7 +626,8 @@ Supports the following entries in OPTIONS as a plist:
        (indent-rigidly start (point) notmuch-hello-indent)
        target-pos))))
 
-(defun notmuch-hello-insert-tags-section (&rest options)
+(defun notmuch-hello-insert-tags-section (title &optional filter filter-count hide-tags initially-hidden
+                                               hide-empty-searches hide-if-empty)
   "Insert a section displaying all tags and message counts for each.
 
 TITLE defaults to \"All tags: \".
@@ -637,13 +635,13 @@ Allowed options are those accepted by `notmuch-hello-insert-searches' and the
 following:
 
 :hide-tags - List of tags that should be excluded."
-  (apply 'notmuch-hello-insert-searches
-        (plist-get options :title)
-        (notmuch-hello-generate-tag-alist
-         (plist-get options :hide-tags)
-         (plist-get options :make-query)
-         (plist-get options :make-count))
-        options))
+  (notmuch-hello-insert-searches title
+                                (notmuch-hello-generate-tag-alist hide-tags filter
+                                                                  (if (string= "" filter-count)
+                                                                      nil filter-count))
+                                :initially-hidden initially-hidden
+                                :hide-empty-searches hide-empty-searches
+                                :hide-if-empty hide-if-empty))
 
 (defun notmuch-hello-insert-inbox ()
   "Show an entry for each saved search and inboxed messages for each tag"

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [PATCH v4 1/2] emacs: User-defined sections in notmuch-hello
  2011-07-08 23:00     ` Michal Sojka
@ 2011-07-08 23:13       ` Daniel Schoepe
  2011-07-09  5:35         ` Michal Sojka
  2011-07-09 17:07       ` Daniel Schoepe
  1 sibling, 1 reply; 57+ messages in thread
From: Daniel Schoepe @ 2011-07-08 23:13 UTC (permalink / raw)
  To: Michal Sojka, notmuch

[-- Attachment #1: Type: text/plain, Size: 2754 bytes --]

On Sat, 09 Jul 2011 01:00:03 +0200, Michal Sojka <sojkam1@fel.cvut.cz> wrote:
> Yes, this is definitely a good idea. My last comment to the patch is
> that I do not like the use of plists in customization interface. It is
> especially weird in the case of boolean options like hide-if-empty,
> because they actually have three states: disabled, off and on. And this
> make not a lot of sense.
> 
> I think that the customization interface is much better arranged when it
> is modified like in the patch below (it is against v3).
> 
> [..]
> -  :type
> -  (let ((opts
> -        '((:title (string :tag "Title for this section"))
> -          (:make-query (string :tag "Filter for each tag"))
> -          (:make-count (string :tag "Different query to generate counts"))
> -          (:hide-tags (repeat :tag "Tags that will be hidden" string))
> -          (:initially-hidden (boolean :tag "Hide this on startup?"))
> -          (:hide-empty-tags (boolean :tag "Hide tags with no matching messages"))
> -          (:hide-if-empty (boolean :tag "Hide if empty")))))
> -    `(list (const :tag "" notmuch-hello-insert-tags-section)
> -          (plist :inline t :options ,opts))))
> +  :type '(list (const :tag "" notmuch-hello-insert-tags-section)
> +              (string :tag "Title for this section")
> +              (string :tag "Filter for each tag")
> +              (string :tag "Different filter to generate counts")
> +              (repeat :tag "Tags that will be hidden" string)
> +              (boolean :tag "Hide this on startup?")
> +              (boolean :tag "Hide tags with no matching messages")
> +              (boolean :tag "Hide if empty")))
>  

> -(defun notmuch-hello-insert-tags-section (&rest options)
> +(defun notmuch-hello-insert-tags-section (title &optional filter filter-count hide-tags initially-hidden
> +                                               hide-empty-searches hide-if-empty)

This would make customization via elisp somewhat more cumbersome though,
because then one has to specify all preceding optional arguments to set
an option near the end of the argument list. Also, it gets harder to
decipher which value belongs to which option.

An alternative would be to force specifying all possible options in the
customization interface while still producing a plist as a
result. Something along these lines should work:

:type '(list (const :tag "" notmuch-hello-insert-tags-section)
             (const :tag "" :make-query)
             (string :tag "Filter for this tag")
             ...)

I guess the format instructions for the elements would have to be
tweaked a bit more to avoid excessive spacing due to the many hidden
elements.

Cheers,
Daniel

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v4 1/2] emacs: User-defined sections in notmuch-hello
  2011-07-08 23:13       ` Daniel Schoepe
@ 2011-07-09  5:35         ` Michal Sojka
  0 siblings, 0 replies; 57+ messages in thread
From: Michal Sojka @ 2011-07-09  5:35 UTC (permalink / raw)
  To: Daniel Schoepe, notmuch

On Fri, 08 Jul 2011, Daniel Schoepe wrote:
Non-text part: multipart/signed
> On Sat, 09 Jul 2011 01:00:03 +0200, Michal Sojka <sojkam1@fel.cvut.cz> wrote:
> > Yes, this is definitely a good idea. My last comment to the patch is
> > that I do not like the use of plists in customization interface. It is
> > especially weird in the case of boolean options like hide-if-empty,
> > because they actually have three states: disabled, off and on. And this
> > make not a lot of sense.
> > 
> > I think that the customization interface is much better arranged when it
> > is modified like in the patch below (it is against v3).
> > 
> > [..]
> > -  :type
> > -  (let ((opts
> > -        '((:title (string :tag "Title for this section"))
> > -          (:make-query (string :tag "Filter for each tag"))
> > -          (:make-count (string :tag "Different query to generate counts"))
> > -          (:hide-tags (repeat :tag "Tags that will be hidden" string))
> > -          (:initially-hidden (boolean :tag "Hide this on startup?"))
> > -          (:hide-empty-tags (boolean :tag "Hide tags with no matching messages"))
> > -          (:hide-if-empty (boolean :tag "Hide if empty")))))
> > -    `(list (const :tag "" notmuch-hello-insert-tags-section)
> > -          (plist :inline t :options ,opts))))
> > +  :type '(list (const :tag "" notmuch-hello-insert-tags-section)
> > +              (string :tag "Title for this section")
> > +              (string :tag "Filter for each tag")
> > +              (string :tag "Different filter to generate counts")
> > +              (repeat :tag "Tags that will be hidden" string)
> > +              (boolean :tag "Hide this on startup?")
> > +              (boolean :tag "Hide tags with no matching messages")
> > +              (boolean :tag "Hide if empty")))
> >  
> 
> > -(defun notmuch-hello-insert-tags-section (&rest options)
> > +(defun notmuch-hello-insert-tags-section (title &optional filter filter-count hide-tags initially-hidden
> > +                                               hide-empty-searches hide-if-empty)
> 
> This would make customization via elisp somewhat more cumbersome though,
> because then one has to specify all preceding optional arguments to set
> an option near the end of the argument list. Also, it gets harder to
> decipher which value belongs to which option.

Not necessarily. In fact, notmuch-hello-insert-tags-section is only a
thin wrapper of notmuch-hello-insert-searches. In elisp you can always
use directly those plist-based "low-level" functions.

-Michal

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v4 1/2] emacs: User-defined sections in notmuch-hello
  2011-07-08 23:00     ` Michal Sojka
  2011-07-08 23:13       ` Daniel Schoepe
@ 2011-07-09 17:07       ` Daniel Schoepe
  1 sibling, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2011-07-09 17:07 UTC (permalink / raw)
  To: Michal Sojka, notmuch

[-- Attachment #1: Type: text/plain, Size: 587 bytes --]

On Sat, 09 Jul 2011 01:00:03 +0200, Michal Sojka <sojkam1@fel.cvut.cz> wrote:
> Yes, this is definitely a good idea. My last comment to the patch is
> that I do not like the use of plists in customization interface. It is
> especially weird in the case of boolean options like hide-if-empty,
> because they actually have three states: disabled, off and on. And this
> make not a lot of sense.

I just realized we overlooked the simplest solution: Changing the (boolean ..)
elements to (const :tag "..." t) elements resolves this nicely.

I'll send a new version shortly.

Cheers,
Daniel

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello
  2011-07-07 22:53 ` [PATCH v4 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
  2011-07-07 22:53   ` [PATCH v4 1/2] " Daniel Schoepe
  2011-07-07 22:53   ` [PATCH v4 2/2] emacs: Tests for user-defined sections Daniel Schoepe
@ 2011-07-09 18:03   ` Daniel Schoepe
  2011-07-09 18:03     ` [PATCH v5 1/2] " Daniel Schoepe
                       ` (3 more replies)
  2012-02-17  7:48   ` [PATCH v9 " Dmitry Kurochkin
  2012-02-17 14:48   ` [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello Dmitry Kurochkin
  4 siblings, 4 replies; 57+ messages in thread
From: Daniel Schoepe @ 2011-07-09 18:03 UTC (permalink / raw)
  To: notmuch

More documentation/customize improvements.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v5 1/2] emacs: User-defined sections in notmuch-hello
  2011-07-09 18:03   ` [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
@ 2011-07-09 18:03     ` Daniel Schoepe
  2011-07-09 18:03     ` [PATCH v5 2/2] emacs: Tests for user-defined sections Daniel Schoepe
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2011-07-09 18:03 UTC (permalink / raw)
  To: notmuch

This patch makes the notmuch-hello screen fully customizable
by allowing the user to add and remove arbitrary sections. It
also provides some convenience functions for constructing sections,
e.g. showing the unread message count for each tag.

This is done by specifying a list of functions that will be run
when notmuch-hello is invoked.
---
 emacs/notmuch-hello.el |  624 ++++++++++++++++++++++++++++++++----------------
 1 files changed, 423 insertions(+), 201 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index 65fde75..1f51281 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -70,11 +70,6 @@ should return a filter for that tag, or nil to hide the tag."
 		 (const :tag "Custom filter function" function))
   :group 'notmuch)
 
-(defcustom notmuch-hello-hide-tags nil
-  "List of tags to be hidden in the \"all tags\"-section."
-  :type '(repeat string)
-  :group 'notmuch)
-
 (defface notmuch-hello-logo-background
   '((((class color)
       (background dark))
@@ -123,6 +118,94 @@ Typically \",\" in the US and UK and \".\" in Europe."
 
 (defvar notmuch-hello-recent-searches nil)
 
+(defvar notmuch-custom-section-options
+  '((:filter (string :tag "Filter for each tag"))
+    (:filter-count (string :tag "Different filter message counts"))
+    (:initially-hidden (const :tag "Hide this on startup?" t))
+    (:show-empty-searches (const :tag "Show queries with no matching messages" t))
+    (:hide-if-empty (const :tag "Hide if empty" t)))
+  "Various customization-options for notmuch-hello-tags/query-section.")
+
+(define-widget 'notmuch-hello-tags-section 'lazy
+  "Customize-type for notmuch-hello tag-list sections."
+  :tag "Customized tag-list (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-tags-section)
+	 (string :tag "Title for this section")
+	 (plist
+	  :inline t
+	  :options
+	  ,(append notmuch-custom-section-options
+		   '((:hide-tags (repeat :tag "Tags that will be hidden"
+					 string)))))))
+
+(define-widget 'notmuch-hello-query-section 'lazy
+  "Customize-type for custom saved-search-like sections"
+  :tag "Customized queries section (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-query-list)
+	 (string :tag "Title for this section")
+	 (repeat :tag "Queries"
+		 (cons (string :tag "Name") (string :tag "Query")))
+	 (plist :inline t :options ,notmuch-custom-section-options)))
+
+(defcustom notmuch-hello-sections
+  (list #'notmuch-hello-insert-header
+	#'notmuch-hello-insert-saved-searches
+	#'notmuch-hello-insert-search
+	#'notmuch-hello-insert-recent-searches
+	#'notmuch-hello-insert-alltags
+	#'notmuch-hello-insert-footer)
+  "Sections for notmuch-hello.
+
+Each entry of this list should be a function of no arguments that
+should return if `notmuch-hello-target' is produced as part of its
+output and nil otherwise. 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 list.
+
+The functions will be run to construct the content of the
+notmuch-hello buffer in the order they appear in this list. A
+section should not end with an empty line, because a newline will
+be inserted after each section by `notmuch-hello'.
+
+A \"Customized tag-list\" item in the customize-interface
+displays a list of all tags, optionally hiding some of them. It
+is also possible to filter the list of messages matching each tag
+by an additional filter query. Similarly, the count of messages
+displayed next to the buttons can be generated by applying a
+different filter to the tag query. These filters are also
+supported for \"Customized queries section\" items."
+  :group 'notmuch
+  :type 
+  '(repeat
+    (choice (function-item notmuch-hello-insert-header)
+	    (function-item notmuch-hello-insert-saved-searches)
+	    (function-item notmuch-hello-insert-search)
+	    (function-item notmuch-hello-insert-recent-searches)
+	    (function-item notmuch-hello-insert-alltags)
+	    (function-item notmuch-hello-insert-footer)
+	    (function-item notmuch-hello-insert-inbox)
+	    notmuch-hello-tags-section
+	    notmuch-hello-query-section
+	    (function :tag "Custom function"))))
+
+(defvar notmuch-hello-target nil
+  "Button at position of point before rebuilding the notmuch-buffer
+
+This variable contains the string of the button, if any, the
+point was positioned at before the notmuch-hello buffer was
+rebuilt. This is never actually set globally and 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 query section titles whose contents are hidden")
+
+(defvar notmuch-hello-first-run t)
+
 (defun notmuch-hello-remember-search (search)
   (if (not (member search notmuch-hello-recent-searches))
       (push search notmuch-hello-recent-searches))
@@ -173,8 +256,8 @@ Typically \",\" in the US and UK and \".\" in Europe."
     (message "Saved '%s' as '%s'." search name)
     (notmuch-hello-update)))
 
-(defun notmuch-hello-longest-label (tag-alist)
-  (or (loop for elem in tag-alist
+(defun notmuch-hello-longest-label (searches-alist)
+  (or (loop for elem in searches-alist
 	    maximize (length (car elem)))
       0))
 
@@ -238,12 +321,71 @@ should be. Returns a cons cell `(tags-per-line width)'."
 				   (* tags-per-line (+ 9 1))))
 			   tags-per-line))))
 
-(defun notmuch-hello-insert-tags (tag-alist widest target)
-  (let* ((tags-and-width (notmuch-hello-tags-per-line widest))
+(defun notmuch-hello-filtered-query (query filter)
+  "Constructs a query to search all messages matching QUERY and FILTER.
+
+If FILTER is a string, it is directly used in the returned query.
+
+If FILTER is a function, it is called with QUERY as a parameter and
+the string it returns is used as the filter.
+
+Otherwise, FILTER is ignored.
+"
+  (cond
+   ((functionp filter)
+    (let ((result (funcall filter query)))
+      (and result (concat query " and (" result ")"))))
+   ((stringp filter)
+    (concat query " and (" filter ")"))
+   (t (concat query))))
+
+(defun notmuch-hello-query-counts (query-alist &rest options)
+  "Compute list of counts of matched messages from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated query.
+
+The result is the list of elements of the form (NAME QUERY COUNT).
+
+The values :show-empty-searches, :filter and :filter-count from
+options will be handled as specified for
+`notmuch-hello-insert-searches'."
+  (notmuch-remove-if-not
+   #'identity
+   (mapcar
+    (lambda (elem)
+      (let* ((name (car elem))
+	     (query-and-count (if (consp (cdr elem))
+				  ;; do we have a different query for the message count?
+				  (cons (second elem) (third elem))
+				(cons (cdr elem) (cdr elem))))
+	     (message-count
+	      (string-to-number
+	       (notmuch-saved-search-count
+		(notmuch-hello-filtered-query (cdr query-and-count)
+					      (or (plist-get options :filter-count)
+						 (plist-get options :filter)))))))
+	(and (or (plist-get options :show-empty-searches) (> message-count 0))
+	   (list name (notmuch-hello-filtered-query
+		       (car query-and-count) (plist-get options :filter))
+		 message-count))))
+    query-alist)))
+
+(defun notmuch-hello-insert-buttons (searches)
+  "Insert buttons for SEARCHES.
+
+SEARCHES must be a list containing lists of the form (NAME QUERY COUNT), where
+QUERY is the query to start when the button for the corresponding entry is
+activated. COUNT should be the number of messages matching the query.
+Such a list can be computed with `notmuch-hello-query-counts'."
+  (let* ((widest (notmuch-hello-longest-label searches))
+	 (tags-and-width (notmuch-hello-tags-per-line widest))
 	 (tags-per-line (car tags-and-width))
 	 (widest (cdr tags-and-width))
 	 (count 0)
-	 (reordered-list (notmuch-hello-reflect tag-alist tags-per-line))
+	 (reordered-list (notmuch-hello-reflect searches tags-per-line))
 	 ;; Hack the display of the buttons used.
 	 (widget-push-button-prefix "")
 	 (widget-push-button-suffix "")
@@ -253,13 +395,13 @@ should be. Returns a cons cell `(tags-per-line width)'."
     (mapc (lambda (elem)
 	    ;; (not elem) indicates an empty slot in the matrix.
 	    (when elem
-	      (let* ((name (car elem))
-		     (query (cdr elem))
+	      (let* ((name (first elem))
+		     (query (second elem))
+		     (count (third elem))
 		     (formatted-name (format "%s " name)))
 		(widget-insert (format "%8s "
-				       (notmuch-hello-nice-number
-					(string-to-number (notmuch-saved-search-count query)))))
-		(if (string= formatted-name target)
+				       (notmuch-hello-nice-number count)))
+		(if (string= formatted-name notmuch-hello-target)
 		    (setq found-target-pos (point-marker)))
 		(widget-create 'push-button
 			       :notify #'notmuch-hello-widget-search
@@ -277,7 +419,7 @@ should be. Returns a cons cell `(tags-per-line width)'."
 	    (setq count (1+ count))
 	    (if (eq (% count tags-per-line) 0)
 		(widget-insert "\n")))
-	  reordered-list)
+	  searches)
 
     ;; If the last line was not full (and hence did not include a
     ;; carriage return), insert one now.
@@ -325,59 +467,275 @@ should be. Returns a cons cell `(tags-per-line width)'."
 (fset 'notmuch-hello-mode-map notmuch-hello-mode-map)
 
 (defun notmuch-hello-mode ()
- "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
+  "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
 
 Complete list of currently available key bindings:
 
 \\{notmuch-hello-mode-map}"
- (interactive)
- (kill-all-local-variables)
- (use-local-map notmuch-hello-mode-map)
- (setq major-mode 'notmuch-hello-mode
-       mode-name "notmuch-hello")
- ;;(setq buffer-read-only t)
-)
-
-(defun notmuch-hello-generate-tag-alist ()
+  (interactive)
+  (kill-all-local-variables)
+  (use-local-map notmuch-hello-mode-map)
+  (setq major-mode 'notmuch-hello-mode
+	mode-name "notmuch-hello"))
+
+(defun notmuch-hello-generate-tag-alist (&optional hide-tags filter-query filter-count)
   "Return an alist from tags to queries to display in the all-tags section."
   (notmuch-remove-if-not
-   #'cdr
+   #'identity
    (mapcar (lambda (tag)
-	     (cons tag
-		   (cond
-		    ((functionp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     (funcall notmuch-hello-tag-list-make-query tag) ")"))
-		    ((stringp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     notmuch-hello-tag-list-make-query ")"))
-		    (t (concat "tag:" tag)))))
+	     (let ((query (notmuch-hello-filtered-query (concat "tag:" tag)
+							filter-query)))
+	       (when query
+		 (if filter-count
+		     (list tag (notmuch-hello-filtered-query tag filter-query)
+			   (notmuch-hello-filtered-query (concat "tag:" tag)
+							 filter-count))
+		   (cons tag (notmuch-hello-filtered-query
+			      (concat "tag:" tag) filter-query))))))
 	   (notmuch-remove-if-not
 	    (lambda (tag)
-	      (not (member tag notmuch-hello-hide-tags)))
+	      (not (member tag hide-tags)))
 	    (process-lines notmuch-command "search-tags")))))
 
+(defun notmuch-hello-insert-header ()
+  "Insert the default notmuch-hello header."
+  (when notmuch-show-logo
+    (let ((image notmuch-hello-logo))
+      ;; The notmuch logo uses transparency. That can display poorly
+      ;; when inserting the image into an emacs buffer (black logo on
+      ;; a black background), so force the background colour of the
+      ;; image. We use a face to represent the colour so that
+      ;; `defface' can be used to declare the different possible
+      ;; colours, which depend on whether the frame has a light or
+      ;; dark background.
+      (setq image (cons 'image
+			(append (cdr image)
+				(list :background (face-background 'notmuch-hello-logo-background)))))
+      (insert-image image))
+    (widget-insert "  "))
+
+  (widget-insert "Welcome to ")
+  ;; Hack the display of the links used.
+  (let ((widget-link-prefix "")
+	(widget-link-suffix ""))
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (browse-url notmuch-hello-url))
+		   :help-echo "Visit the notmuch website."
+		   "notmuch")
+    (widget-insert ". ")
+    (widget-insert "You have ")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (notmuch-hello-update))
+		   :help-echo "Refresh"
+		   (notmuch-hello-nice-number
+		    (string-to-number (car (process-lines notmuch-command "count")))))
+    (widget-insert " messages.\n")))
+
+
+(defun notmuch-hello-insert-saved-searches ()
+  "Insert the saved-searches section."
+  (let ((searches (notmuch-hello-query-counts
+		  notmuch-saved-searches
+		  :show-empty-searches notmuch-show-empty-saved-searches))
+	found-target-pos final-target-pos)
+    (when searches
+      (widget-insert "Saved searches: ")
+      (widget-create 'push-button
+		     :notify (lambda (&rest ignore)
+			       (customize-variable 'notmuch-saved-searches))
+		     "edit")
+      (widget-insert "\n\n")
+      (setq final-target-pos (point-marker))
+      (let ((start (point)))
+	(setq found-target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(if found-target-pos
+	    (setq final-target-pos found-target-pos))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	final-target-pos))))
+
+(defun notmuch-hello-insert-search ()
+  "Insert a search widget."
+  (widget-insert "Search: ")
+  (setq notmuch-hello-search-bar-marker (point-marker))
+  (widget-create 'editable-field
+		 ;; Leave some space at the start and end of the
+		 ;; search boxes.
+		 :size (max 8 (- (window-width) notmuch-hello-indent
+				 (length "Search: ")))
+		 :action (lambda (widget &rest ignore)
+			   (notmuch-hello-search (widget-value widget))))
+  (insert "\n"))
+
+(defun notmuch-hello-insert-recent-searches ()
+  "Insert recent searches."
+  (when notmuch-hello-recent-searches
+    (widget-insert "Recent searches: ")
+    (widget-create 'push-button
+		   :notify (lambda (&rest ignore)
+			     (setq notmuch-hello-recent-searches nil)
+			     (notmuch-hello-update))
+		   "clear")
+    (widget-insert "\n\n")
+    (let ((start (point))
+	  (nth 0))
+      (mapc '(lambda (search)
+	       (let ((widget-symbol (intern (format "notmuch-hello-search-%d" nth))))
+		 (set widget-symbol
+		      (widget-create 'editable-field
+				     ;; Don't let the search boxes be
+				     ;; less than 8 characters wide.
+				     :size (max 8
+						(- (window-width)
+						   ;; Leave some space
+						   ;; at the start and
+						   ;; end of the
+						   ;; boxes.
+						   (* 2 notmuch-hello-indent)
+						   ;; 1 for the space
+						   ;; before the
+						   ;; `[save]' button. 6
+						   ;; for the `[save]'
+						   ;; button.
+						   1 6))
+				     :action (lambda (widget &rest ignore)
+					       (notmuch-hello-search (widget-value widget)))
+				     search))
+		 (widget-insert " ")
+		 (widget-create 'push-button
+				:notify (lambda (widget &rest ignore)
+					  (notmuch-hello-add-saved-search widget))
+				:notmuch-saved-search-widget widget-symbol
+				"save"))
+	       (widget-insert "\n")
+	       (setq nth (1+ nth)))
+	    notmuch-hello-recent-searches)
+      (indent-rigidly start (point) notmuch-hello-indent))))
+
+(defun notmuch-hello-insert-searches (title query-alist &rest options)
+  "Insert a section with TITLE showing a list of buttons made from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated item.
+
+Supports the following entries in OPTIONS as a plist:
+:initially-hidden - if non-nil, section will be hidden on startup
+:show-empty-searches - show buttons with no matching messages
+:hide-if-empty - hide if no buttons would be shown
+   (only makes sense without :show-empty-searches)
+:filter - This can be a function that takes the search query as its argument and
+    returns a filter to be used in conjuction with the query for that search or nil
+    to hide the element. This can also be a string that is used as a combined with
+    each query using \"and\".
+:filter-count - Separate filter to generate the count displayed each search. Accepts
+    the same values as :filter. If :filter and :filter-count are specified, this
+    will be used instead of :filter, not in conjunction with it."
+  (widget-insert title ": ")
+  (if (and notmuch-hello-first-run (plist-get options :initially-hidden))
+      (add-to-list 'notmuch-hello-hidden-sections title))
+  (let ((is-hidden (member title notmuch-hello-hidden-sections))
+	(start (point)))
+    (if is-hidden
+	(widget-create 'push-button
+		       :notify `(lambda (widget &rest ignore)
+				  (setq notmuch-hello-hidden-sections
+					(delete ,title notmuch-hello-hidden-sections))
+				  (notmuch-hello-update))
+		       "show")
+      (widget-create 'push-button
+		     :notify `(lambda (widget &rest ignore)
+				(add-to-list 'notmuch-hello-hidden-sections
+					     ,title)
+				(notmuch-hello-update))
+		     "hide"))
+    (widget-insert "\n")
+    (let (target-pos
+	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
+      (when (and (not is-hidden)
+	       (or (not (plist-get options :hide-if-empty))
+		  searches))
+	(widget-insert "\n")
+	(setq target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	target-pos))))
+
+(defun notmuch-hello-insert-tags-section (&optional title &rest options)
+  "Insert a section displaying all tags and message counts for each.
+
+TITLE defaults to \"All tags\".
+Allowed options are those accepted by `notmuch-hello-insert-searches' and the
+following:
+
+:hide-tags - List of tags that should be excluded."
+  (apply 'notmuch-hello-insert-searches
+	 (or title "All tags")
+	 (notmuch-hello-generate-tag-alist
+	  (plist-get options :hide-tags)
+	  (plist-get options :filter)
+	  (plist-get options :filter-count))
+	 options))
+
+(defun notmuch-hello-insert-inbox ()
+  "Show an entry for each saved search and inboxed messages for each tag"
+  (notmuch-hello-insert-searches "What's in your inbox: "
+				 (append
+				  (notmuch-saved-searches)
+				  (notmuch-hello-generate-tag-alist))
+				 :filter "tag:inbox"))
+
+(defun notmuch-hello-insert-alltags ()
+  "Insert a section displaying all tags and associated message counts"
+  (notmuch-hello-insert-tags-section
+   nil
+   :initially-hidden (not notmuch-show-all-tags-list)
+   :filter notmuch-hello-tag-list-make-query))
+
+(defun notmuch-hello-insert-footer ()
+  "Insert the notmuch-hello footer."
+  (let ((start (point)))
+    (widget-insert "Type a search query and hit RET to view matching threads.\n")
+    (when notmuch-hello-recent-searches
+      (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
+      (widget-insert "Save recent searches with the `save' button.\n"))
+    (when notmuch-saved-searches
+      (widget-insert "Edit saved searches with the `edit' button.\n"))
+    (widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
+    (widget-insert "`=' refreshes this screen. `s' jumps to the search box. `q' to quit.\n")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (customize-variable 'notmuch-hello-sections))
+		   :button-prefix "" :button-suffix ""
+		   "Customize")
+    (widget-insert " this page.\n")
+    (let ((fill-column (- (window-width) notmuch-hello-indent)))
+      (center-region start (point)))))
+
 ;;;###autoload
 (defun notmuch-hello (&optional no-display)
   "Run notmuch and display saved searches, known tags, etc."
   (interactive)
 
-  ; Jump through a hoop to get this value from the deprecated variable
-  ; name (`notmuch-folders') or from the default value.
+					; Jump through a hoop to get this value from the deprecated variable
+					; name (`notmuch-folders') or from the default value.
   (if (not notmuch-saved-searches)
-    (setq notmuch-saved-searches (notmuch-saved-searches)))
+      (setq notmuch-saved-searches (notmuch-saved-searches)))
 
   (if no-display
       (set-buffer "*notmuch-hello*")
     (switch-to-buffer "*notmuch-hello*"))
 
-  (let ((target (if (widget-at)
-		   (widget-value (widget-at))
-		 (condition-case nil
-		     (progn
-		       (widget-forward 1)
-		       (widget-value (widget-at)))
-		   (error nil)))))
+  (let ((notmuch-hello-target (if (widget-at)
+				  (widget-value (widget-at))
+				(condition-case nil
+				    (progn
+				      (widget-forward 1)
+				      (widget-value (widget-at)))
+				  (error nil)))))
 
     (kill-all-local-variables)
     (let ((inhibit-read-only t))
@@ -391,167 +749,31 @@ Complete list of currently available key bindings:
       (mapc 'delete-overlay (car all))
       (mapc 'delete-overlay (cdr all)))
 
-    (when notmuch-show-logo
-      (let ((image notmuch-hello-logo))
-	;; The notmuch logo uses transparency. That can display poorly
-	;; when inserting the image into an emacs buffer (black logo on
-	;; a black background), so force the background colour of the
-	;; image. We use a face to represent the colour so that
-	;; `defface' can be used to declare the different possible
-	;; colours, which depend on whether the frame has a light or
-	;; dark background.
-	(setq image (cons 'image
-			  (append (cdr image)
-				  (list :background (face-background 'notmuch-hello-logo-background)))))
-	(insert-image image))
-      (widget-insert "  "))
-
-    (widget-insert "Welcome to ")
-    ;; Hack the display of the links used.
-    (let ((widget-link-prefix "")
-	  (widget-link-suffix ""))
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (browse-url notmuch-hello-url))
-		     :help-echo "Visit the notmuch website."
-		     "notmuch")
-      (widget-insert ". ")
-      (widget-insert "You have ")
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (notmuch-hello-update))
-		     :help-echo "Refresh"
-		     (notmuch-hello-nice-number
-		      (string-to-number (car (process-lines notmuch-command "count")))))
-      (widget-insert " messages.\n"))
-
-    (let ((found-target-pos nil)
-	  (final-target-pos nil))
-      (let* ((saved-alist
-	      ;; Filter out empty saved searches if required.
-	      (if notmuch-show-empty-saved-searches
-		  notmuch-saved-searches
-		(loop for elem in notmuch-saved-searches
-		      if (> (string-to-number (notmuch-saved-search-count (cdr elem))) 0)
-		      collect elem)))
-	     (saved-widest (notmuch-hello-longest-label saved-alist))
-	     (alltags-alist (if notmuch-show-all-tags-list (notmuch-hello-generate-tag-alist)))
-	     (alltags-widest (notmuch-hello-longest-label alltags-alist))
-	     (widest (max saved-widest alltags-widest)))
-
-	(when saved-alist
-	  (widget-insert "\nSaved searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (customize-variable 'notmuch-saved-searches))
-			 "edit")
-	  (widget-insert "\n\n")
-	  (setq final-target-pos (point-marker))
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags saved-alist widest target))
-	    (if found-target-pos
-		(setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\nSearch: ")
-	(setq notmuch-hello-search-bar-marker (point-marker))
-	(widget-create 'editable-field
-		       ;; Leave some space at the start and end of the
-		       ;; search boxes.
-		       :size (max 8 (- (window-width) notmuch-hello-indent
-				       (length "Search: ")))
-		       :action (lambda (widget &rest ignore)
-				 (notmuch-hello-search (widget-value widget))))
-	(widget-insert "\n")
-
-	(when notmuch-hello-recent-searches
-	  (widget-insert "\nRecent searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (setq notmuch-hello-recent-searches nil)
-				   (notmuch-hello-update))
-			 "clear")
-	  (widget-insert "\n\n")
-	  (let ((start (point))
-		(nth 0))
-	    (mapc '(lambda (search)
-		     (let ((widget-symbol (intern (format "notmuch-hello-search-%d" nth))))
-		       (set widget-symbol
-			    (widget-create 'editable-field
-				       ;; Don't let the search boxes be
-				       ;; less than 8 characters wide.
-				       :size (max 8
-						  (- (window-width)
-						     ;; Leave some space
-						     ;; at the start and
-						     ;; end of the
-						     ;; boxes.
-						     (* 2 notmuch-hello-indent)
-						     ;; 1 for the space
-						     ;; before the
-						     ;; `[save]' button. 6
-						     ;; for the `[save]'
-						     ;; button.
-						     1 6))
-				       :action (lambda (widget &rest ignore)
-						 (notmuch-hello-search (widget-value widget)))
-				       search))
-		       (widget-insert " ")
-		       (widget-create 'push-button
-				      :notify (lambda (widget &rest ignore)
-						(notmuch-hello-add-saved-search widget))
-				      :notmuch-saved-search-widget widget-symbol
-				      "save"))
-		     (widget-insert "\n")
-		     (setq nth (1+ nth)))
-		  notmuch-hello-recent-searches)
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(when alltags-alist
-	  (widget-insert "\nAll tags: ")
-	  (widget-create 'push-button
-			 :notify (lambda (widget &rest ignore)
-				   (setq notmuch-show-all-tags-list nil)
-				   (notmuch-hello-update))
-			 "hide")
-	  (widget-insert "\n\n")
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags alltags-alist widest target))
-	    (if (not final-target-pos)
-		(setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\n")
-
-	(if (not notmuch-show-all-tags-list)
-	    (widget-create 'push-button
-			   :notify (lambda (widget &rest ignore)
-				     (setq notmuch-show-all-tags-list t)
-				     (notmuch-hello-update))
-			   "Show all tags")))
-
-      (let ((start (point)))
-	(widget-insert "\n\n")
-	(widget-insert "Type a search query and hit RET to view matching threads.\n")
-	(when notmuch-hello-recent-searches
-	  (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
-	  (widget-insert "Save recent searches with the `save' button.\n"))
-	(when notmuch-saved-searches
-	  (widget-insert "Edit saved searches with the `edit' button.\n"))
-	(widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
-	(widget-insert "`=' refreshes this screen. `s' jumps to the search box. `q' to quit.\n")
-	(let ((fill-column (- (window-width) notmuch-hello-indent)))
-	  (center-region start (point))))
+    (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)
-	(notmuch-hello-goto-search)))))
+	(notmuch-hello-goto-search)))
+    (setq notmuch-hello-first-run nil)))
 
 (defun notmuch-folder ()
   "Deprecated function for invoking notmuch---calling `notmuch' is preferred now."
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v5 2/2] emacs: Tests for user-defined sections
  2011-07-09 18:03   ` [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
  2011-07-09 18:03     ` [PATCH v5 1/2] " Daniel Schoepe
@ 2011-07-09 18:03     ` Daniel Schoepe
  2011-07-11 10:32     ` [PATCH] emacs: NEWS entry " Daniel Schoepe
  2011-08-14 16:55     ` [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
  3 siblings, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2011-07-09 18:03 UTC (permalink / raw)
  To: notmuch

---
 test/emacs                                         |   37 ++++++++++++++++++++
 test/emacs.expected-output/notmuch-hello           |    4 ++-
 .../notmuch-hello-new-section                      |    4 ++
 .../notmuch-hello-no-saved-searches                |    4 ++-
 .../notmuch-hello-section-counts                   |    5 +++
 .../notmuch-hello-section-hidden-tag               |    4 ++
 .../notmuch-hello-section-with-empty               |    4 ++
 .../emacs.expected-output/notmuch-hello-with-empty |    4 ++-
 8 files changed, 63 insertions(+), 3 deletions(-)
 create mode 100644 test/emacs.expected-output/notmuch-hello-new-section
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-counts
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-hidden-tag
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-with-empty

diff --git a/test/emacs b/test/emacs
index 53f455a..2bf5c08 100755
--- a/test/emacs
+++ b/test/emacs
@@ -34,6 +34,43 @@ test_emacs '(let ((notmuch-saved-searches
 	      (test-output))'
 test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-no-saved-searches
 
+test_begin_subtest "User defined section with inbox tag"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test\" '((\"inbox\" . \"tag:inbox\")))))))
+           (notmuch-hello)
+           (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-new-section
+
+test_begin_subtest "User defined section with empty, hidden entry"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test-with-empty\" 
+                                     '((\"inbox\" . \"tag:inbox\")
+                                       (\"doesnotexist\" . \"tag:doesnotexist\"))
+                                     :hide-empty-searches t)))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-with-empty
+
+test_begin_subtest "User defined section, unread tag filtered out"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-filtered\"
+                                     :hide-tags '(\"unread\"))))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-hidden-tag
+
+test_begin_subtest "User defined section, different query for counts"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-counts\"
+                                     :filter-count \"tag:signed\")))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-counts
+
 test_begin_subtest "Basic notmuch-search view in emacs"
 test_emacs '(notmuch-search "tag:inbox")
 	    (notmuch-test-wait)
diff --git a/test/emacs.expected-output/notmuch-hello b/test/emacs.expected-output/notmuch-hello
index 64b7e42..fd512d3 100644
--- a/test/emacs.expected-output/notmuch-hello
+++ b/test/emacs.expected-output/notmuch-hello
@@ -6,9 +6,11 @@ Saved searches: [edit]
 
 Search:                                                                     
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
diff --git a/test/emacs.expected-output/notmuch-hello-new-section b/test/emacs.expected-output/notmuch-hello-new-section
new file mode 100644
index 0000000..be7b26a
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-new-section
@@ -0,0 +1,4 @@
+Test: [hide]
+
+	  50 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-no-saved-searches b/test/emacs.expected-output/notmuch-hello-no-saved-searches
index 7f8206a..37b2303 100644
--- a/test/emacs.expected-output/notmuch-hello-no-saved-searches
+++ b/test/emacs.expected-output/notmuch-hello-no-saved-searches
@@ -2,9 +2,11 @@
 
 Search:                                                                     
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-counts b/test/emacs.expected-output/notmuch-hello-section-counts
new file mode 100644
index 0000000..12d19ed
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-counts
@@ -0,0 +1,5 @@
+Test-with-counts: [hide]
+
+	   2 attachment             7 inbox                  7 signed          
+	   7 unread          
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-hidden-tag b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
new file mode 100644
index 0000000..eb21c07
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
@@ -0,0 +1,4 @@
+Test-with-filtered: [hide]
+
+	   4 attachment            50 inbox                  7 signed          
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-with-empty b/test/emacs.expected-output/notmuch-hello-section-with-empty
new file mode 100644
index 0000000..4481dca
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-with-empty
@@ -0,0 +1,4 @@
+Test-with-empty: [hide]
+
+	  50 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-with-empty b/test/emacs.expected-output/notmuch-hello-with-empty
index a9ed630..220fac4 100644
--- a/test/emacs.expected-output/notmuch-hello-with-empty
+++ b/test/emacs.expected-output/notmuch-hello-with-empty
@@ -6,9 +6,11 @@ Saved searches: [edit]
 
 Search:                                                                     
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH] emacs: NEWS entry for user-defined sections
  2011-07-09 18:03   ` [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
  2011-07-09 18:03     ` [PATCH v5 1/2] " Daniel Schoepe
  2011-07-09 18:03     ` [PATCH v5 2/2] emacs: Tests for user-defined sections Daniel Schoepe
@ 2011-07-11 10:32     ` Daniel Schoepe
  2011-08-14 16:55     ` [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
  3 siblings, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2011-07-11 10:32 UTC (permalink / raw)
  To: notmuch

---
 NEWS |   13 +++++++++++++
 1 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/NEWS b/NEWS
index f3fefad..98a6b28 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,16 @@
+Notmuch 0.7 (YYYY-MM-DD)
+=======================
+New emacs-interface features
+----------------------------
+Customizable notmuch-hello page
+
+  The notmuch-hello page can now be customized using the
+  notmuch-hello-sections variable. It allows removing and adding
+  arbitrary sections (functions that insert UI elements).  Various
+  often-used, configurable section types are also provided. This
+  feature is also fully usable via the customize-interface. For more
+  information, see `M-x describe-variable notmuch-hello-sections'.
+
 Notmuch 0.6 (2011-07-01)
 =======================
 New, general features
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello
  2011-07-09 18:03   ` [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
                       ` (2 preceding siblings ...)
  2011-07-11 10:32     ` [PATCH] emacs: NEWS entry " Daniel Schoepe
@ 2011-08-14 16:55     ` Daniel Schoepe
  2011-08-15  8:40       ` Michal Sojka
  3 siblings, 1 reply; 57+ messages in thread
From: Daniel Schoepe @ 2011-08-14 16:55 UTC (permalink / raw)
  To: notmuch

[-- Attachment #1: Type: text/plain, Size: 346 bytes --]

On Sat,  9 Jul 2011 20:03:16 +0200, Daniel Schoepe <daniel.schoepe@googlemail.com> wrote:
> More documentation/customize improvements.

I know this is quite a large commit, but has anyone else had the time to
review this? I didn't notice any more flaws since the fifth version and
it made the hello page a lot more useful for me.

Cheers,
Daniel

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello
  2011-08-14 16:55     ` [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
@ 2011-08-15  8:40       ` Michal Sojka
  2011-10-10 13:39         ` [PATCH v6 " Daniel Schoepe
  0 siblings, 1 reply; 57+ messages in thread
From: Michal Sojka @ 2011-08-15  8:40 UTC (permalink / raw)
  To: Daniel Schoepe, notmuch

On Sun, 14 Aug 2011, Daniel Schoepe wrote:
> On Sat,  9 Jul 2011 20:03:16 +0200, Daniel Schoepe <daniel.schoepe@googlemail.com> wrote:
> > More documentation/customize improvements.
> 
> I know this is quite a large commit, but has anyone else had the time to
> review this? I didn't notice any more flaws since the fifth version and
> it made the hello page a lot more useful for me.

Hi Daniel,

I'm probably not "anyone else", but I discovered one cosmetic thing
which can be changed in the patch:

-  (notmuch-hello-insert-searches "What's in your inbox: "
+  (notmuch-hello-insert-searches "What's in your inbox"
  
-Michal

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v6 0/2] emacs: User-defined sections in notmuch-hello
  2011-08-15  8:40       ` Michal Sojka
@ 2011-10-10 13:39         ` Daniel Schoepe
  2011-10-10 13:39           ` [PATCH v6 1/2] " Daniel Schoepe
                             ` (3 more replies)
  0 siblings, 4 replies; 57+ messages in thread
From: Daniel Schoepe @ 2011-10-10 13:39 UTC (permalink / raw)
  To: notmuch

Fixed that and rebased against master.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v6 1/2] emacs: User-defined sections in notmuch-hello
  2011-10-10 13:39         ` [PATCH v6 " Daniel Schoepe
@ 2011-10-10 13:39           ` Daniel Schoepe
  2011-11-24 13:54             ` David Bremner
  2011-12-14  3:11             ` Dmitry Kurochkin
  2011-10-10 13:39           ` [PATCH v6 " Daniel Schoepe
                             ` (2 subsequent siblings)
  3 siblings, 2 replies; 57+ messages in thread
From: Daniel Schoepe @ 2011-10-10 13:39 UTC (permalink / raw)
  To: notmuch; +Cc: Daniel Schoepe

From: Daniel Schoepe <daniel.schoepe@googlemail.com>

This patch makes the notmuch-hello screen fully customizable
by allowing the user to add and remove arbitrary sections. It
also provides some convenience functions for constructing sections,
e.g. showing the unread message count for each tag.

This is done by specifying a list of functions that will be run
when notmuch-hello is invoked.
---
 emacs/notmuch-hello.el |  624 ++++++++++++++++++++++++++++++++----------------
 1 files changed, 423 insertions(+), 201 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index 65fde75..f7ec4dd 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -70,11 +70,6 @@ should return a filter for that tag, or nil to hide the tag."
 		 (const :tag "Custom filter function" function))
   :group 'notmuch)
 
-(defcustom notmuch-hello-hide-tags nil
-  "List of tags to be hidden in the \"all tags\"-section."
-  :type '(repeat string)
-  :group 'notmuch)
-
 (defface notmuch-hello-logo-background
   '((((class color)
       (background dark))
@@ -123,6 +118,94 @@ Typically \",\" in the US and UK and \".\" in Europe."
 
 (defvar notmuch-hello-recent-searches nil)
 
+(defvar notmuch-custom-section-options
+  '((:filter (string :tag "Filter for each tag"))
+    (:filter-count (string :tag "Different filter message counts"))
+    (:initially-hidden (const :tag "Hide this on startup?" t))
+    (:show-empty-searches (const :tag "Show queries with no matching messages" t))
+    (:hide-if-empty (const :tag "Hide if empty" t)))
+  "Various customization-options for notmuch-hello-tags/query-section.")
+
+(define-widget 'notmuch-hello-tags-section 'lazy
+  "Customize-type for notmuch-hello tag-list sections."
+  :tag "Customized tag-list (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-tags-section)
+	 (string :tag "Title for this section")
+	 (plist
+	  :inline t
+	  :options
+	  ,(append notmuch-custom-section-options
+		   '((:hide-tags (repeat :tag "Tags that will be hidden"
+					 string)))))))
+
+(define-widget 'notmuch-hello-query-section 'lazy
+  "Customize-type for custom saved-search-like sections"
+  :tag "Customized queries section (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-query-list)
+	 (string :tag "Title for this section")
+	 (repeat :tag "Queries"
+		 (cons (string :tag "Name") (string :tag "Query")))
+	 (plist :inline t :options ,notmuch-custom-section-options)))
+
+(defcustom notmuch-hello-sections
+  (list #'notmuch-hello-insert-header
+	#'notmuch-hello-insert-saved-searches
+	#'notmuch-hello-insert-search
+	#'notmuch-hello-insert-recent-searches
+	#'notmuch-hello-insert-alltags
+	#'notmuch-hello-insert-footer)
+  "Sections for notmuch-hello.
+
+Each entry of this list should be a function of no arguments that
+should return if `notmuch-hello-target' is produced as part of its
+output and nil otherwise. 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 list.
+
+The functions will be run to construct the content of the
+notmuch-hello buffer in the order they appear in this list. A
+section should not end with an empty line, because a newline will
+be inserted after each section by `notmuch-hello'.
+
+A \"Customized tag-list\" item in the customize-interface
+displays a list of all tags, optionally hiding some of them. It
+is also possible to filter the list of messages matching each tag
+by an additional filter query. Similarly, the count of messages
+displayed next to the buttons can be generated by applying a
+different filter to the tag query. These filters are also
+supported for \"Customized queries section\" items."
+  :group 'notmuch
+  :type 
+  '(repeat
+    (choice (function-item notmuch-hello-insert-header)
+	    (function-item notmuch-hello-insert-saved-searches)
+	    (function-item notmuch-hello-insert-search)
+	    (function-item notmuch-hello-insert-recent-searches)
+	    (function-item notmuch-hello-insert-alltags)
+	    (function-item notmuch-hello-insert-footer)
+	    (function-item notmuch-hello-insert-inbox)
+	    notmuch-hello-tags-section
+	    notmuch-hello-query-section
+	    (function :tag "Custom function"))))
+
+(defvar notmuch-hello-target nil
+  "Button at position of point before rebuilding the notmuch-buffer
+
+This variable contains the string of the button, if any, the
+point was positioned at before the notmuch-hello buffer was
+rebuilt. This is never actually set globally and 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 query section titles whose contents are hidden")
+
+(defvar notmuch-hello-first-run t)
+
 (defun notmuch-hello-remember-search (search)
   (if (not (member search notmuch-hello-recent-searches))
       (push search notmuch-hello-recent-searches))
@@ -173,8 +256,8 @@ Typically \",\" in the US and UK and \".\" in Europe."
     (message "Saved '%s' as '%s'." search name)
     (notmuch-hello-update)))
 
-(defun notmuch-hello-longest-label (tag-alist)
-  (or (loop for elem in tag-alist
+(defun notmuch-hello-longest-label (searches-alist)
+  (or (loop for elem in searches-alist
 	    maximize (length (car elem)))
       0))
 
@@ -238,12 +321,71 @@ should be. Returns a cons cell `(tags-per-line width)'."
 				   (* tags-per-line (+ 9 1))))
 			   tags-per-line))))
 
-(defun notmuch-hello-insert-tags (tag-alist widest target)
-  (let* ((tags-and-width (notmuch-hello-tags-per-line widest))
+(defun notmuch-hello-filtered-query (query filter)
+  "Constructs a query to search all messages matching QUERY and FILTER.
+
+If FILTER is a string, it is directly used in the returned query.
+
+If FILTER is a function, it is called with QUERY as a parameter and
+the string it returns is used as the filter.
+
+Otherwise, FILTER is ignored.
+"
+  (cond
+   ((functionp filter)
+    (let ((result (funcall filter query)))
+      (and result (concat query " and (" result ")"))))
+   ((stringp filter)
+    (concat query " and (" filter ")"))
+   (t (concat query))))
+
+(defun notmuch-hello-query-counts (query-alist &rest options)
+  "Compute list of counts of matched messages from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated query.
+
+The result is the list of elements of the form (NAME QUERY COUNT).
+
+The values :show-empty-searches, :filter and :filter-count from
+options will be handled as specified for
+`notmuch-hello-insert-searches'."
+  (notmuch-remove-if-not
+   #'identity
+   (mapcar
+    (lambda (elem)
+      (let* ((name (car elem))
+	     (query-and-count (if (consp (cdr elem))
+				  ;; do we have a different query for the message count?
+				  (cons (second elem) (third elem))
+				(cons (cdr elem) (cdr elem))))
+	     (message-count
+	      (string-to-number
+	       (notmuch-saved-search-count
+		(notmuch-hello-filtered-query (cdr query-and-count)
+					      (or (plist-get options :filter-count)
+						 (plist-get options :filter)))))))
+	(and (or (plist-get options :show-empty-searches) (> message-count 0))
+	   (list name (notmuch-hello-filtered-query
+		       (car query-and-count) (plist-get options :filter))
+		 message-count))))
+    query-alist)))
+
+(defun notmuch-hello-insert-buttons (searches)
+  "Insert buttons for SEARCHES.
+
+SEARCHES must be a list containing lists of the form (NAME QUERY COUNT), where
+QUERY is the query to start when the button for the corresponding entry is
+activated. COUNT should be the number of messages matching the query.
+Such a list can be computed with `notmuch-hello-query-counts'."
+  (let* ((widest (notmuch-hello-longest-label searches))
+	 (tags-and-width (notmuch-hello-tags-per-line widest))
 	 (tags-per-line (car tags-and-width))
 	 (widest (cdr tags-and-width))
 	 (count 0)
-	 (reordered-list (notmuch-hello-reflect tag-alist tags-per-line))
+	 (reordered-list (notmuch-hello-reflect searches tags-per-line))
 	 ;; Hack the display of the buttons used.
 	 (widget-push-button-prefix "")
 	 (widget-push-button-suffix "")
@@ -253,13 +395,13 @@ should be. Returns a cons cell `(tags-per-line width)'."
     (mapc (lambda (elem)
 	    ;; (not elem) indicates an empty slot in the matrix.
 	    (when elem
-	      (let* ((name (car elem))
-		     (query (cdr elem))
+	      (let* ((name (first elem))
+		     (query (second elem))
+		     (count (third elem))
 		     (formatted-name (format "%s " name)))
 		(widget-insert (format "%8s "
-				       (notmuch-hello-nice-number
-					(string-to-number (notmuch-saved-search-count query)))))
-		(if (string= formatted-name target)
+				       (notmuch-hello-nice-number count)))
+		(if (string= formatted-name notmuch-hello-target)
 		    (setq found-target-pos (point-marker)))
 		(widget-create 'push-button
 			       :notify #'notmuch-hello-widget-search
@@ -277,7 +419,7 @@ should be. Returns a cons cell `(tags-per-line width)'."
 	    (setq count (1+ count))
 	    (if (eq (% count tags-per-line) 0)
 		(widget-insert "\n")))
-	  reordered-list)
+	  searches)
 
     ;; If the last line was not full (and hence did not include a
     ;; carriage return), insert one now.
@@ -325,59 +467,275 @@ should be. Returns a cons cell `(tags-per-line width)'."
 (fset 'notmuch-hello-mode-map notmuch-hello-mode-map)
 
 (defun notmuch-hello-mode ()
- "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
+  "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
 
 Complete list of currently available key bindings:
 
 \\{notmuch-hello-mode-map}"
- (interactive)
- (kill-all-local-variables)
- (use-local-map notmuch-hello-mode-map)
- (setq major-mode 'notmuch-hello-mode
-       mode-name "notmuch-hello")
- ;;(setq buffer-read-only t)
-)
-
-(defun notmuch-hello-generate-tag-alist ()
+  (interactive)
+  (kill-all-local-variables)
+  (use-local-map notmuch-hello-mode-map)
+  (setq major-mode 'notmuch-hello-mode
+	mode-name "notmuch-hello"))
+
+(defun notmuch-hello-generate-tag-alist (&optional hide-tags filter-query filter-count)
   "Return an alist from tags to queries to display in the all-tags section."
   (notmuch-remove-if-not
-   #'cdr
+   #'identity
    (mapcar (lambda (tag)
-	     (cons tag
-		   (cond
-		    ((functionp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     (funcall notmuch-hello-tag-list-make-query tag) ")"))
-		    ((stringp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     notmuch-hello-tag-list-make-query ")"))
-		    (t (concat "tag:" tag)))))
+	     (let ((query (notmuch-hello-filtered-query (concat "tag:" tag)
+							filter-query)))
+	       (when query
+		 (if filter-count
+		     (list tag (notmuch-hello-filtered-query tag filter-query)
+			   (notmuch-hello-filtered-query (concat "tag:" tag)
+							 filter-count))
+		   (cons tag (notmuch-hello-filtered-query
+			      (concat "tag:" tag) filter-query))))))
 	   (notmuch-remove-if-not
 	    (lambda (tag)
-	      (not (member tag notmuch-hello-hide-tags)))
+	      (not (member tag hide-tags)))
 	    (process-lines notmuch-command "search-tags")))))
 
+(defun notmuch-hello-insert-header ()
+  "Insert the default notmuch-hello header."
+  (when notmuch-show-logo
+    (let ((image notmuch-hello-logo))
+      ;; The notmuch logo uses transparency. That can display poorly
+      ;; when inserting the image into an emacs buffer (black logo on
+      ;; a black background), so force the background colour of the
+      ;; image. We use a face to represent the colour so that
+      ;; `defface' can be used to declare the different possible
+      ;; colours, which depend on whether the frame has a light or
+      ;; dark background.
+      (setq image (cons 'image
+			(append (cdr image)
+				(list :background (face-background 'notmuch-hello-logo-background)))))
+      (insert-image image))
+    (widget-insert "  "))
+
+  (widget-insert "Welcome to ")
+  ;; Hack the display of the links used.
+  (let ((widget-link-prefix "")
+	(widget-link-suffix ""))
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (browse-url notmuch-hello-url))
+		   :help-echo "Visit the notmuch website."
+		   "notmuch")
+    (widget-insert ". ")
+    (widget-insert "You have ")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (notmuch-hello-update))
+		   :help-echo "Refresh"
+		   (notmuch-hello-nice-number
+		    (string-to-number (car (process-lines notmuch-command "count")))))
+    (widget-insert " messages.\n")))
+
+
+(defun notmuch-hello-insert-saved-searches ()
+  "Insert the saved-searches section."
+  (let ((searches (notmuch-hello-query-counts
+		  notmuch-saved-searches
+		  :show-empty-searches notmuch-show-empty-saved-searches))
+	found-target-pos final-target-pos)
+    (when searches
+      (widget-insert "Saved searches: ")
+      (widget-create 'push-button
+		     :notify (lambda (&rest ignore)
+			       (customize-variable 'notmuch-saved-searches))
+		     "edit")
+      (widget-insert "\n\n")
+      (setq final-target-pos (point-marker))
+      (let ((start (point)))
+	(setq found-target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(if found-target-pos
+	    (setq final-target-pos found-target-pos))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	final-target-pos))))
+
+(defun notmuch-hello-insert-search ()
+  "Insert a search widget."
+  (widget-insert "Search: ")
+  (setq notmuch-hello-search-bar-marker (point-marker))
+  (widget-create 'editable-field
+		 ;; Leave some space at the start and end of the
+		 ;; search boxes.
+		 :size (max 8 (- (window-width) notmuch-hello-indent
+				 (length "Search: ")))
+		 :action (lambda (widget &rest ignore)
+			   (notmuch-hello-search (widget-value widget))))
+  (insert "\n"))
+
+(defun notmuch-hello-insert-recent-searches ()
+  "Insert recent searches."
+  (when notmuch-hello-recent-searches
+    (widget-insert "Recent searches: ")
+    (widget-create 'push-button
+		   :notify (lambda (&rest ignore)
+			     (setq notmuch-hello-recent-searches nil)
+			     (notmuch-hello-update))
+		   "clear")
+    (widget-insert "\n\n")
+    (let ((start (point))
+	  (nth 0))
+      (mapc '(lambda (search)
+	       (let ((widget-symbol (intern (format "notmuch-hello-search-%d" nth))))
+		 (set widget-symbol
+		      (widget-create 'editable-field
+				     ;; Don't let the search boxes be
+				     ;; less than 8 characters wide.
+				     :size (max 8
+						(- (window-width)
+						   ;; Leave some space
+						   ;; at the start and
+						   ;; end of the
+						   ;; boxes.
+						   (* 2 notmuch-hello-indent)
+						   ;; 1 for the space
+						   ;; before the
+						   ;; `[save]' button. 6
+						   ;; for the `[save]'
+						   ;; button.
+						   1 6))
+				     :action (lambda (widget &rest ignore)
+					       (notmuch-hello-search (widget-value widget)))
+				     search))
+		 (widget-insert " ")
+		 (widget-create 'push-button
+				:notify (lambda (widget &rest ignore)
+					  (notmuch-hello-add-saved-search widget))
+				:notmuch-saved-search-widget widget-symbol
+				"save"))
+	       (widget-insert "\n")
+	       (setq nth (1+ nth)))
+	    notmuch-hello-recent-searches)
+      (indent-rigidly start (point) notmuch-hello-indent))))
+
+(defun notmuch-hello-insert-searches (title query-alist &rest options)
+  "Insert a section with TITLE showing a list of buttons made from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated item.
+
+Supports the following entries in OPTIONS as a plist:
+:initially-hidden - if non-nil, section will be hidden on startup
+:show-empty-searches - show buttons with no matching messages
+:hide-if-empty - hide if no buttons would be shown
+   (only makes sense without :show-empty-searches)
+:filter - This can be a function that takes the search query as its argument and
+    returns a filter to be used in conjuction with the query for that search or nil
+    to hide the element. This can also be a string that is used as a combined with
+    each query using \"and\".
+:filter-count - Separate filter to generate the count displayed each search. Accepts
+    the same values as :filter. If :filter and :filter-count are specified, this
+    will be used instead of :filter, not in conjunction with it."
+  (widget-insert title ": ")
+  (if (and notmuch-hello-first-run (plist-get options :initially-hidden))
+      (add-to-list 'notmuch-hello-hidden-sections title))
+  (let ((is-hidden (member title notmuch-hello-hidden-sections))
+	(start (point)))
+    (if is-hidden
+	(widget-create 'push-button
+		       :notify `(lambda (widget &rest ignore)
+				  (setq notmuch-hello-hidden-sections
+					(delete ,title notmuch-hello-hidden-sections))
+				  (notmuch-hello-update))
+		       "show")
+      (widget-create 'push-button
+		     :notify `(lambda (widget &rest ignore)
+				(add-to-list 'notmuch-hello-hidden-sections
+					     ,title)
+				(notmuch-hello-update))
+		     "hide"))
+    (widget-insert "\n")
+    (let (target-pos
+	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
+      (when (and (not is-hidden)
+	       (or (not (plist-get options :hide-if-empty))
+		  searches))
+	(widget-insert "\n")
+	(setq target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	target-pos))))
+
+(defun notmuch-hello-insert-tags-section (&optional title &rest options)
+  "Insert a section displaying all tags and message counts for each.
+
+TITLE defaults to \"All tags\".
+Allowed options are those accepted by `notmuch-hello-insert-searches' and the
+following:
+
+:hide-tags - List of tags that should be excluded."
+  (apply 'notmuch-hello-insert-searches
+	 (or title "All tags")
+	 (notmuch-hello-generate-tag-alist
+	  (plist-get options :hide-tags)
+	  (plist-get options :filter)
+	  (plist-get options :filter-count))
+	 options))
+
+(defun notmuch-hello-insert-inbox ()
+  "Show an entry for each saved search and inboxed messages for each tag"
+  (notmuch-hello-insert-searches "What's in your inbox"
+				 (append
+				  (notmuch-saved-searches)
+				  (notmuch-hello-generate-tag-alist))
+				 :filter "tag:inbox"))
+
+(defun notmuch-hello-insert-alltags ()
+  "Insert a section displaying all tags and associated message counts"
+  (notmuch-hello-insert-tags-section
+   nil
+   :initially-hidden (not notmuch-show-all-tags-list)
+   :filter notmuch-hello-tag-list-make-query))
+
+(defun notmuch-hello-insert-footer ()
+  "Insert the notmuch-hello footer."
+  (let ((start (point)))
+    (widget-insert "Type a search query and hit RET to view matching threads.\n")
+    (when notmuch-hello-recent-searches
+      (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
+      (widget-insert "Save recent searches with the `save' button.\n"))
+    (when notmuch-saved-searches
+      (widget-insert "Edit saved searches with the `edit' button.\n"))
+    (widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
+    (widget-insert "`=' refreshes this screen. `s' jumps to the search box. `q' to quit.\n")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (customize-variable 'notmuch-hello-sections))
+		   :button-prefix "" :button-suffix ""
+		   "Customize")
+    (widget-insert " this page.\n")
+    (let ((fill-column (- (window-width) notmuch-hello-indent)))
+      (center-region start (point)))))
+
 ;;;###autoload
 (defun notmuch-hello (&optional no-display)
   "Run notmuch and display saved searches, known tags, etc."
   (interactive)
 
-  ; Jump through a hoop to get this value from the deprecated variable
-  ; name (`notmuch-folders') or from the default value.
+					; Jump through a hoop to get this value from the deprecated variable
+					; name (`notmuch-folders') or from the default value.
   (if (not notmuch-saved-searches)
-    (setq notmuch-saved-searches (notmuch-saved-searches)))
+      (setq notmuch-saved-searches (notmuch-saved-searches)))
 
   (if no-display
       (set-buffer "*notmuch-hello*")
     (switch-to-buffer "*notmuch-hello*"))
 
-  (let ((target (if (widget-at)
-		   (widget-value (widget-at))
-		 (condition-case nil
-		     (progn
-		       (widget-forward 1)
-		       (widget-value (widget-at)))
-		   (error nil)))))
+  (let ((notmuch-hello-target (if (widget-at)
+				  (widget-value (widget-at))
+				(condition-case nil
+				    (progn
+				      (widget-forward 1)
+				      (widget-value (widget-at)))
+				  (error nil)))))
 
     (kill-all-local-variables)
     (let ((inhibit-read-only t))
@@ -391,167 +749,31 @@ Complete list of currently available key bindings:
       (mapc 'delete-overlay (car all))
       (mapc 'delete-overlay (cdr all)))
 
-    (when notmuch-show-logo
-      (let ((image notmuch-hello-logo))
-	;; The notmuch logo uses transparency. That can display poorly
-	;; when inserting the image into an emacs buffer (black logo on
-	;; a black background), so force the background colour of the
-	;; image. We use a face to represent the colour so that
-	;; `defface' can be used to declare the different possible
-	;; colours, which depend on whether the frame has a light or
-	;; dark background.
-	(setq image (cons 'image
-			  (append (cdr image)
-				  (list :background (face-background 'notmuch-hello-logo-background)))))
-	(insert-image image))
-      (widget-insert "  "))
-
-    (widget-insert "Welcome to ")
-    ;; Hack the display of the links used.
-    (let ((widget-link-prefix "")
-	  (widget-link-suffix ""))
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (browse-url notmuch-hello-url))
-		     :help-echo "Visit the notmuch website."
-		     "notmuch")
-      (widget-insert ". ")
-      (widget-insert "You have ")
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (notmuch-hello-update))
-		     :help-echo "Refresh"
-		     (notmuch-hello-nice-number
-		      (string-to-number (car (process-lines notmuch-command "count")))))
-      (widget-insert " messages.\n"))
-
-    (let ((found-target-pos nil)
-	  (final-target-pos nil))
-      (let* ((saved-alist
-	      ;; Filter out empty saved searches if required.
-	      (if notmuch-show-empty-saved-searches
-		  notmuch-saved-searches
-		(loop for elem in notmuch-saved-searches
-		      if (> (string-to-number (notmuch-saved-search-count (cdr elem))) 0)
-		      collect elem)))
-	     (saved-widest (notmuch-hello-longest-label saved-alist))
-	     (alltags-alist (if notmuch-show-all-tags-list (notmuch-hello-generate-tag-alist)))
-	     (alltags-widest (notmuch-hello-longest-label alltags-alist))
-	     (widest (max saved-widest alltags-widest)))
-
-	(when saved-alist
-	  (widget-insert "\nSaved searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (customize-variable 'notmuch-saved-searches))
-			 "edit")
-	  (widget-insert "\n\n")
-	  (setq final-target-pos (point-marker))
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags saved-alist widest target))
-	    (if found-target-pos
-		(setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\nSearch: ")
-	(setq notmuch-hello-search-bar-marker (point-marker))
-	(widget-create 'editable-field
-		       ;; Leave some space at the start and end of the
-		       ;; search boxes.
-		       :size (max 8 (- (window-width) notmuch-hello-indent
-				       (length "Search: ")))
-		       :action (lambda (widget &rest ignore)
-				 (notmuch-hello-search (widget-value widget))))
-	(widget-insert "\n")
-
-	(when notmuch-hello-recent-searches
-	  (widget-insert "\nRecent searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (setq notmuch-hello-recent-searches nil)
-				   (notmuch-hello-update))
-			 "clear")
-	  (widget-insert "\n\n")
-	  (let ((start (point))
-		(nth 0))
-	    (mapc '(lambda (search)
-		     (let ((widget-symbol (intern (format "notmuch-hello-search-%d" nth))))
-		       (set widget-symbol
-			    (widget-create 'editable-field
-				       ;; Don't let the search boxes be
-				       ;; less than 8 characters wide.
-				       :size (max 8
-						  (- (window-width)
-						     ;; Leave some space
-						     ;; at the start and
-						     ;; end of the
-						     ;; boxes.
-						     (* 2 notmuch-hello-indent)
-						     ;; 1 for the space
-						     ;; before the
-						     ;; `[save]' button. 6
-						     ;; for the `[save]'
-						     ;; button.
-						     1 6))
-				       :action (lambda (widget &rest ignore)
-						 (notmuch-hello-search (widget-value widget)))
-				       search))
-		       (widget-insert " ")
-		       (widget-create 'push-button
-				      :notify (lambda (widget &rest ignore)
-						(notmuch-hello-add-saved-search widget))
-				      :notmuch-saved-search-widget widget-symbol
-				      "save"))
-		     (widget-insert "\n")
-		     (setq nth (1+ nth)))
-		  notmuch-hello-recent-searches)
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(when alltags-alist
-	  (widget-insert "\nAll tags: ")
-	  (widget-create 'push-button
-			 :notify (lambda (widget &rest ignore)
-				   (setq notmuch-show-all-tags-list nil)
-				   (notmuch-hello-update))
-			 "hide")
-	  (widget-insert "\n\n")
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags alltags-alist widest target))
-	    (if (not final-target-pos)
-		(setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\n")
-
-	(if (not notmuch-show-all-tags-list)
-	    (widget-create 'push-button
-			   :notify (lambda (widget &rest ignore)
-				     (setq notmuch-show-all-tags-list t)
-				     (notmuch-hello-update))
-			   "Show all tags")))
-
-      (let ((start (point)))
-	(widget-insert "\n\n")
-	(widget-insert "Type a search query and hit RET to view matching threads.\n")
-	(when notmuch-hello-recent-searches
-	  (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
-	  (widget-insert "Save recent searches with the `save' button.\n"))
-	(when notmuch-saved-searches
-	  (widget-insert "Edit saved searches with the `edit' button.\n"))
-	(widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
-	(widget-insert "`=' refreshes this screen. `s' jumps to the search box. `q' to quit.\n")
-	(let ((fill-column (- (window-width) notmuch-hello-indent)))
-	  (center-region start (point))))
+    (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)
-	(notmuch-hello-goto-search)))))
+	(notmuch-hello-goto-search)))
+    (setq notmuch-hello-first-run nil)))
 
 (defun notmuch-folder ()
   "Deprecated function for invoking notmuch---calling `notmuch' is preferred now."
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v6 2/2] emacs: Tests for user-defined sections
  2011-10-10 13:39         ` [PATCH v6 " Daniel Schoepe
  2011-10-10 13:39           ` [PATCH v6 1/2] " Daniel Schoepe
@ 2011-10-10 13:39           ` Daniel Schoepe
  2011-10-13 14:09           ` [PATCH] emacs-hello: Do not calculate the count of the messages in Michal Sojka
  2012-01-16 10:59           ` [PATCH v6 0/2] emacs: User-defined sections in notmuch-hello David Edmondson
  3 siblings, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2011-10-10 13:39 UTC (permalink / raw)
  To: notmuch; +Cc: Daniel Schoepe

From: Daniel Schoepe <daniel.schoepe@googlemail.com>

---
 test/emacs                                         |   37 ++++++++++++++++++++
 test/emacs.expected-output/notmuch-hello           |    4 ++-
 .../notmuch-hello-new-section                      |    4 ++
 .../notmuch-hello-no-saved-searches                |    4 ++-
 .../notmuch-hello-section-counts                   |    5 +++
 .../notmuch-hello-section-hidden-tag               |    4 ++
 .../notmuch-hello-section-with-empty               |    4 ++
 .../emacs.expected-output/notmuch-hello-with-empty |    4 ++-
 8 files changed, 63 insertions(+), 3 deletions(-)
 create mode 100644 test/emacs.expected-output/notmuch-hello-new-section
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-counts
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-hidden-tag
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-with-empty

diff --git a/test/emacs b/test/emacs
index 0303d7d..15854ae 100755
--- a/test/emacs
+++ b/test/emacs
@@ -29,6 +29,43 @@ test_emacs '(let ((notmuch-saved-searches
 	      (test-output))'
 test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-no-saved-searches
 
+test_begin_subtest "User defined section with inbox tag"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test\" '((\"inbox\" . \"tag:inbox\")))))))
+           (notmuch-hello)
+           (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-new-section
+
+test_begin_subtest "User defined section with empty, hidden entry"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test-with-empty\" 
+                                     '((\"inbox\" . \"tag:inbox\")
+                                       (\"doesnotexist\" . \"tag:doesnotexist\"))
+                                     :hide-empty-searches t)))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-with-empty
+
+test_begin_subtest "User defined section, unread tag filtered out"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-filtered\"
+                                     :hide-tags '(\"unread\"))))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-hidden-tag
+
+test_begin_subtest "User defined section, different query for counts"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-counts\"
+                                     :filter-count \"tag:signed\")))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-counts
+
 test_begin_subtest "Basic notmuch-search view in emacs"
 test_emacs '(notmuch-search "tag:inbox")
 	    (notmuch-test-wait)
diff --git a/test/emacs.expected-output/notmuch-hello b/test/emacs.expected-output/notmuch-hello
index 64b7e42..fd512d3 100644
--- a/test/emacs.expected-output/notmuch-hello
+++ b/test/emacs.expected-output/notmuch-hello
@@ -6,9 +6,11 @@ Saved searches: [edit]
 
 Search:                                                                     
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
diff --git a/test/emacs.expected-output/notmuch-hello-new-section b/test/emacs.expected-output/notmuch-hello-new-section
new file mode 100644
index 0000000..be7b26a
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-new-section
@@ -0,0 +1,4 @@
+Test: [hide]
+
+	  50 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-no-saved-searches b/test/emacs.expected-output/notmuch-hello-no-saved-searches
index 7f8206a..37b2303 100644
--- a/test/emacs.expected-output/notmuch-hello-no-saved-searches
+++ b/test/emacs.expected-output/notmuch-hello-no-saved-searches
@@ -2,9 +2,11 @@
 
 Search:                                                                     
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-counts b/test/emacs.expected-output/notmuch-hello-section-counts
new file mode 100644
index 0000000..12d19ed
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-counts
@@ -0,0 +1,5 @@
+Test-with-counts: [hide]
+
+	   2 attachment             7 inbox                  7 signed          
+	   7 unread          
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-hidden-tag b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
new file mode 100644
index 0000000..eb21c07
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
@@ -0,0 +1,4 @@
+Test-with-filtered: [hide]
+
+	   4 attachment            50 inbox                  7 signed          
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-with-empty b/test/emacs.expected-output/notmuch-hello-section-with-empty
new file mode 100644
index 0000000..4481dca
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-with-empty
@@ -0,0 +1,4 @@
+Test-with-empty: [hide]
+
+	  50 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-with-empty b/test/emacs.expected-output/notmuch-hello-with-empty
index a9ed630..220fac4 100644
--- a/test/emacs.expected-output/notmuch-hello-with-empty
+++ b/test/emacs.expected-output/notmuch-hello-with-empty
@@ -6,9 +6,11 @@ Saved searches: [edit]
 
 Search:                                                                     
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
-- 
1.7.6.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH] emacs-hello: Do not calculate the count of the messages in
  2011-10-10 13:39         ` [PATCH v6 " Daniel Schoepe
  2011-10-10 13:39           ` [PATCH v6 1/2] " Daniel Schoepe
  2011-10-10 13:39           ` [PATCH v6 " Daniel Schoepe
@ 2011-10-13 14:09           ` Michal Sojka
  2012-01-16 11:33             ` David Edmondson
  2012-01-16 10:59           ` [PATCH v6 0/2] emacs: User-defined sections in notmuch-hello David Edmondson
  3 siblings, 1 reply; 57+ messages in thread
From: Michal Sojka @ 2011-10-13 14:09 UTC (permalink / raw)
  To: Daniel Schoepe, notmuch

On Mon, 10 Oct 2011, Daniel Schoepe wrote:
> Fixed that and rebased against master.

Hi Daniel,

I've an improvement for your patches. Recently, I decided to speed up
notmuch hello startup times and I found that hiding a section does not
eliminate the execution of "notmuch count" commands for buttons in the
hidden section. The following patch (applies on top of v5) should fix
it. Now I can enjoy blazingly fast notmuch startup, because I have only
one section shown. :-)

Another improvement could be the addition of [hide] button for saved
searches sections.

Cheers,
-Michal

--8<---------------cut here---------------start------------->8---
The result is that hello screen shows much faster when some sections are
hidden.
---
 emacs/notmuch-hello.el |   20 ++++++++++----------
 1 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index 1f51281..9dcd0f9 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -653,16 +653,16 @@ Supports the following entries in OPTIONS as a plist:
 				(notmuch-hello-update))
 		     "hide"))
     (widget-insert "\n")
-    (let (target-pos
-	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
-      (when (and (not is-hidden)
-	       (or (not (plist-get options :hide-if-empty))
-		  searches))
-	(widget-insert "\n")
-	(setq target-pos
-	      (notmuch-hello-insert-buttons searches))
-	(indent-rigidly start (point) notmuch-hello-indent)
-	target-pos))))
+    (let (target-pos)
+      (when (not is-hidden)
+	(let ((searches (apply 'notmuch-hello-query-counts query-alist options)))
+	  (when (or (not (plist-get options :hide-if-empty))
+		    searches)
+	    (widget-insert "\n")
+	    (setq target-pos
+		  (notmuch-hello-insert-buttons searches))
+	    (indent-rigidly start (point) notmuch-hello-indent))))
+      target-pos)))
 
 (defun notmuch-hello-insert-tags-section (&optional title &rest options)
   "Insert a section displaying all tags and message counts for each.
-- 
1.7.5.4

--8<---------------cut here---------------end--------------->8---

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [PATCH v6 1/2] emacs: User-defined sections in notmuch-hello
  2011-10-10 13:39           ` [PATCH v6 1/2] " Daniel Schoepe
@ 2011-11-24 13:54             ` David Bremner
  2011-11-24 14:01               ` Daniel Schoepe
  2011-12-14  3:11             ` Dmitry Kurochkin
  1 sibling, 1 reply; 57+ messages in thread
From: David Bremner @ 2011-11-24 13:54 UTC (permalink / raw)
  To: Daniel Schoepe, notmuch; +Cc: Daniel Schoepe

On Mon, 10 Oct 2011 15:39:41 +0200, Daniel Schoepe <daniel@schoepe.org> wrote:
> From: Daniel Schoepe <daniel.schoepe@googlemail.com>
> 
> This patch makes the notmuch-hello screen fully customizable
> by allowing the user to add and remove arbitrary sections. It
> also provides some convenience functions for constructing sections,
> e.g. showing the unread message count for each tag.
> 

Yow, that is a big patch, I think it needs some more review.

d

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v6 1/2] emacs: User-defined sections in notmuch-hello
  2011-11-24 13:54             ` David Bremner
@ 2011-11-24 14:01               ` Daniel Schoepe
  2011-11-24 15:43                 ` Michal Sojka
  2011-11-28  4:06                 ` Dmitry Kurochkin
  0 siblings, 2 replies; 57+ messages in thread
From: Daniel Schoepe @ 2011-11-24 14:01 UTC (permalink / raw)
  To: David Bremner, notmuch

[-- Attachment #1: Type: text/plain, Size: 991 bytes --]

On Thu, 24 Nov 2011 09:54:50 -0400, David Bremner <david@tethera.net> wrote:
> On Mon, 10 Oct 2011 15:39:41 +0200, Daniel Schoepe <daniel@schoepe.org> wrote:
> > From: Daniel Schoepe <daniel.schoepe@googlemail.com>
> > 
> > This patch makes the notmuch-hello screen fully customizable
> > by allowing the user to add and remove arbitrary sections. It
> > also provides some convenience functions for constructing sections,
> > e.g. showing the unread message count for each tag.
> > 
> 
> Yow, that is a big patch, I think it needs some more review.

Yes; Unfortunately I didn't manage to come up with good partitioning
into smaller patches, because the changes are quite interconnected.

Is there something I can do to help with the review?

I think there's also one performance improvement written by Michal
Sojka, that isn't included in this version yet, but since patches are
getting applied more timely now, I guess that can be merged afterwards.

Cheers,
Daniel

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v6 1/2] emacs: User-defined sections in notmuch-hello
  2011-11-24 14:01               ` Daniel Schoepe
@ 2011-11-24 15:43                 ` Michal Sojka
  2011-11-28  4:06                 ` Dmitry Kurochkin
  1 sibling, 0 replies; 57+ messages in thread
From: Michal Sojka @ 2011-11-24 15:43 UTC (permalink / raw)
  To: Daniel Schoepe, David Bremner, notmuch

On Thu, 24 Nov 2011, Daniel Schoepe wrote:
> On Thu, 24 Nov 2011 09:54:50 -0400, David Bremner <david@tethera.net> wrote:
> > On Mon, 10 Oct 2011 15:39:41 +0200, Daniel Schoepe <daniel@schoepe.org> wrote:
> > > From: Daniel Schoepe <daniel.schoepe@googlemail.com>
> > > 
> > > This patch makes the notmuch-hello screen fully customizable
> > > by allowing the user to add and remove arbitrary sections. It
> > > also provides some convenience functions for constructing sections,
> > > e.g. showing the unread message count for each tag.
> > > 
> > 
> > Yow, that is a big patch, I think it needs some more review.

I use this patch on a daily basis. There is probably some space for
improvements, especially in the coding style, but I'd like to see a more
experience elisp hacker than me looking at it.

> Yes; Unfortunately I didn't manage to come up with good partitioning
> into smaller patches, because the changes are quite interconnected.
> 
> Is there something I can do to help with the review?
> 
> I think there's also one performance improvement written by Michal
> Sojka, that isn't included in this version yet, 

Yes, that's true. I'm aware of one bug in this improvement. I usually
start notmuch by

  emacsclient -c -a '' -e "(notmuch)"

and the bug is that sometimes the emacs window disappear immediately
after appearing. This did not happen without this improvement and I
guess it is caused by some unexpected return value, perhaps in the code
which determines the initial position of the point.

Cheers,
-Michal

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v6 1/2] emacs: User-defined sections in notmuch-hello
  2011-11-24 14:01               ` Daniel Schoepe
  2011-11-24 15:43                 ` Michal Sojka
@ 2011-11-28  4:06                 ` Dmitry Kurochkin
  2011-11-28  7:57                   ` Michal Sojka
  1 sibling, 1 reply; 57+ messages in thread
From: Dmitry Kurochkin @ 2011-11-28  4:06 UTC (permalink / raw)
  To: Daniel Schoepe, David Bremner, notmuch

Hi Daniel.

On Thu, 24 Nov 2011 15:01:01 +0100, Daniel Schoepe <daniel@schoepe.org> wrote:
> On Thu, 24 Nov 2011 09:54:50 -0400, David Bremner <david@tethera.net> wrote:
> > On Mon, 10 Oct 2011 15:39:41 +0200, Daniel Schoepe <daniel@schoepe.org> wrote:
> > > From: Daniel Schoepe <daniel.schoepe@googlemail.com>
> > > 
> > > This patch makes the notmuch-hello screen fully customizable
> > > by allowing the user to add and remove arbitrary sections. It
> > > also provides some convenience functions for constructing sections,
> > > e.g. showing the unread message count for each tag.
> > > 
> > 
> > Yow, that is a big patch, I think it needs some more review.
> 
> Yes; Unfortunately I didn't manage to come up with good partitioning
> into smaller patches, because the changes are quite interconnected.
> 
> Is there something I can do to help with the review?
> 

Looking at earlier threads, I found an unanswered email from Austin [1].
IMO it has some good ideas and I would like to see your answer to it.

Sorry if I just missed it.  Would appreciate a link if that is the case.

Regards,
  Dmitry

[1] id:"BANLkTinKCLg1epsGvfxtyy-_G2Hm=gjUMQ@mail.gmail.com"

> I think there's also one performance improvement written by Michal
> Sojka, that isn't included in this version yet, but since patches are
> getting applied more timely now, I guess that can be merged afterwards.
> 
> Cheers,
> Daniel
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v6 1/2] emacs: User-defined sections in notmuch-hello
  2011-11-28  4:06                 ` Dmitry Kurochkin
@ 2011-11-28  7:57                   ` Michal Sojka
  0 siblings, 0 replies; 57+ messages in thread
From: Michal Sojka @ 2011-11-28  7:57 UTC (permalink / raw)
  To: Dmitry Kurochkin, Daniel Schoepe, David Bremner, notmuch

On Mon, 28 Nov 2011, Dmitry Kurochkin wrote:
> Hi Daniel.
> 
> On Thu, 24 Nov 2011 15:01:01 +0100, Daniel Schoepe <daniel@schoepe.org> wrote:
> > On Thu, 24 Nov 2011 09:54:50 -0400, David Bremner <david@tethera.net> wrote:
> > > On Mon, 10 Oct 2011 15:39:41 +0200, Daniel Schoepe <daniel@schoepe.org> wrote:
> > > > From: Daniel Schoepe <daniel.schoepe@googlemail.com>
> > > > 
> > > > This patch makes the notmuch-hello screen fully customizable
> > > > by allowing the user to add and remove arbitrary sections. It
> > > > also provides some convenience functions for constructing sections,
> > > > e.g. showing the unread message count for each tag.
> > > > 
> > > 
> > > Yow, that is a big patch, I think it needs some more review.
> > 
> > Yes; Unfortunately I didn't manage to come up with good partitioning
> > into smaller patches, because the changes are quite interconnected.
> > 
> > Is there something I can do to help with the review?
> > 
> 
> Looking at earlier threads, I found an unanswered email from Austin [1].
> IMO it has some good ideas and I would like to see your answer to it.

It seems to me that all Austin's ideas are already implemented in the
later versions of the patch.

-Michal

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v6 1/2] emacs: User-defined sections in notmuch-hello
  2011-10-10 13:39           ` [PATCH v6 1/2] " Daniel Schoepe
  2011-11-24 13:54             ` David Bremner
@ 2011-12-14  3:11             ` Dmitry Kurochkin
  2011-12-14 12:55               ` Dmitry Kurochkin
  2012-01-22  0:39               ` Daniel Schoepe
  1 sibling, 2 replies; 57+ messages in thread
From: Dmitry Kurochkin @ 2011-12-14  3:11 UTC (permalink / raw)
  To: Daniel Schoepe, notmuch; +Cc: Daniel Schoepe

Hi Daniel.

I have finished reviewing this patch at last.  Sorry, it is a bit messy.
Overall, I like the patch.  It is a very nice improvement.

I am sure I have missed some important points, but I guess this is the
best I can do right now.  Perhaps I will find more comments for the next
version of the patch :)

As we already discussed on IRC, there are some trailing whitespaces to
cleanup.

Here is the review:

+(defvar notmuch-custom-section-options

s/notmuch-custom-section-options/notmuch-hello-custom-section-options/ for consistency?

+    (:filter-count (string :tag "Different filter message counts"))

It was not clear to me what this option is for from the docstring.
Perhaps something like: "Count query filter, if different from :filter"?

+    (:initially-hidden (const :tag "Hide this on startup?" t))

"This" refers to section, right?  If yes, let's state it explicitly:
"Hide this section on startup".  Also, we should probably remove the
question mark, or add it to other options for consistency.

Should the default be to show all sections?

+    (:hide-if-empty (const :tag "Hide if empty" t)))

As I understand, this controls whether the whole sections is visible.
It is not clear what "if empty" means.  Does it mean that all queries
are empty?  Or all queries are empty and :show-empty-sections is
false?  Consider changing to something like: "Hide this section if all
queries are empty [and hidden]".

+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-query-list)

Do we need to explicitly specify empty tags?  Aren't they empty by
default?

+  :tag "Customized tag-list (see docstring for details)"
+  :tag "Customized queries section (see docstring for details)"

Perhaps it would be more useful to add reference to
`notmuch-hello-sections'?  I.e. "see `notmuch-hello-sections' for
details.

Please s/Customized tag-list/Customized tag-list section/ everywhere for
consistency (or remove section from "Customized queries section").

+Each entry of this list should be a function of no arguments that
+should return if `notmuch-hello-target' is produced as part of its
+output and nil otherwise.

Something is missing between "return if".  IMO it is really hard to
understand what the function should actually do and what it should
return.  Are this functions expected to add section content to current
position?  As I understand, the return value indicates whether cursor
should be positioned somewhere inside this section.  It is a minor
detail, but it is described in the first (and complex sentence) as if
it was the most important part.  Consider moving the return and "no
arguments" to the 3rd paragraph which describes details about the
functions.  I would also swap 2nd and 3rd paragraph.  Smth like:

  The list contains functions which are used to construct sections in
  notmuch-hello buffer.  When notmuch-hello buffer is constructed,
  these functions are run in the order they appear in this list.  Each
  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 [something].
  Otherwise, it should return nil.

  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
  list.

  [ details about customized tag-list and queries sections ]

This is just a draft.  Feel free to use it or ignore it.

+ For convenience an element can also be

Remove space the leading space and do `fill-paragraph'.

+	    (function :tag "Custom function"))))

Perhaps "Custom section" would be more accurate?

+  "Button at position of point before rebuilding the notmuch-buffer

Missing dot at the end.

s/Button/Button text/?

+This variable contains the string of the button, if any, the

s/the string/text/ or label?

+rebuilt. This is never actually set globally and defined as a

s/is never actually set/should never be set/?

+(defvar notmuch-hello-hidden-sections nil
+  "List of query section titles whose contents are hidden")

Is this really for query sections only?

Does this duplicate :initially-hidden option from
notmuch-custom-section-options?

How about adding a global alist variable notmuch-hello-state to store
the state needed for section functions?  Currently, it would contain
two values: :first-run and :target.  This would allow us to add more
state variables in the future without polluting the global namespace.
Also, it would make clear what variables are section function are
supposed to use and perhaps even change (docstring should explain that
of course).

`notmuch-hello-filtered-query':

+      (and result (concat query " and (" result ")"))))

How about using the result as query instead of filter?  I.e. returning
the result without adding the query to it.  IMO it is simpler and more
flexible.  In particular, that would allow the function to return nil
in case the query should not be shown.

Query should be put in ().

+    (concat query " and (" filter ")"))

Same here.

+   (t (concat query))))

Why do we need concat here?

`notmuch-hello-query-counts':

+		(notmuch-hello-filtered-query (cdr query-and-count)
+					      (or (plist-get options :filter-count)
+						 (plist-get options :filter)))))))

and

+	   (list name (notmuch-hello-filtered-query
+		       (car query-and-count) (plist-get options :filter))
+		 message-count))))

We already handled :filter and :filter-count options in
`notmuch-hello-generate-tag-alist'.  We should just use the generated
queries passed in query-alist.

It seems that `notmuch-hello-query-counts' should handle only
:show-empty-searches option.  If that is true, docstring should be
updated accordingly.  Also, I think it is better to pass a single
:show-empty-searches option as a parameter instead of the whole
options plist.

-	  reordered-list)
+	  searches)

I am not sure if this is a mistake.  I hope it is not, because it
allows us to remove some code :) If this change is correct, please
make it a separate patch and remove unused reordered-list variable,
notmuch-hello-reflect and notmuch-hello-reflect-generate-row
functions.  Otherwise, revert the change.

- "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
+  "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.

Please revert.

- (interactive)
- (kill-all-local-variables)
- (use-local-map notmuch-hello-mode-map)
- (setq major-mode 'notmuch-hello-mode
-       mode-name "notmuch-hello")
- ;;(setq buffer-read-only t)
-)
-
+  (interactive)
+  (kill-all-local-variables)
+  (use-local-map notmuch-hello-mode-map)
+  (setq major-mode 'notmuch-hello-mode
+	mode-name "notmuch-hello"))
+

Please revert.  The commented out line may be removed in a separate patch.

`notmuch-hello-generate-tag-alist':

+		     (list tag (notmuch-hello-filtered-query tag filter-query)

It should be (concat "tag:" tag) instead of tag.  Besides we already
have it in the query variable, so just use it.

+		   (cons tag (notmuch-hello-filtered-query
+			      (concat "tag:" tag) filter-query))))))

Same as above: use the query variable.

`notmuch-hello-insert-saved-searches':

Looks like we do not need both `final-target-pos'.  Can we just return
`found-target-pos'?

`notmuch-hello-insert-search':

+  (insert "\n"))

Should this be `widget-insert'?

Note that there are changes in master that need to be merged into
`notmuch-hello-insert-search' during rebase.

`notmuch-hello-insert-searches':

if my above comments on `notmuch-hello-query-counts' are correct, the
docstring should be fixed because `notmuch-hello-insert-searches' does
not handle :filter and :filter-count options.  Would be nice to move
this documentation somewhere instead of deleting it.

+	  (searches (apply 'notmuch-hello-query-counts query-alist options)))

Why do we need `apply' here?

`notmuch-hello-insert-tags-section':

+  "Insert a section displaying all tags and message counts for each.

Perhaps s/and message counts for each/with message counts/?

`notmuch-hello-insert-inbox':

Perhaps change docstring to something more consistent with other
notmuch-hello-insert-* functions?  E.g.:

  Insert a section displaying saved search and inbox messages for each tag.

+				  (notmuch-hello-generate-tag-alist))
+				 :filter "tag:inbox"))

If my above comments are correct, then :filter option should be given
to `notmuch-hello-generate-tag-alist' instead of
`notmuch-hello-insert-searches'.

`notmuch-hello-insert-alltags':

Missing dot at the end of docstring.

Perhaps s/and associated message counts/with message counts/?

`notmuch-hello':

-  ; Jump through a hoop to get this value from the deprecated variable
-  ; name (`notmuch-folders') or from the default value.
+					; Jump through a hoop to get this value from the deprecated variable
+					; name (`notmuch-folders') or from the default value.

Please revert.

   (if (not notmuch-saved-searches)
-    (setq notmuch-saved-searches (notmuch-saved-searches)))
+      (setq notmuch-saved-searches (notmuch-saved-searches)))

Please revert.

+    (setq notmuch-hello-first-run nil)))

Please move this statement to the top level.  There is no need for it
to be inside let.


Regards,
  Dmitry

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v6 1/2] emacs: User-defined sections in notmuch-hello
  2011-12-14  3:11             ` Dmitry Kurochkin
@ 2011-12-14 12:55               ` Dmitry Kurochkin
  2012-01-22  0:39               ` Daniel Schoepe
  1 sibling, 0 replies; 57+ messages in thread
From: Dmitry Kurochkin @ 2011-12-14 12:55 UTC (permalink / raw)
  To: Daniel Schoepe, notmuch; +Cc: Daniel Schoepe

On Wed, 14 Dec 2011 07:11:21 +0400, Dmitry Kurochkin <dmitry.kurochkin@gmail.com> wrote:
> Hi Daniel.
> 
> I have finished reviewing this patch at last.  Sorry, it is a bit messy.
> Overall, I like the patch.  It is a very nice improvement.
> 
> I am sure I have missed some important points, but I guess this is the
> best I can do right now.  Perhaps I will find more comments for the next
> version of the patch :)
> 
> As we already discussed on IRC, there are some trailing whitespaces to
> cleanup.
> 
> Here is the review:
> 
> +(defvar notmuch-custom-section-options
> 
> s/notmuch-custom-section-options/notmuch-hello-custom-section-options/ for consistency?
> 
> +    (:filter-count (string :tag "Different filter message counts"))
> 
> It was not clear to me what this option is for from the docstring.
> Perhaps something like: "Count query filter, if different from :filter"?
> 
> +    (:initially-hidden (const :tag "Hide this on startup?" t))
> 
> "This" refers to section, right?  If yes, let's state it explicitly:
> "Hide this section on startup".  Also, we should probably remove the
> question mark, or add it to other options for consistency.
> 
> Should the default be to show all sections?
> 
> +    (:hide-if-empty (const :tag "Hide if empty" t)))
> 
> As I understand, this controls whether the whole sections is visible.
> It is not clear what "if empty" means.  Does it mean that all queries
> are empty?  Or all queries are empty and :show-empty-sections is
> false?  Consider changing to something like: "Hide this section if all
> queries are empty [and hidden]".
> 
> +  `(list :tag ""
> +	 (const :tag "" notmuch-hello-insert-query-list)
> 
> Do we need to explicitly specify empty tags?  Aren't they empty by
> default?
> 
> +  :tag "Customized tag-list (see docstring for details)"
> +  :tag "Customized queries section (see docstring for details)"
> 
> Perhaps it would be more useful to add reference to
> `notmuch-hello-sections'?  I.e. "see `notmuch-hello-sections' for
> details.
> 
> Please s/Customized tag-list/Customized tag-list section/ everywhere for
> consistency (or remove section from "Customized queries section").
> 
> +Each entry of this list should be a function of no arguments that
> +should return if `notmuch-hello-target' is produced as part of its
> +output and nil otherwise.
> 
> Something is missing between "return if".  IMO it is really hard to
> understand what the function should actually do and what it should
> return.  Are this functions expected to add section content to current
> position?  As I understand, the return value indicates whether cursor
> should be positioned somewhere inside this section.  It is a minor
> detail, but it is described in the first (and complex sentence) as if
> it was the most important part.  Consider moving the return and "no
> arguments" to the 3rd paragraph which describes details about the
> functions.  I would also swap 2nd and 3rd paragraph.  Smth like:
> 
>   The list contains functions which are used to construct sections in
>   notmuch-hello buffer.  When notmuch-hello buffer is constructed,
>   these functions are run in the order they appear in this list.  Each
>   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 [something].
>   Otherwise, it should return nil.
> 
>   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
>   list.
> 
>   [ details about customized tag-list and queries sections ]
> 
> This is just a draft.  Feel free to use it or ignore it.
> 
> + For convenience an element can also be
> 
> Remove space the leading space and do `fill-paragraph'.
> 
> +	    (function :tag "Custom function"))))
> 
> Perhaps "Custom section" would be more accurate?
> 
> +  "Button at position of point before rebuilding the notmuch-buffer
> 
> Missing dot at the end.
> 
> s/Button/Button text/?
> 
> +This variable contains the string of the button, if any, the
> 
> s/the string/text/ or label?
> 
> +rebuilt. This is never actually set globally and defined as a
> 
> s/is never actually set/should never be set/?
> 
> +(defvar notmuch-hello-hidden-sections nil
> +  "List of query section titles whose contents are hidden")
> 
> Is this really for query sections only?
> 
> Does this duplicate :initially-hidden option from
> notmuch-custom-section-options?
> 
> How about adding a global alist variable notmuch-hello-state to store
> the state needed for section functions?  Currently, it would contain
> two values: :first-run and :target.  This would allow us to add more
> state variables in the future without polluting the global namespace.
> Also, it would make clear what variables are section function are
> supposed to use and perhaps even change (docstring should explain that
> of course).
> 
> `notmuch-hello-filtered-query':
> 
> +      (and result (concat query " and (" result ")"))))
> 
> How about using the result as query instead of filter?  I.e. returning
> the result without adding the query to it.  IMO it is simpler and more
> flexible.  In particular, that would allow the function to return nil
> in case the query should not be shown.
> 
> Query should be put in ().
> 
> +    (concat query " and (" filter ")"))
> 
> Same here.
> 
> +   (t (concat query))))
> 
> Why do we need concat here?
> 
> `notmuch-hello-query-counts':
> 
> +		(notmuch-hello-filtered-query (cdr query-and-count)
> +					      (or (plist-get options :filter-count)
> +						 (plist-get options :filter)))))))
> 
> and
> 
> +	   (list name (notmuch-hello-filtered-query
> +		       (car query-and-count) (plist-get options :filter))
> +		 message-count))))
> 
> We already handled :filter and :filter-count options in
> `notmuch-hello-generate-tag-alist'.  We should just use the generated
> queries passed in query-alist.
> 
> It seems that `notmuch-hello-query-counts' should handle only
> :show-empty-searches option.  If that is true, docstring should be
> updated accordingly.  Also, I think it is better to pass a single
> :show-empty-searches option as a parameter instead of the whole
> options plist.
> 

After thinking more about it, handling :filter and :filter-count in
`notmuch-hello-query-counts' is useful.  I may want to set "not
tag:spam" filter for all saved searches (if we add this customize option
later).  So `notmuch-hello-query-counts' should handle :filter and
:filter-count options, while `notmuch-hello-generate-tag-alist' should
just handle :hide-tags and return ("name" . "tag:name") list.

Actually, we should rename :hide-tags to more general :hide-queries or
:hide-search and handle it in `notmuch-hello-query-counts' as well.
`notmuch-hello-generate-tag-alist' would become very simple: it would
just get all tags from notmuch and make list of ("name" . "tag:name")
for each.  All query-related options would be handled on a single place.
Currently, :hide-tags is used only for tags, it makes little sense to
hide saved searches.  But one may want to add a section which gets list
of queries from some external source (similar to notmuch search-tags)
and hiding some queries would make sense.

Regards,
  Dmitry

> -	  reordered-list)
> +	  searches)
> 
> I am not sure if this is a mistake.  I hope it is not, because it
> allows us to remove some code :) If this change is correct, please
> make it a separate patch and remove unused reordered-list variable,
> notmuch-hello-reflect and notmuch-hello-reflect-generate-row
> functions.  Otherwise, revert the change.
> 
> - "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
> +  "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
> 
> Please revert.
> 
> - (interactive)
> - (kill-all-local-variables)
> - (use-local-map notmuch-hello-mode-map)
> - (setq major-mode 'notmuch-hello-mode
> -       mode-name "notmuch-hello")
> - ;;(setq buffer-read-only t)
> -)
> -
> +  (interactive)
> +  (kill-all-local-variables)
> +  (use-local-map notmuch-hello-mode-map)
> +  (setq major-mode 'notmuch-hello-mode
> +	mode-name "notmuch-hello"))
> +
> 
> Please revert.  The commented out line may be removed in a separate patch.
> 
> `notmuch-hello-generate-tag-alist':
> 
> +		     (list tag (notmuch-hello-filtered-query tag filter-query)
> 
> It should be (concat "tag:" tag) instead of tag.  Besides we already
> have it in the query variable, so just use it.
> 
> +		   (cons tag (notmuch-hello-filtered-query
> +			      (concat "tag:" tag) filter-query))))))
> 
> Same as above: use the query variable.
> 
> `notmuch-hello-insert-saved-searches':
> 
> Looks like we do not need both `final-target-pos'.  Can we just return
> `found-target-pos'?
> 
> `notmuch-hello-insert-search':
> 
> +  (insert "\n"))
> 
> Should this be `widget-insert'?
> 
> Note that there are changes in master that need to be merged into
> `notmuch-hello-insert-search' during rebase.
> 
> `notmuch-hello-insert-searches':
> 
> if my above comments on `notmuch-hello-query-counts' are correct, the
> docstring should be fixed because `notmuch-hello-insert-searches' does
> not handle :filter and :filter-count options.  Would be nice to move
> this documentation somewhere instead of deleting it.
> 
> +	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
> 
> Why do we need `apply' here?
> 
> `notmuch-hello-insert-tags-section':
> 
> +  "Insert a section displaying all tags and message counts for each.
> 
> Perhaps s/and message counts for each/with message counts/?
> 
> `notmuch-hello-insert-inbox':
> 
> Perhaps change docstring to something more consistent with other
> notmuch-hello-insert-* functions?  E.g.:
> 
>   Insert a section displaying saved search and inbox messages for each tag.
> 
> +				  (notmuch-hello-generate-tag-alist))
> +				 :filter "tag:inbox"))
> 
> If my above comments are correct, then :filter option should be given
> to `notmuch-hello-generate-tag-alist' instead of
> `notmuch-hello-insert-searches'.
> 
> `notmuch-hello-insert-alltags':
> 
> Missing dot at the end of docstring.
> 
> Perhaps s/and associated message counts/with message counts/?
> 
> `notmuch-hello':
> 
> -  ; Jump through a hoop to get this value from the deprecated variable
> -  ; name (`notmuch-folders') or from the default value.
> +					; Jump through a hoop to get this value from the deprecated variable
> +					; name (`notmuch-folders') or from the default value.
> 
> Please revert.
> 
>    (if (not notmuch-saved-searches)
> -    (setq notmuch-saved-searches (notmuch-saved-searches)))
> +      (setq notmuch-saved-searches (notmuch-saved-searches)))
> 
> Please revert.
> 
> +    (setq notmuch-hello-first-run nil)))
> 
> Please move this statement to the top level.  There is no need for it
> to be inside let.
> 
> 
> Regards,
>   Dmitry

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v6 0/2] emacs: User-defined sections in notmuch-hello
  2011-10-10 13:39         ` [PATCH v6 " Daniel Schoepe
                             ` (2 preceding siblings ...)
  2011-10-13 14:09           ` [PATCH] emacs-hello: Do not calculate the count of the messages in Michal Sojka
@ 2012-01-16 10:59           ` David Edmondson
  2012-01-16 11:13             ` Daniel Schoepe
  3 siblings, 1 reply; 57+ messages in thread
From: David Edmondson @ 2012-01-16 10:59 UTC (permalink / raw)
  To: Daniel Schoepe, notmuch

[-- Attachment #1: Type: text/plain, Size: 313 bytes --]

Daniel, there was a bunch of feedback from Dmitry about the patch and a
couple of other comments.

Do you plan to produce a new version?

It would be good to get this applied - it allows us to address various
comments that have been made about unifying the different search
mechanisms and looks generally useful.

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v6 0/2] emacs: User-defined sections in notmuch-hello
  2012-01-16 10:59           ` [PATCH v6 0/2] emacs: User-defined sections in notmuch-hello David Edmondson
@ 2012-01-16 11:13             ` Daniel Schoepe
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2012-01-16 11:13 UTC (permalink / raw)
  To: David Edmondson, notmuch

[-- Attachment #1: Type: text/plain, Size: 448 bytes --]

On Mon, 16 Jan 2012 10:59:39 +0000, David Edmondson <dme@dme.org> wrote:
> Daniel, there was a bunch of feedback from Dmitry about the patch and a
> couple of other comments.
> 
> Do you plan to produce a new version?

Yes, I plan to do that (I know I already said that in December) and
already have a partially rebased version. I hope I will get to finish it
up and incorporate the suggestions from this thread soon.

Cheers,
Daniel


[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH] emacs-hello: Do not calculate the count of the messages in
  2011-10-13 14:09           ` [PATCH] emacs-hello: Do not calculate the count of the messages in Michal Sojka
@ 2012-01-16 11:33             ` David Edmondson
  2012-01-16 12:39               ` Daniel Schoepe
  0 siblings, 1 reply; 57+ messages in thread
From: David Edmondson @ 2012-01-16 11:33 UTC (permalink / raw)
  To: Michal Sojka, Daniel Schoepe, notmuch

[-- Attachment #1: Type: text/plain, Size: 774 bytes --]

On Thu, 13 Oct 2011 16:09:23 +0200, Michal Sojka <sojkam1@fel.cvut.cz> wrote:
> On Mon, 10 Oct 2011, Daniel Schoepe wrote:
> > Fixed that and rebased against master.
> 
> Hi Daniel,
> 
> I've an improvement for your patches. Recently, I decided to speed up
> notmuch hello startup times and I found that hiding a section does not
> eliminate the execution of "notmuch count" commands for buttons in the
> hidden section. The following patch (applies on top of v5) should fix
> it. Now I can enjoy blazingly fast notmuch startup, because I have only
> one section shown. :-)
> 
> Another improvement could be the addition of [hide] button for saved
> searches sections.

Daniel, will you include this in your next version? (I'd like to mark it
'obsolete'.)

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH] emacs-hello: Do not calculate the count of the messages in
  2012-01-16 11:33             ` David Edmondson
@ 2012-01-16 12:39               ` Daniel Schoepe
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2012-01-16 12:39 UTC (permalink / raw)
  To: David Edmondson, Michal Sojka, notmuch

[-- Attachment #1: Type: text/plain, Size: 205 bytes --]

On Mon, 16 Jan 2012 11:33:59 +0000, David Edmondson <dme@dme.org> wrote:
> Daniel, will you include this in your next version? (I'd like to mark it
> 'obsolete'.)

Yes, it sounds like a great improvement.

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v6 1/2] emacs: User-defined sections in notmuch-hello
  2011-12-14  3:11             ` Dmitry Kurochkin
  2011-12-14 12:55               ` Dmitry Kurochkin
@ 2012-01-22  0:39               ` Daniel Schoepe
  2012-01-22  0:54                 ` [PATCH v7 " Daniel Schoepe
  1 sibling, 1 reply; 57+ messages in thread
From: Daniel Schoepe @ 2012-01-22  0:39 UTC (permalink / raw)
  To: Dmitry Kurochkin, notmuch

[-- Attachment #1: Type: text/plain, Size: 13495 bytes --]

On Wed, 14 Dec 2011 07:11:21 +0400, Dmitry Kurochkin <dmitry.kurochkin@gmail.com> wrote:
> Hi Daniel.
> 
> I have finished reviewing this patch at last.  Sorry, it is a bit messy.
> Overall, I like the patch.  It is a very nice improvement.
> 
> I am sure I have missed some important points, but I guess this is the
> best I can do right now.  Perhaps I will find more comments for the next
> version of the patch :)
> 
> As we already discussed on IRC, there are some trailing whitespaces to
> cleanup.
> 
> Here is the review:
> 
> +(defvar notmuch-custom-section-options
> 
> s/notmuch-custom-section-options/notmuch-hello-custom-section-options/
> for consistency?

Agreed.

> 
> +    (:filter-count (string :tag "Different filter message counts"))
> 
> It was not clear to me what this option is for from the docstring.
> Perhaps something like: "Count query filter, if different from :filter"?

This option is for displaying a different number of messages next to the
button, than actually match the query the button links to. I think this is
something that was requested a while ago in the context of my patch that
added notmuch-hello-tag-list-make-query.

> +    (:initially-hidden (const :tag "Hide this on startup?" t))
> 
> "This" refers to section, right?  If yes, let's state it explicitly:
> "Hide this section on startup".  Also, we should probably remove the
> question mark, or add it to other options for consistency.

Agreed.

> Should the default be to show all sections?

That's the default I'd prefer, since I want to see most of the section
I define by default. If you have some less bike-shedy arguments for
changing this, I'm all ears.

> 
> +    (:hide-if-empty (const :tag "Hide if empty" t)))
> 
> As I understand, this controls whether the whole sections is visible.
> It is not clear what "if empty" means.  Does it mean that all queries
> are empty?  Or all queries are empty and :show-empty-sections is
> false?  Consider changing to something like: "Hide this section if all
> queries are empty [and hidden]".

Okay, I'll clarify this in the next version.

> +  `(list :tag ""
> +	 (const :tag "" notmuch-hello-insert-query-list)
> 
> Do we need to explicitly specify empty tags?  Aren't they empty by
> default?

It displays the symbol after the const if this is missing.

> 
> +  :tag "Customized tag-list (see docstring for details)"
> +  :tag "Customized queries section (see docstring for details)"
> 
> Perhaps it would be more useful to add reference to
> `notmuch-hello-sections'?  I.e. "see `notmuch-hello-sections' for
> details.

Since this is mainly displayed in the drop-down menu for the section
type in the customize page for notmuch-hello-sections (or
customize-group 'notmuch), references a) don't work there and b)
usually would point to the same thing as the user is currently editing.

> Please s/Customized tag-list/Customized tag-list section/ everywhere for
> consistency (or remove section from "Customized queries section").

Done.

> 
> +Each entry of this list should be a function of no arguments that
> +should return if `notmuch-hello-target' is produced as part of its
> +output and nil otherwise.
> 
> Something is missing between "return if".  IMO it is really hard to
> understand what the function should actually do and what it should
> return.  Are this functions expected to add section content to current
> position?  As I understand, the return value indicates whether cursor
> should be positioned somewhere inside this section.  It is a minor
> detail, but it is described in the first (and complex sentence) as if
> it was the most important part.  Consider moving the return and "no
> arguments" to the 3rd paragraph which describes details about the
> functions.  I would also swap 2nd and 3rd paragraph.  Smth like:
> 
>   The list contains functions which are used to construct sections in
>   notmuch-hello buffer.  When notmuch-hello buffer is constructed,
>   these functions are run in the order they appear in this list.  Each
>   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 [something].
>   Otherwise, it should return nil.
> 
>   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
>   list.
> 
>   [ details about customized tag-list and queries sections ]
> 
> This is just a draft.  Feel free to use it or ignore it.

Thanks, that is quite a bit clearer than what I wrote.

> + For convenience an element can also be
> 
> Remove space the leading space and do `fill-paragraph'.
> 
> +	    (function :tag "Custom function"))))
> 
> Perhaps "Custom section" would be more accurate?

Yes, it would.

> 
> +  "Button at position of point before rebuilding the notmuch-buffer
> 
> Missing dot at the end.
> 
> s/Button/Button text/?
> 
> +This variable contains the string of the button, if any, the
> 
> s/the string/text/ or label?
> 
> +rebuilt. This is never actually set globally and defined as a
> 
> s/is never actually set/should never be set/?

Sounds good.

> 
> +(defvar notmuch-hello-hidden-sections nil
> +  "List of query section titles whose contents are hidden")
> 
> Is this really for query sections only?

No, good catch.

> 
> Does this duplicate :initially-hidden option from
> notmuch-custom-section-options?

This is actually for keeping track of which sections the user chose to
hide via the "[hide]"-button. Changing the corresponding field
:initially-hidden field in notmuch-sections is not possible for
arbitrary sections, i.e. functions and would alter what the users for
customize. Also, exiting notmuch and then starting it again would no
longer do what the user set in his configuration, if he didn't want to
hide it in the beginning, but then clicked on [hide] later.

> 
> How about adding a global alist variable notmuch-hello-state to store
> the state needed for section functions?  Currently, it would contain
> two values: :first-run and :target.  This would allow us to add more
> state variables in the future without polluting the global namespace.
> Also, it would make clear what variables are section function are
> supposed to use and perhaps even change (docstring should explain that
> of course).

Given that all the variables are prefixed with notmuch-hello already, I
don't think that the polluting the namespace is really a problem. And
given that section functions would have to access target, I'd rather
keep it in a separate variable so people who write a new type of section
don't have to inspect some detail in a variable whose other components
are internal implementation details (this is the case for first-run and
hidden-sections). If this actually becomes a problem we can still
move them there later.

> 
> `notmuch-hello-filtered-query':
> 
> +      (and result (concat query " and (" result ")"))))
> 
> How about using the result as query instead of filter?  I.e. returning
> the result without adding the query to it.  IMO it is simpler and more
> flexible.  In particular, that would allow the function to return nil
> in case the query should not be shown.

The entry is already hidden if the function returns nil, because of the
`and'. I agree that it's simpler though, so I changed it.

> 
> Query should be put in ().
> 
> +    (concat query " and (" filter ")"))
> 
> Same here.
> 
> +   (t (concat query))))
> 
> Why do we need concat here?

Fixed.

> 
> `notmuch-hello-query-counts':
> 
> +		(notmuch-hello-filtered-query (cdr query-and-count)
> +					      (or (plist-get options :filter-count)
> +						 (plist-get options :filter)))))))
> 
> and
> 
> +	   (list name (notmuch-hello-filtered-query
> +		       (car query-and-count) (plist-get options :filter))
> +		 message-count))))
> 
> We already handled :filter and :filter-count options in
> `notmuch-hello-generate-tag-alist'.  We should just use the generated
> queries passed in query-alist.

Yes, I handled that by moving all the code for that to -query-counts,
like you suggested in your other mail.

> It seems that `notmuch-hello-query-counts' should handle only
> :show-empty-searches option.  If that is true, docstring should be
> updated accordingly.  Also, I think it is better to pass a single
> :show-empty-searches option as a parameter instead of the whole
> options plist.
> 
> -	  reordered-list)
> +	  searches)
> 
> I am not sure if this is a mistake.  I hope it is not, because it
> allows us to remove some code :) If this change is correct, please
> make it a separate patch and remove unused reordered-list variable,
> notmuch-hello-reflect and notmuch-hello-reflect-generate-row
> functions.  Otherwise, revert the change.

It was definitely unintended and changes the default behavior, which is
to have the strings sorted within the columns instead of the rows.

> 
> - "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
> +  "Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
> 
> Please revert.
> 
> - (interactive)
> - (kill-all-local-variables)
> - (use-local-map notmuch-hello-mode-map)
> - (setq major-mode 'notmuch-hello-mode
> -       mode-name "notmuch-hello")
> - ;;(setq buffer-read-only t)
> -)
> -
> +  (interactive)
> +  (kill-all-local-variables)
> +  (use-local-map notmuch-hello-mode-map)
> +  (setq major-mode 'notmuch-hello-mode
> +	mode-name "notmuch-hello"))
> +
> 
> Please revert.  The commented out line may be removed in a separate patch.
> 
> `notmuch-hello-generate-tag-alist':
> 
> +		     (list tag (notmuch-hello-filtered-query tag filter-query)
> 
> It should be (concat "tag:" tag) instead of tag.  Besides we already
> have it in the query variable, so just use it.
> 
> +		   (cons tag (notmuch-hello-filtered-query
> +			      (concat "tag:" tag) filter-query))))))
> 
> Same as above: use the query variable.
> 
> `notmuch-hello-insert-saved-searches':
> 
> Looks like we do not need both `final-target-pos'.  Can we just return
> `found-target-pos'?
> 
> `notmuch-hello-insert-search':
> 
> +  (insert "\n"))
> 
> Should this be `widget-insert'?
> 
> Note that there are changes in master that need to be merged into
> `notmuch-hello-insert-search' during rebase.
> 
> `notmuch-hello-insert-searches':
> 
> if my above comments on `notmuch-hello-query-counts' are correct, the
> docstring should be fixed because `notmuch-hello-insert-searches' does
> not handle :filter and :filter-count options.  Would be nice to move
> this documentation somewhere instead of deleting it.

I moved it to notmuch-hello-insert-tags-section, which actually
does handle those option and is a high-level function that will
probably be used a lot more by users.

> 
> +	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
> 
> Why do we need `apply' here?

Because we want each item in `options' to be passed as an individual
argument. Note that apply is a bit peculiar about its last argument.

> `notmuch-hello-insert-tags-section':
> 
> +  "Insert a section displaying all tags and message counts for each.
> 
> Perhaps s/and message counts for each/with message counts/?
> 
> `notmuch-hello-insert-inbox':
> 
> Perhaps change docstring to something more consistent with other
> notmuch-hello-insert-* functions?  E.g.:
> 
>   Insert a section displaying saved search and inbox messages for each
>   tag.

Changed, thanks.

> 
> +				  (notmuch-hello-generate-tag-alist))
> +				 :filter "tag:inbox"))
> 
> If my above comments are correct, then :filter option should be given
> to `notmuch-hello-generate-tag-alist' instead of
> `notmuch-hello-insert-searches'.
> 
> `notmuch-hello-insert-alltags':
> 
> Missing dot at the end of docstring.
> 
> Perhaps s/and associated message counts/with message counts/?
> 
> `notmuch-hello':
> 
> -  ; Jump through a hoop to get this value from the deprecated variable
> -  ; name (`notmuch-folders') or from the default value.
> +					; Jump through a hoop to get this value from the deprecated variable
> +					; name (`notmuch-folders') or from the default value.
> 
> Please revert.
> 
>    (if (not notmuch-saved-searches)
> -    (setq notmuch-saved-searches (notmuch-saved-searches)))
> +      (setq notmuch-saved-searches (notmuch-saved-searches)))
> 
> Please revert.
> 
> +    (setq notmuch-hello-first-run nil)))
> 
> Please move this statement to the top level.  There is no need for it
> to be inside let.

Fixed.

I'll send my current version shortly, which does not yet include
Michal's performance improvement, because there probably still be a few
rough edges. The performance improvement could also be put in separate
patch so I don't have to keep rebasing this uncomfortably big patch for
much longer.

Thank you for your very detailed review.

Cheers,
Daniel

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v7 1/2] emacs: User-defined sections in notmuch-hello
  2012-01-22  0:39               ` Daniel Schoepe
@ 2012-01-22  0:54                 ` Daniel Schoepe
  2012-01-22  0:54                   ` [PATCH v7 2/2] emacs: Tests for user-defined sections Daniel Schoepe
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Schoepe @ 2012-01-22  0:54 UTC (permalink / raw)
  To: notmuch; +Cc: Daniel Schoepe

From: Daniel Schoepe <daniel.schoepe@googlemail.com>

This patch makes the notmuch-hello screen fully customizable
by allowing the user to add and remove arbitrary sections. It
also provides some convenience functions for constructing sections,
e.g. showing the unread message count for each tag.

This is done by specifying a list of functions that will be run
when notmuch-hello is invoked.
---
 emacs/notmuch-hello.el |  620 ++++++++++++++++++++++++++++++++----------------
 1 files changed, 420 insertions(+), 200 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index 63f2e07..5ba9a11 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -159,6 +159,101 @@ International Bureau of Weights and Measures."
 
 (defvar notmuch-hello-recent-searches nil)
 
+(defvar notmuch-hello-custom-section-options
+  '((:filter (string :tag "Filter for each tag"))
+    (:filter-count (string :tag "Different filter to generate message counts"))
+    (:initially-hidden (const :tag "Hide this section on startup" t))
+    (:show-empty-searches (const :tag "Show queries with no matching messages" t))
+    (:hide-if-empty (const :tag "Hide this section if all queries are empty
+\(and not hidden by show-if-empty)" t)))
+  "Various customization-options for notmuch-hello-tags/query-section.")
+
+(define-widget 'notmuch-hello-tags-section 'lazy
+  "Customize-type for notmuch-hello tag-list sections."
+  :tag "Customized tag-list section (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-tags-section)
+	 (string :tag "Title for this section")
+	 (plist
+	  :inline t
+	  :options
+	  ,(append notmuch-hello-custom-section-options
+		   '((:hide-tags (repeat :tag "Tags that will be hidden"
+					 string)))))))
+
+(define-widget 'notmuch-hello-query-section 'lazy
+  "Customize-type for custom saved-search-like sections"
+  :tag "Customized queries section (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-query-list)
+	 (string :tag "Title for this section")
+	 (repeat :tag "Queries"
+		 (cons (string :tag "Name") (string :tag "Query")))
+	 (plist :inline t :options ,notmuch-hello-custom-section-options)))
+
+(defcustom notmuch-hello-sections
+  (list #'notmuch-hello-insert-header
+	#'notmuch-hello-insert-saved-searches
+	#'notmuch-hello-insert-search
+	#'notmuch-hello-insert-recent-searches
+	#'notmuch-hello-insert-alltags
+	#'notmuch-hello-insert-footer)
+  "Sections for notmuch-hello.
+
+The list contains functions which are used to construct sections in
+notmuch-hello buffer.  When notmuch-hello buffer is constructed,
+these functions are run in the order they appear in this list.  Each
+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.
+
+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
+list.
+
+A \"Customized tag-list section\" item in the customize-interface
+displays a list of all tags, optionally hiding some of them. It
+is also possible to filter the list of messages matching each tag
+by an additional filter query. Similarly, the count of messages
+displayed next to the buttons can be generated by applying a
+different filter to the tag query. These filters are also
+supported for \"Customized queries section\" items."
+  :group 'notmuch
+  :type
+  '(repeat
+    (choice (function-item notmuch-hello-insert-header)
+	    (function-item notmuch-hello-insert-saved-searches)
+	    (function-item notmuch-hello-insert-search)
+	    (function-item notmuch-hello-insert-recent-searches)
+	    (function-item notmuch-hello-insert-alltags)
+	    (function-item notmuch-hello-insert-footer)
+	    (function-item notmuch-hello-insert-inbox)
+	    notmuch-hello-tags-section
+	    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 globaly 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")
+
+(defvar notmuch-hello-first-run t)
+
 (defun notmuch-hello-remember-search (search)
   (setq notmuch-hello-recent-searches
 	(delete search notmuch-hello-recent-searches))
@@ -210,8 +305,8 @@ International Bureau of Weights and Measures."
     (message "Saved '%s' as '%s'." search name)
     (notmuch-hello-update)))
 
-(defun notmuch-hello-longest-label (tag-alist)
-  (or (loop for elem in tag-alist
+(defun notmuch-hello-longest-label (searches-alist)
+  (or (loop for elem in searches-alist
 	    maximize (length (car elem)))
       0))
 
@@ -275,12 +370,70 @@ should be. Returns a cons cell `(tags-per-line width)'."
 				   (* tags-per-line (+ 9 1))))
 			   tags-per-line))))
 
-(defun notmuch-hello-insert-tags (tag-alist widest target)
-  (let* ((tags-and-width (notmuch-hello-tags-per-line widest))
+(defun notmuch-hello-filtered-query (query filter)
+  "Constructs a query to search all messages matching QUERY and FILTER.
+
+If FILTER is a string, it is directly used in the returned query.
+
+If FILTER is a function, it is called with QUERY as a parameter and
+the string it returns is used as the query. If nil is returned,
+the entry is hidden.
+
+Otherwise, FILTER is ignored.
+"
+  (cond
+   ((functionp filter) (funcall filter query))
+   ((stringp filter)
+    (concat "(" query ") and (" filter ")"))
+   (t query)))
+
+(defun notmuch-hello-query-counts (query-alist &rest options)
+  "Compute list of counts of matched messages from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated query.
+
+The result is the list of elements of the form (NAME QUERY COUNT).
+
+The values :show-empty-searches, :filter and :filter-count from
+options will be handled as specified for
+`notmuch-hello-insert-searches'."
+  (notmuch-remove-if-not
+   #'identity
+   (mapcar
+    (lambda (elem)
+      (let* ((name (car elem))
+	     (query-and-count (if (consp (cdr elem))
+				  ;; do we have a different query for the message count?
+				  (cons (second elem) (third elem))
+				(cons (cdr elem) (cdr elem))))
+	     (message-count
+	      (string-to-number
+	       (notmuch-saved-search-count
+		(notmuch-hello-filtered-query (cdr query-and-count)
+					      (or (plist-get options :filter-count)
+						 (plist-get options :filter)))))))
+	(and (or (plist-get options :show-empty-searches) (> message-count 0))
+	     (list name (notmuch-hello-filtered-query
+			 (car query-and-count) (plist-get options :filter))
+		   message-count))))
+    query-alist)))
+
+(defun notmuch-hello-insert-buttons (searches)
+  "Insert buttons for SEARCHES.
+
+SEARCHES must be a list containing lists of the form (NAME QUERY COUNT), where
+QUERY is the query to start when the button for the corresponding entry is
+activated. COUNT should be the number of messages matching the query.
+Such a list can be computed with `notmuch-hello-query-counts'."
+  (let* ((widest (notmuch-hello-longest-label searches))
+	 (tags-and-width (notmuch-hello-tags-per-line widest))
 	 (tags-per-line (car tags-and-width))
 	 (widest (cdr tags-and-width))
 	 (count 0)
-	 (reordered-list (notmuch-hello-reflect tag-alist tags-per-line))
+	 (reordered-list (notmuch-hello-reflect searches tags-per-line))
 	 ;; Hack the display of the buttons used.
 	 (widget-push-button-prefix "")
 	 (widget-push-button-suffix "")
@@ -290,13 +443,13 @@ should be. Returns a cons cell `(tags-per-line width)'."
     (mapc (lambda (elem)
 	    ;; (not elem) indicates an empty slot in the matrix.
 	    (when elem
-	      (let* ((name (car elem))
-		     (query (cdr elem))
+	      (let* ((name (first elem))
+		     (query (second elem))
+		     (msg-count (third elem))
 		     (formatted-name (format "%s " name)))
 		(widget-insert (format "%8s "
-				       (notmuch-hello-nice-number
-					(string-to-number (notmuch-saved-search-count query)))))
-		(if (string= formatted-name target)
+				       (notmuch-hello-nice-number msg-count)))
+		(if (string= formatted-name notmuch-hello-target)
 		    (setq found-target-pos (point-marker)))
 		(widget-create 'push-button
 			       :notify #'notmuch-hello-widget-search
@@ -373,29 +526,245 @@ Complete list of currently available key bindings:
  (kill-all-local-variables)
  (use-local-map notmuch-hello-mode-map)
  (setq major-mode 'notmuch-hello-mode
-       mode-name "notmuch-hello")
+	mode-name "notmuch-hello")
  (run-mode-hooks 'notmuch-hello-mode-hook)
  ;;(setq buffer-read-only t)
 )
 
-(defun notmuch-hello-generate-tag-alist ()
+(defun notmuch-hello-generate-tag-alist (&optional hide-tags)
   "Return an alist from tags to queries to display in the all-tags section."
-  (notmuch-remove-if-not
-   #'cdr
-   (mapcar (lambda (tag)
-	     (cons tag
-		   (cond
-		    ((functionp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     (funcall notmuch-hello-tag-list-make-query tag) ")"))
-		    ((stringp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     notmuch-hello-tag-list-make-query ")"))
-		    (t (concat "tag:" tag)))))
-	   (notmuch-remove-if-not
-	    (lambda (tag)
-	      (not (member tag notmuch-hello-hide-tags)))
-	    (process-lines notmuch-command "search-tags")))))
+  (mapcar (lambda (tag)
+	    (cons tag (format "tag:%s" tag)))
+	  (notmuch-remove-if-not
+	   (lambda (tag)
+	     (not (member tag hide-tags)))
+	   (process-lines notmuch-command "search-tags"))))
+
+(defun notmuch-hello-insert-header ()
+  "Insert the default notmuch-hello header."
+  (when notmuch-show-logo
+    (let ((image notmuch-hello-logo))
+      ;; The notmuch logo uses transparency. That can display poorly
+      ;; when inserting the image into an emacs buffer (black logo on
+      ;; a black background), so force the background colour of the
+      ;; image. We use a face to represent the colour so that
+      ;; `defface' can be used to declare the different possible
+      ;; colours, which depend on whether the frame has a light or
+      ;; dark background.
+      (setq image (cons 'image
+			(append (cdr image)
+				(list :background (face-background 'notmuch-hello-logo-background)))))
+      (insert-image image))
+    (widget-insert "  "))
+
+  (widget-insert "Welcome to ")
+  ;; Hack the display of the links used.
+  (let ((widget-link-prefix "")
+	(widget-link-suffix ""))
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (browse-url notmuch-hello-url))
+		   :help-echo "Visit the notmuch website."
+		   "notmuch")
+    (widget-insert ". ")
+    (widget-insert "You have ")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (notmuch-hello-update))
+		   :help-echo "Refresh"
+		   (notmuch-hello-nice-number
+		    (string-to-number (car (process-lines notmuch-command "count")))))
+    (widget-insert " messages.\n")))
+
+
+(defun notmuch-hello-insert-saved-searches ()
+  "Insert the saved-searches section."
+  (let ((searches (notmuch-hello-query-counts
+		  (if notmuch-saved-search-sort-function
+		      (funcall notmuch-saved-search-sort-function
+			       notmuch-saved-searches)
+		    notmuch-saved-searches)
+		  :show-empty-searches notmuch-show-empty-saved-searches))
+	found-target-pos final-target-pos)
+    (when searches
+      (widget-insert "Saved searches: ")
+      (widget-create 'push-button
+		     :notify (lambda (&rest ignore)
+			       (customize-variable 'notmuch-saved-searches))
+		     "edit")
+      (widget-insert "\n\n")
+      (setq final-target-pos (point-marker))
+      (let ((start (point)))
+	(setq found-target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(if found-target-pos
+	    (setq final-target-pos found-target-pos))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	final-target-pos))))
+
+(defun notmuch-hello-insert-search ()
+  "Insert a search widget."
+  (widget-insert "Search: ")
+  (setq notmuch-hello-search-bar-marker (point-marker))
+  (widget-create 'editable-field
+		 ;; Leave some space at the start and end of the
+		 ;; search boxes.
+		 :size (max 8 (- (window-width) notmuch-hello-indent
+				 (length "Search: ")))
+		 :action (lambda (widget &rest ignore)
+			   (notmuch-hello-search (widget-value widget))))
+  ;; Add an invisible dot to make `widget-end-of-line' ignore
+  ;; trailing spaces in the search widget field.  A dot is used
+  ;; instead of a space to make `show-trailing-whitespace'
+  ;; happy, i.e. avoid it marking the whole line as trailing
+  ;; spaces.
+  (widget-insert ".")
+  (put-text-property (1- (point)) (point) 'invisible t)
+  (widget-insert "\n"))
+
+(defun notmuch-hello-insert-recent-searches ()
+  "Insert recent searches."
+  (when notmuch-hello-recent-searches
+    (widget-insert "Recent searches: ")
+    (widget-create 'push-button
+		   :notify (lambda (&rest ignore)
+			     (setq notmuch-hello-recent-searches nil)
+			     (notmuch-hello-update))
+		   "clear")
+    (widget-insert "\n\n")
+    (let ((start (point))
+	  (nth 0))
+      (mapc (lambda (search)
+	      (let ((widget-symbol (intern (format "notmuch-hello-search-%d" nth))))
+		(set widget-symbol
+		     (widget-create 'editable-field
+				    ;; Don't let the search boxes be
+				    ;; less than 8 characters wide.
+				    :size (max 8
+					       (- (window-width)
+						  ;; Leave some space
+						  ;; at the start and
+						  ;; end of the
+						  ;; boxes.
+						  (* 2 notmuch-hello-indent)
+						  ;; 1 for the space
+						  ;; before the
+						  ;; `[save]' button. 6
+						  ;; for the `[save]'
+						  ;; button.
+						  1 6))
+				    :action (lambda (widget &rest ignore)
+					      (notmuch-hello-search (widget-value widget)))
+				    search))
+		(widget-insert " ")
+		(widget-create 'push-button
+			       :notify (lambda (widget &rest ignore)
+					 (notmuch-hello-add-saved-search widget))
+			       :notmuch-saved-search-widget widget-symbol
+			       "save"))
+	      (widget-insert "\n")
+	      (setq nth (1+ nth)))
+	    notmuch-hello-recent-searches)
+      (indent-rigidly start (point) notmuch-hello-indent))))
+
+(defun notmuch-hello-insert-searches (title query-alist &rest options)
+  "Insert a section with TITLE showing a list of buttons made from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated item.
+
+Supports the following entries in OPTIONS as a plist:
+:initially-hidden - if non-nil, section will be hidden on startup
+:show-empty-searches - show buttons with no matching messages
+:hide-if-empty - hide if no buttons would be shown
+   (only makes sense without :show-empty-searches)
+:filter - This can be a function that takes the search query as its argument and
+    returns a filter to be used in conjuction with the query for that search or nil
+    to hide the element. This can also be a string that is used as a combined with
+    each query using \"and\".
+:filter-count - Separate filter to generate the count displayed each search. Accepts
+    the same values as :filter. If :filter and :filter-count are specified, this
+    will be used instead of :filter, not in conjunction with it."
+  (widget-insert title ": ")
+  (if (and notmuch-hello-first-run (plist-get options :initially-hidden))
+      (add-to-list 'notmuch-hello-hidden-sections title))
+  (let ((is-hidden (member title notmuch-hello-hidden-sections))
+	(start (point)))
+    (if is-hidden
+	(widget-create 'push-button
+		       :notify `(lambda (widget &rest ignore)
+				  (setq notmuch-hello-hidden-sections
+					(delete ,title notmuch-hello-hidden-sections))
+				  (notmuch-hello-update))
+		       "show")
+      (widget-create 'push-button
+		     :notify `(lambda (widget &rest ignore)
+				(add-to-list 'notmuch-hello-hidden-sections
+					     ,title)
+				(notmuch-hello-update))
+		     "hide"))
+    (widget-insert "\n")
+    (let (target-pos
+	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
+      (when (and (not is-hidden)
+	       (or (not (plist-get options :hide-if-empty))
+		  searches))
+	(widget-insert "\n")
+	(setq target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	target-pos))))
+
+(defun notmuch-hello-insert-tags-section (&optional title &rest options)
+  "Insert a section displaying all tags with message counts.
+
+TITLE defaults to \"All tags\".
+Allowed options are those accepted by `notmuch-hello-insert-searches' and the
+following:
+
+:hide-tags - List of tags that should be excluded."
+  (apply 'notmuch-hello-insert-searches
+	 (or title "All tags")
+	 (notmuch-hello-generate-tag-alist (plist-get options :hide-tags))
+	 options))
+
+(defun notmuch-hello-insert-inbox ()
+  "Show an entry for each saved search and inboxed messages for each tag"
+  (notmuch-hello-insert-searches "What's in your inbox"
+				 (append
+				  (notmuch-saved-searches)
+				  (notmuch-hello-generate-tag-alist))
+				 :filter "tag:inbox"))
+
+(defun notmuch-hello-insert-alltags ()
+  "Insert a section displaying all tags and associated message counts"
+  (notmuch-hello-insert-tags-section
+   nil
+   :initially-hidden (not notmuch-show-all-tags-list)
+   :hide-tags notmuch-hello-hide-tags
+   :filter notmuch-hello-tag-list-make-query))
+
+(defun notmuch-hello-insert-footer ()
+  "Insert the notmuch-hello footer."
+  (let ((start (point)))
+    (widget-insert "Type a search query and hit RET to view matching threads.\n")
+    (when notmuch-hello-recent-searches
+      (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
+      (widget-insert "Save recent searches with the `save' button.\n"))
+    (when notmuch-saved-searches
+      (widget-insert "Edit saved searches with the `edit' button.\n"))
+    (widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
+    (widget-insert "`=' refreshes this screen. `s' jumps to the search box. `q' to quit.\n")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (customize-variable 'notmuch-hello-sections))
+		   :button-prefix "" :button-suffix ""
+		   "Customize")
+    (widget-insert " this page.\n")
+    (let ((fill-column (- (window-width) notmuch-hello-indent)))
+      (center-region start (point)))))
 
 ;;;###autoload
 (defun notmuch-hello (&optional no-display)
@@ -411,13 +780,13 @@ Complete list of currently available key bindings:
       (set-buffer "*notmuch-hello*")
     (switch-to-buffer "*notmuch-hello*"))
 
-  (let ((target (if (widget-at)
-		   (widget-value (widget-at))
-		 (condition-case nil
-		     (progn
-		       (widget-forward 1)
-		       (widget-value (widget-at)))
-		   (error nil))))
+  (let ((notmuch-hello-target (if (widget-at)
+				  (widget-value (widget-at))
+				(condition-case nil
+				    (progn
+				      (widget-forward 1)
+				      (widget-value (widget-at)))
+				  (error nil))))
 	(inhibit-read-only t))
 
     ;; Delete all editable widget fields.  Editable widget fields are
@@ -436,169 +805,20 @@ Complete list of currently available key bindings:
       (mapc 'delete-overlay (car all))
       (mapc 'delete-overlay (cdr all)))
 
-    (when notmuch-show-logo
-      (let ((image notmuch-hello-logo))
-	;; The notmuch logo uses transparency. That can display poorly
-	;; when inserting the image into an emacs buffer (black logo on
-	;; a black background), so force the background colour of the
-	;; image. We use a face to represent the colour so that
-	;; `defface' can be used to declare the different possible
-	;; colours, which depend on whether the frame has a light or
-	;; dark background.
-	(setq image (cons 'image
-			  (append (cdr image)
-				  (list :background (face-background 'notmuch-hello-logo-background)))))
-	(insert-image image))
-      (widget-insert "  "))
-
-    (widget-insert "Welcome to ")
-    ;; Hack the display of the links used.
-    (let ((widget-link-prefix "")
-	  (widget-link-suffix ""))
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (browse-url notmuch-hello-url))
-		     :help-echo "Visit the notmuch website."
-		     "notmuch")
-      (widget-insert ". ")
-      (widget-insert "You have ")
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (notmuch-hello-update))
-		     :help-echo "Refresh"
-		     (notmuch-hello-nice-number
-		      (string-to-number (car (process-lines notmuch-command "count")))))
-      (widget-insert " messages.\n"))
-
-    (let ((found-target-pos nil)
-	  (final-target-pos nil))
-      (let* ((saved-alist
-	      ;; Filter out empty saved searches if required.
-	      (if notmuch-show-empty-saved-searches
-		  notmuch-saved-searches
-		(loop for elem in notmuch-saved-searches
-		      if (> (string-to-number (notmuch-saved-search-count (cdr elem))) 0)
-		      collect elem)))
-	     (saved-widest (notmuch-hello-longest-label saved-alist))
-	     (alltags-alist (if notmuch-show-all-tags-list (notmuch-hello-generate-tag-alist)))
-	     (alltags-widest (notmuch-hello-longest-label alltags-alist))
-	     (widest (max saved-widest alltags-widest)))
-
-	(when saved-alist
-	  ;; Sort saved searches if required.
-	  (when notmuch-saved-search-sort-function
-	    (setq saved-alist
-		  (funcall notmuch-saved-search-sort-function saved-alist)))
-	  (widget-insert "\nSaved searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (customize-variable 'notmuch-saved-searches))
-			 "edit")
-	  (widget-insert "\n\n")
-	  (setq final-target-pos (point-marker))
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags saved-alist widest target))
-	    (if found-target-pos
-		(setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\nSearch: ")
-	(setq notmuch-hello-search-bar-marker (point-marker))
-	(widget-create 'editable-field
-		       ;; Leave some space at the start and end of the
-		       ;; search boxes.
-		       :size (max 8 (- (window-width) notmuch-hello-indent
-				       (length "Search: ")))
-		       :action (lambda (widget &rest ignore)
-				 (notmuch-hello-search (widget-value widget))))
-	;; Add an invisible dot to make `widget-end-of-line' ignore
-	;; trailing spaces in the search widget field.  A dot is used
-	;; instead of a space to make `show-trailing-whitespace'
-	;; happy, i.e. avoid it marking the whole line as trailing
-	;; spaces.
-	(widget-insert ".")
-	(put-text-property (1- (point)) (point) 'invisible t)
-	(widget-insert "\n")
-
-	(when notmuch-hello-recent-searches
-	  (widget-insert "\nRecent searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (setq notmuch-hello-recent-searches nil)
-				   (notmuch-hello-update))
-			 "clear")
-	  (widget-insert "\n\n")
-	  (let ((start (point))
-		(nth 0))
-	    (mapc (lambda (search)
-		    (let ((widget-symbol (intern (format "notmuch-hello-search-%d" nth))))
-		      (set widget-symbol
-			   (widget-create 'editable-field
-					  ;; Don't let the search boxes be
-					  ;; less than 8 characters wide.
-					  :size (max 8
-						     (- (window-width)
-							;; Leave some space
-							;; at the start and
-							;; end of the
-							;; boxes.
-							(* 2 notmuch-hello-indent)
-							;; 1 for the space
-							;; before the
-							;; `[save]' button. 6
-							;; for the `[save]'
-							;; button.
-							1 6))
-					  :action (lambda (widget &rest ignore)
-						    (notmuch-hello-search (widget-value widget)))
-					  search))
-		      (widget-insert " ")
-		      (widget-create 'push-button
-				     :notify (lambda (widget &rest ignore)
-					       (notmuch-hello-add-saved-search widget))
-				     :notmuch-saved-search-widget widget-symbol
-				     "save"))
-		    (widget-insert "\n")
-		    (setq nth (1+ nth)))
-		  notmuch-hello-recent-searches)
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(when alltags-alist
-	  (widget-insert "\nAll tags: ")
-	  (widget-create 'push-button
-			 :notify (lambda (widget &rest ignore)
-				   (setq notmuch-show-all-tags-list nil)
-				   (notmuch-hello-update))
-			 "hide")
-	  (widget-insert "\n\n")
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags alltags-alist widest target))
-	    (unless final-target-pos
-	      (setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\n")
-
-	(unless notmuch-show-all-tags-list
-	  (widget-create 'push-button
-			 :notify (lambda (widget &rest ignore)
-				   (setq notmuch-show-all-tags-list t)
-				   (notmuch-hello-update))
-			 "Show all tags")))
-
-      (let ((start (point)))
-	(widget-insert "\n\n")
-	(widget-insert "Type a search query and hit RET to view matching threads.\n")
-	(when notmuch-hello-recent-searches
-	  (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
-	  (widget-insert "Save recent searches with the `save' button.\n"))
-	(when notmuch-saved-searches
-	  (widget-insert "Edit saved searches with the `edit' button.\n"))
-	(widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
-	(widget-insert "`=' refreshes this screen. `s' jumps to the search box. `q' to quit.\n")
-	(let ((fill-column (- (window-width) notmuch-hello-indent)))
-	  (center-region start (point))))
-
+    (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
@@ -608,8 +828,8 @@ Complete list of currently available key bindings:
 
       (unless (widget-at)
 	(notmuch-hello-goto-search))))
-
-  (run-hooks 'notmuch-hello-refresh-hook))
+  (run-hooks 'notmuch-hello-refresh-hook)
+  (setq notmuch-hello-first-run nil))
 
 (defun notmuch-folder ()
   "Deprecated function for invoking notmuch---calling `notmuch' is preferred now."
-- 
1.7.8.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v7 2/2] emacs: Tests for user-defined sections
  2012-01-22  0:54                 ` [PATCH v7 " Daniel Schoepe
@ 2012-01-22  0:54                   ` Daniel Schoepe
  2012-01-23 23:07                     ` Dmitry Kurochkin
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Schoepe @ 2012-01-22  0:54 UTC (permalink / raw)
  To: notmuch; +Cc: Daniel Schoepe

From: Daniel Schoepe <daniel.schoepe@googlemail.com>

---
 test/emacs                                         |   37 ++++++++++++++++++++
 test/emacs.expected-output/notmuch-hello           |    4 ++-
 .../notmuch-hello-new-section                      |    4 ++
 .../notmuch-hello-no-saved-searches                |    4 ++-
 .../notmuch-hello-section-counts                   |    5 +++
 .../notmuch-hello-section-hidden-tag               |    4 ++
 .../notmuch-hello-section-with-empty               |    4 ++
 .../emacs.expected-output/notmuch-hello-with-empty |    4 ++-
 8 files changed, 63 insertions(+), 3 deletions(-)
 create mode 100644 test/emacs.expected-output/notmuch-hello-new-section
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-counts
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-hidden-tag
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-with-empty

diff --git a/test/emacs b/test/emacs
index ac47b16..8b1d16c 100755
--- a/test/emacs
+++ b/test/emacs
@@ -29,6 +29,43 @@ test_emacs '(let ((notmuch-saved-searches
 	      (test-output))'
 test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-no-saved-searches
 
+test_begin_subtest "User defined section with inbox tag"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test\" '((\"inbox\" . \"tag:inbox\")))))))
+           (notmuch-hello)
+           (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-new-section
+
+test_begin_subtest "User defined section with empty, hidden entry"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test-with-empty\" 
+                                     '((\"inbox\" . \"tag:inbox\")
+                                       (\"doesnotexist\" . \"tag:doesnotexist\"))
+                                     :hide-empty-searches t)))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-with-empty
+
+test_begin_subtest "User defined section, unread tag filtered out"
+test_emacs "(let ((notmuch-hello-sections 
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-filtered\"
+                                     :hide-tags '(\"unread\"))))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-hidden-tag
+
+test_begin_subtest "User defined section, different query for counts"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-counts\"
+                                     :filter-count \"tag:signed\")))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-counts
+
 test_begin_subtest "Basic notmuch-search view in emacs"
 test_emacs '(notmuch-search "tag:inbox")
 	    (notmuch-test-wait)
diff --git a/test/emacs.expected-output/notmuch-hello b/test/emacs.expected-output/notmuch-hello
index 196112e..cf49cb4 100644
--- a/test/emacs.expected-output/notmuch-hello
+++ b/test/emacs.expected-output/notmuch-hello
@@ -6,9 +6,11 @@ Saved searches: [edit]
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
diff --git a/test/emacs.expected-output/notmuch-hello-new-section b/test/emacs.expected-output/notmuch-hello-new-section
new file mode 100644
index 0000000..c64d712
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-new-section
@@ -0,0 +1,4 @@
+Test: [hide]
+
+	  52 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-no-saved-searches b/test/emacs.expected-output/notmuch-hello-no-saved-searches
index f4cfe49..cec0f91 100644
--- a/test/emacs.expected-output/notmuch-hello-no-saved-searches
+++ b/test/emacs.expected-output/notmuch-hello-no-saved-searches
@@ -2,9 +2,11 @@
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-counts b/test/emacs.expected-output/notmuch-hello-section-counts
new file mode 100644
index 0000000..9d79659
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-counts
@@ -0,0 +1,5 @@
+Test-with-counts: [hide]
+
+	   2 attachment             7 signed          
+	   7 inbox                  7 unread          
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-hidden-tag b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
new file mode 100644
index 0000000..3688e7c
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
@@ -0,0 +1,4 @@
+Test-with-filtered: [hide]
+
+	   4 attachment            52 inbox                  7 signed 
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-with-empty b/test/emacs.expected-output/notmuch-hello-section-with-empty
new file mode 100644
index 0000000..8209fed
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-with-empty
@@ -0,0 +1,4 @@
+Test-with-empty: [hide]
+
+	  52 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-with-empty b/test/emacs.expected-output/notmuch-hello-with-empty
index a860a72..76ab2e1 100644
--- a/test/emacs.expected-output/notmuch-hello-with-empty
+++ b/test/emacs.expected-output/notmuch-hello-with-empty
@@ -6,9 +6,11 @@ Saved searches: [edit]
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
     `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
+			    Customize this page.
+
-- 
1.7.8.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [PATCH v7 2/2] emacs: Tests for user-defined sections
  2012-01-22  0:54                   ` [PATCH v7 2/2] emacs: Tests for user-defined sections Daniel Schoepe
@ 2012-01-23 23:07                     ` Dmitry Kurochkin
  2012-01-28 21:30                       ` Daniel Schoepe
  0 siblings, 1 reply; 57+ messages in thread
From: Dmitry Kurochkin @ 2012-01-23 23:07 UTC (permalink / raw)
  To: Daniel Schoepe, notmuch; +Cc: Daniel Schoepe

Hi Daniel.

There are some trailing whitespaces in the tests.

Also, please consider moving the hello sections tests to a separate file
(emacs-hello-sections, perhaps?).  I am worried that test/emacs file
becomes too big.  And we will probably add more notmuch-hello
section-related tests in the future.

Regards,
  Dmitry

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v7 2/2] emacs: Tests for user-defined sections
  2012-01-23 23:07                     ` Dmitry Kurochkin
@ 2012-01-28 21:30                       ` Daniel Schoepe
  2012-01-28 21:44                         ` [PATCH v8 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
  2012-01-28 22:48                         ` [PATCH v7 " Dmitry Kurochkin
  0 siblings, 2 replies; 57+ messages in thread
From: Daniel Schoepe @ 2012-01-28 21:30 UTC (permalink / raw)
  To: Dmitry Kurochkin, notmuch

[-- Attachment #1: Type: text/plain, Size: 873 bytes --]

Hi Dmitry,

On Tue, 24 Jan 2012 03:07, Dmitry Kurochkin <dmitry.kurochkin@gmail.com> wrote:
> There are some trailing whitespaces in the tests.

those are also produced by the various notmuch-hello functions, and are
also in the existing tests. If someone is bothered by this, it should be
fixed in a separate patch.

> 
> Also, please consider moving the hello sections tests to a separate file
> (emacs-hello-sections, perhaps?).  I am worried that test/emacs file
> becomes too big.  And we will probably add more notmuch-hello
> section-related tests in the future.

I think it's better to split it up into more than just
emacs-hello-sections and emacs, as there are also quite a few tests
relating solely to, e.g., -show or -search.

Anyhow, I'll send another rebased version fixing conflicts with the
search-interface changes.

Cheers,
Daniel

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v8 0/2] emacs: User-defined sections in notmuch-hello 
  2012-01-28 21:30                       ` Daniel Schoepe
@ 2012-01-28 21:44                         ` Daniel Schoepe
  2012-01-28 21:44                           ` [PATCH v8 1/2] " Daniel Schoepe
  2012-01-28 21:44                           ` [PATCH v8 2/2] emacs: Tests for user-defined sections Daniel Schoepe
  2012-01-28 22:48                         ` [PATCH v7 " Dmitry Kurochkin
  1 sibling, 2 replies; 57+ messages in thread
From: Daniel Schoepe @ 2012-01-28 21:44 UTC (permalink / raw)
  To: notmuch

Rebased against master.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v8 1/2] emacs: User-defined sections in notmuch-hello
  2012-01-28 21:44                         ` [PATCH v8 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
@ 2012-01-28 21:44                           ` Daniel Schoepe
  2012-01-28 21:44                           ` [PATCH v8 2/2] emacs: Tests for user-defined sections Daniel Schoepe
  1 sibling, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2012-01-28 21:44 UTC (permalink / raw)
  To: notmuch; +Cc: Daniel Schoepe

From: Daniel Schoepe <daniel.schoepe@googlemail.com>

This patch makes the notmuch-hello screen fully customizable
by allowing the user to add and remove arbitrary sections. It
also provides some convenience functions for constructing sections,
e.g. showing the unread message count for each tag.

This is done by specifying a list of functions that will be run
when notmuch-hello is invoked.
---
 emacs/notmuch-hello.el |  637 +++++++++++++++++++++++++++++++++---------------
 1 files changed, 437 insertions(+), 200 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index d17a30f..0decac2 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -154,6 +154,116 @@ International Bureau of Weights and Measures."
 (defvar notmuch-hello-url "http://notmuchmail.org"
   "The `notmuch' web site.")
 
+(defvar notmuch-hello-recent-searches nil)
+
+(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"))
+    (:initially-hidden (const :tag "Hide this section on startup" t))
+    (:show-empty-searches (const :tag "Show queries with no matching messages" t))
+    (:hide-if-empty (const :tag "Hide this section if all queries are empty
+\(and not hidden by show-if-empty)" t)))
+  "Various customization-options for notmuch-hello-tags/query-section.")
+
+(define-widget 'notmuch-hello-tags-section 'lazy
+  "Customize-type for notmuch-hello tag-list sections."
+  :tag "Customized tag-list section (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-tags-section)
+	 (string :tag "Title for this section")
+	 (plist
+	  :inline t
+	  :options
+	  ,(append notmuch-hello-custom-section-options
+		   '((:hide-tags (repeat :tag "Tags that will be hidden"
+					 string)))))))
+
+(define-widget 'notmuch-hello-query-section 'lazy
+  "Customize-type for custom saved-search-like sections"
+  :tag "Customized queries section (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-query-list)
+	 (string :tag "Title for this section")
+	 (repeat :tag "Queries"
+		 (cons (string :tag "Name") (string :tag "Query")))
+	 (plist :inline t :options ,notmuch-hello-custom-section-options)))
+
+(defcustom notmuch-hello-sections
+  (list #'notmuch-hello-insert-header
+	#'notmuch-hello-insert-saved-searches
+	#'notmuch-hello-insert-search
+	#'notmuch-hello-insert-recent-searches
+	#'notmuch-hello-insert-alltags
+	#'notmuch-hello-insert-footer)
+  "Sections for notmuch-hello.
+
+The list contains functions which are used to construct sections in
+notmuch-hello buffer.  When notmuch-hello buffer is constructed,
+these functions are run in the order they appear in this list.  Each
+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.
+
+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
+list.
+
+A \"Customized tag-list section\" item in the customize-interface
+displays a list of all tags, optionally hiding some of them. It
+is also possible to filter the list of messages matching each tag
+by an additional filter query. Similarly, the count of messages
+displayed next to the buttons can be generated by applying a
+different filter to the tag query. These filters are also
+supported for \"Customized queries section\" items."
+  :group 'notmuch
+  :type
+  '(repeat
+    (choice (function-item notmuch-hello-insert-header)
+	    (function-item notmuch-hello-insert-saved-searches)
+	    (function-item notmuch-hello-insert-search)
+	    (function-item notmuch-hello-insert-recent-searches)
+	    (function-item notmuch-hello-insert-alltags)
+	    (function-item notmuch-hello-insert-footer)
+	    (function-item notmuch-hello-insert-inbox)
+	    notmuch-hello-tags-section
+	    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 globaly 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")
+
+(defvar notmuch-hello-first-run t)
+
+(defun notmuch-hello-remember-search (search)
+  (setq notmuch-hello-recent-searches
+	(delete search notmuch-hello-recent-searches))
+  (push search notmuch-hello-recent-searches)
+  (if (> (length notmuch-hello-recent-searches)
+	 notmuch-hello-recent-searches-max)
+      (setq notmuch-hello-recent-searches (butlast notmuch-hello-recent-searches))))
+
 (defun notmuch-hello-nice-number (n)
   (let (result)
     (while (> n 0)
@@ -201,8 +311,8 @@ International Bureau of Weights and Measures."
     (message "Saved '%s' as '%s'." search name)
     (notmuch-hello-update)))
 
-(defun notmuch-hello-longest-label (tag-alist)
-  (or (loop for elem in tag-alist
+(defun notmuch-hello-longest-label (searches-alist)
+  (or (loop for elem in searches-alist
 	    maximize (length (car elem)))
       0))
 
@@ -266,12 +376,70 @@ should be. Returns a cons cell `(tags-per-line width)'."
 				   (* tags-per-line (+ 9 1))))
 			   tags-per-line))))
 
-(defun notmuch-hello-insert-tags (tag-alist widest target)
-  (let* ((tags-and-width (notmuch-hello-tags-per-line widest))
+(defun notmuch-hello-filtered-query (query filter)
+  "Constructs a query to search all messages matching QUERY and FILTER.
+
+If FILTER is a string, it is directly used in the returned query.
+
+If FILTER is a function, it is called with QUERY as a parameter and
+the string it returns is used as the query. If nil is returned,
+the entry is hidden.
+
+Otherwise, FILTER is ignored.
+"
+  (cond
+   ((functionp filter) (funcall filter query))
+   ((stringp filter)
+    (concat "(" query ") and (" filter ")"))
+   (t query)))
+
+(defun notmuch-hello-query-counts (query-alist &rest options)
+  "Compute list of counts of matched messages from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated query.
+
+The result is the list of elements of the form (NAME QUERY COUNT).
+
+The values :show-empty-searches, :filter and :filter-count from
+options will be handled as specified for
+`notmuch-hello-insert-searches'."
+  (notmuch-remove-if-not
+   #'identity
+   (mapcar
+    (lambda (elem)
+      (let* ((name (car elem))
+	     (query-and-count (if (consp (cdr elem))
+				  ;; do we have a different query for the message count?
+				  (cons (second elem) (third elem))
+				(cons (cdr elem) (cdr elem))))
+	     (message-count
+	      (string-to-number
+	       (notmuch-saved-search-count
+		(notmuch-hello-filtered-query (cdr query-and-count)
+					      (or (plist-get options :filter-count)
+						 (plist-get options :filter)))))))
+	(and (or (plist-get options :show-empty-searches) (> message-count 0))
+	     (list name (notmuch-hello-filtered-query
+			 (car query-and-count) (plist-get options :filter))
+		   message-count))))
+    query-alist)))
+
+(defun notmuch-hello-insert-buttons (searches)
+  "Insert buttons for SEARCHES.
+
+SEARCHES must be a list containing lists of the form (NAME QUERY COUNT), where
+QUERY is the query to start when the button for the corresponding entry is
+activated. COUNT should be the number of messages matching the query.
+Such a list can be computed with `notmuch-hello-query-counts'."
+  (let* ((widest (notmuch-hello-longest-label searches))
+	 (tags-and-width (notmuch-hello-tags-per-line widest))
 	 (tags-per-line (car tags-and-width))
 	 (widest (cdr tags-and-width))
 	 (count 0)
-	 (reordered-list (notmuch-hello-reflect tag-alist tags-per-line))
+	 (reordered-list (notmuch-hello-reflect searches tags-per-line))
 	 ;; Hack the display of the buttons used.
 	 (widget-push-button-prefix "")
 	 (widget-push-button-suffix "")
@@ -281,13 +449,13 @@ should be. Returns a cons cell `(tags-per-line width)'."
     (mapc (lambda (elem)
 	    ;; (not elem) indicates an empty slot in the matrix.
 	    (when elem
-	      (let* ((name (car elem))
-		     (query (cdr elem))
+	      (let* ((name (first elem))
+		     (query (second elem))
+		     (msg-count (third elem))
 		     (formatted-name (format "%s " name)))
 		(widget-insert (format "%8s "
-				       (notmuch-hello-nice-number
-					(string-to-number (notmuch-saved-search-count query)))))
-		(if (string= formatted-name target)
+				       (notmuch-hello-nice-number msg-count)))
+		(if (string= formatted-name notmuch-hello-target)
 		    (setq found-target-pos (point-marker)))
 		(widget-create 'push-button
 			       :notify #'notmuch-hello-widget-search
@@ -359,29 +527,245 @@ Complete list of currently available key bindings:
  (kill-all-local-variables)
  (use-local-map notmuch-hello-mode-map)
  (setq major-mode 'notmuch-hello-mode
-       mode-name "notmuch-hello")
+	mode-name "notmuch-hello")
  (run-mode-hooks 'notmuch-hello-mode-hook)
  ;;(setq buffer-read-only t)
 )
 
-(defun notmuch-hello-generate-tag-alist ()
+(defun notmuch-hello-generate-tag-alist (&optional hide-tags)
   "Return an alist from tags to queries to display in the all-tags section."
-  (notmuch-remove-if-not
-   #'cdr
-   (mapcar (lambda (tag)
-	     (cons tag
-		   (cond
-		    ((functionp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     (funcall notmuch-hello-tag-list-make-query tag) ")"))
-		    ((stringp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     notmuch-hello-tag-list-make-query ")"))
-		    (t (concat "tag:" tag)))))
-	   (notmuch-remove-if-not
-	    (lambda (tag)
-	      (not (member tag notmuch-hello-hide-tags)))
-	    (process-lines notmuch-command "search-tags")))))
+  (mapcar (lambda (tag)
+	    (cons tag (format "tag:%s" tag)))
+	  (notmuch-remove-if-not
+	   (lambda (tag)
+	     (not (member tag hide-tags)))
+	   (process-lines notmuch-command "search-tags"))))
+
+(defun notmuch-hello-insert-header ()
+  "Insert the default notmuch-hello header."
+  (when notmuch-show-logo
+    (let ((image notmuch-hello-logo))
+      ;; The notmuch logo uses transparency. That can display poorly
+      ;; when inserting the image into an emacs buffer (black logo on
+      ;; a black background), so force the background colour of the
+      ;; image. We use a face to represent the colour so that
+      ;; `defface' can be used to declare the different possible
+      ;; colours, which depend on whether the frame has a light or
+      ;; dark background.
+      (setq image (cons 'image
+			(append (cdr image)
+				(list :background (face-background 'notmuch-hello-logo-background)))))
+      (insert-image image))
+    (widget-insert "  "))
+
+  (widget-insert "Welcome to ")
+  ;; Hack the display of the links used.
+  (let ((widget-link-prefix "")
+	(widget-link-suffix ""))
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (browse-url notmuch-hello-url))
+		   :help-echo "Visit the notmuch website."
+		   "notmuch")
+    (widget-insert ". ")
+    (widget-insert "You have ")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (notmuch-hello-update))
+		   :help-echo "Refresh"
+		   (notmuch-hello-nice-number
+		    (string-to-number (car (process-lines notmuch-command "count")))))
+    (widget-insert " messages.\n")))
+
+
+(defun notmuch-hello-insert-saved-searches ()
+  "Insert the saved-searches section."
+  (let ((searches (notmuch-hello-query-counts
+		  (if notmuch-saved-search-sort-function
+		      (funcall notmuch-saved-search-sort-function
+			       notmuch-saved-searches)
+		    notmuch-saved-searches)
+		  :show-empty-searches notmuch-show-empty-saved-searches))
+	found-target-pos final-target-pos)
+    (when searches
+      (widget-insert "Saved searches: ")
+      (widget-create 'push-button
+		     :notify (lambda (&rest ignore)
+			       (customize-variable 'notmuch-saved-searches))
+		     "edit")
+      (widget-insert "\n\n")
+      (setq final-target-pos (point-marker))
+      (let ((start (point)))
+	(setq found-target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(if found-target-pos
+	    (setq final-target-pos found-target-pos))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	final-target-pos))))
+
+(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.
+		 :size (max 8 (- (window-width) notmuch-hello-indent
+				 (length "Search: ")))
+		 :action (lambda (widget &rest ignore)
+			   (notmuch-hello-search (widget-value widget))))
+  ;; Add an invisible dot to make `widget-end-of-line' ignore
+  ;; trailing spaces in the search widget field.  A dot is used
+  ;; instead of a space to make `show-trailing-whitespace'
+  ;; happy, i.e. avoid it marking the whole line as trailing
+  ;; spaces.
+  (widget-insert ".")
+  (put-text-property (1- (point)) (point) 'invisible t)
+  (widget-insert "\n"))
+
+(defun notmuch-hello-insert-recent-searches ()
+  "Insert recent searches."
+  (when notmuch-search-history
+    (widget-insert "Recent searches: ")
+    (widget-create 'push-button
+		   :notify (lambda (&rest ignore)
+			     (setq notmuch-search-history nil)
+			     (notmuch-hello-update))
+		   "clear")
+    (widget-insert "\n\n")
+    (let ((start (point))
+	  (nth 0))
+      (mapc (lambda (search)
+	      (let ((widget-symbol (intern (format "notmuch-hello-search-%d" nth))))
+		(set widget-symbol
+		     (widget-create 'editable-field
+				    ;; Don't let the search boxes be
+				    ;; less than 8 characters wide.
+				    :size (max 8
+					       (- (window-width)
+						  ;; Leave some space
+						  ;; at the start and
+						  ;; end of the
+						  ;; boxes.
+						  (* 2 notmuch-hello-indent)
+						  ;; 1 for the space
+						  ;; before the
+						  ;; `[save]' button. 6
+						  ;; for the `[save]'
+						  ;; button.
+						  1 6))
+				    :action (lambda (widget &rest ignore)
+					      (notmuch-hello-search (widget-value widget)))
+				    search))
+		(widget-insert " ")
+		(widget-create 'push-button
+			       :notify (lambda (widget &rest ignore)
+					 (notmuch-hello-add-saved-search widget))
+			       :notmuch-saved-search-widget widget-symbol
+			       "save"))
+	      (widget-insert "\n")
+	      (setq nth (1+ nth)))
+	    notmuch-hello-recent-searches)
+      (indent-rigidly start (point) notmuch-hello-indent))))
+
+(defun notmuch-hello-insert-searches (title query-alist &rest options)
+  "Insert a section with TITLE showing a list of buttons made from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated item.
+
+Supports the following entries in OPTIONS as a plist:
+:initially-hidden - if non-nil, section will be hidden on startup
+:show-empty-searches - show buttons with no matching messages
+:hide-if-empty - hide if no buttons would be shown
+   (only makes sense without :show-empty-searches)
+:filter - This can be a function that takes the search query as its argument and
+    returns a filter to be used in conjuction with the query for that search or nil
+    to hide the element. This can also be a string that is used as a combined with
+    each query using \"and\".
+:filter-count - Separate filter to generate the count displayed each search. Accepts
+    the same values as :filter. If :filter and :filter-count are specified, this
+    will be used instead of :filter, not in conjunction with it."
+  (widget-insert title ": ")
+  (if (and notmuch-hello-first-run (plist-get options :initially-hidden))
+      (add-to-list 'notmuch-hello-hidden-sections title))
+  (let ((is-hidden (member title notmuch-hello-hidden-sections))
+	(start (point)))
+    (if is-hidden
+	(widget-create 'push-button
+		       :notify `(lambda (widget &rest ignore)
+				  (setq notmuch-hello-hidden-sections
+					(delete ,title notmuch-hello-hidden-sections))
+				  (notmuch-hello-update))
+		       "show")
+      (widget-create 'push-button
+		     :notify `(lambda (widget &rest ignore)
+				(add-to-list 'notmuch-hello-hidden-sections
+					     ,title)
+				(notmuch-hello-update))
+		     "hide"))
+    (widget-insert "\n")
+    (let (target-pos
+	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
+      (when (and (not is-hidden)
+	       (or (not (plist-get options :hide-if-empty))
+		  searches))
+	(widget-insert "\n")
+	(setq target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	target-pos))))
+
+(defun notmuch-hello-insert-tags-section (&optional title &rest options)
+  "Insert a section displaying all tags with message counts.
+
+TITLE defaults to \"All tags\".
+Allowed options are those accepted by `notmuch-hello-insert-searches' and the
+following:
+
+:hide-tags - List of tags that should be excluded."
+  (apply 'notmuch-hello-insert-searches
+	 (or title "All tags")
+	 (notmuch-hello-generate-tag-alist (plist-get options :hide-tags))
+	 options))
+
+(defun notmuch-hello-insert-inbox ()
+  "Show an entry for each saved search and inboxed messages for each tag"
+  (notmuch-hello-insert-searches "What's in your inbox"
+				 (append
+				  (notmuch-saved-searches)
+				  (notmuch-hello-generate-tag-alist))
+				 :filter "tag:inbox"))
+
+(defun notmuch-hello-insert-alltags ()
+  "Insert a section displaying all tags and associated message counts"
+  (notmuch-hello-insert-tags-section
+   nil
+   :initially-hidden (not notmuch-show-all-tags-list)
+   :hide-tags notmuch-hello-hide-tags
+   :filter notmuch-hello-tag-list-make-query))
+
+(defun notmuch-hello-insert-footer ()
+  "Insert the notmuch-hello footer."
+  (let ((start (point)))
+    (widget-insert "Type a search query and hit RET to view matching threads.\n")
+    (when notmuch-hello-recent-searches
+      (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
+      (widget-insert "Save recent searches with the `save' button.\n"))
+    (when notmuch-saved-searches
+      (widget-insert "Edit saved searches with the `edit' button.\n"))
+    (widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
+    (widget-insert "`=' to refresh this screen. `s' to search messages. `q' to quit.\n")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (customize-variable 'notmuch-hello-sections))
+		   :button-prefix "" :button-suffix ""
+		   "Customize")
+    (widget-insert " this page.")
+    (let ((fill-column (- (window-width) notmuch-hello-indent)))
+      (center-region start (point)))))
 
 ;;;###autoload
 (defun notmuch-hello (&optional no-display)
@@ -397,13 +781,13 @@ Complete list of currently available key bindings:
       (set-buffer "*notmuch-hello*")
     (switch-to-buffer "*notmuch-hello*"))
 
-  (let ((target (if (widget-at)
-		   (widget-value (widget-at))
-		 (condition-case nil
-		     (progn
-		       (widget-forward 1)
-		       (widget-value (widget-at)))
-		   (error nil))))
+  (let ((notmuch-hello-target (if (widget-at)
+				  (widget-value (widget-at))
+				(condition-case nil
+				    (progn
+				      (widget-forward 1)
+				      (widget-value (widget-at)))
+				  (error nil))))
 	(inhibit-read-only t))
 
     ;; Delete all editable widget fields.  Editable widget fields are
@@ -422,168 +806,20 @@ Complete list of currently available key bindings:
       (mapc 'delete-overlay (car all))
       (mapc 'delete-overlay (cdr all)))
 
-    (when notmuch-show-logo
-      (let ((image notmuch-hello-logo))
-	;; The notmuch logo uses transparency. That can display poorly
-	;; when inserting the image into an emacs buffer (black logo on
-	;; a black background), so force the background colour of the
-	;; image. We use a face to represent the colour so that
-	;; `defface' can be used to declare the different possible
-	;; colours, which depend on whether the frame has a light or
-	;; dark background.
-	(setq image (cons 'image
-			  (append (cdr image)
-				  (list :background (face-background 'notmuch-hello-logo-background)))))
-	(insert-image image))
-      (widget-insert "  "))
-
-    (widget-insert "Welcome to ")
-    ;; Hack the display of the links used.
-    (let ((widget-link-prefix "")
-	  (widget-link-suffix ""))
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (browse-url notmuch-hello-url))
-		     :help-echo "Visit the notmuch website."
-		     "notmuch")
-      (widget-insert ". ")
-      (widget-insert "You have ")
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (notmuch-hello-update))
-		     :help-echo "Refresh"
-		     (notmuch-hello-nice-number
-		      (string-to-number (car (process-lines notmuch-command "count")))))
-      (widget-insert " messages.\n"))
-
-    (let ((found-target-pos nil)
-	  (final-target-pos nil)
-	  (default-pos))
-      (let* ((saved-alist
-	      ;; Filter out empty saved searches if required.
-	      (if notmuch-show-empty-saved-searches
-		  notmuch-saved-searches
-		(loop for elem in notmuch-saved-searches
-		      if (> (string-to-number (notmuch-saved-search-count (cdr elem))) 0)
-		      collect elem)))
-	     (saved-widest (notmuch-hello-longest-label saved-alist))
-	     (alltags-alist (if notmuch-show-all-tags-list (notmuch-hello-generate-tag-alist)))
-	     (alltags-widest (notmuch-hello-longest-label alltags-alist))
-	     (widest (max saved-widest alltags-widest)))
-
-	(when saved-alist
-	  ;; Sort saved searches if required.
-	  (when notmuch-saved-search-sort-function
-	    (setq saved-alist
-		  (funcall notmuch-saved-search-sort-function saved-alist)))
-	  (widget-insert "\nSaved searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (customize-variable 'notmuch-saved-searches))
-			 "edit")
-	  (widget-insert "\n\n")
-	  (setq final-target-pos (point-marker))
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags saved-alist widest target))
-	    (if found-target-pos
-		(setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\nSearch: ")
-	(setq default-pos (point-marker))
-	(widget-create 'editable-field
-		       ;; Leave some space at the start and end of the
-		       ;; search boxes.
-		       :size (max 8 (- (window-width) notmuch-hello-indent
-				       (length "Search: ")))
-		       :action (lambda (widget &rest ignore)
-				 (notmuch-hello-search (widget-value widget))))
-	;; Add an invisible dot to make `widget-end-of-line' ignore
-	;; trailing spaces in the search widget field.  A dot is used
-	;; instead of a space to make `show-trailing-whitespace'
-	;; happy, i.e. avoid it marking the whole line as trailing
-	;; spaces.
-	(widget-insert ".")
-	(put-text-property (1- (point)) (point) 'invisible t)
-	(widget-insert "\n")
-
-	(when notmuch-search-history
-	  (widget-insert "\nRecent searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (setq notmuch-search-history nil)
-				   (notmuch-hello-update))
-			 "clear")
-	  (widget-insert "\n\n")
-	  (let ((start (point)))
-	    (loop for i from 1 to notmuch-hello-recent-searches-max
-		  for search in notmuch-search-history do
-		    (let ((widget-symbol (intern (format "notmuch-hello-search-%d" i))))
-		      (set widget-symbol
-			   (widget-create 'editable-field
-					  ;; Don't let the search boxes be
-					  ;; less than 8 characters wide.
-					  :size (max 8
-						     (- (window-width)
-							;; Leave some space
-							;; at the start and
-							;; end of the
-							;; boxes.
-							(* 2 notmuch-hello-indent)
-							;; 1 for the space
-							;; before the
-							;; `[save]' button. 6
-							;; for the `[save]'
-							;; button.
-							1 6))
-					  :action (lambda (widget &rest ignore)
-						    (notmuch-hello-search (widget-value widget)))
-					  search))
-		      (widget-insert " ")
-		      (widget-create 'push-button
-				     :notify (lambda (widget &rest ignore)
-					       (notmuch-hello-add-saved-search widget))
-				     :notmuch-saved-search-widget widget-symbol
-				     "save"))
-		    (widget-insert "\n"))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(when alltags-alist
-	  (widget-insert "\nAll tags: ")
-	  (widget-create 'push-button
-			 :notify (lambda (widget &rest ignore)
-				   (setq notmuch-show-all-tags-list nil)
-				   (notmuch-hello-update))
-			 "hide")
-	  (widget-insert "\n\n")
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags alltags-alist widest target))
-	    (unless final-target-pos
-	      (setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\n")
-
-	(unless notmuch-show-all-tags-list
-	  (widget-create 'push-button
-			 :notify (lambda (widget &rest ignore)
-				   (setq notmuch-show-all-tags-list t)
-				   (notmuch-hello-update))
-			 "Show all tags")))
-
-      (let ((start (point)))
-	(widget-insert "\n\n")
-	(widget-insert "Type a search query and hit RET to view matching threads.\n")
-	(when notmuch-search-history
-	  (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
-	  (widget-insert "Save recent searches with the `save' button.\n"))
-	(when notmuch-saved-searches
-	  (widget-insert "Edit saved searches with the `edit' button.\n"))
-	(widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
-	(widget-insert "`=' to refresh this screen. `s' to search messages. `q' to quit.\n")
-	(let ((fill-column (- (window-width) notmuch-hello-indent)))
-	  (center-region start (point))))
-
+    (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
@@ -592,9 +828,10 @@ Complete list of currently available key bindings:
 	  (widget-forward 1)))
 
       (unless (widget-at)
-	(goto-char default-pos))))
-
-  (run-hooks 'notmuch-hello-refresh-hook))
+	(when notmuch-hello-search-pos
+	  (goto-char notmuch-hello-search-pos)))))
+  (run-hooks 'notmuch-hello-refresh-hook)
+  (setq notmuch-hello-first-run nil))
 
 (defun notmuch-folder ()
   "Deprecated function for invoking notmuch---calling `notmuch' is preferred now."
-- 
1.7.8.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v8 2/2] emacs: Tests for user-defined sections
  2012-01-28 21:44                         ` [PATCH v8 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
  2012-01-28 21:44                           ` [PATCH v8 1/2] " Daniel Schoepe
@ 2012-01-28 21:44                           ` Daniel Schoepe
  1 sibling, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2012-01-28 21:44 UTC (permalink / raw)
  To: notmuch; +Cc: Daniel Schoepe

From: Daniel Schoepe <daniel.schoepe@googlemail.com>

---
 test/emacs                                         |   37 ++++++++++++++++++++
 test/emacs.expected-output/notmuch-hello           |    3 +-
 .../notmuch-hello-new-section                      |    4 ++
 .../notmuch-hello-no-saved-searches                |    3 +-
 .../notmuch-hello-section-counts                   |    5 +++
 .../notmuch-hello-section-hidden-tag               |    4 ++
 .../notmuch-hello-section-with-empty               |    4 ++
 .../emacs.expected-output/notmuch-hello-with-empty |    3 +-
 8 files changed, 60 insertions(+), 3 deletions(-)
 create mode 100644 test/emacs.expected-output/notmuch-hello-new-section
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-counts
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-hidden-tag
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-with-empty

diff --git a/test/emacs b/test/emacs
index 8ca4c8a..93445d7 100755
--- a/test/emacs
+++ b/test/emacs
@@ -29,6 +29,43 @@ test_emacs '(let ((notmuch-saved-searches
 	      (test-output))'
 test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-no-saved-searches
 
+test_begin_subtest "User defined section with inbox tag"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test\" '((\"inbox\" . \"tag:inbox\")))))))
+           (notmuch-hello)
+           (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-new-section
+
+test_begin_subtest "User defined section with empty, hidden entry"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test-with-empty\"
+                                     '((\"inbox\" . \"tag:inbox\")
+                                       (\"doesnotexist\" . \"tag:doesnotexist\"))
+                                     :hide-empty-searches t)))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-with-empty
+
+test_begin_subtest "User defined section, unread tag filtered out"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-filtered\"
+                                     :hide-tags '(\"unread\"))))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-hidden-tag
+
+test_begin_subtest "User defined section, different query for counts"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-counts\"
+                                     :filter-count \"tag:signed\")))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-counts
+
 test_begin_subtest "Basic notmuch-search view in emacs"
 test_emacs '(notmuch-search "tag:inbox")
 	    (notmuch-test-wait)
diff --git a/test/emacs.expected-output/notmuch-hello b/test/emacs.expected-output/notmuch-hello
index 3e59595..1470790 100644
--- a/test/emacs.expected-output/notmuch-hello
+++ b/test/emacs.expected-output/notmuch-hello
@@ -6,9 +6,10 @@ Saved searches: [edit]
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
       `=' to refresh this screen. `s' to search messages. `q' to quit.
+			    Customize this page.
diff --git a/test/emacs.expected-output/notmuch-hello-new-section b/test/emacs.expected-output/notmuch-hello-new-section
new file mode 100644
index 0000000..c64d712
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-new-section
@@ -0,0 +1,4 @@
+Test: [hide]
+
+	  52 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-no-saved-searches b/test/emacs.expected-output/notmuch-hello-no-saved-searches
index ef0e5d0..05475b1 100644
--- a/test/emacs.expected-output/notmuch-hello-no-saved-searches
+++ b/test/emacs.expected-output/notmuch-hello-no-saved-searches
@@ -2,9 +2,10 @@
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
       `=' to refresh this screen. `s' to search messages. `q' to quit.
+			    Customize this page.
diff --git a/test/emacs.expected-output/notmuch-hello-section-counts b/test/emacs.expected-output/notmuch-hello-section-counts
new file mode 100644
index 0000000..9d79659
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-counts
@@ -0,0 +1,5 @@
+Test-with-counts: [hide]
+
+	   2 attachment             7 signed          
+	   7 inbox                  7 unread          
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-hidden-tag b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
new file mode 100644
index 0000000..3688e7c
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
@@ -0,0 +1,4 @@
+Test-with-filtered: [hide]
+
+	   4 attachment            52 inbox                  7 signed 
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-with-empty b/test/emacs.expected-output/notmuch-hello-section-with-empty
new file mode 100644
index 0000000..8209fed
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-with-empty
@@ -0,0 +1,4 @@
+Test-with-empty: [hide]
+
+	  52 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-with-empty b/test/emacs.expected-output/notmuch-hello-with-empty
index 71edba7..5e53222 100644
--- a/test/emacs.expected-output/notmuch-hello-with-empty
+++ b/test/emacs.expected-output/notmuch-hello-with-empty
@@ -6,9 +6,10 @@ Saved searches: [edit]
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
       `=' to refresh this screen. `s' to search messages. `q' to quit.
+			    Customize this page.
-- 
1.7.8.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [PATCH v7 2/2] emacs: Tests for user-defined sections
  2012-01-28 21:30                       ` Daniel Schoepe
  2012-01-28 21:44                         ` [PATCH v8 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
@ 2012-01-28 22:48                         ` Dmitry Kurochkin
  2012-01-28 22:54                           ` Daniel Schoepe
  1 sibling, 1 reply; 57+ messages in thread
From: Dmitry Kurochkin @ 2012-01-28 22:48 UTC (permalink / raw)
  To: Daniel Schoepe, notmuch

Hi Daniel.

On Sat, 28 Jan 2012 22:30:57 +0100, Daniel Schoepe <daniel@schoepe.org> wrote:
> Hi Dmitry,
> 
> On Tue, 24 Jan 2012 03:07, Dmitry Kurochkin <dmitry.kurochkin@gmail.com> wrote:
> > There are some trailing whitespaces in the tests.
> 
> those are also produced by the various notmuch-hello functions, and are
> also in the existing tests. If someone is bothered by this, it should be
> fixed in a separate patch.
> 

I only meant trailing whitespaces in test/emacs file in the lines added
by the patch.  Trailing whitespaces in expected results are obviously
fine.

> > 
> > Also, please consider moving the hello sections tests to a separate file
> > (emacs-hello-sections, perhaps?).  I am worried that test/emacs file
> > becomes too big.  And we will probably add more notmuch-hello
> > section-related tests in the future.
> 
> I think it's better to split it up into more than just
> emacs-hello-sections and emacs, as there are also quite a few tests
> relating solely to, e.g., -show or -search.
> 

I think splitting emacs tests based on -hello, -show and -search views
is a good idea.  Splitting existing tests is out of scope of this
patch.  But I think it may be a good opportunity to add a new file with
-hello tests.

> Anyhow, I'll send another rebased version fixing conflicts with the
> search-interface changes.
> 

Thanks, I will try to review it soon.

Regards,
  Dmitry

> Cheers,
> Daniel

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v7 2/2] emacs: Tests for user-defined sections
  2012-01-28 22:48                         ` [PATCH v7 " Dmitry Kurochkin
@ 2012-01-28 22:54                           ` Daniel Schoepe
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2012-01-28 22:54 UTC (permalink / raw)
  To: Dmitry Kurochkin, notmuch

[-- Attachment #1: Type: text/plain, Size: 855 bytes --]

Hi Dmitry.

On Sun, 29 Jan 2012 02:48, Dmitry Kurochkin <dmitry.kurochkin@gmail.com> wrote:
> I only meant trailing whitespaces in test/emacs file in the lines added
> by the patch.  Trailing whitespaces in expected results are obviously
> fine.

Ah, okay. I fixed those with delete-trailing-whitespace, but then
noticed that this also corrected some trailing whitespace in unrelated
tests, reverted that and then forgot about the trailing whitespace in my
tests by the time I responded.

> I think splitting emacs tests based on -hello, -show and -search views
> is a good idea.  Splitting existing tests is out of scope of this
> patch.  But I think it may be a good opportunity to add a new file with
> -hello tests.

You're right; I'll do that if you find other flaws in your next
review. Otherwise, I'll just put it in another patch.

Cheers,
Daniel

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v9 0/2] emacs: User-defined sections in notmuch-hello
  2011-07-07 22:53 ` [PATCH v4 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
                     ` (2 preceding siblings ...)
  2011-07-09 18:03   ` [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
@ 2012-02-17  7:48   ` Dmitry Kurochkin
  2012-02-17  7:48     ` [PATCH v9 1/2] " Dmitry Kurochkin
  2012-02-17  7:48     ` [PATCH v9 2/2] emacs: Tests for user-defined sections Dmitry Kurochkin
  2012-02-17 14:48   ` [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello Dmitry Kurochkin
  4 siblings, 2 replies; 57+ messages in thread
From: Dmitry Kurochkin @ 2012-02-17  7:48 UTC (permalink / raw)
  To: notmuch

Hi all.

I have finished reviewing the user-defined sections v8 series at last.
I found only few minor issues, some of which are fixed by this v9
series and others can be fixed later or ignored (e.g. renaming
notmuch-hello-insert-* functions to notmuch-hello-section-* or
something more appropriate).  I believe it does not make to delay this
patches any more: they have been waiting long enough and each
review/rebase round is hard for both the author and reviewer because
the main patch is pretty big and not trivial.  So I think we should
push this now.  I am sure Daniel and I will fix all issues that we may
have missed :)

Changes in v9 since v8:

* correctly merge bc267b70b01c79f6bdda52641e9cd7574a151eff

* fix :hide-if-empty description:

    s/and not hidden by show-if-empty/and not shown by show-empty-searches/

* fix typo in `notmuch-hello-target' docstring: s/globaly/global/

* add docstring for notmuch-hello-first-run

* fix indentation in `notmuch-hello-insert-saved-searches'

* fix indentation in `notmuch-hello-insert-searches' docstring

* fix target position calculation in `notmuch-hello-insert-saved-searches'

* s/User defined/User-defined/ in tests

* move tests to a new emacs-hello file

Regards,
  Dmitry

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v9 1/2] emacs: User-defined sections in notmuch-hello
  2012-02-17  7:48   ` [PATCH v9 " Dmitry Kurochkin
@ 2012-02-17  7:48     ` Dmitry Kurochkin
  2012-02-17  7:48     ` [PATCH v9 2/2] emacs: Tests for user-defined sections Dmitry Kurochkin
  1 sibling, 0 replies; 57+ messages in thread
From: Dmitry Kurochkin @ 2012-02-17  7:48 UTC (permalink / raw)
  To: notmuch; +Cc: Daniel Schoepe

From: Daniel Schoepe <daniel.schoepe@googlemail.com>

This patch makes the notmuch-hello screen fully customizable
by allowing the user to add and remove arbitrary sections. It
also provides some convenience functions for constructing sections,
e.g. showing the unread message count for each tag.

This is done by specifying a list of functions that will be run
when notmuch-hello is invoked.
---
 emacs/notmuch-hello.el |  624 ++++++++++++++++++++++++++++++++----------------
 1 files changed, 424 insertions(+), 200 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index d17a30f..89c5637 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -154,6 +154,108 @@ 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"))
+    (:initially-hidden (const :tag "Hide this section on startup" t))
+    (:show-empty-searches (const :tag "Show queries with no matching messages" t))
+    (:hide-if-empty (const :tag "Hide this section if all queries are empty
+\(and not shown by show-empty-searches)" t)))
+  "Various customization-options for notmuch-hello-tags/query-section.")
+
+(define-widget 'notmuch-hello-tags-section 'lazy
+  "Customize-type for notmuch-hello tag-list sections."
+  :tag "Customized tag-list section (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-tags-section)
+	 (string :tag "Title for this section")
+	 (plist
+	  :inline t
+	  :options
+	  ,(append notmuch-hello-custom-section-options
+		   '((:hide-tags (repeat :tag "Tags that will be hidden"
+					 string)))))))
+
+(define-widget 'notmuch-hello-query-section 'lazy
+  "Customize-type for custom saved-search-like sections"
+  :tag "Customized queries section (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-query-list)
+	 (string :tag "Title for this section")
+	 (repeat :tag "Queries"
+		 (cons (string :tag "Name") (string :tag "Query")))
+	 (plist :inline t :options ,notmuch-hello-custom-section-options)))
+
+(defcustom notmuch-hello-sections
+  (list #'notmuch-hello-insert-header
+	#'notmuch-hello-insert-saved-searches
+	#'notmuch-hello-insert-search
+	#'notmuch-hello-insert-recent-searches
+	#'notmuch-hello-insert-alltags
+	#'notmuch-hello-insert-footer)
+  "Sections for notmuch-hello.
+
+The list contains functions which are used to construct sections in
+notmuch-hello buffer.  When notmuch-hello buffer is constructed,
+these functions are run in the order they appear in this list.  Each
+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.
+
+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
+list.
+
+A \"Customized tag-list section\" item in the customize-interface
+displays a list of all tags, optionally hiding some of them. It
+is also possible to filter the list of messages matching each tag
+by an additional filter query. Similarly, the count of messages
+displayed next to the buttons can be generated by applying a
+different filter to the tag query. These filters are also
+supported for \"Customized queries section\" items."
+  :group 'notmuch
+  :type
+  '(repeat
+    (choice (function-item notmuch-hello-insert-header)
+	    (function-item notmuch-hello-insert-saved-searches)
+	    (function-item notmuch-hello-insert-search)
+	    (function-item notmuch-hello-insert-recent-searches)
+	    (function-item notmuch-hello-insert-alltags)
+	    (function-item notmuch-hello-insert-footer)
+	    (function-item notmuch-hello-insert-inbox)
+	    notmuch-hello-tags-section
+	    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")
+
+(defvar notmuch-hello-first-run t
+  "True if `notmuch-hello' is run for the first time, set to nil
+afterwards.")
+
 (defun notmuch-hello-nice-number (n)
   (let (result)
     (while (> n 0)
@@ -201,8 +303,8 @@ International Bureau of Weights and Measures."
     (message "Saved '%s' as '%s'." search name)
     (notmuch-hello-update)))
 
-(defun notmuch-hello-longest-label (tag-alist)
-  (or (loop for elem in tag-alist
+(defun notmuch-hello-longest-label (searches-alist)
+  (or (loop for elem in searches-alist
 	    maximize (length (car elem)))
       0))
 
@@ -266,12 +368,70 @@ should be. Returns a cons cell `(tags-per-line width)'."
 				   (* tags-per-line (+ 9 1))))
 			   tags-per-line))))
 
-(defun notmuch-hello-insert-tags (tag-alist widest target)
-  (let* ((tags-and-width (notmuch-hello-tags-per-line widest))
+(defun notmuch-hello-filtered-query (query filter)
+  "Constructs a query to search all messages matching QUERY and FILTER.
+
+If FILTER is a string, it is directly used in the returned query.
+
+If FILTER is a function, it is called with QUERY as a parameter and
+the string it returns is used as the query. If nil is returned,
+the entry is hidden.
+
+Otherwise, FILTER is ignored.
+"
+  (cond
+   ((functionp filter) (funcall filter query))
+   ((stringp filter)
+    (concat "(" query ") and (" filter ")"))
+   (t query)))
+
+(defun notmuch-hello-query-counts (query-alist &rest options)
+  "Compute list of counts of matched messages from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated query.
+
+The result is the list of elements of the form (NAME QUERY COUNT).
+
+The values :show-empty-searches, :filter and :filter-count from
+options will be handled as specified for
+`notmuch-hello-insert-searches'."
+  (notmuch-remove-if-not
+   #'identity
+   (mapcar
+    (lambda (elem)
+      (let* ((name (car elem))
+	     (query-and-count (if (consp (cdr elem))
+				  ;; do we have a different query for the message count?
+				  (cons (second elem) (third elem))
+				(cons (cdr elem) (cdr elem))))
+	     (message-count
+	      (string-to-number
+	       (notmuch-saved-search-count
+		(notmuch-hello-filtered-query (cdr query-and-count)
+					      (or (plist-get options :filter-count)
+						 (plist-get options :filter)))))))
+	(and (or (plist-get options :show-empty-searches) (> message-count 0))
+	     (list name (notmuch-hello-filtered-query
+			 (car query-and-count) (plist-get options :filter))
+		   message-count))))
+    query-alist)))
+
+(defun notmuch-hello-insert-buttons (searches)
+  "Insert buttons for SEARCHES.
+
+SEARCHES must be a list containing lists of the form (NAME QUERY COUNT), where
+QUERY is the query to start when the button for the corresponding entry is
+activated. COUNT should be the number of messages matching the query.
+Such a list can be computed with `notmuch-hello-query-counts'."
+  (let* ((widest (notmuch-hello-longest-label searches))
+	 (tags-and-width (notmuch-hello-tags-per-line widest))
 	 (tags-per-line (car tags-and-width))
 	 (widest (cdr tags-and-width))
 	 (count 0)
-	 (reordered-list (notmuch-hello-reflect tag-alist tags-per-line))
+	 (reordered-list (notmuch-hello-reflect searches tags-per-line))
 	 ;; Hack the display of the buttons used.
 	 (widget-push-button-prefix "")
 	 (widget-push-button-suffix "")
@@ -281,13 +441,13 @@ should be. Returns a cons cell `(tags-per-line width)'."
     (mapc (lambda (elem)
 	    ;; (not elem) indicates an empty slot in the matrix.
 	    (when elem
-	      (let* ((name (car elem))
-		     (query (cdr elem))
+	      (let* ((name (first elem))
+		     (query (second elem))
+		     (msg-count (third elem))
 		     (formatted-name (format "%s " name)))
 		(widget-insert (format "%8s "
-				       (notmuch-hello-nice-number
-					(string-to-number (notmuch-saved-search-count query)))))
-		(if (string= formatted-name target)
+				       (notmuch-hello-nice-number msg-count)))
+		(if (string= formatted-name notmuch-hello-target)
 		    (setq found-target-pos (point-marker)))
 		(widget-create 'push-button
 			       :notify #'notmuch-hello-widget-search
@@ -359,29 +519,240 @@ Complete list of currently available key bindings:
  (kill-all-local-variables)
  (use-local-map notmuch-hello-mode-map)
  (setq major-mode 'notmuch-hello-mode
-       mode-name "notmuch-hello")
+	mode-name "notmuch-hello")
  (run-mode-hooks 'notmuch-hello-mode-hook)
  ;;(setq buffer-read-only t)
 )
 
-(defun notmuch-hello-generate-tag-alist ()
+(defun notmuch-hello-generate-tag-alist (&optional hide-tags)
   "Return an alist from tags to queries to display in the all-tags section."
-  (notmuch-remove-if-not
-   #'cdr
-   (mapcar (lambda (tag)
-	     (cons tag
-		   (cond
-		    ((functionp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     (funcall notmuch-hello-tag-list-make-query tag) ")"))
-		    ((stringp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     notmuch-hello-tag-list-make-query ")"))
-		    (t (concat "tag:" tag)))))
-	   (notmuch-remove-if-not
-	    (lambda (tag)
-	      (not (member tag notmuch-hello-hide-tags)))
-	    (process-lines notmuch-command "search-tags")))))
+  (mapcar (lambda (tag)
+	    (cons tag (format "tag:%s" tag)))
+	  (notmuch-remove-if-not
+	   (lambda (tag)
+	     (not (member tag hide-tags)))
+	   (process-lines notmuch-command "search-tags"))))
+
+(defun notmuch-hello-insert-header ()
+  "Insert the default notmuch-hello header."
+  (when notmuch-show-logo
+    (let ((image notmuch-hello-logo))
+      ;; The notmuch logo uses transparency. That can display poorly
+      ;; when inserting the image into an emacs buffer (black logo on
+      ;; a black background), so force the background colour of the
+      ;; image. We use a face to represent the colour so that
+      ;; `defface' can be used to declare the different possible
+      ;; colours, which depend on whether the frame has a light or
+      ;; dark background.
+      (setq image (cons 'image
+			(append (cdr image)
+				(list :background (face-background 'notmuch-hello-logo-background)))))
+      (insert-image image))
+    (widget-insert "  "))
+
+  (widget-insert "Welcome to ")
+  ;; Hack the display of the links used.
+  (let ((widget-link-prefix "")
+	(widget-link-suffix ""))
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (browse-url notmuch-hello-url))
+		   :help-echo "Visit the notmuch website."
+		   "notmuch")
+    (widget-insert ". ")
+    (widget-insert "You have ")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (notmuch-hello-update))
+		   :help-echo "Refresh"
+		   (notmuch-hello-nice-number
+		    (string-to-number (car (process-lines notmuch-command "count")))))
+    (widget-insert " messages.\n")))
+
+
+(defun notmuch-hello-insert-saved-searches ()
+  "Insert the saved-searches section."
+  (let ((searches (notmuch-hello-query-counts
+		   (if notmuch-saved-search-sort-function
+		       (funcall notmuch-saved-search-sort-function
+				notmuch-saved-searches)
+		     notmuch-saved-searches)
+		   :show-empty-searches notmuch-show-empty-saved-searches))
+	found-target-pos)
+    (when searches
+      (widget-insert "Saved searches: ")
+      (widget-create 'push-button
+		     :notify (lambda (&rest ignore)
+			       (customize-variable 'notmuch-saved-searches))
+		     "edit")
+      (widget-insert "\n\n")
+      (let ((start (point)))
+	(setq found-target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	found-target-pos))))
+
+(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.
+		 :size (max 8 (- (window-width) notmuch-hello-indent
+				 (length "Search: ")))
+		 :action (lambda (widget &rest ignore)
+			   (notmuch-hello-search (widget-value widget))))
+  ;; Add an invisible dot to make `widget-end-of-line' ignore
+  ;; trailing spaces in the search widget field.  A dot is used
+  ;; instead of a space to make `show-trailing-whitespace'
+  ;; happy, i.e. avoid it marking the whole line as trailing
+  ;; spaces.
+  (widget-insert ".")
+  (put-text-property (1- (point)) (point) 'invisible t)
+  (widget-insert "\n"))
+
+(defun notmuch-hello-insert-recent-searches ()
+  "Insert recent searches."
+  (when notmuch-search-history
+    (widget-insert "Recent searches: ")
+    (widget-create 'push-button
+		   :notify (lambda (&rest ignore)
+			     (setq notmuch-search-history nil)
+			     (notmuch-hello-update))
+		   "clear")
+    (widget-insert "\n\n")
+    (let ((start (point)))
+      (loop for i from 1 to notmuch-hello-recent-searches-max
+	    for search in notmuch-search-history do
+	    (let ((widget-symbol (intern (format "notmuch-hello-search-%d" i))))
+	      (set widget-symbol
+		   (widget-create 'editable-field
+				  ;; Don't let the search boxes be
+				  ;; less than 8 characters wide.
+				  :size (max 8
+					     (- (window-width)
+						;; Leave some space
+						;; at the start and
+						;; end of the
+						;; boxes.
+						(* 2 notmuch-hello-indent)
+						;; 1 for the space
+						;; before the
+						;; `[save]' button. 6
+						;; for the `[save]'
+						;; button.
+						1 6))
+				  :action (lambda (widget &rest ignore)
+					    (notmuch-hello-search (widget-value widget)))
+				  search))
+	      (widget-insert " ")
+	      (widget-create 'push-button
+			     :notify (lambda (widget &rest ignore)
+				       (notmuch-hello-add-saved-search widget))
+			     :notmuch-saved-search-widget widget-symbol
+			     "save"))
+	    (widget-insert "\n"))
+      (indent-rigidly start (point) notmuch-hello-indent))))
+
+(defun notmuch-hello-insert-searches (title query-alist &rest options)
+  "Insert a section with TITLE showing a list of buttons made from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated item.
+
+Supports the following entries in OPTIONS as a plist:
+:initially-hidden - if non-nil, section will be hidden on startup
+:show-empty-searches - show buttons with no matching messages
+:hide-if-empty - hide if no buttons would be shown
+   (only makes sense without :show-empty-searches)
+:filter - This can be a function that takes the search query as its argument and
+   returns a filter to be used in conjuction with the query for that search or nil
+   to hide the element. This can also be a string that is used as a combined with
+   each query using \"and\".
+:filter-count - Separate filter to generate the count displayed each search. Accepts
+   the same values as :filter. If :filter and :filter-count are specified, this
+   will be used instead of :filter, not in conjunction with it."
+  (widget-insert title ": ")
+  (if (and notmuch-hello-first-run (plist-get options :initially-hidden))
+      (add-to-list 'notmuch-hello-hidden-sections title))
+  (let ((is-hidden (member title notmuch-hello-hidden-sections))
+	(start (point)))
+    (if is-hidden
+	(widget-create 'push-button
+		       :notify `(lambda (widget &rest ignore)
+				  (setq notmuch-hello-hidden-sections
+					(delete ,title notmuch-hello-hidden-sections))
+				  (notmuch-hello-update))
+		       "show")
+      (widget-create 'push-button
+		     :notify `(lambda (widget &rest ignore)
+				(add-to-list 'notmuch-hello-hidden-sections
+					     ,title)
+				(notmuch-hello-update))
+		     "hide"))
+    (widget-insert "\n")
+    (let (target-pos
+	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
+      (when (and (not is-hidden)
+	       (or (not (plist-get options :hide-if-empty))
+		  searches))
+	(widget-insert "\n")
+	(setq target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	target-pos))))
+
+(defun notmuch-hello-insert-tags-section (&optional title &rest options)
+  "Insert a section displaying all tags with message counts.
+
+TITLE defaults to \"All tags\".
+Allowed options are those accepted by `notmuch-hello-insert-searches' and the
+following:
+
+:hide-tags - List of tags that should be excluded."
+  (apply 'notmuch-hello-insert-searches
+	 (or title "All tags")
+	 (notmuch-hello-generate-tag-alist (plist-get options :hide-tags))
+	 options))
+
+(defun notmuch-hello-insert-inbox ()
+  "Show an entry for each saved search and inboxed messages for each tag"
+  (notmuch-hello-insert-searches "What's in your inbox"
+				 (append
+				  (notmuch-saved-searches)
+				  (notmuch-hello-generate-tag-alist))
+				 :filter "tag:inbox"))
+
+(defun notmuch-hello-insert-alltags ()
+  "Insert a section displaying all tags and associated message counts"
+  (notmuch-hello-insert-tags-section
+   nil
+   :initially-hidden (not notmuch-show-all-tags-list)
+   :hide-tags notmuch-hello-hide-tags
+   :filter notmuch-hello-tag-list-make-query))
+
+(defun notmuch-hello-insert-footer ()
+  "Insert the notmuch-hello footer."
+  (let ((start (point)))
+    (widget-insert "Type a search query and hit RET to view matching threads.\n")
+    (when notmuch-search-history
+      (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
+      (widget-insert "Save recent searches with the `save' button.\n"))
+    (when notmuch-saved-searches
+      (widget-insert "Edit saved searches with the `edit' button.\n"))
+    (widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
+    (widget-insert "`=' to refresh this screen. `s' to search messages. `q' to quit.\n")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (customize-variable 'notmuch-hello-sections))
+		   :button-prefix "" :button-suffix ""
+		   "Customize")
+    (widget-insert " this page.")
+    (let ((fill-column (- (window-width) notmuch-hello-indent)))
+      (center-region start (point)))))
 
 ;;;###autoload
 (defun notmuch-hello (&optional no-display)
@@ -397,13 +768,13 @@ Complete list of currently available key bindings:
       (set-buffer "*notmuch-hello*")
     (switch-to-buffer "*notmuch-hello*"))
 
-  (let ((target (if (widget-at)
-		   (widget-value (widget-at))
-		 (condition-case nil
-		     (progn
-		       (widget-forward 1)
-		       (widget-value (widget-at)))
-		   (error nil))))
+  (let ((notmuch-hello-target (if (widget-at)
+				  (widget-value (widget-at))
+				(condition-case nil
+				    (progn
+				      (widget-forward 1)
+				      (widget-value (widget-at)))
+				  (error nil))))
 	(inhibit-read-only t))
 
     ;; Delete all editable widget fields.  Editable widget fields are
@@ -422,168 +793,20 @@ Complete list of currently available key bindings:
       (mapc 'delete-overlay (car all))
       (mapc 'delete-overlay (cdr all)))
 
-    (when notmuch-show-logo
-      (let ((image notmuch-hello-logo))
-	;; The notmuch logo uses transparency. That can display poorly
-	;; when inserting the image into an emacs buffer (black logo on
-	;; a black background), so force the background colour of the
-	;; image. We use a face to represent the colour so that
-	;; `defface' can be used to declare the different possible
-	;; colours, which depend on whether the frame has a light or
-	;; dark background.
-	(setq image (cons 'image
-			  (append (cdr image)
-				  (list :background (face-background 'notmuch-hello-logo-background)))))
-	(insert-image image))
-      (widget-insert "  "))
-
-    (widget-insert "Welcome to ")
-    ;; Hack the display of the links used.
-    (let ((widget-link-prefix "")
-	  (widget-link-suffix ""))
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (browse-url notmuch-hello-url))
-		     :help-echo "Visit the notmuch website."
-		     "notmuch")
-      (widget-insert ". ")
-      (widget-insert "You have ")
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (notmuch-hello-update))
-		     :help-echo "Refresh"
-		     (notmuch-hello-nice-number
-		      (string-to-number (car (process-lines notmuch-command "count")))))
-      (widget-insert " messages.\n"))
-
-    (let ((found-target-pos nil)
-	  (final-target-pos nil)
-	  (default-pos))
-      (let* ((saved-alist
-	      ;; Filter out empty saved searches if required.
-	      (if notmuch-show-empty-saved-searches
-		  notmuch-saved-searches
-		(loop for elem in notmuch-saved-searches
-		      if (> (string-to-number (notmuch-saved-search-count (cdr elem))) 0)
-		      collect elem)))
-	     (saved-widest (notmuch-hello-longest-label saved-alist))
-	     (alltags-alist (if notmuch-show-all-tags-list (notmuch-hello-generate-tag-alist)))
-	     (alltags-widest (notmuch-hello-longest-label alltags-alist))
-	     (widest (max saved-widest alltags-widest)))
-
-	(when saved-alist
-	  ;; Sort saved searches if required.
-	  (when notmuch-saved-search-sort-function
-	    (setq saved-alist
-		  (funcall notmuch-saved-search-sort-function saved-alist)))
-	  (widget-insert "\nSaved searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (customize-variable 'notmuch-saved-searches))
-			 "edit")
-	  (widget-insert "\n\n")
-	  (setq final-target-pos (point-marker))
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags saved-alist widest target))
-	    (if found-target-pos
-		(setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\nSearch: ")
-	(setq default-pos (point-marker))
-	(widget-create 'editable-field
-		       ;; Leave some space at the start and end of the
-		       ;; search boxes.
-		       :size (max 8 (- (window-width) notmuch-hello-indent
-				       (length "Search: ")))
-		       :action (lambda (widget &rest ignore)
-				 (notmuch-hello-search (widget-value widget))))
-	;; Add an invisible dot to make `widget-end-of-line' ignore
-	;; trailing spaces in the search widget field.  A dot is used
-	;; instead of a space to make `show-trailing-whitespace'
-	;; happy, i.e. avoid it marking the whole line as trailing
-	;; spaces.
-	(widget-insert ".")
-	(put-text-property (1- (point)) (point) 'invisible t)
-	(widget-insert "\n")
-
-	(when notmuch-search-history
-	  (widget-insert "\nRecent searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (setq notmuch-search-history nil)
-				   (notmuch-hello-update))
-			 "clear")
-	  (widget-insert "\n\n")
-	  (let ((start (point)))
-	    (loop for i from 1 to notmuch-hello-recent-searches-max
-		  for search in notmuch-search-history do
-		    (let ((widget-symbol (intern (format "notmuch-hello-search-%d" i))))
-		      (set widget-symbol
-			   (widget-create 'editable-field
-					  ;; Don't let the search boxes be
-					  ;; less than 8 characters wide.
-					  :size (max 8
-						     (- (window-width)
-							;; Leave some space
-							;; at the start and
-							;; end of the
-							;; boxes.
-							(* 2 notmuch-hello-indent)
-							;; 1 for the space
-							;; before the
-							;; `[save]' button. 6
-							;; for the `[save]'
-							;; button.
-							1 6))
-					  :action (lambda (widget &rest ignore)
-						    (notmuch-hello-search (widget-value widget)))
-					  search))
-		      (widget-insert " ")
-		      (widget-create 'push-button
-				     :notify (lambda (widget &rest ignore)
-					       (notmuch-hello-add-saved-search widget))
-				     :notmuch-saved-search-widget widget-symbol
-				     "save"))
-		    (widget-insert "\n"))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(when alltags-alist
-	  (widget-insert "\nAll tags: ")
-	  (widget-create 'push-button
-			 :notify (lambda (widget &rest ignore)
-				   (setq notmuch-show-all-tags-list nil)
-				   (notmuch-hello-update))
-			 "hide")
-	  (widget-insert "\n\n")
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags alltags-alist widest target))
-	    (unless final-target-pos
-	      (setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\n")
-
-	(unless notmuch-show-all-tags-list
-	  (widget-create 'push-button
-			 :notify (lambda (widget &rest ignore)
-				   (setq notmuch-show-all-tags-list t)
-				   (notmuch-hello-update))
-			 "Show all tags")))
-
-      (let ((start (point)))
-	(widget-insert "\n\n")
-	(widget-insert "Type a search query and hit RET to view matching threads.\n")
-	(when notmuch-search-history
-	  (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
-	  (widget-insert "Save recent searches with the `save' button.\n"))
-	(when notmuch-saved-searches
-	  (widget-insert "Edit saved searches with the `edit' button.\n"))
-	(widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
-	(widget-insert "`=' to refresh this screen. `s' to search messages. `q' to quit.\n")
-	(let ((fill-column (- (window-width) notmuch-hello-indent)))
-	  (center-region start (point))))
-
+    (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
@@ -592,9 +815,10 @@ Complete list of currently available key bindings:
 	  (widget-forward 1)))
 
       (unless (widget-at)
-	(goto-char default-pos))))
-
-  (run-hooks 'notmuch-hello-refresh-hook))
+	(when notmuch-hello-search-pos
+	  (goto-char notmuch-hello-search-pos)))))
+  (run-hooks 'notmuch-hello-refresh-hook)
+  (setq notmuch-hello-first-run nil))
 
 (defun notmuch-folder ()
   "Deprecated function for invoking notmuch---calling `notmuch' is preferred now."
-- 
1.7.9

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v9 2/2] emacs: Tests for user-defined sections
  2012-02-17  7:48   ` [PATCH v9 " Dmitry Kurochkin
  2012-02-17  7:48     ` [PATCH v9 1/2] " Dmitry Kurochkin
@ 2012-02-17  7:48     ` Dmitry Kurochkin
  1 sibling, 0 replies; 57+ messages in thread
From: Dmitry Kurochkin @ 2012-02-17  7:48 UTC (permalink / raw)
  To: notmuch; +Cc: Daniel Schoepe

From: Daniel Schoepe <daniel.schoepe@googlemail.com>

A new file was added for notmuch-hello tests.
---
 test/emacs-hello                                   |   47 ++++++++++++++++++++
 test/emacs.expected-output/notmuch-hello           |    3 +-
 .../notmuch-hello-new-section                      |    4 ++
 .../notmuch-hello-no-saved-searches                |    3 +-
 .../notmuch-hello-section-counts                   |    5 ++
 .../notmuch-hello-section-hidden-tag               |    4 ++
 .../notmuch-hello-section-with-empty               |    4 ++
 .../emacs.expected-output/notmuch-hello-with-empty |    3 +-
 test/notmuch-test                                  |    1 +
 9 files changed, 71 insertions(+), 3 deletions(-)
 create mode 100755 test/emacs-hello
 create mode 100644 test/emacs.expected-output/notmuch-hello-new-section
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-counts
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-hidden-tag
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-with-empty

diff --git a/test/emacs-hello b/test/emacs-hello
new file mode 100755
index 0000000..b235e3a
--- /dev/null
+++ b/test/emacs-hello
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+test_description="Testing emacs notmuch-hello view"
+. test-lib.sh
+
+EXPECTED=$TEST_DIRECTORY/emacs.expected-output
+
+add_email_corpus
+
+test_begin_subtest "User-defined section with inbox tag"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test\" '((\"inbox\" . \"tag:inbox\")))))))
+           (notmuch-hello)
+           (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-new-section
+
+test_begin_subtest "User-defined section with empty, hidden entry"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test-with-empty\"
+                                     '((\"inbox\" . \"tag:inbox\")
+                                       (\"doesnotexist\" . \"tag:doesnotexist\"))
+                                     :hide-empty-searches t)))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-with-empty
+
+test_begin_subtest "User-defined section, unread tag filtered out"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-filtered\"
+                                     :hide-tags '(\"unread\"))))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-hidden-tag
+
+test_begin_subtest "User-defined section, different query for counts"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-counts\"
+                                     :filter-count \"tag:signed\")))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-counts
+
+test_done
diff --git a/test/emacs.expected-output/notmuch-hello b/test/emacs.expected-output/notmuch-hello
index 3e59595..1470790 100644
--- a/test/emacs.expected-output/notmuch-hello
+++ b/test/emacs.expected-output/notmuch-hello
@@ -6,9 +6,10 @@ Saved searches: [edit]
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
       `=' to refresh this screen. `s' to search messages. `q' to quit.
+			    Customize this page.
diff --git a/test/emacs.expected-output/notmuch-hello-new-section b/test/emacs.expected-output/notmuch-hello-new-section
new file mode 100644
index 0000000..c64d712
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-new-section
@@ -0,0 +1,4 @@
+Test: [hide]
+
+	  52 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-no-saved-searches b/test/emacs.expected-output/notmuch-hello-no-saved-searches
index ef0e5d0..05475b1 100644
--- a/test/emacs.expected-output/notmuch-hello-no-saved-searches
+++ b/test/emacs.expected-output/notmuch-hello-no-saved-searches
@@ -2,9 +2,10 @@
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
       `=' to refresh this screen. `s' to search messages. `q' to quit.
+			    Customize this page.
diff --git a/test/emacs.expected-output/notmuch-hello-section-counts b/test/emacs.expected-output/notmuch-hello-section-counts
new file mode 100644
index 0000000..9d79659
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-counts
@@ -0,0 +1,5 @@
+Test-with-counts: [hide]
+
+	   2 attachment             7 signed          
+	   7 inbox                  7 unread          
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-hidden-tag b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
new file mode 100644
index 0000000..3688e7c
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
@@ -0,0 +1,4 @@
+Test-with-filtered: [hide]
+
+	   4 attachment            52 inbox                  7 signed 
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-with-empty b/test/emacs.expected-output/notmuch-hello-section-with-empty
new file mode 100644
index 0000000..8209fed
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-with-empty
@@ -0,0 +1,4 @@
+Test-with-empty: [hide]
+
+	  52 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-with-empty b/test/emacs.expected-output/notmuch-hello-with-empty
index 71edba7..5e53222 100644
--- a/test/emacs.expected-output/notmuch-hello-with-empty
+++ b/test/emacs.expected-output/notmuch-hello-with-empty
@@ -6,9 +6,10 @@ Saved searches: [edit]
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
       `=' to refresh this screen. `s' to search messages. `q' to quit.
+			    Customize this page.
diff --git a/test/notmuch-test b/test/notmuch-test
index e14d34e..f03b594 100755
--- a/test/notmuch-test
+++ b/test/notmuch-test
@@ -54,6 +54,7 @@ TESTS="
   argument-parsing
   emacs-test-functions
   emacs-address-cleaning
+  emacs-hello
   emacs-show
 "
 TESTS=${NOTMUCH_TESTS:=$TESTS}
-- 
1.7.9

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello
  2011-07-07 22:53 ` [PATCH v4 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
                     ` (3 preceding siblings ...)
  2012-02-17  7:48   ` [PATCH v9 " Dmitry Kurochkin
@ 2012-02-17 14:48   ` Dmitry Kurochkin
  2012-02-17 14:48     ` [PATCH v10 1/2] " Dmitry Kurochkin
                       ` (2 more replies)
  4 siblings, 3 replies; 57+ messages in thread
From: Dmitry Kurochkin @ 2012-02-17 14:48 UTC (permalink / raw)
  To: notmuch

Found a small bug...

Changes:

v10:

* explicitly return nil from `notmuch-hello-insert-recent-searches',
  otherwise `indent-rigidly' returns a marker pointing to non-existing
  buffer and breaks final-target-pos handling in `notmuch-hello'

v9:

* correctly merge bc267b70b01c79f6bdda52641e9cd7574a151eff

* fix :hide-if-empty description:

    s/and not hidden by show-if-empty/and not shown by show-empty-searches/

* fix typo in `notmuch-hello-target' docstring: s/globaly/global/

* add docstring for notmuch-hello-first-run

* fix indentation in `notmuch-hello-insert-saved-searches'

* fix indentation in `notmuch-hello-insert-searches' docstring

* fix target position calculation in `notmuch-hello-insert-saved-searches'

* s/User defined/User-defined/ in tests

* move tests to a new emacs-hello file

Regards,
  Dmitry

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH v10 1/2] emacs: User-defined sections in notmuch-hello
  2012-02-17 14:48   ` [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello Dmitry Kurochkin
@ 2012-02-17 14:48     ` Dmitry Kurochkin
  2012-03-01 12:36       ` David Bremner
  2012-02-17 14:48     ` [PATCH v10 2/2] emacs: Tests for user-defined sections Dmitry Kurochkin
  2012-02-18 22:10     ` [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello Michal Sojka
  2 siblings, 1 reply; 57+ messages in thread
From: Dmitry Kurochkin @ 2012-02-17 14:48 UTC (permalink / raw)
  To: notmuch; +Cc: Daniel Schoepe

From: Daniel Schoepe <daniel.schoepe@googlemail.com>

This patch makes the notmuch-hello screen fully customizable
by allowing the user to add and remove arbitrary sections. It
also provides some convenience functions for constructing sections,
e.g. showing the unread message count for each tag.

This is done by specifying a list of functions that will be run
when notmuch-hello is invoked.
---
 emacs/notmuch-hello.el |  625 ++++++++++++++++++++++++++++++++---------------
 1 files changed, 425 insertions(+), 200 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index d17a30f..aad373d 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -154,6 +154,108 @@ 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"))
+    (:initially-hidden (const :tag "Hide this section on startup" t))
+    (:show-empty-searches (const :tag "Show queries with no matching messages" t))
+    (:hide-if-empty (const :tag "Hide this section if all queries are empty
+\(and not shown by show-empty-searches)" t)))
+  "Various customization-options for notmuch-hello-tags/query-section.")
+
+(define-widget 'notmuch-hello-tags-section 'lazy
+  "Customize-type for notmuch-hello tag-list sections."
+  :tag "Customized tag-list section (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-tags-section)
+	 (string :tag "Title for this section")
+	 (plist
+	  :inline t
+	  :options
+	  ,(append notmuch-hello-custom-section-options
+		   '((:hide-tags (repeat :tag "Tags that will be hidden"
+					 string)))))))
+
+(define-widget 'notmuch-hello-query-section 'lazy
+  "Customize-type for custom saved-search-like sections"
+  :tag "Customized queries section (see docstring for details)"
+  :type
+  `(list :tag ""
+	 (const :tag "" notmuch-hello-insert-query-list)
+	 (string :tag "Title for this section")
+	 (repeat :tag "Queries"
+		 (cons (string :tag "Name") (string :tag "Query")))
+	 (plist :inline t :options ,notmuch-hello-custom-section-options)))
+
+(defcustom notmuch-hello-sections
+  (list #'notmuch-hello-insert-header
+	#'notmuch-hello-insert-saved-searches
+	#'notmuch-hello-insert-search
+	#'notmuch-hello-insert-recent-searches
+	#'notmuch-hello-insert-alltags
+	#'notmuch-hello-insert-footer)
+  "Sections for notmuch-hello.
+
+The list contains functions which are used to construct sections in
+notmuch-hello buffer.  When notmuch-hello buffer is constructed,
+these functions are run in the order they appear in this list.  Each
+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.
+
+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
+list.
+
+A \"Customized tag-list section\" item in the customize-interface
+displays a list of all tags, optionally hiding some of them. It
+is also possible to filter the list of messages matching each tag
+by an additional filter query. Similarly, the count of messages
+displayed next to the buttons can be generated by applying a
+different filter to the tag query. These filters are also
+supported for \"Customized queries section\" items."
+  :group 'notmuch
+  :type
+  '(repeat
+    (choice (function-item notmuch-hello-insert-header)
+	    (function-item notmuch-hello-insert-saved-searches)
+	    (function-item notmuch-hello-insert-search)
+	    (function-item notmuch-hello-insert-recent-searches)
+	    (function-item notmuch-hello-insert-alltags)
+	    (function-item notmuch-hello-insert-footer)
+	    (function-item notmuch-hello-insert-inbox)
+	    notmuch-hello-tags-section
+	    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")
+
+(defvar notmuch-hello-first-run t
+  "True if `notmuch-hello' is run for the first time, set to nil
+afterwards.")
+
 (defun notmuch-hello-nice-number (n)
   (let (result)
     (while (> n 0)
@@ -201,8 +303,8 @@ International Bureau of Weights and Measures."
     (message "Saved '%s' as '%s'." search name)
     (notmuch-hello-update)))
 
-(defun notmuch-hello-longest-label (tag-alist)
-  (or (loop for elem in tag-alist
+(defun notmuch-hello-longest-label (searches-alist)
+  (or (loop for elem in searches-alist
 	    maximize (length (car elem)))
       0))
 
@@ -266,12 +368,70 @@ should be. Returns a cons cell `(tags-per-line width)'."
 				   (* tags-per-line (+ 9 1))))
 			   tags-per-line))))
 
-(defun notmuch-hello-insert-tags (tag-alist widest target)
-  (let* ((tags-and-width (notmuch-hello-tags-per-line widest))
+(defun notmuch-hello-filtered-query (query filter)
+  "Constructs a query to search all messages matching QUERY and FILTER.
+
+If FILTER is a string, it is directly used in the returned query.
+
+If FILTER is a function, it is called with QUERY as a parameter and
+the string it returns is used as the query. If nil is returned,
+the entry is hidden.
+
+Otherwise, FILTER is ignored.
+"
+  (cond
+   ((functionp filter) (funcall filter query))
+   ((stringp filter)
+    (concat "(" query ") and (" filter ")"))
+   (t query)))
+
+(defun notmuch-hello-query-counts (query-alist &rest options)
+  "Compute list of counts of matched messages from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated query.
+
+The result is the list of elements of the form (NAME QUERY COUNT).
+
+The values :show-empty-searches, :filter and :filter-count from
+options will be handled as specified for
+`notmuch-hello-insert-searches'."
+  (notmuch-remove-if-not
+   #'identity
+   (mapcar
+    (lambda (elem)
+      (let* ((name (car elem))
+	     (query-and-count (if (consp (cdr elem))
+				  ;; do we have a different query for the message count?
+				  (cons (second elem) (third elem))
+				(cons (cdr elem) (cdr elem))))
+	     (message-count
+	      (string-to-number
+	       (notmuch-saved-search-count
+		(notmuch-hello-filtered-query (cdr query-and-count)
+					      (or (plist-get options :filter-count)
+						 (plist-get options :filter)))))))
+	(and (or (plist-get options :show-empty-searches) (> message-count 0))
+	     (list name (notmuch-hello-filtered-query
+			 (car query-and-count) (plist-get options :filter))
+		   message-count))))
+    query-alist)))
+
+(defun notmuch-hello-insert-buttons (searches)
+  "Insert buttons for SEARCHES.
+
+SEARCHES must be a list containing lists of the form (NAME QUERY COUNT), where
+QUERY is the query to start when the button for the corresponding entry is
+activated. COUNT should be the number of messages matching the query.
+Such a list can be computed with `notmuch-hello-query-counts'."
+  (let* ((widest (notmuch-hello-longest-label searches))
+	 (tags-and-width (notmuch-hello-tags-per-line widest))
 	 (tags-per-line (car tags-and-width))
 	 (widest (cdr tags-and-width))
 	 (count 0)
-	 (reordered-list (notmuch-hello-reflect tag-alist tags-per-line))
+	 (reordered-list (notmuch-hello-reflect searches tags-per-line))
 	 ;; Hack the display of the buttons used.
 	 (widget-push-button-prefix "")
 	 (widget-push-button-suffix "")
@@ -281,13 +441,13 @@ should be. Returns a cons cell `(tags-per-line width)'."
     (mapc (lambda (elem)
 	    ;; (not elem) indicates an empty slot in the matrix.
 	    (when elem
-	      (let* ((name (car elem))
-		     (query (cdr elem))
+	      (let* ((name (first elem))
+		     (query (second elem))
+		     (msg-count (third elem))
 		     (formatted-name (format "%s " name)))
 		(widget-insert (format "%8s "
-				       (notmuch-hello-nice-number
-					(string-to-number (notmuch-saved-search-count query)))))
-		(if (string= formatted-name target)
+				       (notmuch-hello-nice-number msg-count)))
+		(if (string= formatted-name notmuch-hello-target)
 		    (setq found-target-pos (point-marker)))
 		(widget-create 'push-button
 			       :notify #'notmuch-hello-widget-search
@@ -359,29 +519,241 @@ Complete list of currently available key bindings:
  (kill-all-local-variables)
  (use-local-map notmuch-hello-mode-map)
  (setq major-mode 'notmuch-hello-mode
-       mode-name "notmuch-hello")
+	mode-name "notmuch-hello")
  (run-mode-hooks 'notmuch-hello-mode-hook)
  ;;(setq buffer-read-only t)
 )
 
-(defun notmuch-hello-generate-tag-alist ()
+(defun notmuch-hello-generate-tag-alist (&optional hide-tags)
   "Return an alist from tags to queries to display in the all-tags section."
-  (notmuch-remove-if-not
-   #'cdr
-   (mapcar (lambda (tag)
-	     (cons tag
-		   (cond
-		    ((functionp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     (funcall notmuch-hello-tag-list-make-query tag) ")"))
-		    ((stringp notmuch-hello-tag-list-make-query)
-		     (concat "tag:" tag " and ("
-			     notmuch-hello-tag-list-make-query ")"))
-		    (t (concat "tag:" tag)))))
-	   (notmuch-remove-if-not
-	    (lambda (tag)
-	      (not (member tag notmuch-hello-hide-tags)))
-	    (process-lines notmuch-command "search-tags")))))
+  (mapcar (lambda (tag)
+	    (cons tag (format "tag:%s" tag)))
+	  (notmuch-remove-if-not
+	   (lambda (tag)
+	     (not (member tag hide-tags)))
+	   (process-lines notmuch-command "search-tags"))))
+
+(defun notmuch-hello-insert-header ()
+  "Insert the default notmuch-hello header."
+  (when notmuch-show-logo
+    (let ((image notmuch-hello-logo))
+      ;; The notmuch logo uses transparency. That can display poorly
+      ;; when inserting the image into an emacs buffer (black logo on
+      ;; a black background), so force the background colour of the
+      ;; image. We use a face to represent the colour so that
+      ;; `defface' can be used to declare the different possible
+      ;; colours, which depend on whether the frame has a light or
+      ;; dark background.
+      (setq image (cons 'image
+			(append (cdr image)
+				(list :background (face-background 'notmuch-hello-logo-background)))))
+      (insert-image image))
+    (widget-insert "  "))
+
+  (widget-insert "Welcome to ")
+  ;; Hack the display of the links used.
+  (let ((widget-link-prefix "")
+	(widget-link-suffix ""))
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (browse-url notmuch-hello-url))
+		   :help-echo "Visit the notmuch website."
+		   "notmuch")
+    (widget-insert ". ")
+    (widget-insert "You have ")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (notmuch-hello-update))
+		   :help-echo "Refresh"
+		   (notmuch-hello-nice-number
+		    (string-to-number (car (process-lines notmuch-command "count")))))
+    (widget-insert " messages.\n")))
+
+
+(defun notmuch-hello-insert-saved-searches ()
+  "Insert the saved-searches section."
+  (let ((searches (notmuch-hello-query-counts
+		   (if notmuch-saved-search-sort-function
+		       (funcall notmuch-saved-search-sort-function
+				notmuch-saved-searches)
+		     notmuch-saved-searches)
+		   :show-empty-searches notmuch-show-empty-saved-searches))
+	found-target-pos)
+    (when searches
+      (widget-insert "Saved searches: ")
+      (widget-create 'push-button
+		     :notify (lambda (&rest ignore)
+			       (customize-variable 'notmuch-saved-searches))
+		     "edit")
+      (widget-insert "\n\n")
+      (let ((start (point)))
+	(setq found-target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	found-target-pos))))
+
+(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.
+		 :size (max 8 (- (window-width) notmuch-hello-indent
+				 (length "Search: ")))
+		 :action (lambda (widget &rest ignore)
+			   (notmuch-hello-search (widget-value widget))))
+  ;; Add an invisible dot to make `widget-end-of-line' ignore
+  ;; trailing spaces in the search widget field.  A dot is used
+  ;; instead of a space to make `show-trailing-whitespace'
+  ;; happy, i.e. avoid it marking the whole line as trailing
+  ;; spaces.
+  (widget-insert ".")
+  (put-text-property (1- (point)) (point) 'invisible t)
+  (widget-insert "\n"))
+
+(defun notmuch-hello-insert-recent-searches ()
+  "Insert recent searches."
+  (when notmuch-search-history
+    (widget-insert "Recent searches: ")
+    (widget-create 'push-button
+		   :notify (lambda (&rest ignore)
+			     (setq notmuch-search-history nil)
+			     (notmuch-hello-update))
+		   "clear")
+    (widget-insert "\n\n")
+    (let ((start (point)))
+      (loop for i from 1 to notmuch-hello-recent-searches-max
+	    for search in notmuch-search-history do
+	    (let ((widget-symbol (intern (format "notmuch-hello-search-%d" i))))
+	      (set widget-symbol
+		   (widget-create 'editable-field
+				  ;; Don't let the search boxes be
+				  ;; less than 8 characters wide.
+				  :size (max 8
+					     (- (window-width)
+						;; Leave some space
+						;; at the start and
+						;; end of the
+						;; boxes.
+						(* 2 notmuch-hello-indent)
+						;; 1 for the space
+						;; before the
+						;; `[save]' button. 6
+						;; for the `[save]'
+						;; button.
+						1 6))
+				  :action (lambda (widget &rest ignore)
+					    (notmuch-hello-search (widget-value widget)))
+				  search))
+	      (widget-insert " ")
+	      (widget-create 'push-button
+			     :notify (lambda (widget &rest ignore)
+				       (notmuch-hello-add-saved-search widget))
+			     :notmuch-saved-search-widget widget-symbol
+			     "save"))
+	    (widget-insert "\n"))
+      (indent-rigidly start (point) notmuch-hello-indent))
+    nil))
+
+(defun notmuch-hello-insert-searches (title query-alist &rest options)
+  "Insert a section with TITLE showing a list of buttons made from QUERY-ALIST.
+
+QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
+or (NAME QUERY COUNT-QUERY). If the latter form is used,
+COUNT-QUERY specifies an alternate query to be used to generate
+the count for the associated item.
+
+Supports the following entries in OPTIONS as a plist:
+:initially-hidden - if non-nil, section will be hidden on startup
+:show-empty-searches - show buttons with no matching messages
+:hide-if-empty - hide if no buttons would be shown
+   (only makes sense without :show-empty-searches)
+:filter - This can be a function that takes the search query as its argument and
+   returns a filter to be used in conjuction with the query for that search or nil
+   to hide the element. This can also be a string that is used as a combined with
+   each query using \"and\".
+:filter-count - Separate filter to generate the count displayed each search. Accepts
+   the same values as :filter. If :filter and :filter-count are specified, this
+   will be used instead of :filter, not in conjunction with it."
+  (widget-insert title ": ")
+  (if (and notmuch-hello-first-run (plist-get options :initially-hidden))
+      (add-to-list 'notmuch-hello-hidden-sections title))
+  (let ((is-hidden (member title notmuch-hello-hidden-sections))
+	(start (point)))
+    (if is-hidden
+	(widget-create 'push-button
+		       :notify `(lambda (widget &rest ignore)
+				  (setq notmuch-hello-hidden-sections
+					(delete ,title notmuch-hello-hidden-sections))
+				  (notmuch-hello-update))
+		       "show")
+      (widget-create 'push-button
+		     :notify `(lambda (widget &rest ignore)
+				(add-to-list 'notmuch-hello-hidden-sections
+					     ,title)
+				(notmuch-hello-update))
+		     "hide"))
+    (widget-insert "\n")
+    (let (target-pos
+	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
+      (when (and (not is-hidden)
+	       (or (not (plist-get options :hide-if-empty))
+		  searches))
+	(widget-insert "\n")
+	(setq target-pos
+	      (notmuch-hello-insert-buttons searches))
+	(indent-rigidly start (point) notmuch-hello-indent)
+	target-pos))))
+
+(defun notmuch-hello-insert-tags-section (&optional title &rest options)
+  "Insert a section displaying all tags with message counts.
+
+TITLE defaults to \"All tags\".
+Allowed options are those accepted by `notmuch-hello-insert-searches' and the
+following:
+
+:hide-tags - List of tags that should be excluded."
+  (apply 'notmuch-hello-insert-searches
+	 (or title "All tags")
+	 (notmuch-hello-generate-tag-alist (plist-get options :hide-tags))
+	 options))
+
+(defun notmuch-hello-insert-inbox ()
+  "Show an entry for each saved search and inboxed messages for each tag"
+  (notmuch-hello-insert-searches "What's in your inbox"
+				 (append
+				  (notmuch-saved-searches)
+				  (notmuch-hello-generate-tag-alist))
+				 :filter "tag:inbox"))
+
+(defun notmuch-hello-insert-alltags ()
+  "Insert a section displaying all tags and associated message counts"
+  (notmuch-hello-insert-tags-section
+   nil
+   :initially-hidden (not notmuch-show-all-tags-list)
+   :hide-tags notmuch-hello-hide-tags
+   :filter notmuch-hello-tag-list-make-query))
+
+(defun notmuch-hello-insert-footer ()
+  "Insert the notmuch-hello footer."
+  (let ((start (point)))
+    (widget-insert "Type a search query and hit RET to view matching threads.\n")
+    (when notmuch-search-history
+      (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
+      (widget-insert "Save recent searches with the `save' button.\n"))
+    (when notmuch-saved-searches
+      (widget-insert "Edit saved searches with the `edit' button.\n"))
+    (widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
+    (widget-insert "`=' to refresh this screen. `s' to search messages. `q' to quit.\n")
+    (widget-create 'link
+		   :notify (lambda (&rest ignore)
+			     (customize-variable 'notmuch-hello-sections))
+		   :button-prefix "" :button-suffix ""
+		   "Customize")
+    (widget-insert " this page.")
+    (let ((fill-column (- (window-width) notmuch-hello-indent)))
+      (center-region start (point)))))
 
 ;;;###autoload
 (defun notmuch-hello (&optional no-display)
@@ -397,13 +769,13 @@ Complete list of currently available key bindings:
       (set-buffer "*notmuch-hello*")
     (switch-to-buffer "*notmuch-hello*"))
 
-  (let ((target (if (widget-at)
-		   (widget-value (widget-at))
-		 (condition-case nil
-		     (progn
-		       (widget-forward 1)
-		       (widget-value (widget-at)))
-		   (error nil))))
+  (let ((notmuch-hello-target (if (widget-at)
+				  (widget-value (widget-at))
+				(condition-case nil
+				    (progn
+				      (widget-forward 1)
+				      (widget-value (widget-at)))
+				  (error nil))))
 	(inhibit-read-only t))
 
     ;; Delete all editable widget fields.  Editable widget fields are
@@ -422,168 +794,20 @@ Complete list of currently available key bindings:
       (mapc 'delete-overlay (car all))
       (mapc 'delete-overlay (cdr all)))
 
-    (when notmuch-show-logo
-      (let ((image notmuch-hello-logo))
-	;; The notmuch logo uses transparency. That can display poorly
-	;; when inserting the image into an emacs buffer (black logo on
-	;; a black background), so force the background colour of the
-	;; image. We use a face to represent the colour so that
-	;; `defface' can be used to declare the different possible
-	;; colours, which depend on whether the frame has a light or
-	;; dark background.
-	(setq image (cons 'image
-			  (append (cdr image)
-				  (list :background (face-background 'notmuch-hello-logo-background)))))
-	(insert-image image))
-      (widget-insert "  "))
-
-    (widget-insert "Welcome to ")
-    ;; Hack the display of the links used.
-    (let ((widget-link-prefix "")
-	  (widget-link-suffix ""))
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (browse-url notmuch-hello-url))
-		     :help-echo "Visit the notmuch website."
-		     "notmuch")
-      (widget-insert ". ")
-      (widget-insert "You have ")
-      (widget-create 'link
-		     :notify (lambda (&rest ignore)
-			       (notmuch-hello-update))
-		     :help-echo "Refresh"
-		     (notmuch-hello-nice-number
-		      (string-to-number (car (process-lines notmuch-command "count")))))
-      (widget-insert " messages.\n"))
-
-    (let ((found-target-pos nil)
-	  (final-target-pos nil)
-	  (default-pos))
-      (let* ((saved-alist
-	      ;; Filter out empty saved searches if required.
-	      (if notmuch-show-empty-saved-searches
-		  notmuch-saved-searches
-		(loop for elem in notmuch-saved-searches
-		      if (> (string-to-number (notmuch-saved-search-count (cdr elem))) 0)
-		      collect elem)))
-	     (saved-widest (notmuch-hello-longest-label saved-alist))
-	     (alltags-alist (if notmuch-show-all-tags-list (notmuch-hello-generate-tag-alist)))
-	     (alltags-widest (notmuch-hello-longest-label alltags-alist))
-	     (widest (max saved-widest alltags-widest)))
-
-	(when saved-alist
-	  ;; Sort saved searches if required.
-	  (when notmuch-saved-search-sort-function
-	    (setq saved-alist
-		  (funcall notmuch-saved-search-sort-function saved-alist)))
-	  (widget-insert "\nSaved searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (customize-variable 'notmuch-saved-searches))
-			 "edit")
-	  (widget-insert "\n\n")
-	  (setq final-target-pos (point-marker))
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags saved-alist widest target))
-	    (if found-target-pos
-		(setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\nSearch: ")
-	(setq default-pos (point-marker))
-	(widget-create 'editable-field
-		       ;; Leave some space at the start and end of the
-		       ;; search boxes.
-		       :size (max 8 (- (window-width) notmuch-hello-indent
-				       (length "Search: ")))
-		       :action (lambda (widget &rest ignore)
-				 (notmuch-hello-search (widget-value widget))))
-	;; Add an invisible dot to make `widget-end-of-line' ignore
-	;; trailing spaces in the search widget field.  A dot is used
-	;; instead of a space to make `show-trailing-whitespace'
-	;; happy, i.e. avoid it marking the whole line as trailing
-	;; spaces.
-	(widget-insert ".")
-	(put-text-property (1- (point)) (point) 'invisible t)
-	(widget-insert "\n")
-
-	(when notmuch-search-history
-	  (widget-insert "\nRecent searches: ")
-	  (widget-create 'push-button
-			 :notify (lambda (&rest ignore)
-				   (setq notmuch-search-history nil)
-				   (notmuch-hello-update))
-			 "clear")
-	  (widget-insert "\n\n")
-	  (let ((start (point)))
-	    (loop for i from 1 to notmuch-hello-recent-searches-max
-		  for search in notmuch-search-history do
-		    (let ((widget-symbol (intern (format "notmuch-hello-search-%d" i))))
-		      (set widget-symbol
-			   (widget-create 'editable-field
-					  ;; Don't let the search boxes be
-					  ;; less than 8 characters wide.
-					  :size (max 8
-						     (- (window-width)
-							;; Leave some space
-							;; at the start and
-							;; end of the
-							;; boxes.
-							(* 2 notmuch-hello-indent)
-							;; 1 for the space
-							;; before the
-							;; `[save]' button. 6
-							;; for the `[save]'
-							;; button.
-							1 6))
-					  :action (lambda (widget &rest ignore)
-						    (notmuch-hello-search (widget-value widget)))
-					  search))
-		      (widget-insert " ")
-		      (widget-create 'push-button
-				     :notify (lambda (widget &rest ignore)
-					       (notmuch-hello-add-saved-search widget))
-				     :notmuch-saved-search-widget widget-symbol
-				     "save"))
-		    (widget-insert "\n"))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(when alltags-alist
-	  (widget-insert "\nAll tags: ")
-	  (widget-create 'push-button
-			 :notify (lambda (widget &rest ignore)
-				   (setq notmuch-show-all-tags-list nil)
-				   (notmuch-hello-update))
-			 "hide")
-	  (widget-insert "\n\n")
-	  (let ((start (point)))
-	    (setq found-target-pos (notmuch-hello-insert-tags alltags-alist widest target))
-	    (unless final-target-pos
-	      (setq final-target-pos found-target-pos))
-	    (indent-rigidly start (point) notmuch-hello-indent)))
-
-	(widget-insert "\n")
-
-	(unless notmuch-show-all-tags-list
-	  (widget-create 'push-button
-			 :notify (lambda (widget &rest ignore)
-				   (setq notmuch-show-all-tags-list t)
-				   (notmuch-hello-update))
-			 "Show all tags")))
-
-      (let ((start (point)))
-	(widget-insert "\n\n")
-	(widget-insert "Type a search query and hit RET to view matching threads.\n")
-	(when notmuch-search-history
-	  (widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
-	  (widget-insert "Save recent searches with the `save' button.\n"))
-	(when notmuch-saved-searches
-	  (widget-insert "Edit saved searches with the `edit' button.\n"))
-	(widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
-	(widget-insert "`=' to refresh this screen. `s' to search messages. `q' to quit.\n")
-	(let ((fill-column (- (window-width) notmuch-hello-indent)))
-	  (center-region start (point))))
-
+    (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
@@ -592,9 +816,10 @@ Complete list of currently available key bindings:
 	  (widget-forward 1)))
 
       (unless (widget-at)
-	(goto-char default-pos))))
-
-  (run-hooks 'notmuch-hello-refresh-hook))
+	(when notmuch-hello-search-pos
+	  (goto-char notmuch-hello-search-pos)))))
+  (run-hooks 'notmuch-hello-refresh-hook)
+  (setq notmuch-hello-first-run nil))
 
 (defun notmuch-folder ()
   "Deprecated function for invoking notmuch---calling `notmuch' is preferred now."
-- 
1.7.9

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [PATCH v10 2/2] emacs: Tests for user-defined sections
  2012-02-17 14:48   ` [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello Dmitry Kurochkin
  2012-02-17 14:48     ` [PATCH v10 1/2] " Dmitry Kurochkin
@ 2012-02-17 14:48     ` Dmitry Kurochkin
  2012-02-18 22:10     ` [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello Michal Sojka
  2 siblings, 0 replies; 57+ messages in thread
From: Dmitry Kurochkin @ 2012-02-17 14:48 UTC (permalink / raw)
  To: notmuch; +Cc: Daniel Schoepe

From: Daniel Schoepe <daniel.schoepe@googlemail.com>

A new file was added for notmuch-hello tests.
---
 test/emacs-hello                                   |   47 ++++++++++++++++++++
 test/emacs.expected-output/notmuch-hello           |    3 +-
 .../notmuch-hello-new-section                      |    4 ++
 .../notmuch-hello-no-saved-searches                |    3 +-
 .../notmuch-hello-section-counts                   |    5 ++
 .../notmuch-hello-section-hidden-tag               |    4 ++
 .../notmuch-hello-section-with-empty               |    4 ++
 .../emacs.expected-output/notmuch-hello-with-empty |    3 +-
 test/notmuch-test                                  |    1 +
 9 files changed, 71 insertions(+), 3 deletions(-)
 create mode 100755 test/emacs-hello
 create mode 100644 test/emacs.expected-output/notmuch-hello-new-section
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-counts
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-hidden-tag
 create mode 100644 test/emacs.expected-output/notmuch-hello-section-with-empty

diff --git a/test/emacs-hello b/test/emacs-hello
new file mode 100755
index 0000000..b235e3a
--- /dev/null
+++ b/test/emacs-hello
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+test_description="Testing emacs notmuch-hello view"
+. test-lib.sh
+
+EXPECTED=$TEST_DIRECTORY/emacs.expected-output
+
+add_email_corpus
+
+test_begin_subtest "User-defined section with inbox tag"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test\" '((\"inbox\" . \"tag:inbox\")))))))
+           (notmuch-hello)
+           (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-new-section
+
+test_begin_subtest "User-defined section with empty, hidden entry"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test-with-empty\"
+                                     '((\"inbox\" . \"tag:inbox\")
+                                       (\"doesnotexist\" . \"tag:doesnotexist\"))
+                                     :hide-empty-searches t)))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-with-empty
+
+test_begin_subtest "User-defined section, unread tag filtered out"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-filtered\"
+                                     :hide-tags '(\"unread\"))))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-hidden-tag
+
+test_begin_subtest "User-defined section, different query for counts"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-counts\"
+                                     :filter-count \"tag:signed\")))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-counts
+
+test_done
diff --git a/test/emacs.expected-output/notmuch-hello b/test/emacs.expected-output/notmuch-hello
index 3e59595..1470790 100644
--- a/test/emacs.expected-output/notmuch-hello
+++ b/test/emacs.expected-output/notmuch-hello
@@ -6,9 +6,10 @@ Saved searches: [edit]
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
       `=' to refresh this screen. `s' to search messages. `q' to quit.
+			    Customize this page.
diff --git a/test/emacs.expected-output/notmuch-hello-new-section b/test/emacs.expected-output/notmuch-hello-new-section
new file mode 100644
index 0000000..c64d712
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-new-section
@@ -0,0 +1,4 @@
+Test: [hide]
+
+	  52 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-no-saved-searches b/test/emacs.expected-output/notmuch-hello-no-saved-searches
index ef0e5d0..05475b1 100644
--- a/test/emacs.expected-output/notmuch-hello-no-saved-searches
+++ b/test/emacs.expected-output/notmuch-hello-no-saved-searches
@@ -2,9 +2,10 @@
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
       `=' to refresh this screen. `s' to search messages. `q' to quit.
+			    Customize this page.
diff --git a/test/emacs.expected-output/notmuch-hello-section-counts b/test/emacs.expected-output/notmuch-hello-section-counts
new file mode 100644
index 0000000..9d79659
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-counts
@@ -0,0 +1,5 @@
+Test-with-counts: [hide]
+
+	   2 attachment             7 signed          
+	   7 inbox                  7 unread          
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-hidden-tag b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
new file mode 100644
index 0000000..3688e7c
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-hidden-tag
@@ -0,0 +1,4 @@
+Test-with-filtered: [hide]
+
+	   4 attachment            52 inbox                  7 signed 
+
diff --git a/test/emacs.expected-output/notmuch-hello-section-with-empty b/test/emacs.expected-output/notmuch-hello-section-with-empty
new file mode 100644
index 0000000..8209fed
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-hello-section-with-empty
@@ -0,0 +1,4 @@
+Test-with-empty: [hide]
+
+	  52 inbox  
+
diff --git a/test/emacs.expected-output/notmuch-hello-with-empty b/test/emacs.expected-output/notmuch-hello-with-empty
index 71edba7..5e53222 100644
--- a/test/emacs.expected-output/notmuch-hello-with-empty
+++ b/test/emacs.expected-output/notmuch-hello-with-empty
@@ -6,9 +6,10 @@ Saved searches: [edit]
 
 Search:                                                                     .
 
-[Show all tags]
+All tags: [show]
 
 	 Type a search query and hit RET to view matching threads.
 		Edit saved searches with the `edit' button.
   Hit RET or click on a saved search or tag name to view matching threads.
       `=' to refresh this screen. `s' to search messages. `q' to quit.
+			    Customize this page.
diff --git a/test/notmuch-test b/test/notmuch-test
index e14d34e..f03b594 100755
--- a/test/notmuch-test
+++ b/test/notmuch-test
@@ -54,6 +54,7 @@ TESTS="
   argument-parsing
   emacs-test-functions
   emacs-address-cleaning
+  emacs-hello
   emacs-show
 "
 TESTS=${NOTMUCH_TESTS:=$TESTS}
-- 
1.7.9

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello
  2012-02-17 14:48   ` [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello Dmitry Kurochkin
  2012-02-17 14:48     ` [PATCH v10 1/2] " Dmitry Kurochkin
  2012-02-17 14:48     ` [PATCH v10 2/2] emacs: Tests for user-defined sections Dmitry Kurochkin
@ 2012-02-18 22:10     ` Michal Sojka
  2012-02-18 22:12       ` [PATCH] emacs-hello: Do not calculate the count of the messages in hidden sections Michal Sojka
  2 siblings, 1 reply; 57+ messages in thread
From: Michal Sojka @ 2012-02-18 22:10 UTC (permalink / raw)
  To: Dmitry Kurochkin, notmuch

On Fri, 17 Feb 2012, Dmitry Kurochkin wrote:
> Found a small bug...
> 
> Changes:
> 
> v10:
> 
> * explicitly return nil from `notmuch-hello-insert-recent-searches',
>   otherwise `indent-rigidly' returns a marker pointing to non-existing
>   buffer and breaks final-target-pos handling in `notmuch-hello'
> 
> v9:
> 
> * correctly merge bc267b70b01c79f6bdda52641e9cd7574a151eff
> 
> * fix :hide-if-empty description:
> 
>     s/and not hidden by show-if-empty/and not shown by show-empty-searches/
> 
> * fix typo in `notmuch-hello-target' docstring: s/globaly/global/
> 
> * add docstring for notmuch-hello-first-run
> 
> * fix indentation in `notmuch-hello-insert-saved-searches'
> 
> * fix indentation in `notmuch-hello-insert-searches' docstring
> 
> * fix target position calculation in `notmuch-hello-insert-saved-searches'
> 
> * s/User defined/User-defined/ in tests
> 
> * move tests to a new emacs-hello file

Hi,

I'd also like to point out to my optimization originally sent in
id:"87fwixasq4.fsf@steelpick.2x.cz". I tried it with v10 and it seems
that the bug I reported in id:"87aa7lle4t.fsf@steelpick.2x.cz" is no
longer there. It looks like that one of Dmitry's "target position
calculation" fixes helped here as well.

I'm sending the patch again as a reply to this mail, because in the
original posting a part of subject was missing.

-Michal

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [PATCH] emacs-hello: Do not calculate the count of the messages in hidden sections
  2012-02-18 22:10     ` [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello Michal Sojka
@ 2012-02-18 22:12       ` Michal Sojka
  2012-03-01 22:18         ` Mark Walters
                           ` (2 more replies)
  0 siblings, 3 replies; 57+ messages in thread
From: Michal Sojka @ 2012-02-18 22:12 UTC (permalink / raw)
  To: notmuch

The result is that hello screen shows much faster when some sections are
hidden.
---
 emacs/notmuch-hello.el |   20 ++++++++++----------
 1 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index aad373d..e9caade 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -695,16 +695,16 @@ Supports the following entries in OPTIONS as a plist:
 				(notmuch-hello-update))
 		     "hide"))
     (widget-insert "\n")
-    (let (target-pos
-	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
-      (when (and (not is-hidden)
-	       (or (not (plist-get options :hide-if-empty))
-		  searches))
-	(widget-insert "\n")
-	(setq target-pos
-	      (notmuch-hello-insert-buttons searches))
-	(indent-rigidly start (point) notmuch-hello-indent)
-	target-pos))))
+    (let (target-pos)
+      (when (not is-hidden)
+	(let ((searches (apply 'notmuch-hello-query-counts query-alist options)))
+	  (when (or (not (plist-get options :hide-if-empty))
+		    searches)
+	    (widget-insert "\n")
+	    (setq target-pos
+		  (notmuch-hello-insert-buttons searches))
+	    (indent-rigidly start (point) notmuch-hello-indent))))
+      target-pos)))
 
 (defun notmuch-hello-insert-tags-section (&optional title &rest options)
   "Insert a section displaying all tags with message counts.
-- 
1.7.7.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [PATCH v10 1/2] emacs: User-defined sections in notmuch-hello
  2012-02-17 14:48     ` [PATCH v10 1/2] " Dmitry Kurochkin
@ 2012-03-01 12:36       ` David Bremner
  2012-03-01 14:57         ` Michal Sojka
  2012-03-07 19:53         ` Pieter Praet
  0 siblings, 2 replies; 57+ messages in thread
From: David Bremner @ 2012-03-01 12:36 UTC (permalink / raw)
  To: Dmitry Kurochkin, notmuch; +Cc: Daniel Schoepe


Pushed, finally. Thanks for both of your hard work on this.

d

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v10 1/2] emacs: User-defined sections in notmuch-hello
  2012-03-01 12:36       ` David Bremner
@ 2012-03-01 14:57         ` Michal Sojka
  2012-03-01 15:00           ` Dmitry Kurochkin
  2012-03-07 19:53         ` Pieter Praet
  1 sibling, 1 reply; 57+ messages in thread
From: Michal Sojka @ 2012-03-01 14:57 UTC (permalink / raw)
  To: David Bremner, Dmitry Kurochkin, notmuch; +Cc: Daniel Schoepe

On Thu, 01 Mar 2012, David Bremner wrote:
> 
> Pushed, finally. Thanks for both of your hard work on this.

Hi,

is anybody willing to review
id:"1329603149-6047-1-git-send-email-sojkam1@fel.cvut.cz"? I'd like to
see it merged togetger with these patches as it makes my work with
notmuch-hello much more pleasant. And others
(id:"8762f7etfo.fsf@qmul.ac.uk") would probably like this patch too.

-Michal

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v10 1/2] emacs: User-defined sections in notmuch-hello
  2012-03-01 14:57         ` Michal Sojka
@ 2012-03-01 15:00           ` Dmitry Kurochkin
  0 siblings, 0 replies; 57+ messages in thread
From: Dmitry Kurochkin @ 2012-03-01 15:00 UTC (permalink / raw)
  To: Michal Sojka, David Bremner, notmuch; +Cc: Daniel Schoepe

Hi Michal.

On Thu, 01 Mar 2012 15:57:32 +0100, Michal Sojka <sojkam1@fel.cvut.cz> wrote:
> On Thu, 01 Mar 2012, David Bremner wrote:
> > 
> > Pushed, finally. Thanks for both of your hard work on this.
> 
> Hi,
> 
> is anybody willing to review
> id:"1329603149-6047-1-git-send-email-sojkam1@fel.cvut.cz"?

I plan to review it soon.

Regards,
  Dmitry

> I'd like to
> see it merged togetger with these patches as it makes my work with
> notmuch-hello much more pleasant. And others
> (id:"8762f7etfo.fsf@qmul.ac.uk") would probably like this patch too.
> 
> -Michal

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH] emacs-hello: Do not calculate the count of the messages in hidden sections
  2012-02-18 22:12       ` [PATCH] emacs-hello: Do not calculate the count of the messages in hidden sections Michal Sojka
@ 2012-03-01 22:18         ` Mark Walters
  2012-03-02  0:34           ` Daniel Schoepe
  2012-03-05  2:00         ` Dmitry Kurochkin
  2012-03-10 14:23         ` David Bremner
  2 siblings, 1 reply; 57+ messages in thread
From: Mark Walters @ 2012-03-01 22:18 UTC (permalink / raw)
  To: Michal Sojka, notmuch

On Sat, 18 Feb 2012 23:12:29 +0100, Michal Sojka <sojkam1@fel.cvut.cz> wrote:
> The result is that hello screen shows much faster when some sections are
> hidden.
> ---
>  emacs/notmuch-hello.el |   20 ++++++++++----------
>  1 files changed, 10 insertions(+), 10 deletions(-)
> 
> diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
> index aad373d..e9caade 100644
> --- a/emacs/notmuch-hello.el
> +++ b/emacs/notmuch-hello.el
> @@ -695,16 +695,16 @@ Supports the following entries in OPTIONS as a plist:
>  				(notmuch-hello-update))
>  		     "hide"))
>      (widget-insert "\n")
> -    (let (target-pos
> -	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
> -      (when (and (not is-hidden)
> -	       (or (not (plist-get options :hide-if-empty))
> -		  searches))
> -	(widget-insert "\n")
> -	(setq target-pos
> -	      (notmuch-hello-insert-buttons searches))
> -	(indent-rigidly start (point) notmuch-hello-indent)
> -	target-pos))))
> +    (let (target-pos)
> +      (when (not is-hidden)
> +	(let ((searches (apply 'notmuch-hello-query-counts query-alist options)))
> +	  (when (or (not (plist-get options :hide-if-empty))
> +		    searches)
> +	    (widget-insert "\n")
> +	    (setq target-pos
> +		  (notmuch-hello-insert-buttons searches))
> +	    (indent-rigidly start (point) notmuch-hello-indent))))
> +      target-pos)))
>  
>  (defun notmuch-hello-insert-tags-section (&optional title &rest options)
>    "Insert a section displaying all tags with message counts.


This looks fine to me and does speed things up when the "All tags"
section is hidden (are there other sections I can hide?)

Best wishes

Mark

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH] emacs-hello: Do not calculate the count of the messages in hidden sections
  2012-03-01 22:18         ` Mark Walters
@ 2012-03-02  0:34           ` Daniel Schoepe
  2012-03-02  0:36             ` Daniel Schoepe
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Schoepe @ 2012-03-02  0:34 UTC (permalink / raw)
  To: Mark Walters, Michal Sojka, notmuch

[-- Attachment #1: Type: text/plain, Size: 449 bytes --]

On Thu, 01 Mar 2012 22:18:04 +0000, Mark Walters <markwalters1009@gmail.com> wrote:
> This looks fine to me and does speed things up when the "All tags"
> section is hidden (are there other sections I can hide?)

You can define other section that you can then hide, which was the main
point of the original patch.

Michal, your patch looks good to me too. Now that my two patches have
been pushed, I think this should go in as well.

Cheers,
Daniel

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH] emacs-hello: Do not calculate the count of the messages in hidden sections
  2012-03-02  0:34           ` Daniel Schoepe
@ 2012-03-02  0:36             ` Daniel Schoepe
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2012-03-02  0:36 UTC (permalink / raw)
  To: Mark Walters, Michal Sojka, notmuch

[-- Attachment #1: Type: text/plain, Size: 271 bytes --]

On Fri, 02 Mar 2012 01:34:03 +0100, Daniel Schoepe <daniel@schoepe.org> wrote:
> You can define other section that you can then hide, which was the main
> point of the original patch.

The defining other sections thing that is, not that you have more
things to hide.. :)

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH] emacs-hello: Do not calculate the count of the messages in hidden sections
  2012-02-18 22:12       ` [PATCH] emacs-hello: Do not calculate the count of the messages in hidden sections Michal Sojka
  2012-03-01 22:18         ` Mark Walters
@ 2012-03-05  2:00         ` Dmitry Kurochkin
  2012-03-10 14:23         ` David Bremner
  2 siblings, 0 replies; 57+ messages in thread
From: Dmitry Kurochkin @ 2012-03-05  2:00 UTC (permalink / raw)
  To: Michal Sojka, notmuch

Hi Michal.

On Sat, 18 Feb 2012 23:12:29 +0100, Michal Sojka <sojkam1@fel.cvut.cz> wrote:
> The result is that hello screen shows much faster when some sections are
> hidden.
> ---

The patch looks good to me.

Please do not send new patches as replies to other patch threads.  It
makes it difficult to track, especially when there are multiple
versions.  You can always add an id: reference to the related thread.

Regards,
  Dmitry

>  emacs/notmuch-hello.el |   20 ++++++++++----------
>  1 files changed, 10 insertions(+), 10 deletions(-)
> 
> diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
> index aad373d..e9caade 100644
> --- a/emacs/notmuch-hello.el
> +++ b/emacs/notmuch-hello.el
> @@ -695,16 +695,16 @@ Supports the following entries in OPTIONS as a plist:
>  				(notmuch-hello-update))
>  		     "hide"))
>      (widget-insert "\n")
> -    (let (target-pos
> -	  (searches (apply 'notmuch-hello-query-counts query-alist options)))
> -      (when (and (not is-hidden)
> -	       (or (not (plist-get options :hide-if-empty))
> -		  searches))
> -	(widget-insert "\n")
> -	(setq target-pos
> -	      (notmuch-hello-insert-buttons searches))
> -	(indent-rigidly start (point) notmuch-hello-indent)
> -	target-pos))))
> +    (let (target-pos)
> +      (when (not is-hidden)
> +	(let ((searches (apply 'notmuch-hello-query-counts query-alist options)))
> +	  (when (or (not (plist-get options :hide-if-empty))
> +		    searches)
> +	    (widget-insert "\n")
> +	    (setq target-pos
> +		  (notmuch-hello-insert-buttons searches))
> +	    (indent-rigidly start (point) notmuch-hello-indent))))
> +      target-pos)))
>  
>  (defun notmuch-hello-insert-tags-section (&optional title &rest options)
>    "Insert a section displaying all tags with message counts.
> -- 
> 1.7.7.3
> 
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v10 1/2] emacs: User-defined sections in notmuch-hello
  2012-03-01 12:36       ` David Bremner
  2012-03-01 14:57         ` Michal Sojka
@ 2012-03-07 19:53         ` Pieter Praet
  2012-03-07 20:04           ` Daniel Schoepe
  2012-03-07 20:11           ` David Bremner
  1 sibling, 2 replies; 57+ messages in thread
From: Pieter Praet @ 2012-03-07 19:53 UTC (permalink / raw)
  To: David Bremner, Dmitry Kurochkin, notmuch; +Cc: Daniel Schoepe

On Thu, 01 Mar 2012 08:36:33 -0400, David Bremner <david@tethera.net> wrote:
> 
> Pushed, finally. Thanks for both of your hard work on this.
>

625 lines changed (425 added, 200 removed), in a *single* commit ?

It's a useful bit of functionality which definitely deserves to be
included, and I do recognize and appreciate all the hard work that
went into it, but... am I the only one who was a bit surprised to
see this patch being applied [1] as-is?

> d
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch


Peace

-- 
Pieter

[1] commit 3557acab

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v10 1/2] emacs: User-defined sections in notmuch-hello
  2012-03-07 19:53         ` Pieter Praet
@ 2012-03-07 20:04           ` Daniel Schoepe
  2012-03-07 20:11           ` David Bremner
  1 sibling, 0 replies; 57+ messages in thread
From: Daniel Schoepe @ 2012-03-07 20:04 UTC (permalink / raw)
  To: Pieter Praet, David Bremner, Dmitry Kurochkin, notmuch

[-- Attachment #1: Type: text/plain, Size: 1248 bytes --]

On Wed, 07 Mar 2012 20:53:48 +0100, Pieter Praet <pieter@praet.org> wrote:
> On Thu, 01 Mar 2012 08:36:33 -0400, David Bremner <david@tethera.net> wrote:
> > 
> > Pushed, finally. Thanks for both of your hard work on this.
> >
> 
> 625 lines changed (425 added, 200 removed), in a *single* commit ?
> 
> It's a useful bit of functionality which definitely deserves to be
> included, and I do recognize and appreciate all the hard work that
> went into it, but... am I the only one who was a bit surprised to
> see this patch being applied [1] as-is?

I think it is hard to split this patch up into meaningful smaller
pieces, since it mainly moves parts from notmuch-hello into separate
functions and adds some defcustoms. So, the main opportunities for
splitting would be to do this moving of code into functions one function
per commit and to put the defcustoms and their quite lengthy
documentation in a separate commit (which would leave you with an
intermediate version that's seriously lacking in documentation).

I'm not sure if those two things are that useful, but if there's
consensus that the patch shouldn't stay in the way it is, I can give it
a shot (although it'd be a lot of tedious work).

Cheers,
Daniel

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH v10 1/2] emacs: User-defined sections in notmuch-hello
  2012-03-07 19:53         ` Pieter Praet
  2012-03-07 20:04           ` Daniel Schoepe
@ 2012-03-07 20:11           ` David Bremner
  1 sibling, 0 replies; 57+ messages in thread
From: David Bremner @ 2012-03-07 20:11 UTC (permalink / raw)
  To: Pieter Praet, Dmitry Kurochkin, notmuch; +Cc: Daniel Schoepe

On Wed, 07 Mar 2012 20:53:48 +0100, Pieter Praet <pieter@praet.org> wrote:

> 625 lines changed (425 added, 200 removed), in a *single* commit ?

I had a similar initial reaction, but we did have 2 willing reviewers
and 10 rounds of review.

d

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [PATCH] emacs-hello: Do not calculate the count of the messages in hidden sections
  2012-02-18 22:12       ` [PATCH] emacs-hello: Do not calculate the count of the messages in hidden sections Michal Sojka
  2012-03-01 22:18         ` Mark Walters
  2012-03-05  2:00         ` Dmitry Kurochkin
@ 2012-03-10 14:23         ` David Bremner
  2 siblings, 0 replies; 57+ messages in thread
From: David Bremner @ 2012-03-10 14:23 UTC (permalink / raw)
  To: Michal Sojka, notmuch

On Sat, 18 Feb 2012 23:12:29 +0100, Michal Sojka <sojkam1@fel.cvut.cz> wrote:
> The result is that hello screen shows much faster when some sections are
> hidden.

pushed.

d

^ permalink raw reply	[flat|nested] 57+ messages in thread

end of thread, other threads:[~2012-03-10 14:23 UTC | newest]

Thread overview: 57+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <id:"1309379221-5617-1-git-send-email-daniel.schoepe@googlemail.com">
2011-07-07 22:53 ` [PATCH v4 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
2011-07-07 22:53   ` [PATCH v4 1/2] " Daniel Schoepe
2011-07-08 23:00     ` Michal Sojka
2011-07-08 23:13       ` Daniel Schoepe
2011-07-09  5:35         ` Michal Sojka
2011-07-09 17:07       ` Daniel Schoepe
2011-07-07 22:53   ` [PATCH v4 2/2] emacs: Tests for user-defined sections Daniel Schoepe
2011-07-09 18:03   ` [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
2011-07-09 18:03     ` [PATCH v5 1/2] " Daniel Schoepe
2011-07-09 18:03     ` [PATCH v5 2/2] emacs: Tests for user-defined sections Daniel Schoepe
2011-07-11 10:32     ` [PATCH] emacs: NEWS entry " Daniel Schoepe
2011-08-14 16:55     ` [PATCH v5 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
2011-08-15  8:40       ` Michal Sojka
2011-10-10 13:39         ` [PATCH v6 " Daniel Schoepe
2011-10-10 13:39           ` [PATCH v6 1/2] " Daniel Schoepe
2011-11-24 13:54             ` David Bremner
2011-11-24 14:01               ` Daniel Schoepe
2011-11-24 15:43                 ` Michal Sojka
2011-11-28  4:06                 ` Dmitry Kurochkin
2011-11-28  7:57                   ` Michal Sojka
2011-12-14  3:11             ` Dmitry Kurochkin
2011-12-14 12:55               ` Dmitry Kurochkin
2012-01-22  0:39               ` Daniel Schoepe
2012-01-22  0:54                 ` [PATCH v7 " Daniel Schoepe
2012-01-22  0:54                   ` [PATCH v7 2/2] emacs: Tests for user-defined sections Daniel Schoepe
2012-01-23 23:07                     ` Dmitry Kurochkin
2012-01-28 21:30                       ` Daniel Schoepe
2012-01-28 21:44                         ` [PATCH v8 0/2] emacs: User-defined sections in notmuch-hello Daniel Schoepe
2012-01-28 21:44                           ` [PATCH v8 1/2] " Daniel Schoepe
2012-01-28 21:44                           ` [PATCH v8 2/2] emacs: Tests for user-defined sections Daniel Schoepe
2012-01-28 22:48                         ` [PATCH v7 " Dmitry Kurochkin
2012-01-28 22:54                           ` Daniel Schoepe
2011-10-10 13:39           ` [PATCH v6 " Daniel Schoepe
2011-10-13 14:09           ` [PATCH] emacs-hello: Do not calculate the count of the messages in Michal Sojka
2012-01-16 11:33             ` David Edmondson
2012-01-16 12:39               ` Daniel Schoepe
2012-01-16 10:59           ` [PATCH v6 0/2] emacs: User-defined sections in notmuch-hello David Edmondson
2012-01-16 11:13             ` Daniel Schoepe
2012-02-17  7:48   ` [PATCH v9 " Dmitry Kurochkin
2012-02-17  7:48     ` [PATCH v9 1/2] " Dmitry Kurochkin
2012-02-17  7:48     ` [PATCH v9 2/2] emacs: Tests for user-defined sections Dmitry Kurochkin
2012-02-17 14:48   ` [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello Dmitry Kurochkin
2012-02-17 14:48     ` [PATCH v10 1/2] " Dmitry Kurochkin
2012-03-01 12:36       ` David Bremner
2012-03-01 14:57         ` Michal Sojka
2012-03-01 15:00           ` Dmitry Kurochkin
2012-03-07 19:53         ` Pieter Praet
2012-03-07 20:04           ` Daniel Schoepe
2012-03-07 20:11           ` David Bremner
2012-02-17 14:48     ` [PATCH v10 2/2] emacs: Tests for user-defined sections Dmitry Kurochkin
2012-02-18 22:10     ` [PATCH v10 0/2] emacs: User-defined sections in notmuch-hello Michal Sojka
2012-02-18 22:12       ` [PATCH] emacs-hello: Do not calculate the count of the messages in hidden sections Michal Sojka
2012-03-01 22:18         ` Mark Walters
2012-03-02  0:34           ` Daniel Schoepe
2012-03-02  0:36             ` Daniel Schoepe
2012-03-05  2:00         ` Dmitry Kurochkin
2012-03-10 14:23         ` David Bremner

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).