diff --git a/mitmproxy/addons/view.py b/mitmproxy/addons/view.py index c7bced94b..dd5795850 100644 --- a/mitmproxy/addons/view.py +++ b/mitmproxy/addons/view.py @@ -353,6 +353,8 @@ class View(collections.Sequence): the view, negative from the end of the view, so that 0 is the first flow, -1 is the last flow. """ + if len(self) == 0: + return if dst < 0: dst = len(self) + dst if dst < 0: diff --git a/mitmproxy/tools/console/commands.py b/mitmproxy/tools/console/commands.py index 84455a88a..76827a999 100644 --- a/mitmproxy/tools/console/commands.py +++ b/mitmproxy/tools/console/commands.py @@ -120,6 +120,12 @@ class CommandsList(urwid.ListBox): 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) @@ -159,7 +165,6 @@ class Commands(urwid.Pile): self.master = master def keypress(self, size, key): - key = common.shortcuts(key) if key == "tab": self.focus_position = ( self.focus_position + 1 diff --git a/mitmproxy/tools/console/common.py b/mitmproxy/tools/console/common.py index 58129bd00..de024d1aa 100644 --- a/mitmproxy/tools/console/common.py +++ b/mitmproxy/tools/console/common.py @@ -77,20 +77,6 @@ def format_keyvals(lst, key="key", val="text", indent=0): return ret -def shortcuts(k): - if k == " ": - k = "page down" - elif k == "ctrl f": - k = "page down" - elif k == "ctrl b": - k = "page up" - elif k == "j": - k = "down" - elif k == "k": - k = "up" - return k - - def fcol(s, attr): s = str(s) return ( diff --git a/mitmproxy/tools/console/flowlist.py b/mitmproxy/tools/console/flowlist.py index b14d27e76..8e28ff0f8 100644 --- a/mitmproxy/tools/console/flowlist.py +++ b/mitmproxy/tools/console/flowlist.py @@ -58,13 +58,12 @@ class LogBufferBox(urwid.ListBox): super().set_focus(index) def keypress(self, size, key): - key = common.shortcuts(key) if key == "z": self.master.clear_events() key = None - elif key == "G": + elif key == "m_end": self.set_focus(len(self.master.logbuffer) - 1) - elif key == "g": + elif key == "m_start": self.set_focus(0) return urwid.ListBox.keypress(self, size, key) @@ -136,8 +135,7 @@ class FlowItem(urwid.WidgetWrap): return True def keypress(self, xxx_todo_changeme, key): - (maxcol,) = xxx_todo_changeme - return common.shortcuts(key) + return key class FlowListWalker(urwid.ListWalker): @@ -183,7 +181,10 @@ class FlowListBox(urwid.ListBox): super().__init__(FlowListWalker(master)) def keypress(self, size, key): - key = common.shortcuts(key) + if key == "m_start": + self.master.commands.call("view.go 0") + elif key == "m_end": + self.master.commands.call("view.go -1") return urwid.ListBox.keypress(self, size, key) def view_changed(self): diff --git a/mitmproxy/tools/console/flowview.py b/mitmproxy/tools/console/flowview.py index c564ce5af..009516101 100644 --- a/mitmproxy/tools/console/flowview.py +++ b/mitmproxy/tools/console/flowview.py @@ -271,7 +271,6 @@ class FlowDetails(tabs.Tabs): def keypress(self, size, key): key = super().keypress(size, key) - key = common.shortcuts(key) return self._w.keypress(size, key) diff --git a/mitmproxy/tools/console/grideditor/base.py b/mitmproxy/tools/console/grideditor/base.py index fa7f04399..35ae655f5 100644 --- a/mitmproxy/tools/console/grideditor/base.py +++ b/mitmproxy/tools/console/grideditor/base.py @@ -253,6 +253,7 @@ FIRST_WIDTH_MIN = 20 class BaseGridEditor(urwid.WidgetWrap): + def __init__( self, master: "mitmproxy.tools.console.master.ConsoleMaster", @@ -345,15 +346,14 @@ class BaseGridEditor(urwid.WidgetWrap): self._w.keypress(size, key) return None - key = common.shortcuts(key) column = self.columns[self.walker.focus_col] - if key == "g": + if key == "m_start": self.walker.set_focus(0) - elif key == "G": + elif key == "m_end": self.walker.set_focus(len(self.walker.lst) - 1) - elif key in ["h", "left"]: + elif key == "left": self.walker.left() - elif key in ["l", "right"]: + elif key == "right": self.walker.right() elif key == "tab": self.walker.tab_next() diff --git a/mitmproxy/tools/console/help.py b/mitmproxy/tools/console/help.py index 334186244..ec0c95d96 100644 --- a/mitmproxy/tools/console/help.py +++ b/mitmproxy/tools/console/help.py @@ -4,7 +4,6 @@ import urwid from mitmproxy import flowfilter from mitmproxy.tools.console import common -from mitmproxy.tools.console import signals from mitmproxy import version @@ -85,14 +84,8 @@ class HelpView(urwid.ListBox): return text def keypress(self, size, key): - key = common.shortcuts(key) - if key == "q": - signals.pop_view_state.send(self) - return None - elif key == "?": - key = None - elif key == "g": + if key == "m_start": self.set_focus(0) - elif key == "G": + elif key == "m_end": self.set_focus(len(self.body.contents)) return urwid.ListBox.keypress(self, size, key) diff --git a/mitmproxy/tools/console/keymap.py b/mitmproxy/tools/console/keymap.py index 3b22d5308..62e2dcfbb 100644 --- a/mitmproxy/tools/console/keymap.py +++ b/mitmproxy/tools/console/keymap.py @@ -4,6 +4,7 @@ from mitmproxy.tools.console import commandeditor SupportedContexts = { + "chooser", "commands", "flowlist", "flowview", diff --git a/mitmproxy/tools/console/master.py b/mitmproxy/tools/console/master.py index 115785d3d..3f46c5e39 100644 --- a/mitmproxy/tools/console/master.py +++ b/mitmproxy/tools/console/master.py @@ -83,6 +83,62 @@ class ConsoleAddon: self.master = master self.started = False + @command.command("console.nav.start") + def nav_start(self) -> None: + """ + Go to the start of a list or scrollable. + """ + self.master.inject_key("m_start") + + @command.command("console.nav.end") + def nav_end(self) -> None: + """ + Go to the end of a list or scrollable. + """ + self.master.inject_key("m_end") + + @command.command("console.nav.up") + def nav_up(self) -> None: + """ + Go up. + """ + self.master.inject_key("up") + + @command.command("console.nav.down") + def nav_down(self) -> None: + """ + Go down. + """ + self.master.inject_key("down") + + @command.command("console.nav.pageup") + def nav_pageup(self) -> None: + """ + Go up. + """ + self.master.inject_key("page up") + + @command.command("console.nav.pagedown") + def nav_pagedown(self) -> None: + """ + Go down. + """ + self.master.inject_key("page down") + + @command.command("console.nav.left") + def nav_left(self) -> None: + """ + Go left. + """ + self.master.inject_key("left") + + @command.command("console.nav.right") + def nav_right(self) -> None: + """ + Go right. + """ + self.master.inject_key("right") + @command.command("console.choose") def console_choose( self, prompt: str, choices: typing.Sequence[str], *cmd: typing.Sequence[str] @@ -101,7 +157,7 @@ class ConsoleAddon: except exceptions.CommandError as e: signals.status_message.send(message=str(e)) - self.master.overlay(overlay.Chooser(prompt, choices, "", callback)) + self.master.overlay(overlay.Chooser(self.master, prompt, choices, "", callback)) ctx.log.info(choices) @command.command("console.choose.cmd") @@ -124,7 +180,7 @@ class ConsoleAddon: except exceptions.CommandError as e: signals.status_message.send(message=str(e)) - self.master.overlay(overlay.Chooser(prompt, choices, "", callback)) + self.master.overlay(overlay.Chooser(self.master, prompt, choices, "", callback)) ctx.log.info(choices) @command.command("console.command") @@ -246,7 +302,7 @@ class ConsoleAddon: signals.status_message.send(message=str(e)) opts = [i.name.lower() for i in contentviews.views] - self.master.overlay(overlay.Chooser("Mode", opts, "", callback)) + self.master.overlay(overlay.Chooser(self.master, "Mode", opts, "", callback)) @command.command("console.flowview.mode") def flowview_mode(self) -> str: @@ -288,6 +344,17 @@ def default_keymap(km): km.add("O", "console.view.options", ["global"]) km.add("Q", "console.exit", ["global"]) km.add("q", "console.view.pop", ["global"]) + + km.add("g", "console.nav.start", ["global"]) + km.add("G", "console.nav.end", ["global"]) + km.add("k", "console.nav.up", ["global"]) + km.add("j", "console.nav.down", ["global"]) + km.add("l", "console.nav.right", ["global"]) + km.add("h", "console.nav.left", ["global"]) + km.add(" ", "console.nav.pagedown", ["global"]) + km.add("ctrl f", "console.nav.pagedown", ["global"]) + km.add("ctrl b", "console.nav.pageup", ["global"]) + km.add("i", "console.command set intercept=", ["global"]) km.add("W", "console.command set save_stream_file=", ["global"]) @@ -308,9 +375,7 @@ def default_keymap(km): ) km.add("f", "console.command set view_filter=", ["flowlist"]) km.add("F", "set console_focus_follow=toggle", ["flowlist"]) - km.add("g", "view.go 0", ["flowlist"]) - km.add("G", "view.go -1", ["flowlist"]) - km.add("l", "console.command cut.clip ", ["flowlist", "flowview"]) + km.add("ctrl l", "console.command cut.clip ", ["flowlist", "flowview"]) km.add("L", "console.command view.load ", ["flowlist"]) km.add("m", "flow.mark.toggle @focus", ["flowlist"]) km.add("M", "view.marked.toggle", ["flowlist"]) @@ -361,6 +426,7 @@ def default_keymap(km): ) km.add("p", "view.focus.prev", ["flowview"]) km.add("m", "console.flowview.mode.set", ["flowview"]) + km.add("tab", "console.nav.right", ["flowview"]) km.add( "z", "console.choose \"Part\" request,response " @@ -523,6 +589,9 @@ class ConsoleMaster(master.Master): self.loop.draw_screen() self.loop.set_alarm_in(0.01, self.ticker) + def inject_key(self, key): + self.loop.process_input([key]) + def run(self): self.ui = urwid.raw_display.Screen() self.ui.set_terminal_properties(256) diff --git a/mitmproxy/tools/console/options.py b/mitmproxy/tools/console/options.py index 68967f91c..03b54f36c 100644 --- a/mitmproxy/tools/console/options.py +++ b/mitmproxy/tools/console/options.py @@ -214,10 +214,10 @@ class OptionsList(urwid.ListBox): foc.opt.name, self.master.options.default(foc.opt.name) ) - elif key == "g": + elif key == "m_start": self.set_focus(0) self.walker._modified() - elif key == "G": + elif key == "m_end": self.set_focus(len(self.walker.opts) - 1) self.walker._modified() elif key == "l": @@ -299,7 +299,6 @@ class Options(urwid.Pile): self.master = master def keypress(self, size, key): - key = common.shortcuts(key) if key == "tab": self.focus_position = ( self.focus_position + 1 diff --git a/mitmproxy/tools/console/overlay.py b/mitmproxy/tools/console/overlay.py index 7e05fe813..2fa6aa46b 100644 --- a/mitmproxy/tools/console/overlay.py +++ b/mitmproxy/tools/console/overlay.py @@ -80,7 +80,8 @@ class ChooserListWalker(urwid.ListWalker): class Chooser(urwid.WidgetWrap): - def __init__(self, title, choices, current, callback): + def __init__(self, master, title, choices, current, callback): + self.master = master self.choices = choices self.callback = callback choicewidth = max([len(i) for i in choices]) @@ -103,7 +104,7 @@ class Chooser(urwid.WidgetWrap): return True def keypress(self, size, key): - key = common.shortcuts(key) + key = self.master.keymap.handle("chooser", key) if key == "enter": self.callback(self.choices[self.walker.index]) signals.pop_view_state.send(self) diff --git a/mitmproxy/tools/console/searchable.py b/mitmproxy/tools/console/searchable.py index bb19135fe..f2bb56126 100644 --- a/mitmproxy/tools/console/searchable.py +++ b/mitmproxy/tools/console/searchable.py @@ -35,10 +35,10 @@ class Searchable(urwid.ListBox): self.find_next(False) elif key == "N": self.find_next(True) - elif key == "g": + elif key == "m_start": self.set_focus(0) self.walker._modified() - elif key == "G": + elif key == "m_end": self.set_focus(len(self.walker) - 1) self.walker._modified() else: diff --git a/mitmproxy/tools/console/select.py b/mitmproxy/tools/console/select.py index a990dff87..f7e5d950b 100644 --- a/mitmproxy/tools/console/select.py +++ b/mitmproxy/tools/console/select.py @@ -113,7 +113,6 @@ class Select(urwid.ListBox): if key == "enter" or key == " ": self.get_focus()[0].option.activate() return None - key = common.shortcuts(key) if key in self.keymap: self.keymap[key].activate() self.set_focus(self.options.index(self.keymap[key])) diff --git a/mitmproxy/tools/console/tabs.py b/mitmproxy/tools/console/tabs.py index 4f5f270aa..93d6909e8 100644 --- a/mitmproxy/tools/console/tabs.py +++ b/mitmproxy/tools/console/tabs.py @@ -35,9 +35,9 @@ class Tabs(urwid.WidgetWrap): def keypress(self, size, key): n = len(self.tabs) - if key in ["tab", "l"]: + if key == "right": self.change_tab((self.tab_offset + 1) % n) - elif key == "h": + elif key == "left": self.change_tab((self.tab_offset - 1) % n) return self._w.keypress(size, key) diff --git a/mitmproxy/tools/console/window.py b/mitmproxy/tools/console/window.py index ed29465eb..63f189ec1 100644 --- a/mitmproxy/tools/console/window.py +++ b/mitmproxy/tools/console/window.py @@ -76,6 +76,8 @@ class Window(urwid.Frame): self.call(self.focus, "view_popping") def push(self, wname): + if self.primary_stack and self.primary_stack[-1] == wname: + return self.primary_stack.append(wname) self.body = urwid.AttrWrap( self.windows[wname], "background" diff --git a/test/mitmproxy/addons/test_view.py b/test/mitmproxy/addons/test_view.py index ef969d1da..6da136502 100644 --- a/test/mitmproxy/addons/test_view.py +++ b/test/mitmproxy/addons/test_view.py @@ -221,6 +221,7 @@ def test_resolve(): def test_movement(): v = view.View() with taddons.context(): + v.go(0) v.add([ tflow.tflow(), tflow.tflow(),