Merge pull request #1228 from mhcerri/mu4e-improved-thread-prefix
mu4e: improved thread prefix
This commit is contained in:
@ -571,7 +571,8 @@ count_colons (const char *str)
|
|||||||
|
|
||||||
static MuMsgIterThreadInfo*
|
static MuMsgIterThreadInfo*
|
||||||
thread_info_new (gchar *threadpath, gboolean root, gboolean first_child,
|
thread_info_new (gchar *threadpath, gboolean root, gboolean first_child,
|
||||||
gboolean empty_parent, gboolean has_child, gboolean is_dup)
|
gboolean last_child, gboolean empty_parent,
|
||||||
|
gboolean has_child, gboolean is_dup)
|
||||||
{
|
{
|
||||||
MuMsgIterThreadInfo *ti;
|
MuMsgIterThreadInfo *ti;
|
||||||
|
|
||||||
@ -582,6 +583,7 @@ thread_info_new (gchar *threadpath, gboolean root, gboolean first_child,
|
|||||||
ti->prop = MU_MSG_ITER_THREAD_PROP_NONE;
|
ti->prop = MU_MSG_ITER_THREAD_PROP_NONE;
|
||||||
ti->prop |= root ? MU_MSG_ITER_THREAD_PROP_ROOT : 0;
|
ti->prop |= root ? MU_MSG_ITER_THREAD_PROP_ROOT : 0;
|
||||||
ti->prop |= first_child ? MU_MSG_ITER_THREAD_PROP_FIRST_CHILD : 0;
|
ti->prop |= first_child ? MU_MSG_ITER_THREAD_PROP_FIRST_CHILD : 0;
|
||||||
|
ti->prop |= last_child ? MU_MSG_ITER_THREAD_PROP_LAST_CHILD : 0;
|
||||||
ti->prop |= empty_parent ? MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT : 0;
|
ti->prop |= empty_parent ? MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT : 0;
|
||||||
ti->prop |= is_dup ? MU_MSG_ITER_THREAD_PROP_DUP : 0;
|
ti->prop |= is_dup ? MU_MSG_ITER_THREAD_PROP_DUP : 0;
|
||||||
ti->prop |= has_child ? MU_MSG_ITER_THREAD_PROP_HAS_CHILD : 0;
|
ti->prop |= has_child ? MU_MSG_ITER_THREAD_PROP_HAS_CHILD : 0;
|
||||||
@ -610,12 +612,13 @@ static void
|
|||||||
add_to_thread_info_hash (GHashTable *thread_info_hash, MuContainer *c,
|
add_to_thread_info_hash (GHashTable *thread_info_hash, MuContainer *c,
|
||||||
char *threadpath)
|
char *threadpath)
|
||||||
{
|
{
|
||||||
gboolean is_root, first_child, empty_parent, is_dup, has_child;
|
gboolean is_root, first_child, last_child, empty_parent, is_dup, has_child;
|
||||||
|
|
||||||
/* 'root' means we're a child of the dummy root-container */
|
/* 'root' means we're a child of the dummy root-container */
|
||||||
is_root = (c->parent == NULL);
|
is_root = (c->parent == NULL);
|
||||||
|
|
||||||
first_child = is_root ? FALSE : (c->parent->child == c);
|
first_child = is_root ? FALSE : (c->parent->child == c);
|
||||||
|
last_child = is_root ? FALSE : (c->next == NULL);
|
||||||
empty_parent = is_root ? FALSE : (!c->parent->msg);
|
empty_parent = is_root ? FALSE : (!c->parent->msg);
|
||||||
is_dup = c->flags & MU_CONTAINER_FLAG_DUP;
|
is_dup = c->flags & MU_CONTAINER_FLAG_DUP;
|
||||||
has_child = c->child ? TRUE : FALSE;
|
has_child = c->child ? TRUE : FALSE;
|
||||||
@ -625,6 +628,7 @@ add_to_thread_info_hash (GHashTable *thread_info_hash, MuContainer *c,
|
|||||||
thread_info_new (threadpath,
|
thread_info_new (threadpath,
|
||||||
is_root,
|
is_root,
|
||||||
first_child,
|
first_child,
|
||||||
|
last_child,
|
||||||
empty_parent,
|
empty_parent,
|
||||||
has_child,
|
has_child,
|
||||||
is_dup));
|
is_dup));
|
||||||
|
|||||||
@ -163,9 +163,10 @@ enum _MuMsgIterThreadProp {
|
|||||||
|
|
||||||
MU_MSG_ITER_THREAD_PROP_ROOT = 1 << 0,
|
MU_MSG_ITER_THREAD_PROP_ROOT = 1 << 0,
|
||||||
MU_MSG_ITER_THREAD_PROP_FIRST_CHILD = 1 << 1,
|
MU_MSG_ITER_THREAD_PROP_FIRST_CHILD = 1 << 1,
|
||||||
MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT = 1 << 2,
|
MU_MSG_ITER_THREAD_PROP_LAST_CHILD = 1 << 2,
|
||||||
MU_MSG_ITER_THREAD_PROP_DUP = 1 << 3,
|
MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT = 1 << 3,
|
||||||
MU_MSG_ITER_THREAD_PROP_HAS_CHILD = 1 << 4
|
MU_MSG_ITER_THREAD_PROP_DUP = 1 << 4,
|
||||||
|
MU_MSG_ITER_THREAD_PROP_HAS_CHILD = 1 << 5
|
||||||
};
|
};
|
||||||
typedef guint8 MuMsgIterThreadProp;
|
typedef guint8 MuMsgIterThreadProp;
|
||||||
|
|
||||||
|
|||||||
@ -434,11 +434,13 @@ static void
|
|||||||
append_sexp_thread_info (GString *gstr, const MuMsgIterThreadInfo *ti)
|
append_sexp_thread_info (GString *gstr, const MuMsgIterThreadInfo *ti)
|
||||||
{
|
{
|
||||||
g_string_append_printf
|
g_string_append_printf
|
||||||
(gstr, "\t:thread (:path \"%s\" :level %u%s%s%s%s)\n",
|
(gstr, "\t:thread (:path \"%s\" :level %u%s%s%s%s%s)\n",
|
||||||
ti->threadpath,
|
ti->threadpath,
|
||||||
ti->level,
|
ti->level,
|
||||||
ti->prop & MU_MSG_ITER_THREAD_PROP_FIRST_CHILD ?
|
ti->prop & MU_MSG_ITER_THREAD_PROP_FIRST_CHILD ?
|
||||||
" :first-child t" : "",
|
" :first-child t" : "",
|
||||||
|
ti->prop & MU_MSG_ITER_THREAD_PROP_LAST_CHILD ?
|
||||||
|
" :last-child t" : "",
|
||||||
ti->prop & MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT ?
|
ti->prop & MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT ?
|
||||||
" :empty-parent t" : "",
|
" :empty-parent t" : "",
|
||||||
ti->prop & MU_MSG_ITER_THREAD_PROP_DUP ?
|
ti->prop & MU_MSG_ITER_THREAD_PROP_DUP ?
|
||||||
|
|||||||
@ -232,11 +232,39 @@ one of: `:date', `:subject', `:size', `:prio', `:from', `:to.',
|
|||||||
(defvar mu4e-headers-unread-mark '("u" . "⎕") "Unread.")
|
(defvar mu4e-headers-unread-mark '("u" . "⎕") "Unread.")
|
||||||
|
|
||||||
;; thread prefix marks
|
;; thread prefix marks
|
||||||
(defvar mu4e-headers-has-child-prefix '("+" . "◼ ") "Parent.")
|
(defvar mu4e-headers-thread-child-prefix '("├>" . "┣▶ ")
|
||||||
(defvar mu4e-headers-empty-parent-prefix '("-" . "◽ ") "Orphan.")
|
"Prefix for messages in sub threads that do have a following sibling.
|
||||||
(defvar mu4e-headers-first-child-prefix '("\\" . "┗▶") "First child.")
|
|
||||||
(defvar mu4e-headers-duplicate-prefix '("=" . "≡ ") "Duplicate.")
|
This variable is only used when mu4e-headers-new-thread-style is non-nil.")
|
||||||
(defvar mu4e-headers-default-prefix '("|" . "│ ") "Default.")
|
|
||||||
|
(defvar mu4e-headers-thread-last-child-prefix '("└>" . "┗▶ ")
|
||||||
|
"Prefix for messages in sub threads that do not have a following sibling.
|
||||||
|
|
||||||
|
This variable is only used when mu4e-headers-new-thread-style is non-nil.")
|
||||||
|
|
||||||
|
(defvar mu4e-headers-thread-connection-prefix '("│" . "┃ ")
|
||||||
|
"Prefix to connect sibling messages that do not follow each other.
|
||||||
|
|
||||||
|
This prefix should have the same length as `mu4e-headers-thread-blank-prefix'.
|
||||||
|
|
||||||
|
This variable is only used when mu4e-headers-new-thread-style is non-nil.")
|
||||||
|
|
||||||
|
(defvar mu4e-headers-thread-blank-prefix '(" " . " ")
|
||||||
|
"Prefix to separate non connected messages.
|
||||||
|
|
||||||
|
This prefix should have the same length as `mu4e-headers-thread-connection-prefix'.
|
||||||
|
|
||||||
|
This variable is only used when mu4e-headers-new-thread-style is non-nil.")
|
||||||
|
|
||||||
|
(defvar mu4e-headers-thread-orphan-prefix '("" . "")
|
||||||
|
"Prefix for orphan messages.
|
||||||
|
|
||||||
|
This variable is only used when mu4e-headers-new-thread-style is non-nil.")
|
||||||
|
|
||||||
|
(defvar mu4e-headers-thread-duplicate-prefix '("=" . "≡ ")
|
||||||
|
"Prefix for duplicate messages.
|
||||||
|
|
||||||
|
This variable is only used when mu4e-headers-new-thread-style is non-nil.")
|
||||||
|
|
||||||
(defvar mu4e-headers-actions
|
(defvar mu4e-headers-actions
|
||||||
'( ("capture message" . mu4e-action-capture-message)
|
'( ("capture message" . mu4e-action-capture-message)
|
||||||
@ -411,26 +439,78 @@ into a string."
|
|||||||
(or name email "?"))) contacts ", "))
|
(or name email "?"))) contacts ", "))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
(defsubst mu4e~headers-thread-prefix-map (type)
|
||||||
|
"Return the thread prefix based on the symbol TYPE."
|
||||||
|
(let ((get-prefix
|
||||||
|
(lambda (cell)
|
||||||
|
(if mu4e-use-fancy-chars (cdr cell) (car cell)))))
|
||||||
|
(case type
|
||||||
|
('child (funcall get-prefix mu4e-headers-thread-child-prefix))
|
||||||
|
('last-child (funcall get-prefix mu4e-headers-thread-last-child-prefix))
|
||||||
|
('connection (funcall get-prefix mu4e-headers-thread-connection-prefix))
|
||||||
|
('blank (funcall get-prefix mu4e-headers-thread-blank-prefix))
|
||||||
|
('orphan (funcall get-prefix mu4e-headers-thread-orphan-prefix))
|
||||||
|
('duplicate (funcall get-prefix mu4e-headers-thread-duplicate-prefix))
|
||||||
|
(t "?"))))
|
||||||
|
|
||||||
|
;; In order to print a thread tree with all the message connections,
|
||||||
|
;; it's necessary to keep track of all sub levels that still have
|
||||||
|
;; following messages. For each level, mu4e~headers-thread-state keeps
|
||||||
|
;; the value t for a connection or nil otherwise.
|
||||||
|
(defvar-local mu4e~headers-thread-state '())
|
||||||
|
|
||||||
(defsubst mu4e~headers-thread-prefix (thread)
|
(defsubst mu4e~headers-thread-prefix (thread)
|
||||||
"Calculate the thread prefix based on thread info THREAD."
|
"Calculate the thread prefix based on thread info THREAD."
|
||||||
(when thread
|
(when thread
|
||||||
(let ((get-prefix
|
(let ((prefix "")
|
||||||
(lambda (cell) (if mu4e-use-fancy-chars (cdr cell) (car cell)))))
|
(level (plist-get thread :level))
|
||||||
|
(has-child (plist-get thread :has-child))
|
||||||
|
(empty-parent (plist-get thread :empty-parent))
|
||||||
|
(first-child (plist-get thread :first-child))
|
||||||
|
(last-child (plist-get thread :last-child))
|
||||||
|
(duplicate (plist-get thread :duplicate)))
|
||||||
|
;; Do not prefix root messages.
|
||||||
|
(if (or (= level 0) empty-parent)
|
||||||
|
(setq mu4e~headers-thread-state '()))
|
||||||
|
(if (> level 0)
|
||||||
|
(let* ((length (length mu4e~headers-thread-state))
|
||||||
|
(padding (make-list (max 0 (- level length)) nil)))
|
||||||
|
;; Trim and pad the state to ensure a message will
|
||||||
|
;; always be shown with the correct identation, even if
|
||||||
|
;; a broken thread is returned. It's trimmed to level-1
|
||||||
|
;; because the current level has always an connection
|
||||||
|
;; and it used a special formatting.
|
||||||
|
(setq mu4e~headers-thread-state
|
||||||
|
(subseq (append mu4e~headers-thread-state padding)
|
||||||
|
0 (- level 1)))
|
||||||
|
;; Prepare the thread prefix.
|
||||||
|
(setq prefix
|
||||||
(concat
|
(concat
|
||||||
(make-string (* (if (plist-get thread :empty-parent) 0 1)
|
;; Current mu4e~headers-thread-state, composed by
|
||||||
(plist-get thread :level)) ?\s)
|
;; connections or blanks.
|
||||||
(cond
|
(mapconcat
|
||||||
((plist-get thread :has-child)
|
(lambda (s)
|
||||||
(funcall get-prefix mu4e-headers-has-child-prefix))
|
(if s (mu4e~headers-thread-prefix-map 'connection)
|
||||||
((plist-get thread :empty-parent)
|
(mu4e~headers-thread-prefix-map 'blank)))
|
||||||
(funcall get-prefix mu4e-headers-empty-parent-prefix))
|
mu4e~headers-thread-state "")
|
||||||
((plist-get thread :first-child)
|
;; Current entry.
|
||||||
(funcall get-prefix mu4e-headers-first-child-prefix))
|
(if last-child (mu4e~headers-thread-prefix-map 'last-child)
|
||||||
((plist-get thread :duplicate)
|
(mu4e~headers-thread-prefix-map 'child))))))
|
||||||
(funcall get-prefix mu4e-headers-duplicate-prefix))
|
;; If a new sub-thread will follow (has-child) and the current
|
||||||
(t
|
;; one is still not done (not last-child), then a new
|
||||||
(funcall get-prefix mu4e-headers-default-prefix)))
|
;; connection needs to be added to the tree-state. It's not
|
||||||
" "))))
|
;; necessary to a blank (nil), because padding will handle
|
||||||
|
;; that.
|
||||||
|
(if (and has-child (not last-child))
|
||||||
|
(setq mu4e~headers-thread-state
|
||||||
|
(append mu4e~headers-thread-state '(t))))
|
||||||
|
;; Return the thread prefix.
|
||||||
|
(format "%s%s%s"
|
||||||
|
prefix
|
||||||
|
(if empty-parent
|
||||||
|
(mu4e~headers-thread-prefix-map 'orphan) "")
|
||||||
|
(if duplicate
|
||||||
|
(mu4e~headers-thread-prefix-map 'duplicate) "")))))
|
||||||
|
|
||||||
(defsubst mu4e~headers-flags-str (flags)
|
(defsubst mu4e~headers-flags-str (flags)
|
||||||
"Get a display string for the flags.
|
"Get a display string for the flags.
|
||||||
|
|||||||
@ -787,15 +787,16 @@ An example headers view:
|
|||||||
@cartouche
|
@cartouche
|
||||||
@verbatim
|
@verbatim
|
||||||
Date V Flgs From/To List Subject
|
Date V Flgs From/To List Subject
|
||||||
06:32 Nu To Edmund Dantès GstDev + Re: Gstreamer-V4L...
|
06:32 Nu To Edmund Dantès GstDev Gstreamer-V4L2SINK ...
|
||||||
15:08 Nu Abbé Busoni GstDev + Re: Gstreamer-V...
|
15:08 Nu Abbé Busoni GstDev ├> ...
|
||||||
18:20 Nu Pierre Morrel GstDev \ Re: Gstreamer...
|
18:20 Nu Pierre Morrel GstDev │└> ...
|
||||||
2013-03-18 S Jacopo EmacsUsr + emacs server on win...
|
07:48 Nu To Edmund Dantès GstDev └> ...
|
||||||
2013-03-18 S Mercédès EmacsUsr \ RE: emacs server ...
|
2013-03-18 S Jacopo EmacsUsr emacs server on win...
|
||||||
2013-03-18 S Beachamp EmacsUsr + Re: Copying a whole...
|
2013-03-18 S Mercédès EmacsUsr └> ...
|
||||||
22:07 Nu Albert de Moncerf EmacsUsr \ Re: Copying a who...
|
2013-03-18 S Beachamp EmacsUsr Re: Copying a whole...
|
||||||
2013-03-18 S Gaspard Caderousse GstDev | Issue with GESSimpl...
|
22:07 Nu Albert de Moncerf EmacsUsr └> ...
|
||||||
2013-03-18 Ss Baron Danglars GuileUsr | Guile-SDL 0.4.2 ava...
|
2013-03-18 S Gaspard Caderousse GstDev Issue with GESSimpl...
|
||||||
|
2013-03-18 Ss Baron Danglars GuileUsr Guile-SDL 0.4.2 ava...
|
||||||
End of search results
|
End of search results
|
||||||
@end verbatim
|
@end verbatim
|
||||||
@end cartouche
|
@end cartouche
|
||||||
|
|||||||
Reference in New Issue
Block a user