metalines: use a 3-uple instead of dict
This commit is contained in:
parent
016e4a49f9
commit
f7b4607ed6
|
@ -552,19 +552,19 @@ class Browser:
|
||||||
line_pos = y + py
|
line_pos = y + py
|
||||||
if line_pos >= len(self.current_page.metalines):
|
if line_pos >= len(self.current_page.metalines):
|
||||||
return
|
return
|
||||||
meta, line = self.current_page.metalines[line_pos]
|
ltype, ltext, lextra = self.current_page.metalines[line_pos]
|
||||||
if meta["type"] != LineType.LINK:
|
if ltype != LineType.LINK:
|
||||||
return
|
return
|
||||||
# "url" key is contained only in the first line of the link if its text
|
# "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
|
# is wrapped, so if the user did not click on the first line, rewind to
|
||||||
# get the URL.
|
# get the URL.
|
||||||
while "url" not in meta:
|
while not lextra or "url" not in lextra:
|
||||||
line_pos -= 1
|
line_pos -= 1
|
||||||
meta, line = self.current_page.metalines[line_pos]
|
_, ltext, lextra = self.current_page.metalines[line_pos]
|
||||||
url = meta["url"]
|
url = lextra["url"]
|
||||||
# The click is valid if it is on the link itself or the dimmed preview.
|
# The click is valid if it is on the link itself or the dimmed preview.
|
||||||
col_pos = x + px
|
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)
|
ch = self.page_pad.pad.instr(line_pos, col_pos, 1)
|
||||||
if ch == b' ':
|
if ch == b' ':
|
||||||
return
|
return
|
||||||
|
@ -834,8 +834,8 @@ class Browser:
|
||||||
if not search:
|
if not search:
|
||||||
return
|
return
|
||||||
self.search_res_lines = []
|
self.search_res_lines = []
|
||||||
for index, (_, line) in enumerate(self.current_page.metalines):
|
for index, (_, ltext, _) in enumerate(self.current_page.metalines):
|
||||||
if search in line:
|
if search in ltext:
|
||||||
self.search_res_lines.append(index)
|
self.search_res_lines.append(index)
|
||||||
if self.search_res_lines:
|
if self.search_res_lines:
|
||||||
self.move_to_search_result(Browser.SEARCH_NEXT)
|
self.move_to_search_result(Browser.SEARCH_NEXT)
|
||||||
|
|
|
@ -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
|
Note that metalines can be generated by custom functions without relying on the
|
||||||
elements classes as they are quite coupled to Gemtext parsing/rendering.
|
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
|
import string
|
||||||
|
@ -53,22 +65,12 @@ class RenderOptions:
|
||||||
def generate_metalines(elements: list, options: RenderOptions) -> list:
|
def generate_metalines(elements: list, options: RenderOptions) -> list:
|
||||||
"""Format elements into a list of lines with metadata.
|
"""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:
|
Arguments:
|
||||||
- elements: list of elements to use.
|
- elements: list of elements to use.
|
||||||
- options: RenderOptions to respect when generating metalines.
|
- options: RenderOptions to respect when generating metalines.
|
||||||
"""
|
"""
|
||||||
metalines = []
|
metalines = []
|
||||||
separator = ({"type": LineType.NONE}, "")
|
separator = (LineType.NONE, "", None)
|
||||||
has_margins = False
|
has_margins = False
|
||||||
thin_type = None
|
thin_type = None
|
||||||
for index, element in enumerate(elements):
|
for index, element in enumerate(elements):
|
||||||
|
@ -100,7 +102,7 @@ def generate_metalines(elements: list, options: RenderOptions) -> list:
|
||||||
# rendered as empty lines.
|
# rendered as empty lines.
|
||||||
if options.mode == "dumb":
|
if options.mode == "dumb":
|
||||||
if not element_metalines:
|
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,
|
# If current element requires margins and is not the first elements,
|
||||||
# separate from previous element. Also do it if the current element does
|
# 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
|
# 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):
|
def generate_dumb_metalines(lines):
|
||||||
"""Generate dumb metalines: all lines are given the PARAGRAPH line type."""
|
"""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):
|
def format_title(title: Title, options: RenderOptions):
|
||||||
|
@ -135,13 +137,13 @@ def format_title(title: Title, options: RenderOptions):
|
||||||
else:
|
else:
|
||||||
lines = wrap_words(title.text, width)
|
lines = wrap_words(title.text, width)
|
||||||
# Title levels match the type constants of titles.
|
# 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):
|
def format_paragraph(paragraph: Paragraph, options: RenderOptions):
|
||||||
"""Return metalines for this paragraph."""
|
"""Return metalines for this paragraph."""
|
||||||
lines = wrap_words(paragraph.text, options.width)
|
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):
|
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
|
link_text = link.text or link.url
|
||||||
# Wrap lines, indented by the link anchor length.
|
# Wrap lines, indented by the link anchor length.
|
||||||
lines = wrap_words(link_text, options.width, indent=len(link_anchor))
|
lines = wrap_words(link_text, options.width, indent=len(link_anchor))
|
||||||
first_line_meta = {
|
first_line_extra = {
|
||||||
"type": LineType.LINK,
|
|
||||||
"url": link.url,
|
"url": link.url,
|
||||||
"link_id": link.ident
|
"link_id": link.ident
|
||||||
}
|
}
|
||||||
# Replace first line indentation with the anchor.
|
# Replace first line indentation with the anchor.
|
||||||
first_line_text = link_anchor + lines[0][len(link_anchor):]
|
first_line_text = link_anchor + lines[0][len(link_anchor):]
|
||||||
first_line = [(first_line_meta, first_line_text)]
|
first_line = [(LineType.LINK, first_line_text, first_line_extra)]
|
||||||
other_lines = [({"type": LineType.LINK}, line) for line in lines[1:]]
|
other_lines = [(LineType.LINK, line, None) for line in lines[1:]]
|
||||||
return first_line + other_lines
|
return first_line + other_lines # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def format_preformatted(preformatted: Preformatted, options: RenderOptions):
|
def format_preformatted(preformatted: Preformatted, options: RenderOptions):
|
||||||
"""Return metalines for this preformatted block."""
|
"""Return metalines for this preformatted block."""
|
||||||
return [
|
return [(LineType.PREFORMATTED, line, None) for line in preformatted.lines]
|
||||||
({"type": LineType.PREFORMATTED}, line)
|
|
||||||
for line in preformatted.lines
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def format_blockquote(blockquote: Blockquote, options: RenderOptions):
|
def format_blockquote(blockquote: Blockquote, options: RenderOptions):
|
||||||
"""Return metalines for this blockquote."""
|
"""Return metalines for this blockquote."""
|
||||||
lines = wrap_words(blockquote.text, options.width, indent=2)
|
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):
|
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)
|
lines = wrap_words(item.text, options.width, indent=indent)
|
||||||
first_line = options.bullet + lines[0][indent:]
|
first_line = options.bullet + lines[0][indent:]
|
||||||
lines[0] = first_line
|
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]:
|
def wrap_words(text: str, width: int, indent: int =0) -> List[str]:
|
||||||
|
|
|
@ -37,13 +37,12 @@ def render_lines(metalines, window, max_width):
|
||||||
|
|
||||||
def render_line(metaline, window, max_width):
|
def render_line(metaline, window, max_width):
|
||||||
"""Write a single line to the window."""
|
"""Write a single line to the window."""
|
||||||
meta, line = metaline
|
ltype, ltext, lextra = metaline
|
||||||
line_type = meta["type"]
|
attributes = get_base_line_attributes(ltype)
|
||||||
attributes = get_base_line_attributes(line_type)
|
line = ltext[:max_width - 1]
|
||||||
line = line[:max_width - 1]
|
|
||||||
window.addstr(line, attributes)
|
window.addstr(line, attributes)
|
||||||
if meta["type"] == LineType.LINK and "url" in meta:
|
if ltype == LineType.LINK and lextra and "url" in lextra:
|
||||||
url_text = f' {meta["url"]}'
|
url_text = f' {lextra["url"]}'
|
||||||
attributes = (
|
attributes = (
|
||||||
curses.color_pair(ColorPair.LINK_PREVIEW)
|
curses.color_pair(ColorPair.LINK_PREVIEW)
|
||||||
| curses.A_DIM
|
| curses.A_DIM
|
||||||
|
|
Reference in a new issue