diff options
author | Aldo Cortesi <aldo@corte.si> | 2017-12-15 09:43:09 +1300 |
---|---|---|
committer | Aldo Cortesi <aldo@corte.si> | 2017-12-15 10:07:47 +1300 |
commit | 8c0ba71fd8f2e29e8348c88aa6d653d3161ea20a (patch) | |
tree | 2f49f2ef13002a228e2b6e6cd4f36548dc1321ed | |
parent | 4d358c49fbeafe504cc7b9a8b66ea572c8cbb0ee (diff) | |
download | mitmproxy-8c0ba71fd8f2e29e8348c88aa6d653d3161ea20a.tar.gz mitmproxy-8c0ba71fd8f2e29e8348c88aa6d653d3161ea20a.tar.bz2 mitmproxy-8c0ba71fd8f2e29e8348c88aa6d653d3161ea20a.zip |
commander: tab completion for command names
-rw-r--r-- | mitmproxy/command.py | 2 | ||||
-rw-r--r-- | mitmproxy/tools/console/commander/commander.py | 60 | ||||
-rw-r--r-- | test/mitmproxy/tools/console/test_commander.py | 25 |
3 files changed, 77 insertions, 10 deletions
diff --git a/mitmproxy/command.py b/mitmproxy/command.py index aab721b5..b909dfd5 100644 --- a/mitmproxy/command.py +++ b/mitmproxy/command.py @@ -125,7 +125,7 @@ class Command: class ParseResult(typing.NamedTuple): value: str - type: type + type: typing.Type class CommandManager: diff --git a/mitmproxy/tools/console/commander/commander.py b/mitmproxy/tools/console/commander/commander.py index d961d421..a0c3a3b2 100644 --- a/mitmproxy/tools/console/commander/commander.py +++ b/mitmproxy/tools/console/commander/commander.py @@ -1,23 +1,51 @@ import urwid from urwid.text_layout import calc_coords import typing +import abc import mitmproxy.master import mitmproxy.command -class CompletionState: - def __init__(self, parts: typing.Sequence[mitmproxy.command.ParseResult]) -> None: - self.parts = parts +class Completer: + @abc.abstractmethod + def cycle(self) -> str: + pass + + +class ListCompleter(Completer): + def __init__( + self, + start: str, + options: typing.Sequence[str], + ) -> None: + self.start = start + self.options = [] # type: typing.Sequence[str] + for o in options: + if o.startswith(start): + self.options.append(o) + self.offset = 0 + + def cycle(self) -> str: + if not self.options: + return self.start + ret = self.options[self.offset] + self.offset = (self.offset + 1) % len(self.options) + return ret + + +class CompletionState(typing.NamedTuple): + completer: Completer + parse: typing.Sequence[mitmproxy.command.ParseResult] class CommandBuffer(): def __init__(self, master: mitmproxy.master.Master, start: str = "") -> None: self.master = master self.buf = start - # This is the logical cursor position - the display cursor is one - # character further on. Cursor is always within the range [0:len(buffer)]. + # Cursor is always within the range [0:len(buffer)]. self._cursor = len(self.buf) + self.completion = None # type: CompletionState @property def cursor(self) -> int: @@ -42,16 +70,29 @@ class CommandBuffer(): self.cursor = self.cursor + 1 def cycle_completion(self) -> None: - parts = self.master.commands.parse_partial(self.buf[:self.cursor]) - if parts[-1][1] == str: - return - raise ValueError + if not self.completion: + parts = self.master.commands.parse_partial(self.buf[:self.cursor]) + if parts[-1].type == mitmproxy.command.Cmd: + self.completion = CompletionState( + completer = ListCompleter( + parts[-1].value, + self.master.commands.commands.keys(), + ), + parse = parts, + ) + if self.completion: + nxt = self.completion.completer.cycle() + buf = " ".join([i.value for i in self.completion.parse[:-1]]) + " " + nxt + buf = buf.strip() + self.buf = buf + self.cursor = len(self.buf) def backspace(self) -> None: if self.cursor == 0: return self.buf = self.buf[:self.cursor - 1] + self.buf[self.cursor:] self.cursor = self.cursor - 1 + self.completion = None def insert(self, k: str) -> None: """ @@ -59,6 +100,7 @@ class CommandBuffer(): """ self.buf = self.buf = self.buf[:self.cursor] + k + self.buf[self.cursor:] self.cursor += 1 + self.completion = None class CommandEdit(urwid.WidgetWrap): diff --git a/test/mitmproxy/tools/console/test_commander.py b/test/mitmproxy/tools/console/test_commander.py index 9ef4a318..1ac4c5c6 100644 --- a/test/mitmproxy/tools/console/test_commander.py +++ b/test/mitmproxy/tools/console/test_commander.py @@ -2,6 +2,31 @@ from mitmproxy.tools.console.commander import commander from mitmproxy.test import taddons +class TestListCompleter: + def test_cycle(self): + tests = [ + [ + "", + ["a", "b", "c"], + ["a", "b", "c", "a"] + ], + [ + "xxx", + ["a", "b", "c"], + ["xxx", "xxx", "xxx"] + ], + [ + "b", + ["a", "b", "ba", "bb", "c"], + ["b", "ba", "bb", "b"] + ], + ] + for start, options, cycle in tests: + c = commander.ListCompleter(start, options) + for expected in cycle: + assert c.cycle() == expected + + class TestCommandBuffer: def test_backspace(self): |