From f7b4607ed68177bc66e225bf0bc560a71baf438c Mon Sep 17 00:00:00 2001 From: dece Date: Mon, 28 Jun 2021 02:09:59 +0200 Subject: [PATCH] metalines: use a 3-uple instead of dict --- bebop/browser/browser.py | 16 ++++++------- bebop/metalines.py | 50 +++++++++++++++++++--------------------- bebop/rendering.py | 11 ++++----- 3 files changed, 37 insertions(+), 40 deletions(-) diff --git a/bebop/browser/browser.py b/bebop/browser/browser.py index de52e4e..ee31acc 100644 --- a/bebop/browser/browser.py +++ b/bebop/browser/browser.py @@ -552,19 +552,19 @@ class Browser: line_pos = y + py if line_pos >= len(self.current_page.metalines): return - meta, line = self.current_page.metalines[line_pos] - if meta["type"] != LineType.LINK: + ltype, ltext, lextra = self.current_page.metalines[line_pos] + if ltype != LineType.LINK: return # "url" key is contained only in the first line of the link if its text # is wrapped, so if the user did not click on the first line, rewind to # get the URL. - while "url" not in meta: + while not lextra or "url" not in lextra: line_pos -= 1 - meta, line = self.current_page.metalines[line_pos] - url = meta["url"] + _, ltext, lextra = self.current_page.metalines[line_pos] + url = lextra["url"] # The click is valid if it is on the link itself or the dimmed preview. col_pos = x + px - if col_pos > len(line): + if col_pos > len(ltext): ch = self.page_pad.pad.instr(line_pos, col_pos, 1) if ch == b' ': return @@ -834,8 +834,8 @@ class Browser: if not search: return self.search_res_lines = [] - for index, (_, line) in enumerate(self.current_page.metalines): - if search in line: + for index, (_, ltext, _) in enumerate(self.current_page.metalines): + if search in ltext: self.search_res_lines.append(index) if self.search_res_lines: self.move_to_search_result(Browser.SEARCH_NEXT) diff --git a/bebop/metalines.py b/bebop/metalines.py index 5a64107..f094d99 100644 --- a/bebop/metalines.py +++ b/bebop/metalines.py @@ -6,6 +6,18 @@ displayed, along with associated meta-data such as its type or a link's URL. Note that metalines can be generated by custom functions without relying on the elements classes as they are quite coupled to Gemtext parsing/rendering. + +The metalines are tuples (ltype, line, lextra): +- ltype is the LineType. +- line is the text content itself. +- lextra is either a dict of additional data, or None. + +The lextra part is currently only used for links, and can contain the following +keys: +- url: the URL the link on this line refers to. Note that this key is present + only for the first line of the link, i.e. long link descriptions wrapped on + multiple lines will not have a this key except for the first line. +- link_id: only alongside "url" key, ID generated for this link. """ import string @@ -53,22 +65,12 @@ class RenderOptions: def generate_metalines(elements: list, options: RenderOptions) -> list: """Format elements into a list of lines with metadata. - The returned list ("metalines") are tuples (meta, line), meta being a - dict of metadata and a text line to display. Currently the only metadata - keys used are: - - type: one of the Renderer.TYPE constants. - - url: only for links, the URL the link on this line refers to. Note - that this key is present only for the first line of the link, i.e. - long link descriptions wrapped on multiple lines will not have a this - key except for the first line. - - link_id: only alongside "url" key, ID generated for this link. - Arguments: - elements: list of elements to use. - options: RenderOptions to respect when generating metalines. """ metalines = [] - separator = ({"type": LineType.NONE}, "") + separator = (LineType.NONE, "", None) has_margins = False thin_type = None for index, element in enumerate(elements): @@ -100,7 +102,7 @@ def generate_metalines(elements: list, options: RenderOptions) -> list: # rendered as empty lines. if options.mode == "dumb": if not element_metalines: - element_metalines = [({"type": LineType.PARAGRAPH}, "")] + element_metalines = [(LineType.PARAGRAPH, "", None)] # If current element requires margins and is not the first elements, # separate from previous element. Also do it if the current element does # not require margins but follows an element that required it (e.g. link @@ -119,7 +121,7 @@ def generate_metalines(elements: list, options: RenderOptions) -> list: def generate_dumb_metalines(lines): """Generate dumb metalines: all lines are given the PARAGRAPH line type.""" - return [({"type": LineType.PARAGRAPH}, line) for line in lines] + return [(LineType.PARAGRAPH, line, None) for line in lines] def format_title(title: Title, options: RenderOptions): @@ -135,13 +137,13 @@ def format_title(title: Title, options: RenderOptions): else: lines = wrap_words(title.text, width) # Title levels match the type constants of titles. - return [({"type": LineType(title.level)}, line) for line in lines] + return [(LineType(title.level), line, None) for line in lines] def format_paragraph(paragraph: Paragraph, options: RenderOptions): """Return metalines for this paragraph.""" lines = wrap_words(paragraph.text, options.width) - return [({"type": LineType.PARAGRAPH}, line) for line in lines] + return [(LineType.PARAGRAPH, line, None) for line in lines] def format_link(link: Link, options: RenderOptions): @@ -151,30 +153,26 @@ def format_link(link: Link, options: RenderOptions): link_text = link.text or link.url # Wrap lines, indented by the link anchor length. lines = wrap_words(link_text, options.width, indent=len(link_anchor)) - first_line_meta = { - "type": LineType.LINK, + first_line_extra = { "url": link.url, "link_id": link.ident } # Replace first line indentation with the anchor. first_line_text = link_anchor + lines[0][len(link_anchor):] - first_line = [(first_line_meta, first_line_text)] - other_lines = [({"type": LineType.LINK}, line) for line in lines[1:]] - return first_line + other_lines + first_line = [(LineType.LINK, first_line_text, first_line_extra)] + other_lines = [(LineType.LINK, line, None) for line in lines[1:]] + return first_line + other_lines # type: ignore def format_preformatted(preformatted: Preformatted, options: RenderOptions): """Return metalines for this preformatted block.""" - return [ - ({"type": LineType.PREFORMATTED}, line) - for line in preformatted.lines - ] + return [(LineType.PREFORMATTED, line, None) for line in preformatted.lines] def format_blockquote(blockquote: Blockquote, options: RenderOptions): """Return metalines for this blockquote.""" lines = wrap_words(blockquote.text, options.width, indent=2) - return [({"type": LineType.BLOCKQUOTE}, line) for line in lines] + return [(LineType.BLOCKQUOTE, line, None) for line in lines] def format_list_item(item: ListItem, options: RenderOptions): @@ -183,7 +181,7 @@ def format_list_item(item: ListItem, options: RenderOptions): lines = wrap_words(item.text, options.width, indent=indent) first_line = options.bullet + lines[0][indent:] lines[0] = first_line - return [({"type": LineType.LIST_ITEM}, line) for line in lines] + return [(LineType.LIST_ITEM, line, None) for line in lines] def wrap_words(text: str, width: int, indent: int =0) -> List[str]: diff --git a/bebop/rendering.py b/bebop/rendering.py index 9b3e30a..a290e69 100644 --- a/bebop/rendering.py +++ b/bebop/rendering.py @@ -37,13 +37,12 @@ def render_lines(metalines, window, max_width): def render_line(metaline, window, max_width): """Write a single line to the window.""" - meta, line = metaline - line_type = meta["type"] - attributes = get_base_line_attributes(line_type) - line = line[:max_width - 1] + ltype, ltext, lextra = metaline + attributes = get_base_line_attributes(ltype) + line = ltext[:max_width - 1] window.addstr(line, attributes) - if meta["type"] == LineType.LINK and "url" in meta: - url_text = f' {meta["url"]}' + if ltype == LineType.LINK and lextra and "url" in lextra: + url_text = f' {lextra["url"]}' attributes = ( curses.color_pair(ColorPair.LINK_PREVIEW) | curses.A_DIM