tidier rich text

This commit is contained in:
Will McGugan 2019-11-10 16:44:59 +00:00
parent 7481db402b
commit d0ece455da
1 changed files with 50 additions and 19 deletions

View File

@ -13,9 +13,9 @@ class TextSpan(NamedTuple):
start: int
end: int
style: Optional[str]
style: str
def adjust_offset(self, offset: int) -> TextSpan:
def adjust_offset(self, offset: int, max_length) -> TextSpan:
"""Get a new `TextSpan` with start and end adjusted.
Args:
@ -24,7 +24,11 @@ class TextSpan(NamedTuple):
Returns:
TextSpan: A new text span.
"""
return TextSpan(self.start + offset, self.end + offset, self.style)
return TextSpan(
min(max_length, max(0, self.start + offset)),
min(max_length, max(0, self.end + offset)),
self.style,
)
def slice_text(self, text: str) -> str:
"""Slice the text according to the start and end offsets.
@ -59,32 +63,59 @@ class RichText:
return f"RichText({self.text!r})"
def stylize(self, start: int, end: int, style: str) -> None:
self._spans.append(TextSpan(start, end, style))
"""Apply a style to a portion of the text.
Args:
start (int): Start offset.
end (int): End offset.
style (str): Style name to apply.
Returns:
None:
"""
length = len(self)
if end < 0 or start > length:
# span in range
return
self._spans.append(TextSpan(max(0, start), min(length, end), style))
def __console_render__(
self, console: Console, options: ConsoleOptions
) -> Iterable[StyledText]:
"""Render the rich text to the console.
Args:
console (Console): Console instance.
options (ConsoleOptions): Console options.
Returns:
Iterable[StyledText]: An iterable of styled text.
"""
print(self._spans)
text = self.text
stack: List[Style] = []
get_style = console.get_style
spans = [
(span.start, True, get_style(span.style or "none")) for span in self._spans
]
spans.extend(
(span.end, False, get_style(span.style or "none")) for span in self._spans
)
spans.sort(key=itemgetter(0))
spans.insert(0, (0, True, get_style("none")))
spans.append((len(text), False, get_style("none")))
null_style = Style()
current_style = Style()
start_spans = (
(span.start, True, get_style(span.style) or null_style)
for span in self._spans
)
end_spans = (
(span.end, False, get_style(span.style) or null_style)
for span in self._spans
)
spans = [
(0, True, null_style),
*start_spans,
*end_spans,
(len(text), False, null_style),
]
spans.sort(key=itemgetter(0))
current_style = Style()
for (offset, entering, style), (next_offset, _, _) in zip(spans, spans[1:]):
style = style or null_style
if entering:
stack.append(style)
current_style = current_style.apply(style)
@ -113,7 +144,7 @@ class RichText:
self._text.append(text)
offset = len(self)
text_length = len(text)
self._spans.append(TextSpan(offset, offset + text_length, style))
self._spans.append(TextSpan(offset, offset + text_length, style or "none"))
self._length += text_length
self._text_str = None
@ -131,7 +162,7 @@ class RichText:
for offset, line in zip(offsets, new_lines):
if span.end <= offset or span.start >= offset + len(line):
continue
line._spans.append(span.adjust_offset(-offset))
line._spans.append(span.adjust_offset(-offset, len(line)))
return new_lines