diff options
Diffstat (limited to 'mitmproxy/tools/console/commands.py')
-rw-r--r-- | mitmproxy/tools/console/commands.py | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/mitmproxy/tools/console/commands.py b/mitmproxy/tools/console/commands.py new file mode 100644 index 00000000..76827a99 --- /dev/null +++ b/mitmproxy/tools/console/commands.py @@ -0,0 +1,182 @@ +import urwid +import blinker +import textwrap +from mitmproxy.tools.console import common +from mitmproxy.tools.console import signals + +HELP_HEIGHT = 5 + + +footer = [ + ('heading_key', "enter"), ":edit ", + ('heading_key', "?"), ":help ", +] + + +def _mkhelp(): + text = [] + keys = [ + ("enter", "execute command"), + ] + text.extend(common.format_keyvals(keys, key="key", val="text", indent=4)) + return text + + +help_context = _mkhelp() + + +def fcol(s, width, attr): + s = str(s) + return ( + "fixed", + width, + urwid.Text((attr, s)) + ) + + +command_focus_change = blinker.Signal() + + +class CommandItem(urwid.WidgetWrap): + def __init__(self, walker, cmd, focused): + self.walker, self.cmd, self.focused = walker, cmd, focused + super().__init__(None) + self._w = self.get_widget() + + def get_widget(self): + parts = [ + ("focus", ">> " if self.focused else " "), + ("title", self.cmd.path), + ("text", " "), + ("text", " ".join(self.cmd.paramnames())), + ] + if self.cmd.returntype: + parts.append([ + ("title", " -> "), + ("text", self.cmd.retname()), + ]) + + return urwid.AttrMap( + urwid.Padding(urwid.Text(parts)), + "text" + ) + + def get_edit_text(self): + return self._w[1].get_edit_text() + + def selectable(self): + return True + + def keypress(self, size, key): + return key + + +class CommandListWalker(urwid.ListWalker): + def __init__(self, master): + self.master = master + + self.index = 0 + self.focusobj = None + self.cmds = list(master.commands.commands.values()) + self.cmds.sort(key=lambda x: x.signature_help()) + self.set_focus(0) + + def get_edit_text(self): + return self.focus_obj.get_edit_text() + + def _get(self, pos): + cmd = self.cmds[pos] + return CommandItem(self, cmd, pos == self.index) + + def get_focus(self): + return self.focus_obj, self.index + + def set_focus(self, index): + cmd = self.cmds[index] + self.index = index + self.focus_obj = self._get(self.index) + command_focus_change.send(cmd.help or "") + + def get_next(self, pos): + if pos >= len(self.cmds) - 1: + return None, None + pos = pos + 1 + return self._get(pos), pos + + def get_prev(self, pos): + pos = pos - 1 + if pos < 0: + return None, None + return self._get(pos), pos + + +class CommandsList(urwid.ListBox): + def __init__(self, master): + self.master = master + self.walker = CommandListWalker(master) + super().__init__(self.walker) + + def keypress(self, size, key): + if key == "enter": + foc, idx = self.get_focus() + signals.status_prompt_command.send(partial=foc.cmd.path + " ") + elif key == "m_start": + self.set_focus(0) + self.walker._modified() + elif key == "m_end": + self.set_focus(len(self.walker.cmds) - 1) + self.walker._modified() + return super().keypress(size, key) + + +class CommandHelp(urwid.Frame): + def __init__(self, master): + self.master = master + super().__init__(self.widget("")) + self.set_active(False) + command_focus_change.connect(self.sig_mod) + + def set_active(self, val): + h = urwid.Text("Command Help") + style = "heading" if val else "heading_inactive" + self.header = urwid.AttrWrap(h, style) + + def widget(self, txt): + cols, _ = self.master.ui.get_cols_rows() + return urwid.ListBox( + [urwid.Text(i) for i in textwrap.wrap(txt, cols)] + ) + + def sig_mod(self, txt): + self.set_body(self.widget(txt)) + + +class Commands(urwid.Pile): + keyctx = "commands" + + def __init__(self, master): + oh = CommandHelp(master) + super().__init__( + [ + CommandsList(master), + (HELP_HEIGHT, oh), + ] + ) + self.master = master + + def keypress(self, size, key): + if key == "tab": + self.focus_position = ( + self.focus_position + 1 + ) % len(self.widget_list) + self.widget_list[1].set_active(self.focus_position == 1) + key = None + + # This is essentially a copypasta from urwid.Pile's keypress handler. + # So much for "closed for modification, but open for extension". + item_rows = None + if len(size) == 2: + item_rows = self.get_item_rows(size, focus = True) + i = self.widget_list.index(self.focus_item) + tsize = self.get_item_size(size, i, True, item_rows) + return self.focus_item.keypress(tsize, key) |