an alternative commandline interface to notmuch
Peter John Hartman
peterjohnhartman at gmail.com
Sat Oct 1 07:29:06 PDT 2011
Hi folks,
Rafa released slmenu, a dmenu-clone that runs on the commandline, and I
contributed a couple of patches (verticality and tokens) to make it work
nice with notmuch. So far, I'm pretty happy. To get slmenu:
hg clone http://hg.suckless.org/slmenu
You'll need to apply the patch at the bottom of this e-mail[1].
You'll also want to use the shell script (nmsl) at the bottom of this e-mail[2].
How it works:
Basically, it pipes output (after being mangled a bit) from notmuch search
into slmenu/dmenu (so you can select the thread) and then sends that output
to mutt, either via mbox (if notmuch is hosted remote) or maildir (if local).
It also allows you to prepend an entry with v (tag viewed), t +tag (tag
tag), x (tag x-del).
All this will be at:
http://durandus.trilidun.org/durandus/code/notmuch-slmenu/
Best,
Peter
[1] token.patch to slmenu
diff -r 7896c4e3bf21 slmenu.c
--- a/slmenu.c Thu Sep 29 12:45:34 2011 +0200
+++ b/slmenu.c Sat Oct 01 10:11:29 2011 -0400
@@ -36,7 +36,7 @@
static void drawmenu(void);
static char *fstrstr(const char*, const char*);
static void insert(const char*, ssize_t);
-static void match(int);
+static void match(void);
static size_t nextrune(int);
static void readstdin(void);
static int run(void);
@@ -60,10 +60,10 @@
void
appenditem(Item *item, Item **list, Item **last) {
- if(!*last)
+ if(*last)
+ (*last)->right = item;
+ else
*list = item;
- else
- (*last)->right = item;
item->left = *last;
item->right = NULL;
*last = item;
@@ -132,13 +132,17 @@
Item *item;
int rw;
+ fprintf(stderr, "\033[H");
+ fprintf(stderr, "\033[2J");
+
/* use default colors */
fprintf(stderr, "\033[0m");
+/* setupterm();
+ putp(clear_screen); */
/* place cursor in first column, clear it */
fprintf(stderr, "\033[0G");
fprintf(stderr, "\033[K");
-
if(prompt)
drawtext(prompt, promptw-4, C_Reverse);
@@ -188,9 +192,10 @@
if(n > 0)
memcpy(&text[cursor], str, n);
cursor += n;
- match(n > 0 && text[cursor] == '\0');
+ match();
}
+/*
void
match(int sub) {
size_t len = strlen(text);
@@ -231,6 +236,60 @@
curr = sel = matches;
calcoffsets();
}
+*/
+
+void
+match(void) {
+ static char **tokv = NULL;
+ static int tokn = 0;
+
+ char buf[sizeof text], *s;
+ int i, tokc = 0;
+ size_t len;
+ Item *item, *lprefix, *lsubstr, *prefixend, *substrend;
+
+ strcpy(buf, text);
+ for(s = strtok(buf, " "); s; tokv[tokc-1] = s, s = strtok(NULL, " "))
+ if(++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
+ die("Can't realloc.");
+/* fprintf("cannot realloc %u bytes\n", tokn * sizeof *tokv); */
+ len = tokc ? strlen(tokv[0]) : 0;
+
+ matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
+ for(item = items; item && item->text; item++) {
+ for(i = 0; i < tokc; i++)
+ if(!fstrstr(item->text, tokv[i]))
+ break;
+ if (i != tokc)
+ continue;
+ if(!tokc || !fstrncmp(tokv[0], item->text, len+1))
+ appenditem(item, &matches, &matchend);
+ else if(!strncmp(tokv[0], item->text, len))
+ appenditem(item, &lprefix, &prefixend);
+ else
+ appenditem(item, &lsubstr, &substrend);
+ }
+ if(lprefix) {
+ if(matches) {
+ matchend->right = lprefix;
+ lprefix->left = matchend;
+ }
+ else
+ matches = lprefix;
+ matchend = prefixend;
+ }
+ if(lsubstr) {
+ if(matches) {
+ matchend->right = lsubstr;
+ lsubstr->left = matchend;
+ }
+ else
+ matches = lsubstr;
+ matchend = substrend;
+ }
+ curr = sel = matches;
+ calcoffsets();
+}
size_t
nextrune(int inc) {
@@ -371,7 +430,7 @@
case CONTROL('J'):
if(sel) strncpy(text, sel->text, sizeof text); /* Complete the input first, when hitting return */
cursor = strlen(text);
- match(TRUE);
+ match();
drawmenu();
/* fallthrough */
case CONTROL(']'):
@@ -441,11 +500,11 @@
break;
strncpy(text, sel->text, sizeof text);
cursor = strlen(text);
- match(TRUE);
+ match();
break;
case CONTROL('K'):
text[cursor] = '\0';
- match(FALSE);
+ match();
break;
case CONTROL('U'):
insert(NULL, 0 - cursor);
@@ -514,7 +573,7 @@
lines=MIN(MAX(lines, 0), mh);
promptw=(prompt?textw(prompt):0);
inputw=MIN(inputw, mw/3);
- match(FALSE);
+ match();
if(barpos!=0) resetline();
drawmenu();
}
@@ -542,7 +601,7 @@
puts("slmenu, © 2011 slmenu engineers, see LICENSE for details");
exit(EXIT_SUCCESS);
}
- else if(!strcmp(argv[i], "-i"))
+ else if(!strcmp(argv[i], "-i"))
fstrncmp = strncasecmp;
else if(!strcmp(argv[i], "-t"))
barpos=1;
[2] nmsl shell script
#!/bin/sh
# Copyright (c) Peter John Hartman (peterjohnhartman at gmail dot com)
#
# * Permission to use, copy, modify, and distribute this software for any
# * purpose with or without fee is hereby granted, provided that the above
# * copyright notice and this permission notice appear in all copies.
# *
# * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
# * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
# * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
maildir="/tmp/maildir"
slmenu="slmenu -i -l 30 -t -p [vtx]:"
#slmenu="dmenu -i -l 30 -p [vtx]:"
notmuch="/usr/local/bin/notmuch"
use_maildir=1
usage() {
cat << EOF
usage: $0 options
Example: nmsl `nmdate 3 days ago`..`nmdate yesterday` tag:inbox
Example: nmsl tag:inbox and tag:unread
nmdate = echo $(date +%s -d "$*")
OPTIONS:
-h Show this message
-s Use ssh
EOF
}
while getopts "hsd:" OPTION
do
case $OPTION in
h)
usage
exit 1
;;
s)
notmuch="ssh trillap /usr/local/bin/notmuch"
use_maildir=0
;;
esac
done
shift $((OPTIND-1))
search=$@
if [[ $search == "" ]]; then
search="tag:inbox and tag:unread"
fi
res=$($notmuch search $search |\
sed -r 's/([^ ]*) ([^[]*)([^]]*\]) ([^;]*); (.*) (\([^)]*\))/\2\5; \4 \3 \6 \1/' |\
$slmenu)
if [[ $res != "" ]]; then
option=$(echo $res | cut -d' ' -f1)
case "$option" in
x)
# delete
thread=$(echo $res | awk -F' ' '{print $NF}')
notmuch tag -unread +x-del $thread
nmsl $*
;;
t)
# tag;
thread=$(echo $res | awk -F' ' '{print $NF}')
notmuch tag $(echo $res | cut -d' ' -f2) -inbox $thread
nmsl $*
;;
v)
# quick -unread
thread=$(echo $res | awk -F' ' '{print $NF}')
notmuch tag -unread $thread
nmsl $*
;;
*)
thread=$(echo $res | awk -F' ' '{print $NF}')
clear
if [[ $use_maildir -eq 1 ]]; then
rm -rf $maildir
mkdir $maildir
mkdir $maildir/cur
mkdir $maildir/new
mkdir $maildir/tmp
$notmuch search --output=files $thread | sed -e 's: :\\ :g' |\
xargs --no-run-if-empty ln -s -t $maildir/cur/
mutt -f $maildir
else
$notmuch show --format=mbox $thread > /tmp/nmsearch.$$
mutt -f /tmp/nmsearch.$$
rm -f -- /tmp/nmsearch.$$
fi
nmsl $*
;;
esac
fi
clear
--
sic dicit magister P
University of Toronto / Fordham University
Collins Hall B06; Office Hours TF10-12
http://individual.utoronto.ca/peterjh
gpg 1024D/ED6EF59B (7D1A 522F D08E 30F6 FA42 B269 B860 352B ED6E F59B)
gpg --keyserver pgp.mit.edu --recv-keys ED6EF59B
More information about the notmuch
mailing list