[tor-commits] [stem/master] Tab completion support

atagar at torproject.org atagar at torproject.org
Tue May 6 01:21:13 UTC 2014


commit 2ccd8797ea06933ff4c2a568c502c84378ddd80b
Author: Damian Johnson <atagar at 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





More information about the tor-commits mailing list