192 lines
6.2 KiB
Python
192 lines
6.2 KiB
Python
"""
|
|
This module is reponsible for drawing the quick key help at the bottom of mitmproxy.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Union
|
|
|
|
import urwid
|
|
|
|
from mitmproxy import flow
|
|
from mitmproxy.http import HTTPFlow
|
|
from mitmproxy.tools.console.eventlog import EventLog
|
|
from mitmproxy.tools.console.flowlist import FlowListBox
|
|
from mitmproxy.tools.console.flowview import FlowView
|
|
from mitmproxy.tools.console.grideditor.base import FocusEditor
|
|
from mitmproxy.tools.console.help import HelpView
|
|
from mitmproxy.tools.console.keybindings import KeyBindings
|
|
from mitmproxy.tools.console.keymap import Keymap
|
|
from mitmproxy.tools.console.options import Options
|
|
|
|
|
|
@dataclass
|
|
class BasicKeyHelp:
|
|
"""Quick help for urwid-builtin keybindings (i.e. those keys that do not appear in the keymap)"""
|
|
|
|
key: str
|
|
|
|
|
|
HelpItems = dict[str, Union[str, BasicKeyHelp]]
|
|
"""
|
|
A mapping from the short text that should be displayed in the help bar to the full help text provided for the key
|
|
binding. The order of the items in the dictionary determines the order in which they are displayed in the help bar.
|
|
|
|
Some help items explain builtin urwid functionality, so there is no key binding for them. In this case, the value
|
|
is a BasicKeyHelp object.
|
|
"""
|
|
|
|
|
|
@dataclass
|
|
class QuickHelp:
|
|
top_label: str
|
|
top_items: HelpItems
|
|
bottom_label: str
|
|
bottom_items: HelpItems
|
|
|
|
def make_rows(self, keymap: Keymap) -> tuple[urwid.Columns, urwid.Columns]:
|
|
top = _make_row(self.top_label, self.top_items, keymap)
|
|
bottom = _make_row(self.bottom_label, self.bottom_items, keymap)
|
|
return top, bottom
|
|
|
|
|
|
def make(
|
|
widget: type[urwid.Widget],
|
|
focused_flow: flow.Flow | None,
|
|
is_root_widget: bool,
|
|
) -> QuickHelp:
|
|
top_label = ""
|
|
top_items: HelpItems = {}
|
|
if widget in (FlowListBox, FlowView):
|
|
top_label = "Flow:"
|
|
if focused_flow:
|
|
if widget == FlowListBox:
|
|
top_items["Select"] = "Select"
|
|
else:
|
|
top_items["Edit"] = "Edit a flow component"
|
|
top_items |= {
|
|
"Duplicate": "Duplicate flow",
|
|
"Replay": "Replay this flow",
|
|
"Export": "Export this flow to file",
|
|
"Delete": "Delete flow from view",
|
|
}
|
|
if widget == FlowListBox:
|
|
if focused_flow.marked:
|
|
top_items["Unmark"] = "Toggle mark on this flow"
|
|
else:
|
|
top_items["Mark"] = "Toggle mark on this flow"
|
|
top_items["Edit"] = "Edit a flow component"
|
|
if focused_flow.intercepted:
|
|
top_items["Resume"] = "Resume this intercepted flow"
|
|
if focused_flow.modified():
|
|
top_items["Restore"] = "Revert changes to this flow"
|
|
if isinstance(focused_flow, HTTPFlow) and focused_flow.response:
|
|
top_items["Save body"] = "Save response body to file"
|
|
if widget == FlowView:
|
|
top_items |= {
|
|
"Next flow": "Go to next flow",
|
|
"Prev flow": "Go to previous flow",
|
|
}
|
|
else:
|
|
top_items |= {
|
|
"Load flows": "Load flows from file",
|
|
"Create new": "Create a new flow",
|
|
}
|
|
elif widget == KeyBindings:
|
|
top_label = "Keybindings:"
|
|
top_items |= {
|
|
"Add": "Add a key binding",
|
|
"Edit": "Edit the currently focused key binding",
|
|
"Delete": "Unbind the currently focused key binding",
|
|
"Execute": "Execute the currently focused key binding",
|
|
}
|
|
elif widget == Options:
|
|
top_label = "Options:"
|
|
top_items |= {
|
|
"Edit": BasicKeyHelp("⏎"),
|
|
"Reset": "Reset this option",
|
|
"Reset all": "Reset all options",
|
|
"Load file": "Load from file",
|
|
"Save file": "Save to file",
|
|
}
|
|
elif widget == HelpView:
|
|
top_label = "Help:"
|
|
top_items |= {
|
|
"Scroll down": BasicKeyHelp("↓"),
|
|
"Scroll up": BasicKeyHelp("↑"),
|
|
"Exit help": "Exit help",
|
|
"Next tab": BasicKeyHelp("tab"),
|
|
}
|
|
elif widget == EventLog:
|
|
top_label = "Events:"
|
|
top_items |= {
|
|
"Scroll down": BasicKeyHelp("↓"),
|
|
"Scroll up": BasicKeyHelp("↑"),
|
|
"Clear": "Clear",
|
|
}
|
|
elif issubclass(widget, FocusEditor):
|
|
top_label = f"Edit:"
|
|
top_items |= {
|
|
"Start edit": BasicKeyHelp("⏎"),
|
|
"Stop edit": BasicKeyHelp("esc"),
|
|
"Add row": "Add a row after cursor",
|
|
"Delete row": "Delete this row",
|
|
}
|
|
else:
|
|
pass
|
|
|
|
bottom_label = "Proxy:"
|
|
bottom_items: HelpItems = {
|
|
"Help": "View help",
|
|
}
|
|
if is_root_widget:
|
|
bottom_items["Quit"] = "Exit the current view"
|
|
else:
|
|
bottom_items["Back"] = "Exit the current view"
|
|
bottom_items |= {
|
|
"Events": "View event log",
|
|
"Options": "View options",
|
|
"Intercept": "Set intercept",
|
|
"Filter": "Set view filter",
|
|
}
|
|
if focused_flow:
|
|
bottom_items |= {
|
|
"Save flows": "Save listed flows to file",
|
|
"Clear list": "Clear flow list",
|
|
}
|
|
bottom_items |= {
|
|
"Layout": "Cycle to next layout",
|
|
"Switch": "Focus next layout pane",
|
|
"Follow new": "Set focus follow",
|
|
}
|
|
|
|
label_len = max(len(top_label), len(bottom_label), 8) + 1
|
|
top_label = top_label.ljust(label_len)
|
|
bottom_label = bottom_label.ljust(label_len)
|
|
|
|
return QuickHelp(top_label, top_items, bottom_label, bottom_items)
|
|
|
|
|
|
def _make_row(label: str, items: HelpItems, keymap: Keymap) -> urwid.Columns:
|
|
cols = [
|
|
(len(label), urwid.Text(label)),
|
|
]
|
|
for short, long in items.items():
|
|
if isinstance(long, BasicKeyHelp):
|
|
key_short = long.key
|
|
else:
|
|
b = keymap.binding_for_help(long)
|
|
if b is None:
|
|
continue
|
|
key_short = b.key_short()
|
|
txt = urwid.Text(
|
|
[
|
|
("heading_inactive", key_short),
|
|
" ",
|
|
short,
|
|
],
|
|
wrap="clip",
|
|
)
|
|
cols.append((14, txt))
|
|
|
|
return urwid.Columns(cols)
|