Add focusing, and fixes non-clearance of prev searches. Add
documentation.
This commit is contained in:
parent
932464d0a0
commit
95406bd119
|
@ -63,6 +63,7 @@ def _mkhelp():
|
||||||
("tab", "toggle request/response view"),
|
("tab", "toggle request/response view"),
|
||||||
("space", "next flow"),
|
("space", "next flow"),
|
||||||
("|", "run script on this flow"),
|
("|", "run script on this flow"),
|
||||||
|
("/", "Search in response body (case sensitive)"),
|
||||||
]
|
]
|
||||||
text.extend(common.format_keyvals(keys, key="key", val="text", indent=4))
|
text.extend(common.format_keyvals(keys, key="key", val="text", indent=4))
|
||||||
return text
|
return text
|
||||||
|
@ -85,7 +86,9 @@ class FlowViewHeader(common.WWrap):
|
||||||
|
|
||||||
|
|
||||||
class CallbackCache:
|
class CallbackCache:
|
||||||
@utils.LRUCache(200)
|
#commented decorator because it was breaking search functionality (caching after
|
||||||
|
# searches.) If it can be made to only cache the first time, it'd be great.
|
||||||
|
#@utils.LRUCache(200)
|
||||||
def _callback(self, method, *args, **kwargs):
|
def _callback(self, method, *args, **kwargs):
|
||||||
return getattr(self.obj, method)(*args, **kwargs)
|
return getattr(self.obj, method)(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -109,8 +112,12 @@ class FlowView(common.WWrap):
|
||||||
("options", "o"),
|
("options", "o"),
|
||||||
("edit raw", "e"),
|
("edit raw", "e"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
highlight_color = "key"
|
||||||
|
|
||||||
def __init__(self, master, state, flow):
|
def __init__(self, master, state, flow):
|
||||||
self.master, self.state, self.flow = master, state, flow
|
self.master, self.state, self.flow = master, state, flow
|
||||||
|
self.last_displayed_body = None
|
||||||
if self.state.view_flow_mode == common.VIEW_FLOW_RESPONSE:
|
if self.state.view_flow_mode == common.VIEW_FLOW_RESPONSE:
|
||||||
self.view_response()
|
self.view_response()
|
||||||
else:
|
else:
|
||||||
|
@ -129,7 +136,8 @@ class FlowView(common.WWrap):
|
||||||
limit = sys.maxint
|
limit = sys.maxint
|
||||||
else:
|
else:
|
||||||
limit = contentview.VIEW_CUTOFF
|
limit = contentview.VIEW_CUTOFF
|
||||||
description, text_object = cache.callback(
|
|
||||||
|
description, text_objects = cache.callback(
|
||||||
self, "_cached_content_view",
|
self, "_cached_content_view",
|
||||||
viewmode,
|
viewmode,
|
||||||
tuple(tuple(i) for i in conn.headers.lst),
|
tuple(tuple(i) for i in conn.headers.lst),
|
||||||
|
@ -138,11 +146,12 @@ class FlowView(common.WWrap):
|
||||||
)
|
)
|
||||||
|
|
||||||
if highlight_string:
|
if highlight_string:
|
||||||
text_object = self.search_highlight_text(text_object[0],
|
text_objects, focus_position = self.search_highlight_text(text_objects,
|
||||||
highlight_string)
|
highlight_string)
|
||||||
text_object = [text_object]
|
else:
|
||||||
|
focus_position = None
|
||||||
|
|
||||||
return (description, text_object)
|
return (description, text_objects, focus_position)
|
||||||
|
|
||||||
def conn_text(self, conn, highlight_string=""):
|
def conn_text(self, conn, highlight_string=""):
|
||||||
txt = common.format_keyvals(
|
txt = common.format_keyvals(
|
||||||
|
@ -159,9 +168,9 @@ class FlowView(common.WWrap):
|
||||||
viewmode = self.state.default_body_view if override is None else override
|
viewmode = self.state.default_body_view if override is None else override
|
||||||
|
|
||||||
if conn.content == flow.CONTENT_MISSING:
|
if conn.content == flow.CONTENT_MISSING:
|
||||||
msg, body = "", [urwid.Text([("error", "[content missing]")])]
|
msg, body, text_focus_position = "", [urwid.Text([("error", "[content missing]")])], 0
|
||||||
else:
|
else:
|
||||||
msg, body = self.content_view(viewmode, conn, highlight_string)
|
msg, body, text_focus_position = self.content_view(viewmode, conn, highlight_string)
|
||||||
|
|
||||||
cols = [
|
cols = [
|
||||||
urwid.Text(
|
urwid.Text(
|
||||||
|
@ -190,9 +199,13 @@ class FlowView(common.WWrap):
|
||||||
elif conn.content == flow.CONTENT_MISSING:
|
elif conn.content == flow.CONTENT_MISSING:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print(txt)
|
self.last_displayed_body = urwid.ListBox(txt)
|
||||||
print("\n\n\n")
|
|
||||||
return urwid.ListBox(txt)
|
if text_focus_position :
|
||||||
|
# +2 because of the two header columns
|
||||||
|
self.last_displayed_body.set_focus(text_focus_position + 2)
|
||||||
|
|
||||||
|
return self.last_displayed_body
|
||||||
|
|
||||||
def _tab(self, content, attr):
|
def _tab(self, content, attr):
|
||||||
p = urwid.Text(content)
|
p = urwid.Text(content)
|
||||||
|
@ -229,6 +242,11 @@ class FlowView(common.WWrap):
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def search(self, search_string):
|
def search(self, search_string):
|
||||||
|
|
||||||
|
if search_string == "":
|
||||||
|
search_string = self.state.get_flow_setting(self.flow,
|
||||||
|
"last_search_string")
|
||||||
|
|
||||||
# two things need to happen. 1) text needs to be highlighted. 2) we
|
# two things need to happen. 1) text needs to be highlighted. 2) we
|
||||||
# need to focus on the highlighted text.
|
# need to focus on the highlighted text.
|
||||||
if self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
|
if self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
|
||||||
|
@ -249,36 +267,58 @@ class FlowView(common.WWrap):
|
||||||
def search_get_start(self, search_string):
|
def search_get_start(self, search_string):
|
||||||
last_search_string = self.state.get_flow_setting(self.flow, "last_search_string")
|
last_search_string = self.state.get_flow_setting(self.flow, "last_search_string")
|
||||||
if search_string == last_search_string:
|
if search_string == last_search_string:
|
||||||
|
start_line = self.state.get_flow_setting(self.flow, "last_find_line")
|
||||||
start_index = self.state.get_flow_setting(self.flow,
|
start_index = self.state.get_flow_setting(self.flow,
|
||||||
"last_search_index") + len(search_string)
|
"last_search_index") + len(search_string)
|
||||||
else:
|
else:
|
||||||
self.state.add_flow_setting(self.flow, "last_search_string",
|
self.state.add_flow_setting(self.flow, "last_search_string",
|
||||||
search_string)
|
search_string)
|
||||||
|
start_line = 0
|
||||||
start_index = 0
|
start_index = 0
|
||||||
|
|
||||||
return start_index
|
return (start_line, start_index)
|
||||||
|
|
||||||
def search_highlight_text(self, text_object, search_string):
|
def search_highlight_text(self, text_objects, search_string):
|
||||||
text, style = text_object.get_text()
|
start_line, start_index = self.search_get_start(search_string)
|
||||||
start_index = self.search_get_start(search_string)
|
i = start_line
|
||||||
find_index = text.find(search_string, start_index)
|
|
||||||
if find_index != -1:
|
|
||||||
before = text[:find_index]
|
|
||||||
after = text[find_index+len(search_string):]
|
|
||||||
new_text = urwid.Text(
|
|
||||||
[
|
|
||||||
before,
|
|
||||||
("dark red", search_string),
|
|
||||||
after,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.state.add_flow_setting(self.flow, "last_search_index",
|
found = False
|
||||||
find_index)
|
for text_object in text_objects[start_line:]:
|
||||||
|
if i != start_line:
|
||||||
|
start_index = 0
|
||||||
|
|
||||||
return new_text
|
text, style = text_object.get_text()
|
||||||
else:
|
|
||||||
return text_object
|
find_index = text.find(search_string, start_index)
|
||||||
|
if find_index != -1:
|
||||||
|
before = text[:find_index]
|
||||||
|
after = text[find_index+len(search_string):]
|
||||||
|
new_text = urwid.Text(
|
||||||
|
[
|
||||||
|
before,
|
||||||
|
(self.highlight_color, search_string),
|
||||||
|
after,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state.add_flow_setting(self.flow, "last_search_index",
|
||||||
|
find_index)
|
||||||
|
self.state.add_flow_setting(self.flow, "last_find_line", i)
|
||||||
|
|
||||||
|
text_objects[i] = new_text
|
||||||
|
|
||||||
|
found = True
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if found:
|
||||||
|
focus_pos = i
|
||||||
|
else :
|
||||||
|
focus_post = None
|
||||||
|
|
||||||
|
return text_objects, focus_pos
|
||||||
|
|
||||||
def view_request(self):
|
def view_request(self):
|
||||||
self.state.view_flow_mode = common.VIEW_FLOW_REQUEST
|
self.state.view_flow_mode = common.VIEW_FLOW_REQUEST
|
||||||
|
@ -640,8 +680,10 @@ class FlowView(common.WWrap):
|
||||||
)
|
)
|
||||||
self.master.refresh_flow(self.flow)
|
self.master.refresh_flow(self.flow)
|
||||||
elif key == "/":
|
elif key == "/":
|
||||||
self.master.prompt("Search body: ",
|
last_search_string = self.state.get_flow_setting(self.flow, "last_search_string")
|
||||||
self.state.get_flow_setting(self.flow, "last_search_string"),
|
search_prompt = "Search body ["+last_search_string+"]: " if last_search_string else "Search body:"
|
||||||
|
self.master.prompt(search_prompt,
|
||||||
|
None,
|
||||||
self.search)
|
self.search)
|
||||||
else:
|
else:
|
||||||
return key
|
return key
|
||||||
|
|
|
@ -257,10 +257,53 @@ def test_search_highlights():
|
||||||
# second to be.
|
# second to be.
|
||||||
f = tutils.tflowview()
|
f = tutils.tflowview()
|
||||||
|
|
||||||
ui_elements = f.search("nt")
|
f.search("nt")
|
||||||
text_object = ui_elements.contents()[2]
|
text_object = tutils.get_body_line(f.last_displayed_body, 0)
|
||||||
assert text_object.get_text() == ('content', [(None, 2), ('dark red', 2)])
|
assert text_object.get_text() == ('content', [(None, 2), (f.highlight_color, 2)])
|
||||||
|
|
||||||
|
f.search("nt")
|
||||||
|
text_object = tutils.get_body_line(f.last_displayed_body, 1)
|
||||||
|
assert text_object.get_text() == ('content', [(None, 5), (f.highlight_color, 2)])
|
||||||
|
|
||||||
|
def test_search_highlights_clears_prev():
|
||||||
|
f = tutils.tflowview(request_contents="this is string\nstring is string")
|
||||||
|
|
||||||
|
f.search("string")
|
||||||
|
text_object = tutils.get_body_line(f.last_displayed_body, 0)
|
||||||
|
assert text_object.get_text() == ('this is string', [(None, 8), (f.highlight_color, 6)])
|
||||||
|
|
||||||
|
# search again, it should not be highlighted again.
|
||||||
|
f.search("string")
|
||||||
|
text_object = tutils.get_body_line(f.last_displayed_body, 0)
|
||||||
|
assert text_object.get_text() != ('this is string', [(None, 8), (f.highlight_color, 6)])
|
||||||
|
|
||||||
|
def test_search_highlights_multi_line():
|
||||||
|
f = tutils.tflowview(request_contents="this is string\nstring is string")
|
||||||
|
|
||||||
|
# should highlight the first line.
|
||||||
|
f.search("string")
|
||||||
|
text_object = tutils.get_body_line(f.last_displayed_body, 0)
|
||||||
|
assert text_object.get_text() == ('this is string', [(None, 8), (f.highlight_color, 6)])
|
||||||
|
|
||||||
|
# should highlight second line, first appearance of string.
|
||||||
|
f.search("string")
|
||||||
|
text_object = tutils.get_body_line(f.last_displayed_body, 1)
|
||||||
|
assert text_object.get_text() == ('string is string', [(None, 0), ('key', 6)])
|
||||||
|
|
||||||
|
# should highlight third line, second appearance of string.
|
||||||
|
f.search("string")
|
||||||
|
text_object = tutils.get_body_line(f.last_displayed_body, 1)
|
||||||
|
assert text_object.get_text() == ('string is string', [(None, 10), (f.highlight_color, 6)])
|
||||||
|
|
||||||
|
def test_search_focuses():
|
||||||
|
f = tutils.tflowview(request_contents="this is string\nstring is string")
|
||||||
|
|
||||||
|
# should highlight the first line.
|
||||||
|
f.search("string")
|
||||||
|
|
||||||
|
# should be focusing on the 2nd text line.
|
||||||
|
f.search("string")
|
||||||
|
text_object = tutils.get_body_line(f.last_displayed_body, 1)
|
||||||
|
assert f.last_displayed_body.focus == text_object
|
||||||
|
|
||||||
|
|
||||||
ui_elements = f.search("nt")
|
|
||||||
text_object = ui_elements.contents()[2]
|
|
||||||
assert text_object.get_text() == ('content', [(None, 5), ('dark red', 2)])
|
|
||||||
|
|
|
@ -15,13 +15,14 @@ def SkipWindows(fn):
|
||||||
else:
|
else:
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
def treq(conn=None):
|
def treq(conn=None, content="content"):
|
||||||
if not conn:
|
if not conn:
|
||||||
conn = flow.ClientConnect(("address", 22))
|
conn = flow.ClientConnect(("address", 22))
|
||||||
conn.reply = controller.DummyReply()
|
conn.reply = controller.DummyReply()
|
||||||
headers = flow.ODictCaseless()
|
headers = flow.ODictCaseless()
|
||||||
headers["header"] = ["qvalue"]
|
headers["header"] = ["qvalue"]
|
||||||
r = flow.Request(conn, (1, 1), "host", 80, "http", "GET", "/path", headers, "content")
|
r = flow.Request(conn, (1, 1), "host", 80, "http", "GET", "/path", headers,
|
||||||
|
content)
|
||||||
r.reply = controller.DummyReply()
|
r.reply = controller.DummyReply()
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
@ -44,8 +45,9 @@ def terr(req=None):
|
||||||
return err
|
return err
|
||||||
|
|
||||||
|
|
||||||
def tflow():
|
def tflow(r=None):
|
||||||
r = treq()
|
if r == None:
|
||||||
|
r = treq()
|
||||||
return flow.Flow(r)
|
return flow.Flow(r)
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,13 +62,20 @@ def tflow_err():
|
||||||
f.error = terr(f.request)
|
f.error = terr(f.request)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def tflowview():
|
def tflowview(request_contents=None):
|
||||||
m = Mock()
|
m = Mock()
|
||||||
cs = ConsoleState()
|
cs = ConsoleState()
|
||||||
flow = tflow()
|
if request_contents == None:
|
||||||
|
flow = tflow()
|
||||||
|
else:
|
||||||
|
req = treq(None, request_contents)
|
||||||
|
flow = tflow(req)
|
||||||
|
|
||||||
fv = FlowView(m, cs, flow)
|
fv = FlowView(m, cs, flow)
|
||||||
return fv
|
return fv
|
||||||
|
|
||||||
|
def get_body_line(last_displayed_body, line_nb):
|
||||||
|
return last_displayed_body.contents()[line_nb + 2]
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def tmpdir(*args, **kwargs):
|
def tmpdir(*args, **kwargs):
|
||||||
|
|
Loading…
Reference in New Issue