mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2025-01-30 14:58:38 +00:00
console: auto-generate context key binding help
This commit is contained in:
parent
fd68eca351
commit
7caa7e7538
@ -1,7 +1,6 @@
|
||||
import urwid
|
||||
import blinker
|
||||
import textwrap
|
||||
from mitmproxy.tools.console import common
|
||||
from mitmproxy.tools.console import layoutwidget
|
||||
from mitmproxy.tools.console import signals
|
||||
|
||||
|
@ -5,7 +5,6 @@ from mitmproxy.tools.console import layoutwidget
|
||||
import mitmproxy.tools.console.master # noqa
|
||||
|
||||
|
||||
|
||||
class FlowItem(urwid.WidgetWrap):
|
||||
|
||||
def __init__(self, master, flow):
|
||||
|
@ -80,10 +80,10 @@ class GridRow(urwid.WidgetWrap):
|
||||
) -> None:
|
||||
self.focused = focused
|
||||
self.editor = editor
|
||||
self.edit_col = None # type: Optional[Cell]
|
||||
self.edit_col = None # type: typing.Optional[Cell]
|
||||
|
||||
errors = values[1]
|
||||
self.fields = [] # type: Sequence[Any]
|
||||
self.fields = [] # type: typing.Sequence[typing.Any]
|
||||
for i, v in enumerate(values[0]):
|
||||
if focused == i and editing:
|
||||
self.edit_col = self.editor.columns[i].Edit(v)
|
||||
@ -132,11 +132,11 @@ class GridWalker(urwid.ListWalker):
|
||||
lst: typing.Iterable[list],
|
||||
editor: "GridEditor"
|
||||
) -> None:
|
||||
self.lst = [(i, set()) for i in lst] # type: Sequence[Tuple[Any, Set]]
|
||||
self.lst = [(i, set()) for i in lst] # type: typing.Sequence[typing.Tuple[typing.Any, typing.Set]]
|
||||
self.editor = editor
|
||||
self.focus = 0
|
||||
self.focus_col = 0
|
||||
self.edit_row = None # type: Optional[GridRow]
|
||||
self.edit_row = None # type: typing.Optional[GridRow]
|
||||
|
||||
def _modified(self):
|
||||
self.editor.show_empty_msg()
|
||||
@ -266,6 +266,7 @@ FIRST_WIDTH_MIN = 20
|
||||
|
||||
|
||||
class BaseGridEditor(urwid.WidgetWrap):
|
||||
title = ""
|
||||
keyctx = "grideditor"
|
||||
|
||||
def __init__(
|
||||
@ -317,7 +318,7 @@ class BaseGridEditor(urwid.WidgetWrap):
|
||||
signals.footer_help.send(self, helptext="")
|
||||
self.show_empty_msg()
|
||||
|
||||
def view_popping(self):
|
||||
def layout_popping(self):
|
||||
res = []
|
||||
for i in self.walker.lst:
|
||||
if not i[1] and any([x for x in i[0]]):
|
||||
@ -445,7 +446,7 @@ class BaseGridEditor(urwid.WidgetWrap):
|
||||
|
||||
class GridEditor(BaseGridEditor):
|
||||
title = None # type: str
|
||||
columns = None # type: Sequence[Column]
|
||||
columns = None # type: typing.Sequence[Column]
|
||||
keyctx = "grideditor"
|
||||
|
||||
def __init__(
|
||||
@ -501,8 +502,8 @@ class FocusEditor(urwid.WidgetWrap, layoutwidget.LayoutWidget):
|
||||
def key_responder(self):
|
||||
return self._w
|
||||
|
||||
def view_popping(self):
|
||||
self.call(self._w, "view_popping")
|
||||
def layout_popping(self):
|
||||
self.call(self._w, "layout_popping")
|
||||
|
||||
def focus_changed(self):
|
||||
if self.master.view.focus.flow:
|
||||
|
@ -9,7 +9,6 @@ from mitmproxy.tools.console.grideditor import col_text
|
||||
from mitmproxy.tools.console.grideditor import col_bytes
|
||||
from mitmproxy.tools.console.grideditor import col_subgrid
|
||||
from mitmproxy.tools.console import signals
|
||||
from mitmproxy.net.http import user_agents
|
||||
from mitmproxy.net.http import Headers
|
||||
|
||||
|
||||
|
@ -1,35 +1,67 @@
|
||||
import platform
|
||||
|
||||
import urwid
|
||||
|
||||
from mitmproxy import flowfilter
|
||||
from mitmproxy.tools.console import common
|
||||
from mitmproxy.tools.console import layoutwidget
|
||||
|
||||
from mitmproxy import version
|
||||
|
||||
footer = [
|
||||
("heading", 'mitmproxy {} (Python {}) '.format(version.VERSION, platform.python_version())),
|
||||
('heading_key', "q"), ":back ",
|
||||
]
|
||||
from mitmproxy.tools.console import tabs
|
||||
|
||||
|
||||
class HelpView(urwid.ListBox, layoutwidget.LayoutWidget):
|
||||
class HelpView(tabs.Tabs, layoutwidget.LayoutWidget):
|
||||
title = "Help"
|
||||
keyctx = "help"
|
||||
|
||||
def __init__(self, help_context):
|
||||
urwid.ListBox.__init__(
|
||||
self,
|
||||
self.helptext()
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
self.helpctx = ""
|
||||
super().__init__(
|
||||
[
|
||||
[self.keybindings_title, self.keybindings],
|
||||
[self.filtexp_title, self.filtexp],
|
||||
]
|
||||
)
|
||||
|
||||
def helptext(self):
|
||||
def keybindings_title(self):
|
||||
return "Key Bindings"
|
||||
|
||||
def format_keys(self, binds):
|
||||
kvs = []
|
||||
for b in binds:
|
||||
k = b.key
|
||||
if b.key == " ":
|
||||
k = "space"
|
||||
kvs.append((k, b.command))
|
||||
return common.format_keyvals(kvs)
|
||||
|
||||
def keybindings(self):
|
||||
text = [
|
||||
urwid.Text(
|
||||
[
|
||||
("title", "Keybindings for this view")
|
||||
]
|
||||
)
|
||||
]
|
||||
if self.helpctx:
|
||||
text.extend(self.format_keys(self.master.keymap.list(self.helpctx)))
|
||||
|
||||
text.append(
|
||||
urwid.Text(
|
||||
[
|
||||
"\n",
|
||||
("title", "Global Keybindings"),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
text.extend(self.format_keys(self.master.keymap.list("global")))
|
||||
|
||||
return urwid.ListBox(text)
|
||||
|
||||
def filtexp_title(self):
|
||||
return "Filter Expressions"
|
||||
|
||||
def filtexp(self):
|
||||
text = []
|
||||
|
||||
text.append(urwid.Text([("head", "\n\nFilter expressions:\n")]))
|
||||
text.extend(common.format_keyvals(flowfilter.help, key="key", val="text", indent=4))
|
||||
|
||||
text.append(
|
||||
urwid.Text(
|
||||
[
|
||||
@ -51,11 +83,11 @@ class HelpView(urwid.ListBox, layoutwidget.LayoutWidget):
|
||||
text.extend(
|
||||
common.format_keyvals(examples, key="key", val="text", indent=4)
|
||||
)
|
||||
return text
|
||||
return urwid.ListBox(text)
|
||||
|
||||
def keypress(self, size, key):
|
||||
if key == "m_start":
|
||||
self.set_focus(0)
|
||||
elif key == "m_end":
|
||||
self.set_focus(len(self.body.contents))
|
||||
return urwid.ListBox.keypress(self, size, key)
|
||||
def layout_pushed(self, prev):
|
||||
"""
|
||||
We are just about to push a window onto the stack.
|
||||
"""
|
||||
self.helpctx = prev.keyctx
|
||||
self.show()
|
||||
|
@ -49,6 +49,11 @@ class Keymap:
|
||||
return self.keys[context].get(key, None)
|
||||
return None
|
||||
|
||||
def list(self, context: str) -> typing.Sequence[Binding]:
|
||||
b = [b for b in self.bindings if context in b.contexts]
|
||||
b.sort(key=lambda x: x.key)
|
||||
return b
|
||||
|
||||
def handle(self, context: str, key: str) -> typing.Optional[str]:
|
||||
"""
|
||||
Returns the key if it has not been handled, or None.
|
||||
|
@ -6,7 +6,7 @@ class LayoutWidget:
|
||||
overlay must comply with this API.
|
||||
"""
|
||||
# Title is only required for windows, not overlay components
|
||||
title = None
|
||||
title = ""
|
||||
keyctx = ""
|
||||
|
||||
def key_responder(self):
|
||||
@ -29,8 +29,14 @@ class LayoutWidget:
|
||||
"""
|
||||
pass
|
||||
|
||||
def view_popping(self):
|
||||
def layout_popping(self):
|
||||
"""
|
||||
We are just about to pop a window off the stack, or exit an overlay.
|
||||
"""
|
||||
pass
|
||||
|
||||
def layout_pushed(self, prev):
|
||||
"""
|
||||
We have just pushed a window onto the stack.
|
||||
"""
|
||||
pass
|
||||
|
@ -6,7 +6,6 @@ from typing import Optional, Sequence
|
||||
|
||||
from mitmproxy import exceptions
|
||||
from mitmproxy import optmanager
|
||||
from mitmproxy.tools.console import common
|
||||
from mitmproxy.tools.console import layoutwidget
|
||||
from mitmproxy.tools.console import signals
|
||||
from mitmproxy.tools.console import overlay
|
||||
|
@ -35,8 +35,8 @@ class SimpleOverlay(urwid.Overlay, layoutwidget.LayoutWidget):
|
||||
def view_changed(self):
|
||||
return self.widget.view_changed()
|
||||
|
||||
def view_popping(self):
|
||||
return self.widget.view_popping()
|
||||
def layout_popping(self):
|
||||
return self.widget.layout_popping()
|
||||
|
||||
|
||||
class Choice(urwid.WidgetWrap):
|
||||
@ -157,5 +157,5 @@ class OptionsOverlay(urwid.WidgetWrap, layoutwidget.LayoutWidget):
|
||||
def key_responder(self):
|
||||
return self.ge.key_responder()
|
||||
|
||||
def view_popping(self):
|
||||
return self.ge.view_popping()
|
||||
def layout_popping(self):
|
||||
return self.ge.layout_popping()
|
||||
|
@ -30,7 +30,7 @@ class WindowStack:
|
||||
flowview = flowview.FlowView(master),
|
||||
commands = commands.Commands(master),
|
||||
options = options.Options(master),
|
||||
help = help.HelpView(None),
|
||||
help = help.HelpView(master),
|
||||
eventlog = eventlog.EventLog(master),
|
||||
|
||||
edit_focus_query = grideditor.QueryEditor(master),
|
||||
@ -66,20 +66,21 @@ class WindowStack:
|
||||
def push(self, wname):
|
||||
if self.stack[-1] == wname:
|
||||
return
|
||||
prev = self.top_window()
|
||||
self.stack.append(wname)
|
||||
self.call("layout_pushed", prev)
|
||||
|
||||
def pop(self, *args, **kwargs):
|
||||
"""
|
||||
Pop off the stack, return True if we're already at the top.
|
||||
"""
|
||||
if self.overlay:
|
||||
self.call("view_popping")
|
||||
self.overlay = None
|
||||
elif len(self.stack) > 1:
|
||||
self.call("view_popping")
|
||||
self.stack.pop()
|
||||
else:
|
||||
if not self.overlay and len(self.stack) == 1:
|
||||
return True
|
||||
self.call("layout_popping")
|
||||
if self.overlay:
|
||||
self.overlay = None
|
||||
else:
|
||||
self.stack.pop()
|
||||
|
||||
def call(self, name, *args, **kwargs):
|
||||
"""
|
||||
|
@ -1,11 +0,0 @@
|
||||
import mitmproxy.tools.console.help as help
|
||||
|
||||
from ....conftest import skip_appveyor
|
||||
|
||||
|
||||
@skip_appveyor
|
||||
class TestHelp:
|
||||
|
||||
def test_helptext(self):
|
||||
h = help.HelpView(None)
|
||||
assert h.helptext()
|
@ -5,25 +5,28 @@ import pytest
|
||||
|
||||
|
||||
def test_bind():
|
||||
with taddons.context() as tctx:
|
||||
km = keymap.Keymap(tctx.master)
|
||||
km.executor = mock.Mock()
|
||||
with taddons.context() as tctx:
|
||||
km = keymap.Keymap(tctx.master)
|
||||
km.executor = mock.Mock()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
km.add("foo", "bar", ["unsupported"])
|
||||
with pytest.raises(ValueError):
|
||||
km.add("foo", "bar", ["unsupported"])
|
||||
|
||||
km.add("key", "str", ["options", "commands"])
|
||||
assert km.get("options", "key")
|
||||
assert km.get("commands", "key")
|
||||
assert not km.get("flowlist", "key")
|
||||
km.add("key", "str", ["options", "commands"])
|
||||
assert km.get("options", "key")
|
||||
assert km.get("commands", "key")
|
||||
assert not km.get("flowlist", "key")
|
||||
assert len((km.list("commands"))) == 1
|
||||
|
||||
km.handle("unknown", "unknown")
|
||||
assert not km.executor.called
|
||||
km.handle("unknown", "unknown")
|
||||
assert not km.executor.called
|
||||
|
||||
km.handle("options", "key")
|
||||
assert km.executor.called
|
||||
km.handle("options", "key")
|
||||
assert km.executor.called
|
||||
|
||||
km.add("glob", "str", ["global"])
|
||||
km.executor = mock.Mock()
|
||||
km.handle("options", "glob")
|
||||
assert km.executor.called
|
||||
km.add("glob", "str", ["global"])
|
||||
km.executor = mock.Mock()
|
||||
km.handle("options", "glob")
|
||||
assert km.executor.called
|
||||
|
||||
assert len((km.list("global"))) == 1
|
||||
|
Loading…
Reference in New Issue
Block a user