console: start migrating hotkeys to keymap

This shifts a set of flow list keys to keymaps. Much more to come.
This commit is contained in:
Aldo Cortesi 2017-04-28 12:10:48 +12:00
parent ce01cb9c09
commit cfae95f5c3
8 changed files with 61 additions and 48 deletions

View File

@ -7,7 +7,8 @@ class Core:
"""
Set an option of the form "key[=value]". When the value is omitted,
booleans are set to true, strings and integers are set to None (if
permitted), and sequences are emptied.
permitted), and sequences are emptied. Boolean values can be true,
false or toggle.
"""
try:
ctx.options.set(spec)

View File

@ -293,6 +293,8 @@ class OptManager:
else:
return None
elif o.typespec == bool:
if optstr == "toggle":
return not o.current()
if not optstr or optstr == "true":
return True
elif optstr == "false":

View File

@ -44,7 +44,7 @@ def common_options(parser, opts):
help="""
Set an option. When the value is omitted, booleans are set to true,
strings and integers are set to None (if permitted), and sequences
are emptied.
are emptied. Boolean values can be true, false or toggle.
"""
)
parser.add_argument(

View File

@ -70,9 +70,6 @@ class LogBufferBox(urwid.ListBox):
self.set_focus(len(self.master.logbuffer) - 1)
elif key == "g":
self.set_focus(0)
elif key == "F":
o = self.master.options
o.console_focus_follow = not o.console_focus_follow
return urwid.ListBox.keypress(self, size, key)
@ -106,9 +103,6 @@ class BodyPile(urwid.Pile):
else:
self.widget_list[1].header = self.inactive_header
key = None
elif key == "e":
self.master.toggle_eventlog()
key = None
# This is essentially a copypasta from urwid.Pile's keypress handler.
# So much for "closed for modification, but open for extension".
@ -362,20 +356,12 @@ class FlowListBox(urwid.ListBox):
self.master.view.clear()
elif key == "Z":
self.master.view.clear_not_marked()
elif key == "e":
self.master.toggle_eventlog()
elif key == "g":
if len(self.master.view):
self.master.view.focus.index = 0
elif key == "G":
if len(self.master.view):
self.master.view.focus.index = len(self.master.view) - 1
elif key == "f":
signals.status_prompt.send(
prompt = "Filter View",
text = self.master.options.view_filter,
callback = self.master.options.setter("view_filter")
)
elif key == "L":
signals.status_prompt_path.send(
self,
@ -402,20 +388,5 @@ class FlowListBox(urwid.ListBox):
keys = orders,
callback = change_order
)
elif key == "F":
o = self.master.options
o.console_focus_follow = not o.console_focus_follow
elif key == "v":
val = not self.master.options.console_order_reversed
self.master.options.console_order_reversed = val
elif key == "W":
if self.master.options.save_stream_file:
self.master.options.save_stream_file = None
else:
signals.status_prompt_path.send(
self,
prompt="Stream flows to",
callback= lambda path: self.master.options.update(save_stream_file=path)
)
else:
return urwid.ListBox.keypress(self, size, key)

View File

@ -2,16 +2,29 @@ import typing
from mitmproxy.tools.console import commandeditor
contexts = {
"commands",
"flowlist",
"flowview",
"global",
"grideditor",
"help",
"options",
}
class Keymap:
def __init__(self, master):
self.executor = commandeditor.CommandExecutor(master)
self.keys = {}
def add(self, key: str, command: str, context: str = "") -> None:
def add(self, key: str, command: str, context: str = "global") -> None:
"""
Add a key to the key map. If context is empty, it's considered to be
a global binding.
"""
if context not in contexts:
raise ValueError("Unsupported context: %s" % context)
d = self.keys.setdefault(context, {})
d[key] = command
@ -25,10 +38,8 @@ class Keymap:
Returns the key if it has not been handled, or None.
"""
cmd = self.get(context, key)
if not cmd:
cmd = self.get("global", key)
if cmd:
return self.executor(cmd)
if cmd != "":
cmd = self.get("", key)
if cmd:
return self.executor(cmd)
return key

View File

@ -82,9 +82,12 @@ class ConsoleCommands:
"""
def __init__(self, master):
self.master = master
self.started = False
def command(self, partial: str) -> None:
"""Prompt for a command."""
"""
Prompt the user to edit a command with a (possilby empty) starting value.
"""
signals.status_prompt_command.send(partial=partial)
def view_commands(self) -> None:
@ -118,6 +121,14 @@ class ConsoleCommands:
l.add_command("console.view.options", self.view_options)
l.add_command("console.view.pop", self.view_pop)
def running(self):
self.started = True
def configure(self, updated):
if self.started:
if "console_eventlog" in updated:
self.master.refresh_view()
def default_keymap(km):
km.add(":", "console.command ''")
@ -127,6 +138,12 @@ def default_keymap(km):
km.add("Q", "console.exit")
km.add("q", "console.view.pop")
km.add("i", "console.command 'set intercept='")
km.add("W", "console.command 'set save_stream_file='")
km.add("F", "set console_focus_follow=toggle", context="flowlist")
km.add("v", "set console_order_reversed=toggle", context="flowlist")
km.add("f", "console.command 'set view_filter='", context="flowlist")
km.add("e", "set console_eventlog=toggle", context="flowlist")
class ConsoleMaster(master.Master):
@ -212,7 +229,7 @@ class ConsoleMaster(master.Master):
def sig_replace_view_state(self, sender):
"""
A view has been pushed onto the stack, and is intended to replace
the current view rather tha creating a new stack entry.
the current view rather than creating a new stack entry.
"""
if len(self.view_stack) > 1:
del self.view_stack[1]
@ -244,8 +261,7 @@ class ConsoleMaster(master.Master):
except ValueError as e:
signals.add_log("Input error: %s" % e, "warn")
def toggle_eventlog(self):
self.options.console_eventlog = not self.options.console_eventlog
def refresh_view(self):
self.view_flowlist()
signals.replace_view_state.send(self)
@ -389,7 +405,7 @@ class ConsoleMaster(master.Master):
)
def view_help(self):
hc = self.view_stack[0].helpctx
hc = self.view_stack[-1].helpctx
signals.push_view_state.send(
self,
window = window.Window(
@ -397,7 +413,8 @@ class ConsoleMaster(master.Master):
help.HelpView(hc),
None,
statusbar.StatusBar(self, help.footer),
None
None,
"help"
)
)
@ -413,6 +430,7 @@ class ConsoleMaster(master.Master):
None,
statusbar.StatusBar(self, options.footer),
options.help_context,
"options"
)
)
@ -427,7 +445,8 @@ class ConsoleMaster(master.Master):
commands.Commands(self),
None,
statusbar.StatusBar(self, commands.footer),
options.help_context,
commands.help_context,
"commands"
)
)
@ -439,7 +458,8 @@ class ConsoleMaster(master.Master):
ge,
None,
statusbar.StatusBar(self, grideditor.base.FOOTER),
ge.make_help()
ge.make_help(),
"grideditor"
)
)
@ -459,7 +479,8 @@ class ConsoleMaster(master.Master):
body,
None,
statusbar.StatusBar(self, flowlist.footer),
flowlist.help_context
flowlist.help_context,
"flowlist"
)
)
@ -472,7 +493,8 @@ class ConsoleMaster(master.Master):
flowview.FlowView(self, self.view, flow, tab_offset),
flowview.FlowViewHeader(self, flow),
statusbar.StatusBar(self, flowview.footer),
flowview.help_context
flowview.help_context,
"flowview"
)
)

View File

@ -5,7 +5,7 @@ from mitmproxy.tools.console import signals
class Window(urwid.Frame):
def __init__(self, master, body, header, footer, helpctx):
def __init__(self, master, body, header, footer, helpctx, keyctx):
urwid.Frame.__init__(
self,
urwid.AttrWrap(body, "background"),
@ -14,6 +14,7 @@ class Window(urwid.Frame):
)
self.master = master
self.helpctx = helpctx
self.keyctx = keyctx
signals.focus.connect(self.sig_focus)
def sig_focus(self, sender, section):
@ -82,4 +83,4 @@ class Window(urwid.Frame):
def keypress(self, size, k):
k = super().keypress(size, k)
return self.master.keymap.handle("", k)
return self.master.keymap.handle(self.keyctx, k)

View File

@ -381,6 +381,11 @@ def test_set():
with pytest.raises(exceptions.OptionsError):
opts.set("bool=wobble")
opts.set("bool=toggle")
assert opts.bool is False
opts.set("bool=toggle")
assert opts.bool is True
opts.set("int=1")
assert opts.int == 1
with pytest.raises(exceptions.OptionsError):