mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-30 03:14:22 +00:00
Introduce a custom widget for command editing
The builtin urwid.Edit widget is not sufficiently flexible for what we want to do.
This commit is contained in:
parent
21324086c3
commit
04e19f9171
@ -1,19 +1,10 @@
|
|||||||
import typing
|
import typing
|
||||||
import urwid
|
|
||||||
|
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
from mitmproxy import flow
|
from mitmproxy import flow
|
||||||
from mitmproxy.tools.console import signals
|
from mitmproxy.tools.console import signals
|
||||||
|
|
||||||
|
|
||||||
class CommandEdit(urwid.Edit):
|
|
||||||
def __init__(self, partial):
|
|
||||||
urwid.Edit.__init__(self, ":", partial)
|
|
||||||
|
|
||||||
def keypress(self, size, key):
|
|
||||||
return urwid.Edit.keypress(self, size, key)
|
|
||||||
|
|
||||||
|
|
||||||
class CommandExecutor:
|
class CommandExecutor:
|
||||||
def __init__(self, master):
|
def __init__(self, master):
|
||||||
self.master = master
|
self.master = master
|
||||||
|
1
mitmproxy/tools/console/commander/__init__.py
Normal file
1
mitmproxy/tools/console/commander/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
85
mitmproxy/tools/console/commander/commander.py
Normal file
85
mitmproxy/tools/console/commander/commander.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import urwid
|
||||||
|
from urwid.text_layout import calc_coords
|
||||||
|
|
||||||
|
|
||||||
|
class CommandBuffer():
|
||||||
|
def __init__(self, start: str = ""):
|
||||||
|
self.buf = start
|
||||||
|
# This is the logical cursor position - the display cursor is one
|
||||||
|
# character further on. Cursor is always within the range [0:len(buffer)].
|
||||||
|
self._cursor = len(self.buf)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cursor(self):
|
||||||
|
return self._cursor
|
||||||
|
|
||||||
|
@cursor.setter
|
||||||
|
def cursor(self, x):
|
||||||
|
if x < 0:
|
||||||
|
self._cursor = 0
|
||||||
|
elif x > len(self.buf):
|
||||||
|
self._cursor = len(self.buf)
|
||||||
|
else:
|
||||||
|
self._cursor = x
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
return self.buf
|
||||||
|
|
||||||
|
def left(self):
|
||||||
|
self.cursor = self.cursor - 1
|
||||||
|
|
||||||
|
def right(self):
|
||||||
|
self.cursor = self.cursor + 1
|
||||||
|
|
||||||
|
def backspace(self):
|
||||||
|
if self.cursor == 0:
|
||||||
|
return
|
||||||
|
self.buf = self.buf[:self.cursor - 1] + self.buf[self.cursor:]
|
||||||
|
self.cursor = self.cursor - 1
|
||||||
|
|
||||||
|
def insert(self, k: str):
|
||||||
|
"""
|
||||||
|
Inserts text at the cursor.
|
||||||
|
"""
|
||||||
|
self.buf = self.buf = self.buf[:self.cursor] + k + self.buf[self.cursor:]
|
||||||
|
self.cursor += 1
|
||||||
|
|
||||||
|
|
||||||
|
class CommandEdit(urwid.WidgetWrap):
|
||||||
|
leader = ": "
|
||||||
|
|
||||||
|
def __init__(self, text):
|
||||||
|
self.cbuf = CommandBuffer(text)
|
||||||
|
self._w = urwid.Text(self.leader)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def keypress(self, size, key):
|
||||||
|
if key == "backspace":
|
||||||
|
self.cbuf.backspace()
|
||||||
|
elif key == "left":
|
||||||
|
self.cbuf.left()
|
||||||
|
elif key == "right":
|
||||||
|
self.cbuf.right()
|
||||||
|
elif len(key) == 1:
|
||||||
|
self.cbuf.insert(key)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self._w.set_text([self.leader, self.cbuf.render()])
|
||||||
|
|
||||||
|
def render(self, size, focus=False):
|
||||||
|
(maxcol,) = size
|
||||||
|
canv = self._w.render((maxcol,))
|
||||||
|
canv = urwid.CompositeCanvas(canv)
|
||||||
|
canv.cursor = self.get_cursor_coords((maxcol,))
|
||||||
|
return canv
|
||||||
|
|
||||||
|
def get_cursor_coords(self, size):
|
||||||
|
p = self.cbuf.cursor + len(self.leader)
|
||||||
|
trans = self._w.get_line_translation(size[0])
|
||||||
|
x, y = calc_coords(self._w.get_text()[0], trans, p)
|
||||||
|
return x, y
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
return self.cbuf.buf
|
||||||
|
|
@ -6,6 +6,7 @@ from mitmproxy.tools.console import common
|
|||||||
from mitmproxy.tools.console import signals
|
from mitmproxy.tools.console import signals
|
||||||
from mitmproxy.tools.console import commandeditor
|
from mitmproxy.tools.console import commandeditor
|
||||||
import mitmproxy.tools.console.master # noqa
|
import mitmproxy.tools.console.master # noqa
|
||||||
|
from mitmproxy.tools.console.commander import commander
|
||||||
|
|
||||||
|
|
||||||
class PromptPath:
|
class PromptPath:
|
||||||
@ -66,7 +67,7 @@ class ActionBar(urwid.WidgetWrap):
|
|||||||
|
|
||||||
def sig_prompt_command(self, sender, partial=""):
|
def sig_prompt_command(self, sender, partial=""):
|
||||||
signals.focus.send(self, section="footer")
|
signals.focus.send(self, section="footer")
|
||||||
self._w = commandeditor.CommandEdit(partial)
|
self._w = commander.CommandEdit(partial)
|
||||||
self.prompting = commandeditor.CommandExecutor(self.master)
|
self.prompting = commandeditor.CommandExecutor(self.master)
|
||||||
|
|
||||||
def sig_prompt_onekey(self, sender, prompt, keys, callback, args=()):
|
def sig_prompt_onekey(self, sender, prompt, keys, callback, args=()):
|
||||||
@ -100,7 +101,7 @@ class ActionBar(urwid.WidgetWrap):
|
|||||||
elif k in self.onekey:
|
elif k in self.onekey:
|
||||||
self.prompt_execute(k)
|
self.prompt_execute(k)
|
||||||
elif k == "enter":
|
elif k == "enter":
|
||||||
self.prompt_execute(self._w.get_edit_text())
|
self.prompt_execute(self._w.get_value())
|
||||||
else:
|
else:
|
||||||
if common.is_keypress(k):
|
if common.is_keypress(k):
|
||||||
self._w.keypress(size, k)
|
self._w.keypress(size, k)
|
||||||
|
37
test/mitmproxy/tools/console/test_commander.py
Normal file
37
test/mitmproxy/tools/console/test_commander.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
from mitmproxy.tools.console.commander import commander
|
||||||
|
|
||||||
|
|
||||||
|
class TestCommandBuffer:
|
||||||
|
|
||||||
|
def test_backspace(self):
|
||||||
|
tests = [
|
||||||
|
[("", 0), ("", 0)],
|
||||||
|
[("1", 0), ("1", 0)],
|
||||||
|
[("1", 1), ("", 0)],
|
||||||
|
[("123", 3), ("12", 2)],
|
||||||
|
[("123", 2), ("13", 1)],
|
||||||
|
[("123", 0), ("123", 0)],
|
||||||
|
]
|
||||||
|
for start, output in tests:
|
||||||
|
cb = commander.CommandBuffer()
|
||||||
|
cb.buf, cb.cursor = start[0], start[1]
|
||||||
|
cb.backspace()
|
||||||
|
assert cb.buf == output[0]
|
||||||
|
assert cb.cursor == output[1]
|
||||||
|
|
||||||
|
def test_insert(self):
|
||||||
|
tests = [
|
||||||
|
[("", 0), ("x", 1)],
|
||||||
|
[("a", 0), ("xa", 1)],
|
||||||
|
[("xa", 2), ("xax", 3)],
|
||||||
|
]
|
||||||
|
for start, output in tests:
|
||||||
|
cb = commander.CommandBuffer()
|
||||||
|
cb.buf, cb.cursor = start[0], start[1]
|
||||||
|
cb.insert("x")
|
||||||
|
assert cb.buf == output[0]
|
||||||
|
assert cb.cursor == output[1]
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user