summaryrefslogtreecommitdiff
path: root/alot/commands/__init__.py
blob: d0a260214513bbd0a20c473e9d65b42315cdfc00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import os
import sys
import glob
import shlex
import logging
import argparse
import cStringIO

import alot.settings


class Command(object):
    """base class for commands"""
    def __init__(self, prehook=None, posthook=None):
        self.prehook = prehook
        self.posthook = posthook
        self.undoable = False
        self.help = self.__doc__

    def apply(self, caller):
        pass

    @classmethod
    def get_helpstring(cls):
        return cls.__doc__


COMMANDS = {
    'search': {},
    'envelope': {},
    'bufferlist': {},
    'taglist': {},
    'thread': {},
    '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


def lookup_parser(cmdname, mode):
    return lookup_command(cmdname, mode)[1]


class CommandParseError(Exception):
    pass


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)


class registerCommand(object):
    def __init__(self, mode, name, help=None, usage=None,
                 forced={}, arguments=[]):
        self.mode = mode
        self.name = name
        self.help = help
        self.usage = usage
        self.forced = forced
        self.arguments = arguments

    def __call__(self, klass):
        argparser = CommandArgumentParser(description=self.help,
                                          usage=self.usage,
                                          prog=self.name, add_help=False)
        for args, kwargs in self.arguments:
            argparser.add_argument(*args, **kwargs)
        COMMANDS[self.mode][self.name] = (klass, argparser, self.forced)
        return klass


def commandfactory(cmdline, mode='global'):
    # split commandname and parameters
    if not cmdline:
        return None
    logging.debug('mode:%s got commandline "%s"' % (mode, cmdline))
    # allow to shellescape without a space after '!'
    if cmdline.startswith('!'):
        cmdline = 'shellescape \'%s\'' % cmdline[1:]
    args = shlex.split(cmdline.encode('utf-8'))
    args = map(lambda x: x.decode('utf-8'), args)  # get unicode strings
    logging.debug('ARGS: %s' % args)
    cmdname = args[0]
    args = args[1:]

    # unfold aliases
    if alot.settings.config.has_option('command-aliases', cmdname):
        cmdname = alot.settings.config.get('command-aliases', cmdname)

    # 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(parser)
    parms = vars(parser.parse_args(args))
    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

    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)


#def interpret_commandline(cmdline, mode):
#
#    elif cmd == 'shellescape':
#        return commandfactory(cmd, mode=mode, commandstring=params)
#    elif cmd == 'edit':
#        filepath = os.path.expanduser(params)
#        if os.path.isfile(filepath):
#            return commandfactory(cmd, mode=mode, path=filepath)

__all__ = list(filename[:-3] for filename in glob.glob1(os.path.dirname(__file__), '*.py'))