console: grideditor keybindings for r, R and e

This commit is contained in:
Aldo Cortesi 2017-06-11 11:47:04 +12:00
parent f804495fd8
commit d7cc7e62a2
3 changed files with 72 additions and 70 deletions

View File

@ -1,15 +1,11 @@
import abc import abc
import copy import copy
from typing import Any import os
from typing import Callable import typing
from typing import Container
from typing import Iterable
from typing import Optional
from typing import Sequence
from typing import Tuple
from typing import Set # noqa
import urwid import urwid
from mitmproxy.utils import strutils
from mitmproxy import exceptions
from mitmproxy.tools.console import common from mitmproxy.tools.console import common
from mitmproxy.tools.console import signals from mitmproxy.tools.console import signals
from mitmproxy.tools.console import layoutwidget from mitmproxy.tools.console import layoutwidget
@ -24,6 +20,21 @@ FOOTER_EDITING = [
] ]
def read_file(filename: str, escaped: bool) -> typing.AnyStr:
filename = os.path.expanduser(filename)
try:
with open(filename, "r" if escaped else "rb") as f:
d = f.read()
except IOError as v:
raise exceptions.CommandError(v)
if escaped:
try:
d = strutils.escaped_str_to_bytes(d)
except ValueError:
raise exceptions.CommandError("Invalid Python-style string encoding.")
return d
class Cell(urwid.WidgetWrap): class Cell(urwid.WidgetWrap):
def get_data(self): def get_data(self):
""" """
@ -51,10 +62,10 @@ class Column(metaclass=abc.ABCMeta):
pass pass
@abc.abstractmethod @abc.abstractmethod
def blank(self) -> Any: def blank(self) -> typing.Any:
pass pass
def keypress(self, key: str, editor: "GridEditor") -> Optional[str]: def keypress(self, key: str, editor: "GridEditor") -> typing.Optional[str]:
return key return key
@ -62,10 +73,10 @@ class GridRow(urwid.WidgetWrap):
def __init__( def __init__(
self, self,
focused: Optional[int], focused: typing.Optional[int],
editing: bool, editing: bool,
editor: "GridEditor", editor: "GridEditor",
values: Tuple[Iterable[bytes], Container[int]] values: typing.Tuple[typing.Iterable[bytes], typing.Container[int]]
) -> None: ) -> None:
self.focused = focused self.focused = focused
self.editor = editor self.editor = editor
@ -118,7 +129,7 @@ class GridWalker(urwid.ListWalker):
def __init__( def __init__(
self, self,
lst: Iterable[list], lst: typing.Iterable[list],
editor: "GridEditor" editor: "GridEditor"
) -> None: ) -> None:
self.lst = [(i, set()) for i in lst] # type: Sequence[Tuple[Any, Set]] self.lst = [(i, set()) for i in lst] # type: Sequence[Tuple[Any, Set]]
@ -262,8 +273,8 @@ class BaseGridEditor(urwid.WidgetWrap):
master: "mitmproxy.tools.console.master.ConsoleMaster", master: "mitmproxy.tools.console.master.ConsoleMaster",
title, title,
columns, columns,
value: Any, value: typing.Any,
callback: Callable[..., None], callback: typing.Callable[..., None],
*cb_args, *cb_args,
**cb_kwargs **cb_kwargs
) -> None: ) -> None:
@ -352,20 +363,20 @@ class BaseGridEditor(urwid.WidgetWrap):
elif column.keypress(key, self) and not self.handle_key(key): elif column.keypress(key, self) and not self.handle_key(key):
return self._w.keypress(size, key) return self._w.keypress(size, key)
def data_out(self, data: Sequence[list]) -> Any: def data_out(self, data: typing.Sequence[list]) -> typing.Any:
""" """
Called on raw list data, before data is returned through the Called on raw list data, before data is returned through the
callback. callback.
""" """
return data return data
def data_in(self, data: Any) -> Iterable[list]: def data_in(self, data: typing.Any) -> typing.Iterable[list]:
""" """
Called to prepare provided data. Called to prepare provided data.
""" """
return data return data
def is_error(self, col: int, val: Any) -> Optional[str]: def is_error(self, col: int, val: typing.Any) -> typing.Optional[str]:
""" """
Return None, or a string error message. Return None, or a string error message.
""" """
@ -415,6 +426,19 @@ class BaseGridEditor(urwid.WidgetWrap):
def cmd_delete(self): def cmd_delete(self):
self.walker.delete_focus() self.walker.delete_focus()
def cmd_read_file(self, path):
self.walker.set_current_value(read_file(path, False))
def cmd_read_file_escaped(self, path):
self.walker.set_current_value(read_file(path, True))
def cmd_spawn_editor(self):
o = self.walker.get_current_value()
if o is not None:
n = self.master.spawn_editor(o)
n = strutils.clean_hanging_newline(n)
self.walker.set_current_value(n)
class GridEditor(BaseGridEditor): class GridEditor(BaseGridEditor):
title = None # type: str title = None # type: str
@ -424,8 +448,8 @@ class GridEditor(BaseGridEditor):
def __init__( def __init__(
self, self,
master: "mitmproxy.tools.console.master.ConsoleMaster", master: "mitmproxy.tools.console.master.ConsoleMaster",
value: Any, value: typing.Any,
callback: Callable[..., None], callback: typing.Callable[..., None],
*cb_args, *cb_args,
**cb_kwargs **cb_kwargs
) -> None: ) -> None:
@ -480,7 +504,7 @@ class FocusEditor(urwid.WidgetWrap, layoutwidget.LayoutWidget):
def focus_changed(self): def focus_changed(self):
if self.master.view.focus.flow: if self.master.view.focus.flow:
self._w = BaseGridEditor( self._w = BaseGridEditor(
self.master.view.focus.flow, self.master,
self.title, self.title,
self.columns, self.columns,
self.get_data(self.master.view.focus.flow), self.get_data(self.master.view.focus.flow),

View File

@ -1,34 +1,9 @@
import os
from typing import Callable, Optional
import urwid import urwid
from mitmproxy.tools.console import signals from mitmproxy.tools.console import signals
from mitmproxy.tools.console.grideditor import base from mitmproxy.tools.console.grideditor import base
from mitmproxy.utils import strutils from mitmproxy.utils import strutils
def read_file(filename: str, callback: Callable[..., None], escaped: bool) -> Optional[str]:
if not filename:
return None
filename = os.path.expanduser(filename)
try:
with open(filename, "r" if escaped else "rb") as f:
d = f.read()
except IOError as v:
return str(v)
if escaped:
try:
d = strutils.escaped_str_to_bytes(d)
except ValueError:
return "Invalid Python-style string encoding."
# TODO: Refactor the status_prompt_path signal so that we
# can raise exceptions here and return the content instead.
callback(d)
return None
class Column(base.Column): class Column(base.Column):
def Display(self, data): def Display(self, data):
return Display(data) return Display(data)
@ -40,29 +15,7 @@ class Column(base.Column):
return b"" return b""
def keypress(self, key, editor): def keypress(self, key, editor):
if key == "r": if key in ["enter"]:
if editor.walker.get_current_value() is not None:
signals.status_prompt_path.send(
self,
prompt="Read file",
callback=read_file,
args=(editor.walker.set_current_value, True)
)
elif key == "R":
if editor.walker.get_current_value() is not None:
signals.status_prompt_path.send(
self,
prompt="Read unescaped file",
callback=read_file,
args=(editor.walker.set_current_value, False)
)
elif key == "e":
o = editor.walker.get_current_value()
if o is not None:
n = editor.master.spawn_editor(o)
n = strutils.clean_hanging_newline(n)
editor.walker.set_current_value(n)
elif key in ["enter"]:
editor.walker.start_edit() editor.walker.start_edit()
else: else:
return key return key

View File

@ -356,6 +356,28 @@ class ConsoleAddon:
""" """
self._grideditor().cmd_delete() self._grideditor().cmd_delete()
@command.command("console.grideditor.readfile")
def grideditor_readfile(self, path: str) -> None:
"""
Read a file into the currrent cell.
"""
self._grideditor().cmd_read_file(path)
@command.command("console.grideditor.readfile_escaped")
def grideditor_readfile_escaped(self, path: str) -> None:
"""
Read a file containing a Python-style escaped stringinto the
currrent cell.
"""
self._grideditor().cmd_read_file_escaped(path)
@command.command("console.grideditor.editor")
def grideditor_editor(self) -> None:
"""
Spawn an external editor on the current cell.
"""
self._grideditor().cmd_spawn_editor()
@command.command("console.flowview.mode.set") @command.command("console.flowview.mode.set")
def flowview_mode_set(self) -> None: def flowview_mode_set(self) -> None:
""" """
@ -514,6 +536,9 @@ def default_keymap(km):
km.add("A", "console.grideditor.insert", ["grideditor"]) km.add("A", "console.grideditor.insert", ["grideditor"])
km.add("tab", "console.grideditor.next", ["grideditor"]) km.add("tab", "console.grideditor.next", ["grideditor"])
km.add("d", "console.grideditor.delete", ["grideditor"]) km.add("d", "console.grideditor.delete", ["grideditor"])
km.add("r", "console.command console.grideditor.readfile", ["grideditor"])
km.add("R", "console.command console.grideditor.readfile_escaped", ["grideditor"])
km.add("e", "console.grideditor.editor", ["grideditor"])
class ConsoleMaster(master.Master): class ConsoleMaster(master.Master):