commit 2ccd8797ea06933ff4c2a568c502c84378ddd80b Author: Damian Johnson atagar@torproject.org Date: Sun Apr 6 17:54:53 2014 -0700
Tab completion support
Adding support for tab completion for commands tor will accept. --- stem/interpretor/__init__.py | 6 +++ stem/interpretor/commands.py | 108 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+)
diff --git a/stem/interpretor/__init__.py b/stem/interpretor/__init__.py index 72711c0..72fba90 100644 --- a/stem/interpretor/__init__.py +++ b/stem/interpretor/__init__.py @@ -12,6 +12,7 @@ import sys
import stem.connection import stem.interpretor.arguments +import stem.interpretor.commands import stem.prereq
from stem.util.term import Attr, Color, format @@ -52,6 +53,11 @@ def main(): sys.exit(1)
with controller: + tab_completer = stem.interpretor.commands.Autocomplete(controller) + readline.parse_and_bind("tab: complete") + readline.set_completer(tab_completer.complete) + readline.set_completer_delims('\n') + while True: try: user_input = raw_input(PROMPT) diff --git a/stem/interpretor/commands.py b/stem/interpretor/commands.py new file mode 100644 index 0000000..3b809b4 --- /dev/null +++ b/stem/interpretor/commands.py @@ -0,0 +1,108 @@ +""" +Handles making requests and formatting the responses. +""" + +TOR_CONTROLLER_COMMANDS = [ + 'SAVECONF', + 'MAPADDRESS', + 'EXTENDCIRCUIT', + 'SETCIRCUITPURPOSE', + 'SETROUTERPURPOSE', + 'ATTACHSTREAM', + #'+POSTDESCRIPTOR', # TODO: needs multi-line support + 'REDIRECTSTREAM', + 'CLOSESTREAM', + 'CLOSECIRCUIT', + 'QUIT', + 'RESOLVE', + 'PROTOCOLINFO', + #'+LOADCONF', # TODO: needs multi-line support + 'TAKEOWNERSHIP', + 'AUTHCHALLENGE', + 'DROPGUARDS', +] + + +def _get_commands(controller): + """ + Provides commands recognized by tor. + """ + + commands = list(TOR_CONTROLLER_COMMANDS) + + # GETINFO commands + + getinfo_options = controller.get_info('info/names', None) + + if getinfo_options: + # Lines are of the form '[option] -- [description]'. This strips '*' from + # options that accept values. + + options = [line.split(' ', 1)[0].rstrip('*') for line in getinfo_options.splitlines()] + + commands += ['GETINFO %s' % opt for opt in options] + else: + commands.append('GETINFO ') + + # GETCONF, SETCONF, and RESETCONF commands + + config_options = controller.get_info('config/names', None) + + if config_options: + # individual options are '[option] [type]' pairs + + entries = [opt.split(' ', 1)[0] for opt in config_options.splitlines()] + + commands += ['GETCONF %s' % opt for opt in entries] + commands += ['SETCONF %s ' % opt for opt in entries] + commands += ['RESETCONF %s' % opt for opt in entries] + else: + commands += ['GETCONF ', 'SETCONF ', 'RESETCONF '] + + # SETEVENT commands + + events = controller.get_info('events/names', None) + + if events: + commands += ['SETEVENTS %s' % event for event in events.split(' ')] + else: + commands.append('SETEVENTS ') + + # USEFEATURE commands + + features = controller.get_info('features/names', None) + + if features: + commands += ['USEFEATURE %s' % feature for feature in features.split(' ')] + else: + commands.append('USEFEATURE ') + + # SIGNAL commands + + signals = controller.get_info('signal/names', None) + + if signals: + commands += ['SIGNAL %s' % signal for signal in signals.split(' ')] + else: + commands.append('SIGNAL ') + + return commands + + +class Autocomplete(object): + def __init__(self, controller): + self._commands = _get_commands(controller) + + def complete(self, text, state): + """ + Provides case insensetive autocompletion options, acting as a functor for + the readlines set_completer function. + """ + + lowercase_text = text.lower() + prefix_matches = [cmd for cmd in self._commands if cmd.lower().startswith(lowercase_text)] + + if state < len(prefix_matches): + return prefix_matches[state] + else: + return None
tor-commits@lists.torproject.org