add basic backwards search

This commit is contained in:
Pedro Worcel 2014-02-22 15:00:10 +13:00
parent c2828de4a1
commit 7a154e1ae1
3 changed files with 170 additions and 122 deletions

View File

@ -273,7 +273,19 @@ class FlowView(common.WWrap):
return False
def search(self, search_string):
def search_again(self, backwards=False):
"""
runs the previous search again, forwards or backwards.
"""
last_search_string = self.state.get_flow_setting(self.flow, "last_search_string")
if last_search_string:
message = self.search(last_search_string, backwards)
if message:
self.master.statusbar.message(message)
else:
self.master.statusbar.message("no previous searches have been made")
def search(self, search_string, backwards=False):
"""
similar to view_response or view_request, but instead of just
displaying the conn, it highlights a word that the user is
@ -301,7 +313,7 @@ class FlowView(common.WWrap):
# generate the body, highlight the words and get focus
headers, msg, body = self.conn_text_raw(text)
try:
body, focus_position = self.search_highlight_text(body, search_string)
body, focus_position = self.search_highlight_text(body, search_string, backwards=backwards)
except SearchError:
return "Search not supported in this view."
@ -344,7 +356,7 @@ class FlowView(common.WWrap):
return (start_line, start_index)
def search_highlight_text(self, text_objects, search_string, looping = False):
def search_highlight_text(self, text_objects, search_string, looping = False, backwards = False):
start_line, start_index = self.search_get_start(search_string)
i = start_line
@ -352,40 +364,39 @@ class FlowView(common.WWrap):
text_objects = copy.deepcopy(text_objects)
for text_object in text_objects[start_line:]:
if i != start_line:
start_index = 0
start_index = None
try:
text, style = text_object.get_text()
except AttributeError:
raise SearchError()
find_index = text.find(search_string, start_index)
if backwards == False:
find_index = text.find(search_string, start_index)
else:
if start_index != 0:
start_index -= len(search_string)
find_index = text.rfind(search_string, 0, start_index)
# Found text in line, do the highlight highlight.
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,
]
)
new_text = self.search_highlight_object(text, find_index, search_string)
text_objects[i] = new_text
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
# handle search WRAP
if found:
focus_pos = i
else :
# loop from the beginning, but not forever.
if (start_line == 0 and start_index == 0) or looping:
focus_pos = None
else:
@ -395,6 +406,23 @@ class FlowView(common.WWrap):
return text_objects, focus_pos
def search_highlight_object(self, text_object, find_index, search_string):
"""
just a little abstraction
"""
before = text_object[:find_index]
after = text_object[find_index+len(search_string):]
new_text = urwid.Text(
[
before,
(self.highlight_color, search_string),
after,
]
)
return new_text
def view_request(self):
self.state.view_flow_mode = common.VIEW_FLOW_REQUEST
body = self.conn_text(self.flow.request)
@ -761,13 +789,9 @@ class FlowView(common.WWrap):
None,
self.search)
elif key == "n":
last_search_string = self.state.get_flow_setting(self.flow, "last_search_string")
if last_search_string:
message = self.search(last_search_string)
if message:
self.master.statusbar.message(message)
else:
self.master.statusbar.message("no previous searches have been made")
self.search_again(backwards=False)
elif key == "N":
self.search_again(backwards=True)
else:
return key

View File

@ -276,100 +276,3 @@ if cv.ViewProtobuf.is_available():
def test_get_by_shortcut():
assert cv.get_by_shortcut("h")
def test_search_highlights():
# Default text in requests is content. We will search for nt once, and
# expect the first bit to be highlighted. We will do it again and expect the
# second to be.
f = tutils.tflowview()
f.search("nt")
text_object = tutils.get_body_line(f.last_displayed_body, 0)
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_returns_useful_messages():
f = tutils.tflowview()
# original string is content. this string should not be in there.
response = f.search("oranges and other fruit.")
assert response == "no matches for 'oranges and other fruit.'"
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), (f.highlight_color, 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_loops():
f = tutils.tflowview(request_contents="this is string\nstring is string")
# get to the end.
f.search("string")
f.search("string")
f.search("string")
# should highlight the first line.
message = 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)])
assert message == "search hit BOTTOM, continuing at TOP"
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
def test_search_does_not_crash_on_bad():
"""
this used to crash, kept for reference.
"""
f = tutils.tflowview(request_contents="this is string\nstring is string\n"+("A" * cv.VIEW_CUTOFF)+"AFTERCUTOFF")
f.search("AFTERCUTOFF")
# pretend F
f.state.add_flow_setting(
f.flow,
(f.state.view_flow_mode, "fullcontents"),
True
)
f.master.refresh_flow(f.flow)
# text changed, now this string will exist. can happen when user presses F
# for full text view
f.search("AFTERCUTOFF")

121
test/test_console_search.py Normal file
View File

@ -0,0 +1,121 @@
import sys
import libmproxy.console.contentview as cv
from libmproxy import utils, flow, encoding
import tutils
def test_search_highlights():
# Default text in requests is content. We will search for nt once, and
# expect the first bit to be highlighted. We will do it again and expect the
# second to be.
f = tutils.tflowview()
f.search("nt")
text_object = tutils.get_body_line(f.last_displayed_body, 0)
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_returns_useful_messages():
f = tutils.tflowview()
# original string is content. this string should not be in there.
test_string = "oranges and other fruit."
response = f.search(test_string)
assert response == "no matches for '%s'" % test_string
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), (f.highlight_color, 6)])
# should highlight third line, second appearance of string.
f.search("string")
text_object = tutils.get_body_line(f.last_displayed_body, 1)
print(text_object.get_text(), ('string is string', [(None, 10), (f.highlight_color, 6)]))
assert text_object.get_text() == ('string is string', [(None, 10), (f.highlight_color, 6)])
def test_search_loops():
f = tutils.tflowview(request_contents="this is string\nstring is string")
# get to the end.
f.search("string")
f.search("string")
f.search("string")
# should highlight the first line.
message = 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)])
assert message == "search hit BOTTOM, continuing at TOP"
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
def test_search_does_not_crash_on_bad():
"""
this used to crash, kept for reference.
"""
f = tutils.tflowview(request_contents="this is string\nstring is string\n"+("A" * cv.VIEW_CUTOFF)+"AFTERCUTOFF")
f.search("AFTERCUTOFF")
# pretend F
f.state.add_flow_setting(
f.flow,
(f.state.view_flow_mode, "fullcontents"),
True
)
f.master.refresh_flow(f.flow)
# text changed, now this string will exist. can happen when user presses F
# for full text view
f.search("AFTERCUTOFF")
def test_search_backwards():
f = tutils.tflowview(request_contents="content, content")
first_match = ('content, content', [(None, 2), (f.highlight_color, 2)])
f.search("nt")
text_object = tutils.get_body_line(f.last_displayed_body, 0)
assert text_object.get_text() == first_match
f.search("nt")
text_object = tutils.get_body_line(f.last_displayed_body, 1)
assert text_object.get_text() == ('content, content', [(None, 5), (f.highlight_color, 2)])
f.search_again(backwards=True)
text_object = tutils.get_body_line(f.last_displayed_body, 0)
assert text_object.get_text() == first_match