From d0ece455dad6eb3b11f412d6baca340676920f9d Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 10 Nov 2019 16:44:59 +0000 Subject: [PATCH] tidier rich text --- rich/text.py | 69 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/rich/text.py b/rich/text.py index 8a67fa83..a5ceb95e 100644 --- a/rich/text.py +++ b/rich/text.py @@ -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