This is a proposal for a terminal gui for notmuch mail, written in python. The key ideas are: * OOP approach using python * aim at the look and feel of sup for now * use libraries whenever possible. urwid: http://excess.org/urwid/ The notmuch bindings: https://bitbucket.org/spaetz/cnotmuch/ (mailbox: http://docs.python.org/library/mailbox.html) (urwid-satext: http://wiki.goffi.org/wiki/Urwid-satext) (configparser) You need the notmuch python bindings and urwid to run it. I run it like this > python ng.py -l debug.log -d debug and "tail -f debug.log" in another window. At the moment you can play with the global bindings i, open inbox u, open unread s, python shell \, prompts and stars search v, opens log in vim x, closes current buffer tab, tabs between buffers ;, opens bufferlist and buffer specific bindings: in search: enter, select thread, (look at log, not implemented) in bufferlist: d, removes selected buffer, enter selects. Here is an overview of the current architecture. ng.ui.UI contains the main component: it - handles the urwid.MainLoop that manages the screen etc. - contains a logging.logger that can be used for log/debug messages like so: ui.logger.info('hello logworld') - sets up and updates the header/footer/body widgets - is able to open/close/focus buffers (there's a list ui.buffers of currently known buffers, and a ui.current_buffer, pointing to the focussed one) - handles global keybindings. See below for more on bindings. - can apply (and further down the road undo/redo) commands (see command.py) - is able to open a prompt/dialogs ng.db.DBManager interface to the notmuch index. Currently, it has very limited functionality, but should be able to to this: - store the connection settings (index path and whether or not we use a read_only connection..) - two basic methods for reading from and writing to the index. Lets call it dbman.query(querystring) and dbman.update(updatestring). query will return a notmuch.Query object, i haven't bothered with update so far. - a method for all interesting operations on the index These will use self.query/update accordingly. See dbman.count_messages for a simple example ng.buffer.Buffer Is used as a base class for different types of buffers, or display-modi if you like. So far there's only a SearchBuffer (might be a bad name) that displays the result of a search for mail threads, and a BufferListBuffer, that displays a list of buffers. Technically, ng.buffer.Buffer inherits from urwid.AttrMap. This is done so that it can be directly be set as the body-part of the main gui, thereby intercepting (and filter/handle according to local bindings) the key presses. A Buffer - is a widget, that can be drawn,focussed etc as all widgets. - has a pointer to the main ui, a typename, (e.g. search results, display single thread, envelope, logmode) and knows how to summarise itself. - knows how to handle local (mode-specific) keybindings (self.bindings see below) ng.commands.Command Again, a base class for different commands. A command is an object that represents an atomic action. It will only be be applied once (and is then stored in a undo-list somewhere). It - has a typename, that is used to identify a type of command for the command factory - can be applied using cmd.apply with a fixed interface: I assume we want at least pointers to the main ui and the dbman objects here - should contain a help-info about what it does (to automagically create dynamic help-buffers later on) - should know if its undoable, and if so, implement undo() and redo() - points to pre and post hooks. This might get tricky with undoable cmds.. The idea is, that a cmd gets applied (see ng.ui.UI.apply_command), also its pre and posthooks are called if defined. These should have the same signature as cmd.apply(). See settings.hooks for an example. There's a number of commands i already implemented. Each one should be created by the ng.command.factory (which also attaches the hooks) ng.widgets contains the urwid.Widgets i use do draw notmuch objects. There's a Threadline widget for example, that knows how to present a notmuch.thread object as a textline. Should be pretty self explanatory, definitely needs some love. These inherit from AttrMap, which is not very clean i guess, but they must be "selectable" so that we can use them in a urwid.ListBox (which displays a list and can focus elements). Since ThreadlineWidget is selectable it must include a dummy keypress method. ng.hooks this should later on include methods that look for and call? hooks. The idea is that a user might define either python callables or paths to binaries, and ng.hooks.get_hook(hookname) retrieves them so that they can be placed in commands. This might be a totally stupid idea ng.walker Contains urwid.ListWalker derived classes that implement a listwalker (the content-part of a urwid.ListBox widget) that dynamically allocates its content. The thing is that if one makes a query for all threads in the index and naively creating a ng.widgets.ThreadlineWidget for each and placing them in a urwid.Listbox (actually a urwid.SimplieListWalker, and /that/ in a listbox), one this will potentially eat up your memory and take a long time. So ng.walker.IteratorWalker will dynamically create its next element from the next element of the iterator. For now, there's a NotmuchIteratorWalker class that does the same for notmuch's one-time iterators. This is almost certainly a bad idea methinks. Key Bindings Are currently a hash in objects of the classes ng.ui.UI and ng.buffer.Buffer. Obviously, a key is first handed to the current buffer and in case it doesn't handle it, it will be handled by the main UI. Each buffer-subclass comes with its own default local bindings which whi might want to overwrite at some point. I guess we should have a "map" command that takes a buffertype, a key and something to call. For now, a key in the binding hash is a string that represents a keypress (urwid style: 'shift j', 'k' etc are valid). The value is then a pair of (commandtypestring, parameterhash), that is used by the command.factory to instantiate a command with of type commandtypestring with parameters parameterhash. Values in the parameterhash will be called at cmd creation by command.factory if callable. See self.bindings in ng.buffer.BufferListBuffer for an example.