browser: add text search

This commit is contained in:
dece 2021-06-04 02:25:15 +02:00
parent 7cb6d03668
commit bd8d4bbfb1
4 changed files with 69 additions and 11 deletions

View file

@ -1,6 +1,5 @@
TODO TODO
---------------------------------------- ----------------------------------------
search in page (ugh)
@ -29,6 +28,7 @@ remember scroll pos in history
identity management identity management
"previous/next" pages "previous/next" pages
configurable keybinds configurable keybinds
handle big files (e.g. gemini://tilde.team/~tomasino/irc/log.txt)
@ -58,3 +58,4 @@ different rendering mode
preferences per site preferences per site
basic mouse support basic mouse support
basic local browsing basic local browsing
search in page

View file

@ -64,8 +64,12 @@ class Browser:
returning the page source path. returning the page source path.
- last_download: tuple of MimeType and path, or None. - last_download: tuple of MimeType and path, or None.
- identities: identities map. - identities: identities map.
- search_res_lines: list of lines containing results of the last search.
""" """
SEARCH_NEXT = 0
SEARCH_PREVIOUS = 1
def __init__(self, config, cert_stash): def __init__(self, config, cert_stash):
self.config = config self.config = config
self.stash = cert_stash self.stash = cert_stash
@ -81,6 +85,7 @@ class Browser:
self.special_pages = self.setup_special_pages() self.special_pages = self.setup_special_pages()
self.last_download: Optional[Tuple[MimeType, Path]] = None self.last_download: Optional[Tuple[MimeType, Path]] = None
self.identities = {} self.identities = {}
self.search_res_lines = []
self._current_url = "" self._current_url = ""
@property @property
@ -244,6 +249,12 @@ class Browser:
self.open_history() self.open_history()
elif char == ord("§"): elif char == ord("§"):
self.toggle_render_mode() self.toggle_render_mode()
elif char == ord("/"):
self.search_in_page()
elif char == ord("n"):
self.move_to_search_result(Browser.SEARCH_NEXT)
elif char == ord("N"):
self.move_to_search_result(Browser.SEARCH_PREVIOUS)
elif curses.ascii.isdigit(char): elif curses.ascii.isdigit(char):
self.handle_digit_input(char) self.handle_digit_input(char)
elif char == curses.KEY_MOUSE: elif char == curses.KEY_MOUSE:
@ -494,9 +505,9 @@ class Browser:
If the click is on a link (appropriate line and columns), open it. If the click is on a link (appropriate line and columns), open it.
""" """
if not self.page_pad or not self.page_pad.current_page:
return
page = self.page_pad.current_page page = self.page_pad.current_page
if not page:
return
px, py = self.page_pad.current_column, self.page_pad.current_line px, py = self.page_pad.current_column, self.page_pad.current_line
line_pos = y + py line_pos = y + py
if line_pos >= len(page.metalines): if line_pos >= len(page.metalines):
@ -727,13 +738,14 @@ class Browser:
def show_page_info(self): def show_page_info(self):
"""Show some page informations in the status bar.""" """Show some page informations in the status bar."""
if not self.page_pad or not self.page_pad.current_page:
return
page = self.page_pad.current_page page = self.page_pad.current_page
if not page:
return
mime = page.mime.short if page.mime else "(unknown MIME type)" mime = page.mime.short if page.mime else "(unknown MIME type)"
encoding = page.encoding or "(unknown encoding)" encoding = page.encoding or "(unknown encoding)"
size = f"{len(page.source)} chars" size = f"{len(page.source)} chars"
info = f"{mime} {encoding} {size}" lines = f"{len(page.metalines)} lines"
info = f"{mime} {encoding} {size} {lines}"
self.set_status(info) self.set_status(info)
def set_render_mode(self, mode): def set_render_mode(self, mode):
@ -758,9 +770,9 @@ class Browser:
def toggle_render_mode(self): def toggle_render_mode(self):
"""Switch to the next render mode for the current page.""" """Switch to the next render mode for the current page."""
if not self.page_pad or not self.page_pad.current_page:
return
page = self.page_pad.current_page page = self.page_pad.current_page
if not page:
return
if page.render is None or page.render not in RENDER_MODES: if page.render is None or page.render not in RENDER_MODES:
next_mode = RENDER_MODES[0] next_mode = RENDER_MODES[0]
else: else:
@ -773,3 +785,46 @@ class Browser:
) )
self.load_page(new_page) self.load_page(new_page)
self.set_status(f"Using render mode '{next_mode}'.") self.set_status(f"Using render mode '{next_mode}'.")
def search_in_page(self):
"""Search for words in the page."""
page = self.page_pad.current_page
if not page:
return
search = self.get_user_text_input("Search", CommandLine.CHAR_TEXT)
if not search:
return
self.search_res_lines = []
for index, (_, line) in enumerate(page.metalines):
if search in line:
self.search_res_lines.append(index)
if self.search_res_lines:
self.move_to_search_result(Browser.SEARCH_NEXT)
else:
self.set_status(f"'{search}' not found.")
def move_to_search_result(self, prev_or_next: int):
"""Move to the next or previous search result."""
current_line = self.page_pad.current_line
next_line = None
index = 1
max_index = len(self.search_res_lines)
if prev_or_next == Browser.SEARCH_NEXT:
for line in self.search_res_lines:
if line > current_line:
next_line = line
break
index += 1
elif prev_or_next == Browser.SEARCH_PREVIOUS:
index = max_index
for line in reversed(self.search_res_lines):
if line < current_line:
next_line = line
break
index -= 1
if next_line is None:
return
self.set_status(f"Result {index}/{max_index}")
self.page_pad.current_line = next_line
self.refresh_windows()

View file

@ -38,6 +38,9 @@ Keybinds using the SHIFT key are written uppercase. Keybinds using the ALT (or M
* digits: go to the corresponding link ID * digits: go to the corresponding link ID
* escape: reset status line text * escape: reset status line text
* section sign (§): toggle between render modes for the current page * section sign (§): toggle between render modes for the current page
* slash (/): search for some text
* n: go to next search result
* N: go to previous search result
* C-c: cancel current operation * C-c: cancel current operation
## Commands ## Commands

View file

@ -19,9 +19,8 @@ def render_lines(metalines, window, max_width):
- max_width: line length limit for the pad. - max_width: line length limit for the pad.
Returns: Returns:
The tuple of integers (error, height, width), error being a non-zero value The tuple of integers (height, width), the new dimensions of the resized
if an error occured during rendering, and height and width being the new window.
dimensions of the resized window.
""" """
num_lines = len(metalines) num_lines = len(metalines)
new_dimensions = max(num_lines, 1), max_width new_dimensions = max(num_lines, 1), max_width