mutt stable branch with some hacks

Speed up thread sort when many long threads exist.

Merge request !75 reported a massive slowdown opening a mailbox with
many long threads: on the order of an hour.

This is because mutt_set_virtual() was iterating through the whole
thread for each message.

After taking a closer look at current Mutt behavior, it seems the
num_hidden count is only needed in the root thread. Quoting my
comment in the merge request:

The index-format expando %M only applies to root level headers, so
there are no issues there.

The only concern is the pattern ~v. Limiting always resets collapsed
and num_hidden, so there also can't be any issues there. Tagging and
other pattern operators only work on visible messages. So tagging
while threads are sorted will only tag the root messages of
collapsed threads.

However, if you sort by thread, collapse a thread, and then sort by
date, the collapsed and num_hidden fields are not reset for each
header. In theory this would allow us to tag ~v with date
ordering. However, in master the num_hidden is only set (by
mutt_set_virtual()) for visible messages with a virtual number.

So even in master, switching to date-order and tagging ~v will only
tag the root messages of each collapsed thread.

This commit removes the num_hidden setting in mutt_set_virtual(),
instead directly assigning that value in the roothdr during a collapse
operation.

A subsequent commit will fix the behavior of ~v, when switching
sorting from threaded to something else, by putting num_hidden in
every header in the thread. This is technically a change in behavior,
so I will make that commit in the master branch.

Thanks to Score_Under for the pull request and for testing my
alternate solutions.

+14 -11
+5 -4
mutt.h
··· 203 203 204 204 #define MUTT_THREAD_COLLAPSE (1<<0) 205 205 #define MUTT_THREAD_UNCOLLAPSE (1<<1) 206 - #define MUTT_THREAD_GET_HIDDEN (1<<2) 207 - #define MUTT_THREAD_UNREAD (1<<3) 208 - #define MUTT_THREAD_NEXT_UNREAD (1<<4) 206 + #define MUTT_THREAD_UNREAD (1<<2) 207 + #define MUTT_THREAD_NEXT_UNREAD (1<<3) 209 208 210 209 enum 211 210 { ··· 882 881 /* the following are used to support collapsing threads */ 883 882 unsigned int collapsed : 1; /* is this message part of a collapsed thread? */ 884 883 unsigned int limited : 1; /* is this message in a limited view? */ 885 - size_t num_hidden; /* number of hidden messages in this view */ 884 + size_t num_hidden; /* number of hidden messages in this view. 885 + * only valid for the root header, when 886 + * collapsed is set. */ 886 887 887 888 short recipient; /* user_is_recipient()'s return value, cached */ 888 889
-1
protos.h
··· 52 52 53 53 #define mutt_collapse_thread(x,y) _mutt_traverse_thread (x,y,MUTT_THREAD_COLLAPSE) 54 54 #define mutt_uncollapse_thread(x,y) _mutt_traverse_thread (x,y,MUTT_THREAD_UNCOLLAPSE) 55 - #define mutt_get_hidden(x,y)_mutt_traverse_thread (x,y,MUTT_THREAD_GET_HIDDEN) 56 55 #define mutt_thread_contains_unread(x,y) _mutt_traverse_thread (x,y,MUTT_THREAD_UNREAD) 57 56 #define mutt_thread_next_unread(x,y) _mutt_traverse_thread(x,y,MUTT_THREAD_NEXT_UNREAD) 58 57 int _mutt_traverse_thread (CONTEXT *ctx, HEADER *hdr, int flag);
+9 -6
thread.c
··· 1132 1132 ctx->vcount++; 1133 1133 ctx->vsize += cur->content->length + cur->content->offset - 1134 1134 cur->content->hdr_offset + padding; 1135 - cur->num_hidden = mutt_get_hidden (ctx, cur); 1136 1135 } 1137 1136 } 1138 1137 } ··· 1146 1145 int min_unread_msgno = INT_MAX, min_unread = cur->virtual; 1147 1146 #define CHECK_LIMIT (!ctx->pattern || cur->limited) 1148 1147 1149 - if ((Sort & SORT_MASK) != SORT_THREADS && !(flag & MUTT_THREAD_GET_HIDDEN)) 1148 + if ((Sort & SORT_MASK) != SORT_THREADS) 1150 1149 { 1151 1150 mutt_error (_("Threading is not enabled.")); 1152 1151 return (cur->virtual); ··· 1194 1193 { 1195 1194 /* return value depends on action requested */ 1196 1195 if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE)) 1196 + { 1197 + if (roothdr) 1198 + roothdr->num_hidden = num_hidden; 1197 1199 return (final); 1200 + } 1198 1201 else if (flag & MUTT_THREAD_UNREAD) 1199 1202 return ((old && new) ? new : (old ? old : new)); 1200 - else if (flag & MUTT_THREAD_GET_HIDDEN) 1201 - return (num_hidden); 1202 1203 else if (flag & MUTT_THREAD_NEXT_UNREAD) 1203 1204 return (min_unread); 1204 1205 } ··· 1280 1281 1281 1282 /* return value depends on action requested */ 1282 1283 if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE)) 1284 + { 1285 + if (roothdr) 1286 + roothdr->num_hidden = num_hidden + 1; 1283 1287 return (final); 1288 + } 1284 1289 else if (flag & MUTT_THREAD_UNREAD) 1285 1290 return ((old && new) ? new : (old ? old : new)); 1286 - else if (flag & MUTT_THREAD_GET_HIDDEN) 1287 - return (num_hidden+1); 1288 1291 else if (flag & MUTT_THREAD_NEXT_UNREAD) 1289 1292 return (min_unread); 1290 1293