summaryrefslogtreecommitdiff
path: root/alot/commands
diff options
context:
space:
mode:
authorPatrick Totzke <patricktotzke@gmail.com>2011-10-15 10:43:33 +0100
committerPatrick Totzke <patricktotzke@gmail.com>2011-10-15 10:43:33 +0100
commitc4f6b77f34357db44c91ee1492bab1ead62dcd0f (patch)
tree9f583357fa5c6f3b614152417ff20872c567feaf /alot/commands
parentdf06f6019df34fabfc0748744e67d27d62a2a75d (diff)
infrastructure for argparser in commandfactory
Diffstat (limited to 'alot/commands')
-rw-r--r--alot/commands/__init__.py251
-rw-r--r--alot/commands/envelope.py4
-rw-r--r--alot/commands/globals.py69
3 files changed, 182 insertions, 142 deletions
diff --git a/alot/commands/__init__.py b/alot/commands/__init__.py
index c67ad2a2..0adb75f3 100644
--- a/alot/commands/__init__.py
+++ b/alot/commands/__init__.py
@@ -1,6 +1,9 @@
import os
+import sys
import glob
import logging
+import argparse
+import cStringIO
import alot.settings
@@ -26,140 +29,162 @@ COMMANDS = {
'global': {},
}
+def lookup_command(cmdname, mode):
+ """returns commandclass, argparser and forcedparams
+ for `cmdname` in `mode`"""
+ if cmdname in COMMANDS[mode]:
+ return COMMANDS[mode][cmdname]
+ elif cmdname in COMMANDS['global']:
+ return COMMANDS['global'][cmdname]
+ else:
+ return None, None, None
-class registerCommand(object):
- def __init__(self, mode, name, defaultparms):
- self.mode = mode
- self.name = name
- self.defaultparms = defaultparms
+def lookup_parser(cmdname, mode):
+ return lookup_command(cmdname, mode)[1]
- def __call__(self, klass):
- COMMANDS[self.mode][self.name] = (klass, self.defaultparms)
- return klass
+class CommandParseError(Exception):
+ pass
-def register(klass):
- COMMANDS['classes'] = klass
- return klass
+class CommandArgumentParser(argparse.ArgumentParser):
+ """ArgumentParser that raises `CommandParseError`
+ instead of printing to sys.stderr"""
+ def exit(self, message):
+ raise CommandParseError(message)
+ def error(self, message):
+ raise CommandParseError(message)
-def commandfactory(cmdname, mode='global', **kwargs):
- if cmdname in COMMANDS[mode]:
- (cmdclass, parms) = COMMANDS[mode][cmdname]
- elif cmdname in COMMANDS['global']:
- (cmdclass, parms) = COMMANDS['global'][cmdname]
- else:
- logging.error('there is no command %s' % cmdname)
- parms = parms.copy()
- parms.update(kwargs)
- for (key, value) in kwargs.items():
- if callable(value):
- parms[key] = value()
- else:
- parms[key] = value
- parms['prehook'] = alot.settings.hooks.get('pre_' + cmdname)
- parms['posthook'] = alot.settings.hooks.get('post_' + cmdname)
-
- logging.debug('cmd parms %s' % parms)
- return cmdclass(**parms)
+class registerCommand(object):
+ def __init__(self, mode, name, forced={}, arguments=[]):
+ self.argparser = CommandArgumentParser(prog=name, add_help=False)
+ for args,kwargs in arguments:
+ self.argparser.add_argument(*args,**kwargs)
+ self.mode = mode
+ self.name = name
+ self.forced = forced
+ def __call__(self, klass):
+ COMMANDS[self.mode][self.name] = (klass, self.argparser, self.forced)
+ return klass
-def interpret_commandline(cmdline, mode):
- # TODO: use argparser here!
+def commandfactory(cmdline, mode='global'):
+ # split commandname and parameters
if not cmdline:
return None
logging.debug('mode:%s got commandline "%s"' % (mode, cmdline))
args = cmdline.split(' ', 1)
- cmd = args[0]
+ cmdname = args[0]
if args[1:]:
- params = args[1]
+ argstring = args[1]
else:
- params = ''
+ argstring = ''
# unfold aliases
- if alot.settings.config.has_option('command-aliases', cmd):
- cmd = alot.settings.config.get('command-aliases', cmd)
+ if alot.settings.config.has_option('command-aliases', cmdname):
+ cmdname = alot.settings.config.get('command-aliases', cmdname)
# allow to shellescape without a space after '!'
- if cmd.startswith('!'):
- params = cmd[1:] + ' ' + params
- cmd = 'shellescape'
+ if cmdname.startswith('!'):
+ argstring = cmdname[1:] + ' ' + argstring
+ cmdname = 'shellescape'
+
+ # get class, argparser and forced parameter
+ (cmdclass, parser, forcedparms) = lookup_command(cmdname,mode)
+ if cmdclass is None:
+ msg = 'unknown command: %s' % cmdname
+ logging.debug(msg)
+ raise CommandParseError(msg)
+
+ logging.debug('PARSE: %s' % argstring)
+ #logging.debug(parser)
+ parms = vars(parser.parse_args(argstring.split()))
+ logging.debug('PARMS: %s' % parms)
+ logging.debug(parms)
+
+ parms.update(forcedparms)
+ # still needed?
+ #for (key, value) in kwargs.items():
+ # if callable(value):
+ # parms[key] = value()
+ # else:
+ # parms[key] = value
- # check if this command makes sense in current mode
- if cmd not in COMMANDS[mode] and cmd not in COMMANDS['global']:
- logging.debug('unknown command: %s' % (cmd))
- return None
+ parms['prehook'] = alot.settings.hooks.get('pre_' + cmdname)
+ parms['posthook'] = alot.settings.hooks.get('post_' + cmdname)
- if cmd == 'search':
- return commandfactory(cmd, mode=mode, query=params)
- if cmd in ['move', 'sendkey']:
- return commandfactory(cmd, mode=mode, key=params)
- elif cmd == 'compose':
- h = {}
- if params:
- h = {'To': params}
- return commandfactory(cmd, mode=mode, headers=h)
- elif cmd == 'attach':
- return commandfactory(cmd, mode=mode, path=params)
- elif cmd == 'help':
- return commandfactory(cmd, mode=mode, commandline=params)
- elif cmd == 'forward':
- return commandfactory(cmd, mode=mode, inline=(params == '--inline'))
- elif cmd == 'prompt':
- return commandfactory(cmd, mode=mode, startstring=params)
- elif cmd == 'refine':
- if mode == 'search':
- return commandfactory(cmd, mode=mode, query=params)
- elif mode == 'envelope':
- return commandfactory(cmd, mode=mode, key=params)
-
- elif cmd == 'retag':
- return commandfactory(cmd, mode=mode, tagsstring=params)
- elif cmd == 'shellescape':
- return commandfactory(cmd, mode=mode, commandstring=params)
- elif cmd == 'set':
- key, value = params.split(' ', 1)
- return commandfactory(cmd, mode=mode, key=key, value=value)
- elif cmd == 'toggletag':
- return commandfactory(cmd, mode=mode, tags=params.split())
- elif cmd == 'fold':
- return commandfactory(cmd, mode=mode, all=(params == '--all'))
- elif cmd == 'unfold':
- return commandfactory(cmd, mode=mode, all=(params == '--all'))
- elif cmd == 'save':
- args = params.split(' ')
- allset = False
- pathset = None
- if args:
- if args[0] == '--all':
- allset = True
- pathset = ' '.join(args[1:])
- else:
- pathset = params
- return commandfactory(cmd, mode=mode, all=allset, path=pathset)
- elif cmd == 'edit':
- filepath = os.path.expanduser(params)
- if os.path.isfile(filepath):
- return commandfactory(cmd, mode=mode, path=filepath)
- elif cmd == 'print':
- args = [a.strip() for a in params.split()]
- return commandfactory(cmd, mode=mode,
- whole_thread=('--thread' in args),
- separately=('--separately' in args))
- elif cmd == 'pipeto':
- return commandfactory(cmd, mode=mode, command=params)
-
- elif not params and cmd in ['exit', 'flush', 'pyshell', 'taglist',
- 'bclose', 'compose', 'openfocussed',
- 'closefocussed', 'bnext', 'bprevious', 'retag',
- 'refresh', 'bufferlist', 'refineprompt',
- 'reply', 'open', 'groupreply', 'bounce',
- 'openthread', 'toggleheaders', 'send',
- 'cancel', 'reedit', 'select', 'retagprompt']:
- return commandfactory(cmd, mode=mode)
- else:
- return None
+ logging.debug('cmd parms %s' % parms)
+ return cmdclass(**parms)
+
+
+#def interpret_commandline(cmdline, mode):
+#
+# elif cmd == 'compose':
+# h = {}
+# if params:
+# h = {'To': params}
+# return commandfactory(cmd, mode=mode, headers=h)
+# elif cmd == 'attach':
+# return commandfactory(cmd, mode=mode, path=params)
+# elif cmd == 'help':
+# return commandfactory(cmd, mode=mode, commandline=params)
+# elif cmd == 'forward':
+# return commandfactory(cmd, mode=mode, inline=(params == '--inline'))
+# elif cmd == 'prompt':
+# return commandfactory(cmd, mode=mode, startstring=params)
+# elif cmd == 'refine':
+# if mode == 'search':
+# return commandfactory(cmd, mode=mode, query=params)
+# elif mode == 'envelope':
+# return commandfactory(cmd, mode=mode, key=params)
+#
+# elif cmd == 'retag':
+# return commandfactory(cmd, mode=mode, tagsstring=params)
+# elif cmd == 'shellescape':
+# return commandfactory(cmd, mode=mode, commandstring=params)
+# elif cmd == 'set':
+# key, value = params.split(' ', 1)
+# return commandfactory(cmd, mode=mode, key=key, value=value)
+# elif cmd == 'toggletag':
+# return commandfactory(cmd, mode=mode, tags=params.split())
+# elif cmd == 'fold':
+# return commandfactory(cmd, mode=mode, all=(params == '--all'))
+# elif cmd == 'unfold':
+# return commandfactory(cmd, mode=mode, all=(params == '--all'))
+# elif cmd == 'save':
+# args = params.split(' ')
+# allset = False
+# pathset = None
+# if args:
+# if args[0] == '--all':
+# allset = True
+# pathset = ' '.join(args[1:])
+# else:
+# pathset = params
+# return commandfactory(cmd, mode=mode, all=allset, path=pathset)
+# elif cmd == 'edit':
+# filepath = os.path.expanduser(params)
+# if os.path.isfile(filepath):
+# return commandfactory(cmd, mode=mode, path=filepath)
+# elif cmd == 'print':
+# args = [a.strip() for a in params.split()]
+# return commandfactory(cmd, mode=mode,
+# whole_thread=('--thread' in args),
+# separately=('--separately' in args))
+# elif cmd == 'pipeto':
+# return commandfactory(cmd, mode=mode, command=params)
+#
+# elif not params and cmd in ['exit', 'flush', 'pyshell', 'taglist',
+# 'bclose', 'compose', 'openfocussed',
+# 'closefocussed', 'bnext', 'bprevious', 'retag',
+# 'refresh', 'bufferlist', 'refineprompt',
+# 'reply', 'open', 'groupreply', 'bounce',
+# 'openthread', 'toggleheaders', 'send',
+# 'cancel', 'reedit', 'select', 'retagprompt']:
+# return commandfactory(cmd, mode=mode)
+# else:
+# return None
__all__ = list(filename[:-3] for filename in glob.glob1(os.path.dirname(__file__), '*.py'))
diff --git a/alot/commands/envelope.py b/alot/commands/envelope.py
index f37195db..59d1159f 100644
--- a/alot/commands/envelope.py
+++ b/alot/commands/envelope.py
@@ -21,7 +21,9 @@ from alot.commands.globals import EnvelopeOpenCommand
MODE = 'envelope'
-@registerCommand(MODE, 'attach', {})
+@registerCommand(MODE, 'attach', arguments=[
+ (['path'], {'help':'file(s) to attach'})]
+)
class EnvelopeAttachCommand(Command):
def __init__(self, path=None, mail=None, **kwargs):
Command.__init__(self, **kwargs)
diff --git a/alot/commands/globals.py b/alot/commands/globals.py
index 877071e3..a14c551f 100644
--- a/alot/commands/globals.py
+++ b/alot/commands/globals.py
@@ -23,7 +23,7 @@ from alot import commands
MODE = 'global'
-@registerCommand(MODE, 'exit', {})
+@registerCommand(MODE, 'exit')
class ExitCommand(Command):
"""shuts the MUA down cleanly"""
@defer.inlineCallbacks
@@ -35,14 +35,16 @@ class ExitCommand(Command):
ui.exit()
-@registerCommand(MODE, 'search', {})
+@registerCommand(MODE, 'search', arguments=[
+ (['query'], {'nargs':'*', 'default':'', 'help':'search string'})]
+)
class SearchCommand(Command):
"""open a new search buffer"""
def __init__(self, query, **kwargs):
"""
:param query: initial querystring
"""
- self.query = query
+ self.query = ' '.join(query)
Command.__init__(self, **kwargs)
@defer.inlineCallbacks
@@ -65,18 +67,20 @@ class SearchCommand(Command):
ui.notify('empty query string')
-@registerCommand(MODE, 'prompt', {})
+@registerCommand(MODE, 'prompt', arguments=[
+ (['startwith'], {'nargs':'*', 'default':'', 'help':'initial content of commandprompt'})]
+)
class PromptCommand(Command):
"""starts commandprompt"""
- def __init__(self, startstring=u'', **kwargs):
- self.startstring = startstring
+ def __init__(self, startwith=[], **kwargs):
+ self.startwith = ' '.join(startwith)
Command.__init__(self, **kwargs)
def apply(self, ui):
- ui.commandprompt(self.startstring)
+ ui.commandprompt(self.startwith)
-@registerCommand(MODE, 'refresh', {})
+@registerCommand(MODE, 'refresh')
class RefreshCommand(Command):
"""refreshes the current buffer"""
def apply(self, ui):
@@ -84,7 +88,7 @@ class RefreshCommand(Command):
ui.update()
-@registerCommand(MODE, 'shellescape', {})
+@registerCommand(MODE, 'shellescape')
class ExternalCommand(Command):
"""calls external command"""
def __init__(self, commandstring, path=None, spawn=False, refocus=True,
@@ -153,7 +157,7 @@ class ExternalCommand(Command):
ui.mainloop.screen.start()
-@registerCommand(MODE, 'edit', {})
+@registerCommand(MODE, 'edit')
class EditCommand(ExternalCommand):
def __init__(self, path, spawn=None, **kwargs):
self.path = path
@@ -168,7 +172,7 @@ class EditCommand(ExternalCommand):
**kwargs)
-@registerCommand(MODE, 'pyshell', {})
+@registerCommand(MODE, 'pyshell')
class PythonShellCommand(Command):
"""opens an interactive shell for introspection"""
def apply(self, ui):
@@ -177,7 +181,7 @@ class PythonShellCommand(Command):
ui.mainloop.screen.start()
-@registerCommand(MODE, 'bclose', {})
+@registerCommand(MODE, 'bclose')
@registerCommand('bufferlist', 'closefocussed', {'focussed': True})
class BufferCloseCommand(Command):
"""close a buffer"""
@@ -200,9 +204,9 @@ class BufferCloseCommand(Command):
ui.buffer_focus(ui.current_buffer)
-@registerCommand(MODE, 'bprevious', {'offset': -1})
-@registerCommand(MODE, 'bnext', {'offset': +1})
-@registerCommand('bufferlist', 'openfocussed', {}) # todo separate
+@registerCommand(MODE, 'bprevious', forced={'offset': -1})
+@registerCommand(MODE, 'bnext', forced={'offset': +1})
+@registerCommand('bufferlist', 'openfocussed') # todo separate
class BufferFocusCommand(Command):
"""focus a buffer"""
def __init__(self, buffer=None, offset=0, **kwargs):
@@ -225,7 +229,7 @@ class BufferFocusCommand(Command):
ui.buffer_focus(self.buffer)
-@registerCommand(MODE, 'bufferlist', {})
+@registerCommand(MODE, 'bufferlist')
class OpenBufferlistCommand(Command):
"""open a bufferlist buffer"""
def __init__(self, filtfun=None, **kwargs):
@@ -240,7 +244,7 @@ class OpenBufferlistCommand(Command):
ui.buffer_open(buffers.BufferlistBuffer(ui, self.filtfun))
-@registerCommand(MODE, 'taglist', {})
+@registerCommand(MODE, 'taglist')
class TagListCommand(Command):
"""open a taglisat buffer"""
def __init__(self, filtfun=None, **kwargs):
@@ -255,7 +259,7 @@ class TagListCommand(Command):
ui.buffer_focus(buf)
-@registerCommand(MODE, 'flush', {})
+@registerCommand(MODE, 'flush')
class FlushCommand(Command):
"""Flushes writes to the index. Retries until committed"""
def apply(self, ui):
@@ -272,19 +276,24 @@ class FlushCommand(Command):
return
-@registerCommand(MODE, 'help', {})
+@registerCommand(MODE, 'help', arguments=[
+ (['commandname'], {'help':'command'})]
+)
class HelpCommand(Command):
- def __init__(self, commandline='', **kwargs):
+ def __init__(self, commandname='', **kwargs):
Command.__init__(self, **kwargs)
- self.commandline = commandline.strip()
+ self.commandname = commandname
def apply(self, ui):
- if self.commandline:
- cmd = self.commandline.split(' ', 1)[0]
- # TODO: how to I access COMMANDS from below?
- ui.notify('no help for \'%s\'' % cmd, priority='error')
- titletext = 'help for %s' % cmd
- body = urwid.Text('helpstring')
+ ui.logger.debug('HELP')
+ if self.commandname:
+ ui.logger.debug('HELP %s' % self.commandname)
+ parser = commands.lookup_parser(self.commandname, ui.mode)
+ if parser:
+ ui.notify(parser.format_help())
+ else:
+ ui.notify('command %s not known in mode %s' % (self.commandname,
+ ui.mode))
return
else:
# get mappings
@@ -397,12 +406,16 @@ class ComposeCommand(Command):
ui.apply_command(commands.envelope.EnvelopeEditCommand(mail=self.mail))
-@registerCommand(MODE, 'move', {})
+@registerCommand(MODE, 'move', arguments=[
+ (['key'], {'nargs':'+', 'help':'keypress to send'})]
+)
@registerCommand(MODE, 'cancel', {'key': 'cancel'})
@registerCommand(MODE, 'select', {'key': 'select'})
class SendKeypressCommand(Command):
def __init__(self, key, **kwargs):
Command.__init__(self, **kwargs)
+ if isinstance(key, list):
+ key = ' '.join(key)
self.key = key
def apply(self, ui):