From 260508bfa058d9d7e7f782482e91adf7ddf06342 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 14 Dec 2021 17:57:11 +0000 Subject: [PATCH 01/10] fix for overlapping markip --- CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- rich/text.py | 32 ++++++++------------------------ tests/test_segment.py | 30 +++++++++++++++++++++++++++++- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4218362e..ff135e76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [10.16.1] - Unreleased + +### Fixed + +- Fixed issues with overlaping tags https://github.com/willmcgugan/rich/issues/1755 + ## [10.16.0] - 2021-12-12 ### Fixed diff --git a/pyproject.toml b/pyproject.toml index 7e219d48..c26c6b1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "rich" homepage = "https://github.com/willmcgugan/rich" documentation = "https://rich.readthedocs.io/en/latest/" -version = "10.16.0" +version = "10.16.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" authors = ["Will McGugan "] license = "MIT" diff --git a/rich/text.py b/rich/text.py index b277f6c1..43c089d6 100644 --- a/rich/text.py +++ b/rich/text.py @@ -1033,6 +1033,7 @@ class Text(JupyterMixin): Lines: New RichText instances between offsets. """ _offsets = list(offsets) + if not _offsets: return Lines([self.copy()]) @@ -1056,33 +1057,16 @@ class Text(JupyterMixin): ) if not self._spans: return new_lines - order = {span: span_index for span_index, span in enumerate(self._spans)} - span_stack = sorted(self._spans, key=attrgetter("start"), reverse=True) - pop = span_stack.pop - push = span_stack.append _Span = Span - get_order = order.__getitem__ - for line, (start, end) in zip(new_lines, line_ranges): - if not span_stack: - break - append_span = line._spans.append - position = len(span_stack) - 1 - while span_stack[position].start < end: - span = pop(position) - add_span, remaining_span = span.split(end) - if remaining_span: - push(remaining_span) - order[remaining_span] = order[span] - span_start, span_end, span_style = add_span - line_span = _Span(span_start - start, span_end - start, span_style) - order[line_span] = order[span] - append_span(line_span) - position -= 1 - if position < 0 or not span_stack: - break # pragma: no cover - line._spans.sort(key=get_order) + for line, (line_start, line_end) in zip(new_lines._lines, line_ranges): + for span_start, span_end, style in self._spans: + if span_end > line_start: + new_start = max(0, span_start - line_start) + new_end = min(span_end - line_start, line_end - line_start) + if new_end > new_start: + line._spans.append(_Span(new_start, new_end, style)) return new_lines diff --git a/tests/test_segment.py b/tests/test_segment.py index dcf7b218..9aff243a 100644 --- a/tests/test_segment.py +++ b/tests/test_segment.py @@ -1,4 +1,4 @@ -import sys +from io import StringIO import pytest @@ -166,6 +166,34 @@ def test_divide(): ] +# https://github.com/willmcgugan/rich/issues/1755 +def test_divide_complex(): + MAP = ( + "[on orange4] [on green]XX[on orange4] \n" + " \n" + " \n" + " \n" + " [bright_red on black]Y[on orange4] \n" + "[on green]X[on orange4] [on green]X[on orange4] \n" + " [on green]X[on orange4] [on green]X\n" + "[on orange4] \n" + " [on green]XX[on orange4] \n" + ) + from rich.text import Text + from rich.console import Console + + text = Text.from_markup(MAP) + console = Console( + color_system="truecolor", width=30, force_terminal=True, file=StringIO() + ) + console.print(text) + result = console.file.getvalue() + + print(repr(result)) + expected = "\x1b[48;5;94m \x1b[0m\x1b[42mXX\x1b[0m\x1b[48;5;94m \x1b[0m\n\x1b[48;5;94m \x1b[0m\n\x1b[48;5;94m \x1b[0m\n\x1b[48;5;94m \x1b[0m\n\x1b[48;5;94m \x1b[0m\x1b[91;40mY\x1b[0m\x1b[91;48;5;94m \x1b[0m\n\x1b[91;42mX\x1b[0m\x1b[91;48;5;94m \x1b[0m\x1b[91;42mX\x1b[0m\x1b[91;48;5;94m \x1b[0m\n\x1b[91;48;5;94m \x1b[0m\x1b[91;42mX\x1b[0m\x1b[91;48;5;94m \x1b[0m\x1b[91;42mX\x1b[0m\n\x1b[91;48;5;94m \x1b[0m\n\x1b[91;48;5;94m \x1b[0m\x1b[91;42mXX\x1b[0m\x1b[91;48;5;94m \x1b[0m\n\n" + assert result == expected + + def test_divide_emoji(): bold = Style(bold=True) italic = Style(italic=True) From 7e64cc0b881e0785b9a18ce5cce1dfc9fd7a9448 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 14 Dec 2021 18:00:41 +0000 Subject: [PATCH 02/10] typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff135e76..7b6f790b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Fixed issues with overlaping tags https://github.com/willmcgugan/rich/issues/1755 +- Fixed issues with overlapping tags https://github.com/willmcgugan/rich/issues/1755 ## [10.16.0] - 2021-12-12 From df61680a106c6c5f2edba0c5ecbf5f0a725de09f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 14 Dec 2021 18:02:35 +0000 Subject: [PATCH 03/10] simplify --- rich/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rich/text.py b/rich/text.py index 43c089d6..51fd2b73 100644 --- a/rich/text.py +++ b/rich/text.py @@ -1062,7 +1062,7 @@ class Text(JupyterMixin): for line, (line_start, line_end) in zip(new_lines._lines, line_ranges): for span_start, span_end, style in self._spans: - if span_end > line_start: + if span_end > line_start and span_start < line_end: new_start = max(0, span_start - line_start) new_end = min(span_end - line_start, line_end - line_start) if new_end > new_start: From 6035a800ea647e8be15cfb3c30a20cc1c5b3639d Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 14 Dec 2021 21:29:59 +0000 Subject: [PATCH 04/10] more efficient divide --- rich/text.py | 49 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/rich/text.py b/rich/text.py index 51fd2b73..20173b2f 100644 --- a/rich/text.py +++ b/rich/text.py @@ -1,7 +1,7 @@ import re from functools import partial, reduce from math import gcd -from operator import attrgetter, itemgetter +from operator import itemgetter from rich.emoji import EmojiVariant from typing import ( TYPE_CHECKING, @@ -1058,15 +1058,48 @@ class Text(JupyterMixin): if not self._spans: return new_lines + _lines = new_lines._lines + line_count = len(line_ranges) _Span = Span - for line, (line_start, line_end) in zip(new_lines._lines, line_ranges): - for span_start, span_end, style in self._spans: - if span_end > line_start and span_start < line_end: - new_start = max(0, span_start - line_start) - new_end = min(span_end - line_start, line_end - line_start) - if new_end > new_start: - line._spans.append(_Span(new_start, new_end, style)) + for span_start, span_end, style in self._spans: + + lower_bound = 0 + upper_bound = line_count + start_line_no = (lower_bound + upper_bound) // 2 + + while True: + line_start, line_end = line_ranges[start_line_no] + if span_start < line_start: + upper_bound = start_line_no - 1 + elif span_start > line_end: + lower_bound = start_line_no + 1 + else: + break + start_line_no = (lower_bound + upper_bound) // 2 + + lower_bound = 0 + upper_bound = line_count + end_line_no = start_line_no + + while True: + line_start, line_end = line_ranges[end_line_no] + if span_end < line_start: + upper_bound = end_line_no - 1 + elif span_end > line_end: + lower_bound = end_line_no + 1 + else: + break + end_line_no = (lower_bound + upper_bound) // 2 + + for line, (line_start, line_end) in zip( + _lines[start_line_no : end_line_no + 1], + line_ranges[start_line_no : end_line_no + 1], + ): + new_start = max(0, span_start - line_start) + new_end = min(span_end - line_start, line_end - line_start) + if new_end > new_start: + line._spans.append(_Span(new_start, new_end, style)) return new_lines From 5c2824910045221ccf92a36c3d008411229f69c6 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 14 Dec 2021 21:32:27 +0000 Subject: [PATCH 05/10] alpha build --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c26c6b1d..4cfe98bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "rich" homepage = "https://github.com/willmcgugan/rich" documentation = "https://rich.readthedocs.io/en/latest/" -version = "10.16.1" +version = "10.16.1-alpha1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" authors = ["Will McGugan "] license = "MIT" From 6957cebe726970ebc8f04cd0d2c3b9d2fa3ecf79 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 14 Dec 2021 21:43:40 +0000 Subject: [PATCH 06/10] optimize tweak --- rich/text.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rich/text.py b/rich/text.py index 20173b2f..3cdd58d4 100644 --- a/rich/text.py +++ b/rich/text.py @@ -1078,9 +1078,8 @@ class Text(JupyterMixin): break start_line_no = (lower_bound + upper_bound) // 2 - lower_bound = 0 - upper_bound = line_count - end_line_no = start_line_no + end_line_no = lower_bound = start_line_no + upper_bound = line_count while True: line_start, line_end = line_ranges[end_line_no] From a1dd33912f71a6e1de6923be03dbe774fcc517dc Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 14 Dec 2021 21:48:37 +0000 Subject: [PATCH 07/10] formatting --- rich/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rich/text.py b/rich/text.py index 3cdd58d4..7606d6c4 100644 --- a/rich/text.py +++ b/rich/text.py @@ -1079,7 +1079,7 @@ class Text(JupyterMixin): start_line_no = (lower_bound + upper_bound) // 2 end_line_no = lower_bound = start_line_no - upper_bound = line_count + upper_bound = line_count while True: line_start, line_end = line_ranges[end_line_no] From c2c506f03c445eb69c6aabf9967b693680c3d949 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 14 Dec 2021 21:51:48 +0000 Subject: [PATCH 08/10] optimization --- rich/text.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rich/text.py b/rich/text.py index 7606d6c4..ec5cd10f 100644 --- a/rich/text.py +++ b/rich/text.py @@ -1058,7 +1058,7 @@ class Text(JupyterMixin): if not self._spans: return new_lines - _lines = new_lines._lines + _lines = [line._spans.append for line in new_lines._lines] line_count = len(line_ranges) _Span = Span @@ -1098,7 +1098,7 @@ class Text(JupyterMixin): new_start = max(0, span_start - line_start) new_end = min(span_end - line_start, line_end - line_start) if new_end > new_start: - line._spans.append(_Span(new_start, new_end, style)) + line(_Span(new_start, new_end, style)) return new_lines From c3e0f7de579849dbed180d2fe5d3d608d4452330 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 14 Dec 2021 21:56:30 +0000 Subject: [PATCH 09/10] optimization --- rich/text.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/rich/text.py b/rich/text.py index ec5cd10f..8061f8d1 100644 --- a/rich/text.py +++ b/rich/text.py @@ -1058,7 +1058,7 @@ class Text(JupyterMixin): if not self._spans: return new_lines - _lines = [line._spans.append for line in new_lines._lines] + _line_appends = [line._spans.append for line in new_lines._lines] line_count = len(line_ranges) _Span = Span @@ -1091,14 +1091,12 @@ class Text(JupyterMixin): break end_line_no = (lower_bound + upper_bound) // 2 - for line, (line_start, line_end) in zip( - _lines[start_line_no : end_line_no + 1], - line_ranges[start_line_no : end_line_no + 1], - ): + for line_no in range(start_line_no, end_line_no + 1): + line_start, line_end = line_ranges[line_no] new_start = max(0, span_start - line_start) new_end = min(span_end - line_start, line_end - line_start) if new_end > new_start: - line(_Span(new_start, new_end, style)) + _line_appends[line_no](_Span(new_start, new_end, style)) return new_lines From a4c9fdb12c5e25c96192e0e3d48bb2c56a4a55a3 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 15 Dec 2021 09:26:05 +0000 Subject: [PATCH 10/10] version bump --- CHANGELOG.md | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b6f790b..95cccbb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [10.16.1] - Unreleased +## [10.16.1] - 2021-12-15 ### Fixed diff --git a/pyproject.toml b/pyproject.toml index 4cfe98bf..c26c6b1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "rich" homepage = "https://github.com/willmcgugan/rich" documentation = "https://rich.readthedocs.io/en/latest/" -version = "10.16.1-alpha1" +version = "10.16.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" authors = ["Will McGugan "] license = "MIT"