codekingpro/portable-devtools
114k
1"""2This module is reponsible for drawing the quick key help at the bottom of mitmproxy.3"""4 5from dataclasses import dataclass6from typing import Union7 8import urwid9 10from mitmproxy import flow11from mitmproxy.http import HTTPFlow12from mitmproxy.tools.console.eventlog import EventLog13from mitmproxy.tools.console.flowlist import FlowListBox14from mitmproxy.tools.console.flowview import FlowView15from mitmproxy.tools.console.grideditor.base import FocusEditor16from mitmproxy.tools.console.help import HelpView17from mitmproxy.tools.console.keybindings import KeyBindings18from mitmproxy.tools.console.keymap import Keymap19from mitmproxy.tools.console.options import Options20 21 22@dataclass23class BasicKeyHelp:24 """Quick help for urwid-builtin keybindings (i.e. those keys that do not appear in the keymap)"""25 26 key: str27 28 29HelpItems = dict[str, Union[str, BasicKeyHelp]]30"""31A mapping from the short text that should be displayed in the help bar to the full help text provided for the key32binding. The order of the items in the dictionary determines the order in which they are displayed in the help bar.33 34Some help items explain builtin urwid functionality, so there is no key binding for them. In this case, the value35is a BasicKeyHelp object.36"""37 38 39@dataclass40class QuickHelp:41 top_label: str42 top_items: HelpItems43 bottom_label: str44 bottom_items: HelpItems45 46 def make_rows(self, keymap: Keymap) -> tuple[urwid.Columns, urwid.Columns]:47 top = _make_row(self.top_label, self.top_items, keymap)48 bottom = _make_row(self.bottom_label, self.bottom_items, keymap)49 return top, bottom50 51 52def make(53 widget: type[urwid.Widget],54 focused_flow: flow.Flow | None,55 is_root_widget: bool,56) -> QuickHelp:57 top_label = ""58 top_items: HelpItems = {}59 if widget in (FlowListBox, FlowView):60 top_label = "Flow:"61 if focused_flow:62 if widget == FlowListBox:63 top_items["Select"] = "Select"64 else:65 top_items["Edit"] = "Edit a flow component"66 top_items |= {67 "Duplicate": "Duplicate flow",68 "Replay": "Replay this flow",69 "Export": "Export this flow to file",70 "Delete": "Delete flow from view",71 }72 if widget == FlowListBox:73 if focused_flow.marked:74 top_items["Unmark"] = "Toggle mark on this flow"75 else:76 top_items["Mark"] = "Toggle mark on this flow"77 top_items["Edit"] = "Edit a flow component"78 if focused_flow.intercepted:79 top_items["Resume"] = "Resume this intercepted flow"80 if focused_flow.modified():81 top_items["Restore"] = "Revert changes to this flow"82 if isinstance(focused_flow, HTTPFlow) and focused_flow.response:83 top_items["Save body"] = "Save response body to file"84 if widget == FlowView:85 top_items |= {86 "Next flow": "Go to next flow",87 "Prev flow": "Go to previous flow",88 }89 else:90 top_items |= {91 "Load flows": "Load flows from file",92 "Create new": "Create a new flow",93 }94 elif widget == KeyBindings:95 top_label = "Keybindings:"96 top_items |= {97 "Add": "Add a key binding",98 "Edit": "Edit the currently focused key binding",99 "Delete": "Unbind the currently focused key binding",100 "Execute": "Execute the currently focused key binding",101 }102 elif widget == Options:103 top_label = "Options:"104 top_items |= {105 "Edit": BasicKeyHelp("⏎"),106 "Reset": "Reset this option",107 "Reset all": "Reset all options",108 "Load file": "Load from file",109 "Save file": "Save to file",110 }111 elif widget == HelpView:112 top_label = "Help:"113 top_items |= {114 "Scroll down": BasicKeyHelp("↓"),115 "Scroll up": BasicKeyHelp("↑"),116 "Exit help": "Exit help",117 "Next tab": BasicKeyHelp("tab"),118 }119 elif widget == EventLog:120 top_label = "Events:"121 top_items |= {122 "Scroll down": BasicKeyHelp("↓"),123 "Scroll up": BasicKeyHelp("↑"),124 "Clear": "Clear",125 }126 elif issubclass(widget, FocusEditor):127 top_label = f"Edit:"128 top_items |= {129 "Start edit": BasicKeyHelp("⏎"),130 "Stop edit": BasicKeyHelp("esc"),131 "Add row": "Add a row after cursor",132 "Delete row": "Delete this row",133 }134 else:135 pass136 137 bottom_label = "Proxy:"138 bottom_items: HelpItems = {139 "Help": "View help",140 }141 if is_root_widget:142 bottom_items["Quit"] = "Exit the current view"143 else:144 bottom_items["Back"] = "Exit the current view"145 bottom_items |= {146 "Events": "View event log",147 "Options": "View options",148 "Intercept": "Set intercept",149 "Filter": "Set view filter",150 }151 if focused_flow:152 bottom_items |= {153 "Save flows": "Save listed flows to file",154 "Clear list": "Clear flow list",155 }156 bottom_items |= {157 "Layout": "Cycle to next layout",158 "Switch": "Focus next layout pane",159 "Follow new": "Set focus follow",160 }161 162 label_len = max(len(top_label), len(bottom_label), 8) + 1163 top_label = top_label.ljust(label_len)164 bottom_label = bottom_label.ljust(label_len)165 166 return QuickHelp(top_label, top_items, bottom_label, bottom_items)167 168 169def _make_row(label: str, items: HelpItems, keymap: Keymap) -> urwid.Columns:170 cols = [171 (len(label), urwid.Text(label)),172 ]173 for short, long in items.items():174 if isinstance(long, BasicKeyHelp):175 key_short = long.key176 else:177 b = keymap.binding_for_help(long)178 if b is None:179 continue180 key_short = b.key_short()181 txt = urwid.Text(182 [183 ("heading_inactive", key_short),184 " ",185 short,186 ],187 wrap="clip",188 )189 cols.append((14, txt))190 191 return urwid.Columns(cols)192 