[PATCH 1/2] emacs: User-defined sections in notmuch-hello

2011-07-02 Thread Daniel Schoepe
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 |  570 ++--
 1 files changed, 356 insertions(+), 214 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index 65fde75..19756e9 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -55,26 +55,6 @@
   :type 'boolean
   :group 'notmuch)

-(defcustom notmuch-hello-tag-list-make-query nil
-  "Function or string to generate queries for the all tags list.
-
-This variable controls which query results are shown for each tag
-in the \"all tags\" list. If nil, it will use all messages with
-that tag. If this is set to a string, it is used as a filter for
-messages having that tag (equivalent to \"tag:TAG and (THIS-VARIABLE)\").
-Finally this can be a function that will be called for each tag and
-should return a filter for that tag, or nil to hide the tag."
-  :type '(choice (const :tag "All messages" nil)
-(const :tag "Unread messages" "tag:unread")
-(const :tag "Custom filter" string)
-(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 +103,73 @@ Typically \",\" in the US and UK and \".\" in Europe."

 (defvar notmuch-hello-recent-searches nil)

+(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
+
+(define-widget 'notmuch-hello-query-section 'lazy
+  "Customize-type for custom saved-search-like sections"
+  :tag "Customized queries section"
+  :type
+  '(list (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
+   ((: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"))
+
+(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."
+  :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)
+   notmuch-hello-tags-section
+   notmuch-hello-query-section)))
+
+;; only defined to avoid compilation warnings about free variables
+(defvar notmuch-hello-target nil)
+
+(defvar notmuch-hello-hidden-sections nil
+  "List of query sections 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))
@@ -238,12 +285,40 @@ should be. Returns a cons cell `(tags-per-line width)'."
   (* tags-per-line (+ 

[PATCH 1/2] emacs: User-defined sections in notmuch-hello

2011-07-02 Thread Daniel Schoepe
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 |  570 ++--
 1 files changed, 356 insertions(+), 214 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index 65fde75..19756e9 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -55,26 +55,6 @@
   :type 'boolean
   :group 'notmuch)
 
-(defcustom notmuch-hello-tag-list-make-query nil
-  Function or string to generate queries for the all tags list.
-
-This variable controls which query results are shown for each tag
-in the \all tags\ list. If nil, it will use all messages with
-that tag. If this is set to a string, it is used as a filter for
-messages having that tag (equivalent to \tag:TAG and (THIS-VARIABLE)\).
-Finally this can be a function that will be called for each tag and
-should return a filter for that tag, or nil to hide the tag.
-  :type '(choice (const :tag All messages nil)
-(const :tag Unread messages tag:unread)
-(const :tag Custom filter string)
-(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 +103,73 @@ Typically \,\ in the US and UK and \.\ in Europe.
 
 (defvar notmuch-hello-recent-searches nil)
 
+(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
+
+(define-widget 'notmuch-hello-query-section 'lazy
+  Customize-type for custom saved-search-like sections
+  :tag Customized queries section
+  :type
+  '(list (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
+   ((: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))
+
+(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.
+  :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)
+   notmuch-hello-tags-section
+   notmuch-hello-query-section)))
+
+;; only defined to avoid compilation warnings about free variables
+(defvar notmuch-hello-target nil)
+
+(defvar notmuch-hello-hidden-sections nil
+  List of query sections 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))
@@ -238,12 +285,40 @@ should be. Returns a cons cell `(tags-per-line width)'.
   (* tags-per-line (+ 9 1
   tags-per-line
 
-(defun 

[PATCH 1/2] emacs: User-defined sections in notmuch-hello

2011-06-29 Thread Daniel Schoepe
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 |  553 +---
 1 files changed, 339 insertions(+), 214 deletions(-)

diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index 65fde75..e4c9307 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -55,26 +55,6 @@
   :type 'boolean
   :group 'notmuch)
 
-(defcustom notmuch-hello-tag-list-make-query nil
-  Function or string to generate queries for the all tags list.
-
-This variable controls which query results are shown for each tag
-in the \all tags\ list. If nil, it will use all messages with
-that tag. If this is set to a string, it is used as a filter for
-messages having that tag (equivalent to \tag:TAG and (THIS-VARIABLE)\).
-Finally this can be a function that will be called for each tag and
-should return a filter for that tag, or nil to hide the tag.
-  :type '(choice (const :tag All messages nil)
-(const :tag Unread messages tag:unread)
-(const :tag Custom filter string)
-(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 +103,58 @@ Typically \,\ in the US and UK and \.\ in Europe.
 
 (defvar notmuch-hello-recent-searches nil)
 
+(define-widget 'notmuch-hello-customized-section 'lazy
+  Customize-type for notmuch-hello sections.
+  :tag Customized section
+  :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 :tag  (const :tag  lambda) (const :tag  nil)
+  (list :tag Options
+(const :tag  apply)
+(const :tag  (quote notmuch-hello-insert-all-tags))
+(list :tag  (const :tag  quote)
+  (plist :options ,opts))
+
+(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. The functions will be run
+to construct the content of the notmuch-hello buffer
+in the order they appear in this list.
+  :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)
+   notmuch-hello-customized-section)))
+
+;; only defined to avoid compilation warnings about free variables
+(defvar notmuch-hello-target nil)
+
+(defvar notmuch-hello-hidden-sections nil
+  List of query sections 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))
@@ -238,12 +270,40 @@ 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-query-entries (tag-alist optional hide-empty)
+  Compute list of counts and queries for TAG-ALIST.
+
+If HIDE-EMPTY is non-nil, entries with no matching messages will be
+removed from the result.
+  (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))