mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
acme/Mail: fix redrawn line offsets, add support for flag filters
maintaining ->nsub was fragile, and didn't save very many cycles; instead just compute it every time; it's only going to hurt with a ton of giant threads.
This commit is contained in:
parent
af83b606f9
commit
8e2a071b8b
3 changed files with 93 additions and 48 deletions
|
@ -101,11 +101,17 @@ are listed in
|
||||||
.IR upasfs (4)
|
.IR upasfs (4)
|
||||||
.PD 0
|
.PD 0
|
||||||
.TP
|
.TP
|
||||||
.B Filter [pattern]
|
.B Filter [filter] [*flags]
|
||||||
Shows only messages where the sender or subject match
|
Shows only messages where the sender or subject match
|
||||||
the
|
the
|
||||||
.I pattern
|
.I filter
|
||||||
regexp.
|
regexp,
|
||||||
|
or where the
|
||||||
|
.I flags
|
||||||
|
string matches the state of the message. The flags
|
||||||
|
used are the same as for Mark, with the addition of
|
||||||
|
.I u
|
||||||
|
to represent unread messages.
|
||||||
Filter without an argument resets the filtering,
|
Filter without an argument resets the filtering,
|
||||||
showing all messages again.
|
showing all messages again.
|
||||||
.PD 0
|
.PD 0
|
||||||
|
@ -127,15 +133,12 @@ As with the message view, but applied to the open message.
|
||||||
.TP
|
.TP
|
||||||
.B Mark
|
.B Mark
|
||||||
As with the message view, but applied to the open message.
|
As with the message view, but applied to the open message.
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
The following text commands are recognized by the composition
|
The following text commands are recognized by the composition
|
||||||
window:
|
window:
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B Post
|
.B Post
|
||||||
Sends the message currently being composed.
|
Sends the message currently being composed.
|
||||||
|
|
||||||
.SS Format strings
|
.SS Format strings
|
||||||
The formatting of messages in the list view is controlled by the
|
The formatting of messages in the list view is controlled by the
|
||||||
format string defined through the
|
format string defined through the
|
||||||
|
|
|
@ -16,6 +16,7 @@ enum {
|
||||||
Stoplev = 1<<1, /* not a response to anything */
|
Stoplev = 1<<1, /* not a response to anything */
|
||||||
Sopen = 1<<2, /* opened for viewing */
|
Sopen = 1<<2, /* opened for viewing */
|
||||||
Szap = 1<<3, /* flushed, to be removed from list */
|
Szap = 1<<3, /* flushed, to be removed from list */
|
||||||
|
Shide = 1<<4, /* hidden from view */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -95,7 +96,6 @@ struct Mesg {
|
||||||
Mesg *parent;
|
Mesg *parent;
|
||||||
Mesg **child;
|
Mesg **child;
|
||||||
int nchild;
|
int nchild;
|
||||||
int nsub; /* transitive children */
|
|
||||||
|
|
||||||
Mesg *body; /* best attachment to use, or nil */
|
Mesg *body; /* best attachment to use, or nil */
|
||||||
Mesg **parts;
|
Mesg **parts;
|
||||||
|
|
|
@ -32,6 +32,7 @@ Mesg dead = {.messageid="", .hash=42};
|
||||||
|
|
||||||
Reprog *mesgpat;
|
Reprog *mesgpat;
|
||||||
Reprog *filterpat;
|
Reprog *filterpat;
|
||||||
|
char *filterflags;
|
||||||
|
|
||||||
int threadsort = 1;
|
int threadsort = 1;
|
||||||
int sender;
|
int sender;
|
||||||
|
@ -125,6 +126,45 @@ rcmpmesg(void *pa, void *pb)
|
||||||
return a->time - b->time;
|
return a->time - b->time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
matchfilter(Mesg *m)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
ok = 1;
|
||||||
|
if(filterpat != nil
|
||||||
|
&& m->subject != nil
|
||||||
|
&& m->from != nil){
|
||||||
|
if(!regexec(filterpat, m->subject, nil, 0)
|
||||||
|
&& !regexec(filterpat, m->from, nil, 0))
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
for(p = filterflags; p && *p; p++){
|
||||||
|
switch(*p){
|
||||||
|
case 's': ok = ok && (m->flags & Fseen); break;
|
||||||
|
case 'u': ok = ok && !(m->flags & Fseen); break;
|
||||||
|
case 'a': ok = ok && (m->flags & Fresp); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nsub(Mesg *m)
|
||||||
|
{
|
||||||
|
Mesg *c;
|
||||||
|
int n, i;
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
for(i = 0; i < m->nchild; i++){
|
||||||
|
c = m->child[i];
|
||||||
|
if(!(c->state & (Sdummy|Shide)))
|
||||||
|
n += nsub(c)+1;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mesglineno(Mesg *msg, int *depth)
|
mesglineno(Mesg *msg, int *depth)
|
||||||
{
|
{
|
||||||
|
@ -143,9 +183,9 @@ mesglineno(Mesg *msg, int *depth)
|
||||||
for(i = 0; i < p->nchild; i++){
|
for(i = 0; i < p->nchild; i++){
|
||||||
if(p->child[i] == m)
|
if(p->child[i] == m)
|
||||||
break;
|
break;
|
||||||
o += p->child[i]->nsub + 1;
|
o += nsub(p->child[i]) + 1;
|
||||||
}
|
}
|
||||||
if(!(p->state & (Sdummy|Szap))){
|
if(!(p->state & (Sdummy|Shide))){
|
||||||
o++;
|
o++;
|
||||||
d++;
|
d++;
|
||||||
}
|
}
|
||||||
|
@ -157,8 +197,8 @@ mesglineno(Mesg *msg, int *depth)
|
||||||
if(m == p)
|
if(m == p)
|
||||||
break;
|
break;
|
||||||
if(m->state & Stoplev){
|
if(m->state & Stoplev){
|
||||||
n += mbox.mesg[i]->nsub;
|
n += nsub(mbox.mesg[i]);
|
||||||
if(!(m->state & (Sdummy|Szap)))
|
if(!(m->state & (Sdummy|Szap|Shide)))
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +210,7 @@ mesglineno(Mesg *msg, int *depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
addchild(Mesg *p, Mesg *m, int d)
|
addchild(Mesg *p, Mesg *m)
|
||||||
{
|
{
|
||||||
Mesg *q;
|
Mesg *q;
|
||||||
|
|
||||||
|
@ -182,8 +222,6 @@ addchild(Mesg *p, Mesg *m, int d)
|
||||||
if(m->time > q->time)
|
if(m->time > q->time)
|
||||||
q->time = m->time;
|
q->time = m->time;
|
||||||
}
|
}
|
||||||
for(q = p; q != nil; q = q->parent)
|
|
||||||
q->nsub += d;
|
|
||||||
p->child = erealloc(p->child, ++p->nchild*sizeof(Mesg*));
|
p->child = erealloc(p->child, ++p->nchild*sizeof(Mesg*));
|
||||||
p->child[p->nchild - 1] = m;
|
p->child[p->nchild - 1] = m;
|
||||||
qsort(p->child, p->nchild, sizeof(Mesg*), rcmpmesg);
|
qsort(p->child, p->nchild, sizeof(Mesg*), rcmpmesg);
|
||||||
|
@ -347,7 +385,6 @@ static Mesg*
|
||||||
load(char *name, char *digest, int ins)
|
load(char *name, char *digest, int ins)
|
||||||
{
|
{
|
||||||
Mesg *m, *p;
|
Mesg *m, *p;
|
||||||
int d;
|
|
||||||
|
|
||||||
if(strncmp(name, mbox.path, strlen(mbox.path)) == 0)
|
if(strncmp(name, mbox.path, strlen(mbox.path)) == 0)
|
||||||
name += strlen(mbox.path);
|
name += strlen(mbox.path);
|
||||||
|
@ -357,13 +394,10 @@ load(char *name, char *digest, int ins)
|
||||||
if(digest != nil && strcmp(digest, m->digest) != 0)
|
if(digest != nil && strcmp(digest, m->digest) != 0)
|
||||||
goto error;
|
goto error;
|
||||||
/* if we already have a dummy, populate it */
|
/* if we already have a dummy, populate it */
|
||||||
d = 1;
|
|
||||||
p = lookupid(m->messageid);
|
p = lookupid(m->messageid);
|
||||||
if(p != nil && (p->state & Sdummy)){
|
if(p != nil && (p->state & Sdummy)){
|
||||||
d = p->nsub + 1;
|
|
||||||
m->child = p->child;
|
m->child = p->child;
|
||||||
m->nchild = p->nchild;
|
m->nchild = p->nchild;
|
||||||
m->nsub = p->nsub;
|
|
||||||
mesgclear(p);
|
mesgclear(p);
|
||||||
memcpy(p, m, sizeof(*p));
|
memcpy(p, m, sizeof(*p));
|
||||||
free(m);
|
free(m);
|
||||||
|
@ -387,8 +421,10 @@ load(char *name, char *digest, int ins)
|
||||||
p = lookupid(m->inreplyto);
|
p = lookupid(m->inreplyto);
|
||||||
if(p == nil)
|
if(p == nil)
|
||||||
p = placeholder(m->inreplyto, m->time, ins);
|
p = placeholder(m->inreplyto, m->time, ins);
|
||||||
if(!addchild(p, m, d))
|
if(!addchild(p, m))
|
||||||
m->state |= Stoplev;
|
m->state |= Stoplev;
|
||||||
|
if(!matchfilter(m))
|
||||||
|
m->state |= Shide;
|
||||||
return m;
|
return m;
|
||||||
error:
|
error:
|
||||||
mesgfree(m);
|
mesgfree(m);
|
||||||
|
@ -576,22 +612,12 @@ fmtmesg(Biobuf *bp, char *fmt, Mesg *m, int depth)
|
||||||
Bputc(bp, '\n');
|
Bputc(bp, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
matchfilter(Mesg *m)
|
|
||||||
{
|
|
||||||
if(filterpat == nil
|
|
||||||
|| regexec(filterpat, m->subject, nil, 0)
|
|
||||||
|| regexec(filterpat, m->from, nil, 0))
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
showmesg(Biobuf *bfd, Mesg *m, int depth, int recurse)
|
showmesg(Biobuf *bfd, Mesg *m, int depth, int recurse)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if(!(m->state & Sdummy) && matchfilter(m)){
|
if(!(m->state & (Sdummy|Shide))){
|
||||||
fmtmesg(bfd, listfmt, m, depth);
|
fmtmesg(bfd, listfmt, m, depth);
|
||||||
depth++;
|
depth++;
|
||||||
}
|
}
|
||||||
|
@ -664,7 +690,7 @@ mbmark(char **f, int nf)
|
||||||
static void
|
static void
|
||||||
relinkmsg(Mesg *p, Mesg *m)
|
relinkmsg(Mesg *p, Mesg *m)
|
||||||
{
|
{
|
||||||
Mesg *c, *pp;
|
Mesg *c;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
/* remove child, preserving order */
|
/* remove child, preserving order */
|
||||||
|
@ -674,14 +700,12 @@ relinkmsg(Mesg *p, Mesg *m)
|
||||||
p->child[j++] = p->child[i];
|
p->child[j++] = p->child[i];
|
||||||
}
|
}
|
||||||
p->nchild = j;
|
p->nchild = j;
|
||||||
for(pp = p; pp != nil; pp = pp->parent)
|
|
||||||
pp->nsub -= m->nsub + 1;
|
|
||||||
|
|
||||||
/* reparent children */
|
/* reparent children */
|
||||||
for(i = 0; i < m->nchild; i++){
|
for(i = 0; i < m->nchild; i++){
|
||||||
c = m->child[i];
|
c = m->child[i];
|
||||||
c->parent = nil;
|
c->parent = nil;
|
||||||
addchild(p, c, c->nsub + 1);
|
addchild(p, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -704,17 +728,15 @@ mbflush(char **, int)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ln = mesglineno(m, nil);
|
ln = mesglineno(m, nil);
|
||||||
fprint(mbox.addr, "%d,%d", ln, ln+m->nsub);
|
fprint(mbox.addr, "%d,%d", ln, ln+nsub(m));
|
||||||
write(mbox.data, "", 0);
|
write(mbox.data, "", 0);
|
||||||
if(m->flags & Ftodel)
|
if(m->flags & Ftodel)
|
||||||
fprint(fd, "delete %s %d", mailbox, atoi(m->name));
|
fprint(fd, "delete %s %d", mailbox, atoi(m->name));
|
||||||
|
|
||||||
removeid(m);
|
removeid(m);
|
||||||
m->state |= Szap;
|
m->state |= Szap;
|
||||||
if(p == nil && m->nsub != 0){
|
if(p == nil && nsub(m) != 0)
|
||||||
p = placeholder(m->messageid, m->time, 1);
|
p = placeholder(m->messageid, m->time, 1);
|
||||||
p->nsub = m->nsub + 1;
|
|
||||||
}
|
|
||||||
if(p != nil)
|
if(p != nil)
|
||||||
relinkmsg(p, m);
|
relinkmsg(p, m);
|
||||||
for(j = 0; j < m->nchild; j++)
|
for(j = 0; j < m->nchild; j++)
|
||||||
|
@ -824,10 +846,10 @@ changemesg(Plumbmsg *pm)
|
||||||
for(r = m; r->parent != nil; r = r->parent)
|
for(r = m; r->parent != nil; r = r->parent)
|
||||||
/* nothing */;
|
/* nothing */;
|
||||||
/* Bump whole thread up in list */
|
/* Bump whole thread up in list */
|
||||||
if(r->nsub > 0){
|
if(nsub(r) > 0){
|
||||||
ln = mesglineno(r, nil);
|
ln = mesglineno(r, nil);
|
||||||
nr = r->nsub-1;
|
nr = nsub(r)-1;
|
||||||
if(!(r->state & Sdummy))
|
if(!(r->state & (Sdummy|Shide)))
|
||||||
nr++;
|
nr++;
|
||||||
/*
|
/*
|
||||||
* We can end up with an empty container
|
* We can end up with an empty container
|
||||||
|
@ -881,19 +903,39 @@ redraw(char **, int)
|
||||||
static void
|
static void
|
||||||
filter(char **filt, int nfilt)
|
filter(char **filt, int nfilt)
|
||||||
{
|
{
|
||||||
if(nfilt > 1){
|
Mesg *m;
|
||||||
fprint(2, "filter: only one argument supported");
|
int i;
|
||||||
|
|
||||||
|
if(nfilt > 2){
|
||||||
|
Usage:
|
||||||
|
fprint(2, "usage: Filter [regexp] [*flags]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
free(filterpat);
|
free(filterpat);
|
||||||
|
free(filterflags);
|
||||||
filterpat = nil;
|
filterpat = nil;
|
||||||
if(nfilt == 1){
|
filterflags = nil;
|
||||||
filterpat = regcomp(filt[0]);
|
for(i = 0; i < nfilt; i++){
|
||||||
if(filterpat == nil){
|
if(*filt[i] == '*'){
|
||||||
fprint(2, "Filter: %r");
|
if(filterflags != nil)
|
||||||
return;
|
goto Usage;
|
||||||
|
filterflags = strdup(filt[i]+1);
|
||||||
|
}else{
|
||||||
|
if(filterpat != nil)
|
||||||
|
goto Usage;
|
||||||
|
filterpat = regcomp(filt[i]);
|
||||||
|
if(filterpat == nil){
|
||||||
|
fprint(2, "recomp: %r");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for(i = 0; i < mbox.nmesg; i++){
|
||||||
|
m = mbox.mesg[i];
|
||||||
|
m->state &= ~Shide;
|
||||||
|
if(!matchfilter(m))
|
||||||
|
m->state |= Shide;
|
||||||
|
}
|
||||||
fprint(mbox.addr, ",");
|
fprint(mbox.addr, ",");
|
||||||
showlist();
|
showlist();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue