From d7ee5d8f85044c93f5982756f49be8315d148e4c Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Wed, 20 Dec 2017 09:09:40 +1300 Subject: commander: palette entries, highlight errors - Add palette entries specific to commander - Highlight errors - Introduce an Unknown type to keep track of extra unknown arguments to commands --- mitmproxy/command.py | 2 +- mitmproxy/tools/console/commander/commander.py | 9 ++++++--- mitmproxy/tools/console/palettes.py | 16 ++++++++++++++++ mitmproxy/types.py | 18 ++++++++++++++++++ test/mitmproxy/test_command.py | 8 ++++++-- test/mitmproxy/test_types.py | 9 +++++++++ 6 files changed, 56 insertions(+), 6 deletions(-) diff --git a/mitmproxy/command.py b/mitmproxy/command.py index e01c767c..e1e56d3a 100644 --- a/mitmproxy/command.py +++ b/mitmproxy/command.py @@ -172,7 +172,7 @@ class CommandManager(mitmproxy.types._CommandBase): if parts[i] in self.commands: params[:] = self.commands[parts[i]].paramtypes else: - typ = str + typ = mitmproxy.types.Unknown to = mitmproxy.types.CommandTypes.get(typ, None) valid = False diff --git a/mitmproxy/tools/console/commander/commander.py b/mitmproxy/tools/console/commander/commander.py index 125bb76f..cfe00356 100644 --- a/mitmproxy/tools/console/commander/commander.py +++ b/mitmproxy/tools/console/commander/commander.py @@ -72,10 +72,13 @@ class CommandBuffer(): parts, _ = self.master.commands.parse_partial(self.text) ret = [] for p in parts: - if p.type == mitmproxy.types.Cmd and p.valid: - ret.append(("title", p.value)) + if p.valid: + if p.type == mitmproxy.types.Cmd: + ret.append(("commander_command", p.value)) + else: + ret.append(("text", p.value)) else: - ret.append(("text", p.value)) + ret.append(("commander_invalid", p.value)) ret.append(("text", " ")) return ret diff --git a/mitmproxy/tools/console/palettes.py b/mitmproxy/tools/console/palettes.py index 7fbdcfd8..1ff8a438 100644 --- a/mitmproxy/tools/console/palettes.py +++ b/mitmproxy/tools/console/palettes.py @@ -32,6 +32,9 @@ class Palette: # Grid Editor 'focusfield', 'focusfield_error', 'field_error', 'editfield', + + # Commander + 'commander_command', 'commander_invalid' ] high = None # type: typing.Mapping[str, typing.Sequence[str]] @@ -117,6 +120,10 @@ class LowDark(Palette): focusfield_error = ('dark red', 'light gray'), field_error = ('dark red', 'default'), editfield = ('white', 'default'), + + + commander_command = ('white,bold', 'default'), + commander_invalid = ('light red', 'default'), ) @@ -183,6 +190,9 @@ class LowLight(Palette): focusfield_error = ('dark red', 'light gray'), field_error = ('dark red', 'black'), editfield = ('black', 'default'), + + commander_command = ('dark magenta', 'default'), + commander_invalid = ('light red', 'default'), ) @@ -267,6 +277,9 @@ class SolarizedLight(LowLight): focusfield_error = (sol_red, sol_base2), field_error = (sol_red, 'default'), editfield = (sol_base01, 'default'), + + commander_command = (sol_cyan, 'default'), + commander_invalid = (sol_orange, 'default'), ) @@ -317,6 +330,9 @@ class SolarizedDark(LowDark): focusfield_error = (sol_red, sol_base02), field_error = (sol_red, 'default'), editfield = (sol_base1, 'default'), + + commander_command = (sol_blue, 'default'), + commander_invalid = (sol_orange, 'default'), ) diff --git a/mitmproxy/types.py b/mitmproxy/types.py index bdbd3924..8ae8b309 100644 --- a/mitmproxy/types.py +++ b/mitmproxy/types.py @@ -18,6 +18,10 @@ class Arg(str): pass +class Unknown(str): + pass + + class CutSpec(typing.Sequence[str]): pass @@ -116,6 +120,20 @@ class _StrType(_BaseType): return isinstance(val, str) +class _UnknownType(_BaseType): + typ = Unknown + display = "unknown" + + def completion(self, manager: _CommandBase, t: type, s: str) -> typing.Sequence[str]: + return [] + + def parse(self, manager: _CommandBase, t: type, s: str) -> str: + return s + + def is_valid(self, manager: _CommandBase, typ: typing.Any, val: typing.Any) -> bool: + return False + + class _IntType(_BaseType): typ = int display = "int" diff --git a/test/mitmproxy/test_command.py b/test/mitmproxy/test_command.py index 25df2e61..a989995c 100644 --- a/test/mitmproxy/test_command.py +++ b/test/mitmproxy/test_command.py @@ -78,8 +78,12 @@ class TestCommand: [ "foo bar", [ - command.ParseResult(value = "foo", type = mitmproxy.types.Cmd, valid = False), - command.ParseResult(value = "bar", type = str, valid = True) + command.ParseResult( + value = "foo", type = mitmproxy.types.Cmd, valid = False + ), + command.ParseResult( + value = "bar", type = mitmproxy.types.Unknown, valid = False + ) ], [], ], diff --git a/test/mitmproxy/test_types.py b/test/mitmproxy/test_types.py index 29e86203..72492fa9 100644 --- a/test/mitmproxy/test_types.py +++ b/test/mitmproxy/test_types.py @@ -43,6 +43,15 @@ def test_str(): assert b.parse(tctx.master.commands, str, "foo") == "foo" +def test_unknown(): + with taddons.context() as tctx: + b = mitmproxy.types._UnknownType() + assert b.is_valid(tctx.master.commands, mitmproxy.types.Unknown, "foo") is False + assert b.is_valid(tctx.master.commands, mitmproxy.types.Unknown, 1) is False + assert b.completion(tctx.master.commands, mitmproxy.types.Unknown, "") == [] + assert b.parse(tctx.master.commands, mitmproxy.types.Unknown, "foo") == "foo" + + def test_int(): with taddons.context() as tctx: b = mitmproxy.types._IntType() -- cgit v1.2.3 From 79ca2c843718c56ff7428f50faf1e155f500e3b3 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Wed, 20 Dec 2017 10:07:35 +1300 Subject: commander: command argument underlay Display context-sensitive argument types as an "underlay" in commander. --- mitmproxy/tools/console/commander/commander.py | 16 +++++- mitmproxy/tools/console/palettes.py | 6 ++- test/mitmproxy/test_command.py | 71 ++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 3 deletions(-) diff --git a/mitmproxy/tools/console/commander/commander.py b/mitmproxy/tools/console/commander/commander.py index cfe00356..30e8b13b 100644 --- a/mitmproxy/tools/console/commander/commander.py +++ b/mitmproxy/tools/console/commander/commander.py @@ -69,7 +69,13 @@ class CommandBuffer(): self._cursor = x def render(self): - parts, _ = self.master.commands.parse_partial(self.text) + """ + This function is somewhat tricky - in order to make the cursor + position valid, we have to make sure there is a + character-for-character offset match in the rendered output, up + to the cursor. Beyond that, we can add stuff. + """ + parts, remhelp = self.master.commands.parse_partial(self.text) ret = [] for p in parts: if p.valid: @@ -77,9 +83,15 @@ class CommandBuffer(): ret.append(("commander_command", p.value)) else: ret.append(("text", p.value)) - else: + elif p.value: ret.append(("commander_invalid", p.value)) + else: + ret.append(("text", "")) + ret.append(("text", " ")) + if remhelp: ret.append(("text", " ")) + for v in remhelp: + ret.append(("commander_hint", "%s " % v)) return ret def flatten(self, txt): diff --git a/mitmproxy/tools/console/palettes.py b/mitmproxy/tools/console/palettes.py index 1ff8a438..465fd574 100644 --- a/mitmproxy/tools/console/palettes.py +++ b/mitmproxy/tools/console/palettes.py @@ -34,7 +34,7 @@ class Palette: 'focusfield', 'focusfield_error', 'field_error', 'editfield', # Commander - 'commander_command', 'commander_invalid' + 'commander_command', 'commander_invalid', 'commander_hint' ] high = None # type: typing.Mapping[str, typing.Sequence[str]] @@ -124,6 +124,7 @@ class LowDark(Palette): commander_command = ('white,bold', 'default'), commander_invalid = ('light red', 'default'), + commander_hint = ('dark gray', 'default'), ) @@ -193,6 +194,7 @@ class LowLight(Palette): commander_command = ('dark magenta', 'default'), commander_invalid = ('light red', 'default'), + commander_hint = ('light gray', 'default'), ) @@ -280,6 +282,7 @@ class SolarizedLight(LowLight): commander_command = (sol_cyan, 'default'), commander_invalid = (sol_orange, 'default'), + commander_hint = (sol_base1, 'default'), ) @@ -333,6 +336,7 @@ class SolarizedDark(LowDark): commander_command = (sol_blue, 'default'), commander_invalid = (sol_orange, 'default'), + commander_hint = (sol_base00, 'default'), ) diff --git a/test/mitmproxy/test_command.py b/test/mitmproxy/test_command.py index a989995c..c777192d 100644 --- a/test/mitmproxy/test_command.py +++ b/test/mitmproxy/test_command.py @@ -23,6 +23,10 @@ class TAddon: def cmd3(self, foo: int) -> int: return foo + @command.command("cmd4") + def cmd4(self, a: int, b: str, c: mitmproxy.types.Path) -> str: + return "ok" + @command.command("subcommand") def subcommand(self, cmd: mitmproxy.types.Cmd, *args: mitmproxy.types.Arg) -> str: return "ok" @@ -46,6 +50,10 @@ class TAddon: def path(self, arg: mitmproxy.types.Path) -> None: pass + @command.command("flow") + def flow(self, f: flow.Flow, s: str) -> None: + pass + class TestCommand: def test_varargs(self): @@ -140,6 +148,69 @@ class TestCommand: ], [] ], + [ + "cmd4", + [ + command.ParseResult(value = "cmd4", type = mitmproxy.types.Cmd, valid = True), + ], + ["int", "str", "path"] + ], + [ + "cmd4 ", + [ + command.ParseResult(value = "cmd4", type = mitmproxy.types.Cmd, valid = True), + command.ParseResult(value = "", type = int, valid = False), + ], + ["str", "path"] + ], + [ + "cmd4 1", + [ + command.ParseResult(value = "cmd4", type = mitmproxy.types.Cmd, valid = True), + command.ParseResult(value = "1", type = int, valid = True), + ], + ["str", "path"] + ], + [ + "cmd4 1", + [ + command.ParseResult(value = "cmd4", type = mitmproxy.types.Cmd, valid = True), + command.ParseResult(value = "1", type = int, valid = True), + ], + ["str", "path"] + ], + [ + "flow", + [ + command.ParseResult(value = "flow", type = mitmproxy.types.Cmd, valid = True), + ], + ["flow", "str"] + ], + [ + "flow ", + [ + command.ParseResult(value = "flow", type = mitmproxy.types.Cmd, valid = True), + command.ParseResult(value = "", type = flow.Flow, valid = False), + ], + ["str"] + ], + [ + "flow x", + [ + command.ParseResult(value = "flow", type = mitmproxy.types.Cmd, valid = True), + command.ParseResult(value = "x", type = flow.Flow, valid = False), + ], + ["str"] + ], + [ + "flow x ", + [ + command.ParseResult(value = "flow", type = mitmproxy.types.Cmd, valid = True), + command.ParseResult(value = "x", type = flow.Flow, valid = False), + command.ParseResult(value = "", type = str, valid = True), + ], + [] + ], ] with taddons.context() as tctx: tctx.master.addons.add(TAddon()) -- cgit v1.2.3