From 4f4a2252cff77c5446a6ed29f36920e2944a95c8 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 4 Dec 2025 19:31:57 +0000 Subject: [PATCH 1/4] added content blank --- CHANGELOG.md | 7 ++++++ src/textual/content.py | 48 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0e387bb74..c82ada20a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## Unreleased + +### Added + +- Added `Content.blank` + + ## [6.7.1] - 2025-12-1 ### Fixed diff --git a/src/textual/content.py b/src/textual/content.py index 968d36e521..7669a4eeb2 100644 --- a/src/textual/content.py +++ b/src/textual/content.py @@ -179,6 +179,20 @@ def __init__( def __str__(self) -> str: return self._text + @property + def _is_regular(self) -> bool: + """Check if the line is regular (spans.start > span.end for all spans). + + This is a debugging aid, and unlikely to be useful in your app. + + Returns: + `True` if the content is regular, `False` if it is not (and broken). + """ + for span in self.spans: + if span.end <= span.start: + return False + return True + @cached_property def markup(self) -> str: """Get content markup to render this Text. @@ -374,6 +388,26 @@ def styled( ) return new_content + @classmethod + def blank(cls, width: int, style: Style | str = "") -> Content: + """Get a Content instance consisting of spaces. + + Args: + width: Width of blank content (number of spaces). + style: Style of blank. + + Returns: + Content instance. + """ + if not width: + return EMPTY_CONTENT + blank = cls( + " " * width, + [Span(0, width, style)] if style else None, + cell_length=width, + ) + return blank + @classmethod def assemble( cls, @@ -431,7 +465,10 @@ def assemble( position += len(part.plain) if end: text_append(end) - return cls("".join(text), spans, strip_control_codes=strip_control_codes) + assembled_content = cls( + "".join(text), spans, strip_control_codes=strip_control_codes + ) + return assembled_content def simplify(self) -> Content: """Simplify spans by joining contiguous spans together. @@ -786,7 +823,7 @@ def get_text_at(offset: int) -> "Content": if stop >= len(self.plain): return self text = self.plain[:stop] - return Content( + sliced_content = Content( text, self._trim_spans(text, self._spans), strip_control_codes=False, @@ -794,11 +831,14 @@ def get_text_at(offset: int) -> "Content": else: text = self.plain[start:stop] spans = [ - span._shift(-start) for span in self._spans if span.end > start + span._shift(-start) + for span in self._spans + if span.end - start > 0 ] - return Content( + sliced_content = Content( text, self._trim_spans(text, spans), strip_control_codes=False ) + return sliced_content else: # This would be a bit of work to implement efficiently From 5ab3233d27f284176c30e49d6427cbcb9b6c9497 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 4 Dec 2025 19:32:39 +0000 Subject: [PATCH 2/4] changelog --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c82ada20a7..ab6ce20e8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added -- Added `Content.blank` - +- Added `Content.blank` https://github.com/Textualize/textual/pull/6264 ## [6.7.1] - 2025-12-1 From fafcbd94fdb21e62791796089f609cbf8ddb6486 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 4 Dec 2025 19:37:22 +0000 Subject: [PATCH 3/4] Content.blank test --- tests/test_content.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_content.py b/tests/test_content.py index 8e60755f1e..eb764dec04 100644 --- a/tests/test_content.py +++ b/tests/test_content.py @@ -570,3 +570,18 @@ def test_fold(content: Content, width: int, expected: list[Content]) -> None: assert len(result) == len(expected) for line, expected_line in zip(result, expected): assert line.is_same(expected_line) + + +@pytest.mark.parametrize( + "width,style,text,spans,cell_length", + [ + (5, None, " ", [], 5), + (0, None, "", [], 0), + (5, "on red", " ", [Span(0, 5, "on red")], 5), + ], +) +def test_blank_method(width, style, text, spans, cell_length): + blank = Content.blank(width, style) + assert blank.plain == text + assert blank.spans == spans + assert blank.cell_length == cell_length From 898f180bde39d14531c29afba4cfdb624411da25 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 4 Dec 2025 19:39:55 +0000 Subject: [PATCH 4/4] test --- src/textual/content.py | 2 +- tests/test_content.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/textual/content.py b/src/textual/content.py index 7669a4eeb2..7176e82377 100644 --- a/src/textual/content.py +++ b/src/textual/content.py @@ -389,7 +389,7 @@ def styled( return new_content @classmethod - def blank(cls, width: int, style: Style | str = "") -> Content: + def blank(cls, width: int, style: Style | str | None = None) -> Content: """Get a Content instance consisting of spaces. Args: diff --git a/tests/test_content.py b/tests/test_content.py index eb764dec04..d261a5f61f 100644 --- a/tests/test_content.py +++ b/tests/test_content.py @@ -580,7 +580,9 @@ def test_fold(content: Content, width: int, expected: list[Content]) -> None: (5, "on red", " ", [Span(0, 5, "on red")], 5), ], ) -def test_blank_method(width, style, text, spans, cell_length): +def test_blank_method( + width: int, style: str | None, text: str, spans: list[Span], cell_length: int +) -> None: blank = Content.blank(width, style) assert blank.plain == text assert blank.spans == spans