diff options
author | Aldo Cortesi <aldo@nullcube.com> | 2017-03-17 07:33:07 +1300 |
---|---|---|
committer | Aldo Cortesi <aldo@corte.si> | 2017-03-19 10:32:22 +1300 |
commit | bc67cee6870af7033e5741d8d21d5bd016dfd132 (patch) | |
tree | 7db702df35097dcafec1dc9611b67598199bb497 | |
parent | 39659c752884ae31ccc14bbe247f3918f97bab9c (diff) | |
download | mitmproxy-bc67cee6870af7033e5741d8d21d5bd016dfd132.tar.gz mitmproxy-bc67cee6870af7033e5741d8d21d5bd016dfd132.tar.bz2 mitmproxy-bc67cee6870af7033e5741d8d21d5bd016dfd132.zip |
console: sketch out look and feel of the new Options editor
-rw-r--r-- | mitmproxy/optmanager.py | 6 | ||||
-rw-r--r-- | mitmproxy/tools/console/options.py | 343 |
2 files changed, 109 insertions, 240 deletions
diff --git a/mitmproxy/optmanager.py b/mitmproxy/optmanager.py index 495354f4..86f833e4 100644 --- a/mitmproxy/optmanager.py +++ b/mitmproxy/optmanager.py @@ -36,7 +36,7 @@ class _Option: self.typespec = typespec self._default = default self.value = unset - self.help = help + self.help = textwrap.dedent(help or "").strip() self.choices = choices def __repr__(self): @@ -397,9 +397,7 @@ def dump_defaults(opts): txt += " Type %s." % t txt = "\n".join( - textwrap.wrap( - textwrap.dedent(txt) - ) + textwrap.wrap(txt) ) s.yaml_set_comment_before_after_key(k, before = "\n" + txt) return ruamel.yaml.round_trip_dump(s) diff --git a/mitmproxy/tools/console/options.py b/mitmproxy/tools/console/options.py index 79bb53c2..903bde5b 100644 --- a/mitmproxy/tools/console/options.py +++ b/mitmproxy/tools/console/options.py @@ -1,14 +1,9 @@ import urwid +import blinker +import textwrap -from mitmproxy import contentviews -from mitmproxy import optmanager from mitmproxy.tools.console import common -from mitmproxy.tools.console import grideditor -from mitmproxy.tools.console import select -from mitmproxy.tools.console import signals -from mitmproxy.addons import replace -from mitmproxy.addons import setheaders footer = [ ('heading_key', "enter/space"), ":toggle ", @@ -31,251 +26,127 @@ def _mkhelp(): help_context = _mkhelp() -def checker(opt, options): - def _check(): - return options.has_changed(opt) - return _check +def fcol(s, width, attr): + s = str(s) + return ( + "fixed", + width, + urwid.Text((attr, s)) + ) -class Options(urwid.WidgetWrap): +option_focus_change = blinker.Signal() - def __init__(self, master): - self.master = master - self.lb = select.Select( - [ - select.Heading("Traffic Manipulation"), - select.Option( - "Header Set Patterns", - "H", - checker("setheaders", master.options), - self.setheaders - ), - select.Option( - "Ignore Patterns", - "I", - checker("ignore_hosts", master.options), - self.ignore_hosts - ), - select.Option( - "Replacement Patterns", - "R", - checker("replacements", master.options), - self.replacepatterns - ), - select.Option( - "Scripts", - "S", - checker("scripts", master.options), - self.scripts - ), - select.Heading("Interface"), - select.Option( - "Default Display Mode", - "M", - checker("default_contentview", master.options), - self.default_displaymode - ), - select.Option( - "Palette", - "P", - checker("console_palette", master.options), - self.palette - ), - select.Option( - "Show Host", - "w", - checker("showhost", master.options), - master.options.toggler("showhost") - ), +class OptionItem(urwid.WidgetWrap): + def __init__(self, master, opt, focused, namewidth): + self.master, self.opt, self.focused = master, opt, focused + self.namewidth = namewidth + w = self.get_text() + urwid.WidgetWrap.__init__(self, w) - select.Heading("Network"), - select.Option( - "Upstream Certs", - "U", - checker("upstream_cert", master.options), - master.options.toggler("upstream_cert") - ), - select.Option( - "TCP Proxying", - "T", - checker("tcp_hosts", master.options), - self.tcp_hosts - ), - select.Option( - "Don't Verify SSL/TLS Certificates", - "V", - checker("ssl_insecure", master.options), - master.options.toggler("ssl_insecure") - ), + def get_text(self): + val = self.opt.current() + if self.opt.typespec == bool: + displayval = "true" if val else "false" + elif val is None: + displayval = "" + else: + displayval = str(val) - select.Heading("Utility"), - select.Option( - "Anti-Cache", - "a", - checker("anticache", master.options), - master.options.toggler("anticache") - ), - select.Option( - "Anti-Compression", - "o", - checker("anticomp", master.options), - master.options.toggler("anticomp") - ), - select.Option( - "Kill Extra", - "x", - checker("replay_kill_extra", master.options), - master.options.toggler("replay_kill_extra") - ), - select.Option( - "No Refresh", - "f", - checker("refresh_server_playback", master.options), - master.options.toggler("refresh_server_playback") - ), - select.Option( - "Sticky Auth", - "A", - checker("stickyauth", master.options), - self.sticky_auth - ), - select.Option( - "Sticky Cookies", - "t", - checker("stickycookie", master.options), - self.sticky_cookie + changed = self.master.options.has_changed(self.opt.name) + namestyle = "option_selected" if self.focused else "title" + valstyle = "option_active" if changed else "text" + return urwid.Columns( + [ + ( + self.namewidth, + urwid.Text([(namestyle, self.opt.name.ljust(self.namewidth))]) ), - ] - ) - title = urwid.Text("Options") - title = urwid.Padding(title, align="left", width=("relative", 100)) - title = urwid.AttrWrap(title, "heading") - w = urwid.Frame( - self.lb, - header = title - ) - super().__init__(w) - - self.master.loop.widget.footer.update("") - signals.update_settings.connect(self.sig_update_settings) - master.options.changed.connect(self.sig_update_settings) - - def sig_update_settings(self, sender, updated=None): - self.lb.walker._modified() - - def keypress(self, size, key): - if key == "C": - self.clearall() - return None - if key == "W": - self.save() - return None - return super().keypress(size, key) - - def do_save(self, path): - optmanager.save(self.master.options, path) - return "Saved" - - def save(self): - signals.status_prompt_path.send( - prompt = "Save options to file", - callback = self.do_save + urwid.Text([(valstyle, displayval)]) + ], + dividechars=2 ) - def clearall(self): - self.master.options.reset() - signals.update_settings.send(self) - signals.status_message.send( - message = "Options cleared", - expire = 1 - ) + def selectable(self): + return True - def setheaders(self): - data = [] - for d in self.master.options.setheaders: - if isinstance(d, str): - data.append(setheaders.parse_setheader(d)) - else: - data.append(d) - self.master.view_grideditor( - grideditor.SetHeadersEditor( - self.master, - data, - self.master.options.setter("setheaders") - ) - ) + def keypress(self, xxx_todo_changeme, key): + key = common.shortcuts(key) + return key - def tcp_hosts(self): - self.master.view_grideditor( - grideditor.HostPatternEditor( - self.master, - self.master.options.tcp_hosts, - self.master.options.setter("tcp_hosts") - ) - ) - def ignore_hosts(self): - self.master.view_grideditor( - grideditor.HostPatternEditor( - self.master, - self.master.options.ignore_hosts, - self.master.options.setter("ignore_hosts") - ) - ) +class OptionListWalker(urwid.ListWalker): + def __init__(self, master): + self.master = master + self.index = 0 + self.opts = sorted(master.options.keys()) + self.maxlen = max(len(i) for i in self.opts) + + # Trigger a help text update for the first selected item + first = self.master.options._options[self.opts[0]] + option_focus_change.send(first.help) + + def _get(self, pos): + name = self.opts[pos] + opt = self.master.options._options[name] + return OptionItem(self.master, opt, pos == self.index, self.maxlen) + + def get_focus(self): + return self._get(self.index), self.index + + def set_focus(self, index): + name = self.opts[index] + opt = self.master.options._options[name] + self.index = index + option_focus_change.send(opt.help) + + def get_next(self, pos): + if pos >= len(self.opts) - 1: + return None, None + pos = pos + 1 + return self._get(pos), pos + + def get_prev(self, pos): + pos = pos - 1 + if pos < 0: + return None, None + return self._get(pos), pos + + +class OptionsList(urwid.ListBox): + def __init__(self, master): + self.master = master + super().__init__(OptionListWalker(master)) - def replacepatterns(self): - data = [] - for d in self.master.options.replacements: - if isinstance(d, str): - data.append(replace.parse_hook(d)) - else: - data.append(d) - self.master.view_grideditor( - grideditor.ReplaceEditor( - self.master, - data, - self.master.options.setter("replacements") - ) - ) - def scripts(self): - def edit_scripts(scripts): - self.master.options.scripts = [x[0] for x in scripts] - self.master.view_grideditor( - grideditor.ScriptEditor( - self.master, - [[i] for i in self.master.options.scripts], - edit_scripts - ) - ) +class OptionHelp(urwid.Frame): + def __init__(self): + h = urwid.Text("Option Help") + h = urwid.Padding(h, align="left", width=("relative", 100)) + h = urwid.AttrWrap(h, "heading") + super().__init__(self.widget(""), header=h) + option_focus_change.connect(self.sig_mod) - def default_displaymode(self): - signals.status_prompt_onekey.send( - prompt = "Global default display mode", - keys = contentviews.view_prompts, - callback = self.change_default_display_mode - ) + def selectable(self): + return False - def change_default_display_mode(self, t): - v = contentviews.get_by_shortcut(t) - self.master.options.default_contentview = v.name - if self.master.view.focus.flow: - signals.flow_change.send(self, flow = self.master.view.focus.flow) - - def sticky_auth(self): - signals.status_prompt.send( - prompt = "Sticky auth filter", - text = self.master.options.stickyauth, - callback = self.master.options.setter("stickyauth") + def widget(self, txt): + return urwid.ListBox( + [urwid.Text(i) for i in textwrap.wrap(txt)] ) - def sticky_cookie(self): - signals.status_prompt.send( - prompt = "Sticky cookie filter", - text = self.master.options.stickycookie, - callback = self.master.options.setter("stickycookie") - ) + def sig_mod(self, txt): + self.set_body(self.widget(txt)) + - def palette(self): - self.master.view_palette_picker() +class Options(urwid.Pile): + def __init__(self, master): + oh = OptionHelp() + super().__init__( + [ + OptionsList(master), + (5, oh), + ] + ) + self.master = master |