Add basic search support (needs a little work, btw HI MODS)

themes
desvox 2018-07-27 17:28:34 -05:00
parent 4177c6080c
commit 22b4a5d5a5
2 changed files with 104 additions and 29 deletions

View File

@ -268,6 +268,12 @@ class App(object):
self.usermap = {} self.usermap = {}
self.window_split = False self.window_split = False
self.last_pos = None self.last_pos = None
self.last_alarm = None
self.match_data = {
"query": "",
"matches": [],
"position": 0,
}
# these can be changed and manipulated by other methods # these can be changed and manipulated by other methods
self.walker = urwid.SimpleFocusListWalker([]) self.walker = urwid.SimpleFocusListWalker([])
@ -732,16 +738,91 @@ class App(object):
pass pass
def search_index_callback(self, query):
simple_query = query.lower().strip()
threads, usermap = network.thread_index()
self.usermap.update(usermap)
results = [
thread for thread in threads
if simple_query in thread["title"].lower().strip()
]
if results:
self.index(threads=results)
if query:
self.set_header("Searching for '{}'", query)
else:
self.temp_footer_message("No results for '{}'".format(query))
def search_thread_callback(self, query):
# TODO: Not that!!
# fetch the thread again because we need all the messages without formatting
thread, _ = network.thread_load(self.thread["thread_id"])
query = query.lower().strip()
self.match_data["matches"] = [
message for message in thread["messages"]
if query in message["body"].lower().strip()
]
if self.match_data["matches"]:
self.match_data["query"] = query
self.match_data["position"] = -1
# self.highlight_query()
self.do_search_result()
else:
self.temp_footer_message("No results for '{}'".format(query))
def do_search_result(self, forward=True):
self.match_data["position"] += 1 if forward else -1
length = len(self.match_data["matches"])
if forward:
if self.match_data["position"] == length:
self.match_data["position"] = 0
else:
if self.match_data["position"] == -1:
self.match_data["position"] = length - 1
self.goto_post(self.match_data["matches"][self.match_data["position"]]["post_id"])
self.temp_footer_message(
"({}/{}) Searching for {} [#]Next [@]Previous".format(
self.match_data["position"] + 1, length, self.match_data["query"]
), 5)
# XXX: Try to find a way to overlay properties onto an existing widget instead of this trainwreck.
# def highlight_query(self):
# # pass
# query = self.match_data["query"]
# for match in self.match_data["matches"]:
# widget = self.walker[match["post_id"] * 5 + 2].base_widget
# # (">>OP\n\nThat's a nice initiative x)", [('50', 4), (None, 2), ('default', 27)])
# text, attrs = widget.get_text()
# spans = [m.span() for m in re.finditer(query, text)]
# start, end = spans.pop(0)
# new_attrs = []
# index = 0
# for prop, length in attrs:
# if index and index > start:
# index = end
# new_attrs.append(("20", end - start))
# start, end = spans.pop(0)
# else:
# index += length
# new_attrs.append((prop, length))
def search_prompt(self): def search_prompt(self):
# XXX: remove if when thread search is supported if self.mode == "index":
if self.mode != "index": callback = self.search_index_callback
elif self.mode == "thread":
callback = self.search_thread_callback
else:
return return
popup = OptionsMenu( popup = OptionsMenu(
urwid.ListBox( urwid.ListBox(
urwid.SimpleFocusListWalker([ urwid.SimpleFocusListWalker([
urwid.Text(("button", "Enter a query:")), urwid.Text(("button", "Enter a query:")),
urwid.AttrMap(StringPrompt(self.search_callback), "opt_prompt"), urwid.AttrMap(StringPrompt(callback), "opt_prompt"),
urwid.Text("Use a blank query to reset the {}.".format(self.mode)) urwid.Text("Use a blank query to reset the {}.".format(self.mode))
])), ])),
**frame_theme()) **frame_theme())
@ -753,24 +834,6 @@ class App(object):
width=("relative", 40), height=6) width=("relative", 40), height=6)
def search_callback(self, query):
threads, usermap = network.thread_index()
self.usermap.update(usermap)
if self.mode == "index":
results = [
thread for thread in threads
if query in thread["title"].lower().strip().replace(" ", "")
]
if results:
self.index(threads=results)
if query:
self.set_header("Searching for '{}'", query)
else:
self.temp_footer_message("No results for '{}'".format(query))
elif self.mode == "thread":
pass
def thread_load(self, button, thread_id): def thread_load(self, button, thread_id):
""" """
Open a thread. Open a thread.
@ -786,6 +849,7 @@ class App(object):
thread, usermap = network.thread_load(thread_id, format="sequential") thread, usermap = network.thread_load(thread_id, format="sequential")
self.usermap.update(usermap) self.usermap.update(usermap)
self.thread = thread self.thread = thread
self.match_data["matches"].clear()
self.walker.clear() self.walker.clear()
for message in thread["messages"]: for message in thread["messages"]:
self.walker += self.make_message_body(message) self.walker += self.make_message_body(message)
@ -1347,19 +1411,20 @@ class App(object):
self.loop.widget.focus_position = "footer" self.loop.widget.focus_position = "footer"
def reset_footer(self, _, from_temp): def reset_footer(self, *_):
if from_temp and self.window_split: if self.window_split:
return return
try: self.set_default_footer()
self.set_default_footer(True) # try:
self.loop.widget.focus_position = "body" # self.loop.widget.focus_position = "body"
except: # except:
# just keep trying until the focus widget can handle it # just keep trying until the focus widget can handle it
self.loop.set_alarm_in(0.5, self.reset_footer) # return self.loop.set_alarm_in(0.25, self.reset_footer)
def temp_footer_message(self, string, duration=3): def temp_footer_message(self, string, duration=3):
self.loop.set_alarm_in(duration, self.reset_footer, True) self.loop.remove_alarm(self.last_alarm)
self.last_alarm = self.loop.set_alarm_in(duration, self.reset_footer)
self.set_footer(string) self.set_footer(string)
@ -1939,6 +2004,12 @@ class ActionBox(urwid.ListBox):
elif keyl in ("r", "f5") and not overlay: elif keyl in ("r", "f5") and not overlay:
app.refresh() app.refresh()
elif key == "#":
app.do_search_result(True)
elif key == "@":
app.do_search_result(False)
elif key == "~": elif key == "~":
# sssssshhhhhhhh # sssssshhhhhhhh
app.loop.stop() app.loop.stop()
@ -1946,6 +2017,9 @@ class ActionBox(urwid.ListBox):
except: pass except: pass
app.loop.start() app.loop.start()
elif keyl == "f12":
app.loop.stop()
elif app.mode == "thread" and not app.window_split and not overlay: elif app.mode == "thread" and not app.window_split and not overlay:
message = app.thread["messages"][app.get_focus_post()] message = app.thread["messages"][app.get_focus_post()]

View File

@ -5,3 +5,4 @@ when given a certain time (kind of like `message_feed`)
* TODO Author-only "announcement" threads * TODO Author-only "announcement" threads
* TODO HTML output from the formatter * TODO HTML output from the formatter
* TODO integrate thread pins to urwid client * TODO integrate thread pins to urwid client
* TODO add search highlights to in-thread searching