From dca94393e77a9fa05df7b1f88865e1aec8d26054 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 20 Aug 2025 21:00:58 +0100 Subject: [PATCH 1/7] optimizations in styles cache --- src/textual/_segment_tools.py | 25 ++++++-- src/textual/_styles_cache.py | 40 +++++-------- src/textual/filter.py | 2 - src/textual/renderables/text_opacity.py | 2 + src/textual/scroll_view.py | 4 +- src/textual/strip.py | 9 ++- src/textual/widget.py | 80 +++++++++++++++---------- 7 files changed, 95 insertions(+), 67 deletions(-) diff --git a/src/textual/_segment_tools.py b/src/textual/_segment_tools.py index de6a656a70..3a4a916b14 100644 --- a/src/textual/_segment_tools.py +++ b/src/textual/_segment_tools.py @@ -5,6 +5,7 @@ from __future__ import annotations import re +from functools import lru_cache from typing import Iterable from rich.segment import Segment @@ -15,6 +16,20 @@ from textual.geometry import Size +@lru_cache(1024 * 8) +def make_blank(width, style: Style) -> Segment: + """Make a blank segment. + + Args: + width: Width of blank. + style: Style of blank. + + Returns: + A single segment + """ + return Segment(" " * width, style) + + class NoCellPositionForIndex(Exception): pass @@ -162,19 +177,19 @@ def line_pad( """ if pad_left and pad_right: return [ - Segment(" " * pad_left, style), + make_blank(pad_left, style), *segments, - Segment(" " * pad_right, style), + make_blank(pad_right, style), ] elif pad_left: return [ - Segment(" " * pad_left, style), + make_blank(pad_left, style), *segments, ] elif pad_right: return [ *segments, - Segment(" " * pad_right, style), + make_blank(pad_right, style), ] return list(segments) @@ -215,7 +230,7 @@ def blank_lines(count: int) -> list[list[Segment]]: Returns: A list of blank lines. """ - return [[Segment(" " * width, style)]] * count + return [[make_blank(width, style)]] * count top_blank_lines = bottom_blank_lines = 0 vertical_excess_space = max(0, height - shape_height) diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index 7897e883d9..7bd0bed68a 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -1,7 +1,6 @@ from __future__ import annotations from functools import lru_cache -from sys import intern from typing import TYPE_CHECKING, Callable, Iterable, Sequence import rich.repr @@ -14,7 +13,7 @@ from textual._border import get_box, render_border_label, render_row from textual._context import active_app from textual._opacity import _apply_opacity -from textual._segment_tools import apply_hatch, line_pad, line_trim +from textual._segment_tools import apply_hatch, line_pad, line_trim, make_blank from textual.color import TRANSPARENT, Color from textual.constants import DEBUG from textual.content import Content @@ -34,20 +33,6 @@ RenderLineCallback: TypeAlias = Callable[[int], Strip] -@lru_cache(1024 * 8) -def make_blank(width, style: RichStyle) -> Segment: - """Make a blank segment. - - Args: - width: Width of blank. - style: Style of blank. - - Returns: - A single segment - """ - return Segment(intern(" " * width), style) - - @rich.repr.auto(angular=True) class StylesCache: """Responsible for rendering CSS Styles and keeping a cache of rendered lines. @@ -105,6 +90,7 @@ def is_dirty(self, y: int) -> bool: def clear(self) -> None: """Clear the styles cache (will cause the content to re-render).""" + self._cache.clear() self._dirty_lines.clear() @@ -263,6 +249,16 @@ def render( return strips + @lru_cache(1024) + def get_inner_outer( + cls, base_background: Color, background: Color + ) -> tuple[Style, Style]: + """Get inner and outer background colors.""" + return ( + Style(background=base_background + background), + Style(background=base_background), + ) + def render_line( self, styles: StylesBase, @@ -319,9 +315,7 @@ def render_line( ) = styles.outline from_color = RichStyle.from_color - - inner = Style(background=(base_background + background)) - outer = Style(background=base_background) + inner, outer = self.get_inner_outer(base_background, background) def line_post(segments: Iterable[Segment]) -> Iterable[Segment]: """Apply effects to segments inside the border.""" @@ -361,7 +355,6 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]: line: Iterable[Segment] # Draw top or bottom borders (A) if (border_top and y == 0) or (border_bottom and y == height - 1): - is_top = y == 0 border_color = base_background + ( border_top_color if is_top else border_bottom_color @@ -453,12 +446,11 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]: line = line.adjust_cell_length(content_width) else: line = [make_blank(content_width, inner.rich_style)] + if inner: line = Segment.apply_style(line, inner.rich_style) - if styles.text_opacity != 1.0: - line = TextOpacity.process_segments( - line, styles.text_opacity, ansi_theme - ) + if (text_opacity := styles.text_opacity) != 1.0: + line = TextOpacity.process_segments(line, text_opacity, ansi_theme) line = line_post(line_pad(line, pad_left, pad_right, inner.rich_style)) if border_left or border_right: diff --git a/src/textual/filter.py b/src/textual/filter.py index 9b37fb7e80..06d714984f 100644 --- a/src/textual/filter.py +++ b/src/textual/filter.py @@ -269,9 +269,7 @@ def apply(self, segments: list[Segment], background: Color) -> list[Segment]: """ _Segment = Segment truecolor_style = self.truecolor_style - background_rich_color = background.rich_color - return [ _Segment( text, diff --git a/src/textual/renderables/text_opacity.py b/src/textual/renderables/text_opacity.py index a2255ab7fc..7ba41e36d1 100644 --- a/src/textual/renderables/text_opacity.py +++ b/src/textual/renderables/text_opacity.py @@ -79,6 +79,8 @@ def process_segments( ): invisible_style = _from_color(bgcolor=style.bgcolor) yield _Segment(cell_len(text) * " ", invisible_style) + elif opacity == 1: + yield from segments else: filter = ANSIToTruecolor(ansi_theme) for segment in filter.apply(list(segments), TRANSPARENT): diff --git a/src/textual/scroll_view.py b/src/textual/scroll_view.py index 03a05013b1..7609d38cc7 100644 --- a/src/textual/scroll_view.py +++ b/src/textual/scroll_view.py @@ -36,13 +36,13 @@ def watch_scroll_x(self, old_value: float, new_value: float) -> None: if self.show_horizontal_scrollbar: self.horizontal_scrollbar.position = new_value if round(old_value) != round(new_value): - self.refresh() + self.refresh(self.size.region) def watch_scroll_y(self, old_value: float, new_value: float) -> None: if self.show_vertical_scrollbar: self.vertical_scrollbar.position = new_value if round(old_value) != round(new_value): - self.refresh() + self.refresh(self.size.region) def on_mount(self): self._refresh_scrollbars() diff --git a/src/textual/strip.py b/src/textual/strip.py index dc69561a5b..8197a14a69 100644 --- a/src/textual/strip.py +++ b/src/textual/strip.py @@ -84,6 +84,7 @@ class Strip: "_render_cache", "_line_length_cache", "_crop_extend_cache", + "_offsets_cache", "_link_ids", ] @@ -104,6 +105,7 @@ def __init__( tuple[int, int, Style | None], Strip, ] = FIFOCache(4) + self._offsets_cache: FIFOCache[tuple[int, int], Strip] = FIFOCache(4) self._render_cache: str | None = None self._link_ids: set[str] | None = None @@ -723,6 +725,9 @@ def apply_offsets(self, x: int, y: int) -> Strip: Returns: New strip. """ + cache_key = (x, y) + if (cached_strip := self._offsets_cache.get(cache_key)) is not None: + return cached_strip segments = self._segments strip_segments: list[Segment] = [] for segment in segments: @@ -732,4 +737,6 @@ def apply_offsets(self, x: int, y: int) -> Strip: Segment(text, style + offset_style if style else offset_style) ) x += len(segment.text) - return Strip(strip_segments, self._cell_length) + strip = Strip(strip_segments, self._cell_length) + self._offsets_cache[cache_key] = strip + return strip diff --git a/src/textual/widget.py b/src/textual/widget.py index 56a8980676..13135b8d01 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -445,6 +445,8 @@ def __init__( self._layout_cache: dict[str, object] = {} """A dict that is refreshed when the widget is resized / refreshed.""" + self._visual_style: VisualStyle | None = None + self._render_cache = _RenderCache(_null_size, []) # Regions which need to be updated (in Widget) self._dirty_regions: set[Region] = set() @@ -688,6 +690,14 @@ def preflight_checks(self) -> None: f"'{self.__class__.__name__}.CSS' will be ignored (use 'DEFAULT_CSS' class variable for widgets)" ) + def pre_render(self) -> None: + """Called prior to rendering. + + If you implement this in a subclass, be sure to call the base class method via super. + + """ + self._visual_style = None + def _cover(self, widget: Widget) -> None: """Set a widget used to replace the visuals of this widget (used for loading indicator). @@ -2527,6 +2537,7 @@ def _set_dirty(self, *regions: Region) -> None: self._dirty_regions.clear() self._repaint_regions.clear() self._styles_cache.clear() + self._styles_cache.set_dirty(self.size.region) outer_size = self.outer_size self._dirty_regions.add(outer_size.region) @@ -3993,41 +4004,43 @@ def _scroll_update(self, virtual_size: Size) -> None: @property def visual_style(self) -> VisualStyle: - background = Color(0, 0, 0, 0) - color = Color(255, 255, 255, 0) + if self._visual_style is None: + background = Color(0, 0, 0, 0) + color = Color(255, 255, 255, 0) - style = Style() - opacity = 1.0 + style = Style() + opacity = 1.0 - for node in reversed(self.ancestors_with_self): - styles = node.styles - has_rule = styles.has_rule - opacity *= styles.opacity - if has_rule("background"): - text_background = background + styles.background.tint( - styles.background_tint - ) - background += ( - styles.background.tint(styles.background_tint) - ).multiply_alpha(opacity) - else: - text_background = background - if has_rule("color"): - color = styles.color - style += styles.text_style - if has_rule("auto_color") and styles.auto_color: - color = text_background.get_contrast_text(color.a) - - return VisualStyle( - background, - color, - bold=style.bold, - dim=style.dim, - italic=style.italic, - reverse=style.reverse, - underline=style.underline, - strike=style.strike, - ) + for node in reversed(self.ancestors_with_self): + styles = node.styles + has_rule = styles.has_rule + opacity *= styles.opacity + if has_rule("background"): + text_background = background + styles.background.tint( + styles.background_tint + ) + background += ( + styles.background.tint(styles.background_tint) + ).multiply_alpha(opacity) + else: + text_background = background + if has_rule("color"): + color = styles.color + style += styles.text_style + if has_rule("auto_color") and styles.auto_color: + color = text_background.get_contrast_text(color.a) + + self._visual_style = VisualStyle( + background, + color, + bold=style.bold, + dim=style.dim, + italic=style.italic, + reverse=style.reverse, + underline=style.underline, + strike=style.strike, + ) + return self._visual_style def get_selection(self, selection: Selection) -> tuple[str, str] | None: """Get the text under the selection. @@ -4480,6 +4493,7 @@ async def broker_event(self, event_name: str, event: events.Event) -> bool: def notify_style_update(self) -> None: self._rich_style_cache.clear() self._visual_style_cache.clear() + self._visual_style = None super().notify_style_update() async def _on_mouse_down(self, event: events.MouseDown) -> None: From 8c7e3821e916528c70c69e8ef8002ded886c05cd Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 20 Aug 2025 21:37:58 +0100 Subject: [PATCH 2/7] optimize filter --- src/textual/_styles_cache.py | 10 ++++------ src/textual/app.py | 6 +++++- src/textual/filter.py | 28 ++++++++++++++++------------ src/textual/visual.py | 1 - tests/test_styles_cache.py | 9 +++++++++ 5 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index 7bd0bed68a..8ab1e8754c 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -116,6 +116,7 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]: base_background, background, widget.render_line, + widget.app._enabled_filters, ( None if border_title is None @@ -135,7 +136,6 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]: content_size=widget.content_region.size, padding=styles.padding, crop=crop, - filters=widget.app._filters, opacity=widget.opacity, ansi_theme=widget.app.ansi_theme, ) @@ -163,12 +163,12 @@ def render( base_background: Color, background: Color, render_content_line: RenderLineCallback, + filters: Sequence[LineFilter], border_title: tuple[Content, Color, Color, Style] | None, border_subtitle: tuple[Content, Color, Color, Style] | None, content_size: Size | None = None, padding: Spacing | None = None, crop: Region | None = None, - filters: Sequence[LineFilter] | None = None, opacity: float = 1.0, ansi_theme: TerminalTheme = DEFAULT_TERMINAL_THEME, ) -> list[Strip]: @@ -209,9 +209,7 @@ def render( is_dirty = self._dirty_lines.__contains__ render_line = self.render_line - apply_filters = ( - [] if filters is None else [filter for filter in filters if filter.enabled] - ) + for y in crop.line_range: if is_dirty(y) or y not in self._cache: strip = render_line( @@ -232,7 +230,7 @@ def render( else: strip = self._cache[y] - for filter in apply_filters: + for filter in filters: strip = strip.apply_filter(filter, background) if DEBUG: diff --git a/src/textual/app.py b/src/textual/app.py index 310df9a07d..362ea0c41d 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -842,6 +842,11 @@ def __init__( ) ) + @property + def _enabled_filters(self) -> list[LineFilter]: + """Filters which are currently enabled.""" + return [filter for filter in self._filters if filter.enabled] + @property def _is_devtools_connected(self) -> bool: """Is the app connected to the devtools?""" @@ -3160,7 +3165,6 @@ async def _process_messages( terminal_size: tuple[int, int] | None = None, message_hook: Callable[[Message], None] | None = None, ) -> None: - self._thread_init() async def app_prelude() -> bool: diff --git a/src/textual/filter.py b/src/textual/filter.py index 06d714984f..72a5382dfd 100644 --- a/src/textual/filter.py +++ b/src/textual/filter.py @@ -240,22 +240,26 @@ def truecolor_style(self, style: Style, background: RichColor) -> Style: New style. """ terminal_theme = self._terminal_theme - color = style.color - if color is not None and color.triplet is None: - color = RichColor.from_rgb( - *color.get_truecolor(terminal_theme, foreground=True) - ) - bgcolor = style.bgcolor - if bgcolor is not None and bgcolor.triplet is None: + + changed = False + if (color := style.color) is not None: + if color.triplet is None: + color = RichColor.from_rgb( + *color.get_truecolor(terminal_theme, foreground=True) + ) + changed = True + if style.dim: + color = dim_color(background, color) + style += NO_DIM + changed = True + + if (bgcolor := style.bgcolor) is not None and bgcolor.triplet is None: bgcolor = RichColor.from_rgb( *bgcolor.get_truecolor(terminal_theme, foreground=False) ) - # Convert dim style to RGB - if style.dim and color is not None: - color = dim_color(background, color) - style += NO_DIM + changed = True - return style + Style.from_color(color, bgcolor) + return style + Style.from_color(color, bgcolor) if changed else style def apply(self, segments: list[Segment], background: Color) -> list[Segment]: """Transform a list of segments. diff --git a/src/textual/visual.py b/src/textual/visual.py index e1882270da..8231a5574c 100644 --- a/src/textual/visual.py +++ b/src/textual/visual.py @@ -255,7 +255,6 @@ def to_strips( align_vertical, ) ) - return strips diff --git a/tests/test_styles_cache.py b/tests/test_styles_cache.py index 615ed70aa6..a94cfa682f 100644 --- a/tests/test_styles_cache.py +++ b/tests/test_styles_cache.py @@ -40,6 +40,7 @@ def test_no_styles(): Color.parse("blue"), Color.parse("green"), content.__getitem__, + [], None, None, content_size=Size(3, 3), @@ -73,6 +74,7 @@ def test_border(): Color.parse("blue"), Color.parse("green"), content.__getitem__, + [], None, None, content_size=Size(3, 3), @@ -106,6 +108,7 @@ def test_padding(): Color.parse("blue"), Color.parse("green"), content.__getitem__, + [], None, None, content_size=Size(3, 3), @@ -140,6 +143,7 @@ def test_padding_border(): Color.parse("blue"), Color.parse("green"), content.__getitem__, + [], None, None, content_size=Size(3, 3), @@ -175,6 +179,7 @@ def test_outline(): Color.parse("blue"), Color.parse("green"), content.__getitem__, + [], None, None, content_size=Size(3, 3), @@ -205,6 +210,7 @@ def test_crop(): Color.parse("blue"), Color.parse("green"), content.__getitem__, + [], None, None, content_size=Size(3, 3), @@ -243,6 +249,7 @@ def get_content_line(y: int) -> Strip: Color.parse("blue"), Color.parse("green"), get_content_line, + [], None, None, content_size=Size(3, 3), @@ -270,6 +277,7 @@ def get_content_line(y: int) -> Strip: Color.parse("blue"), Color.parse("green"), get_content_line, + [], None, None, content_size=Size(3, 3), @@ -288,6 +296,7 @@ def get_content_line(y: int) -> Strip: Color.parse("blue"), Color.parse("green"), get_content_line, + [], None, None, content_size=Size(3, 3), From bb2b3c56aebd0c75e613d043a45e3af3257e6c0b Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 20 Aug 2025 21:53:42 +0100 Subject: [PATCH 3/7] optimize ansi to truecolor --- src/textual/_styles_cache.py | 1 - src/textual/app.py | 1 - src/textual/filter.py | 8 ++++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index 8ab1e8754c..6a6104fb58 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -335,7 +335,6 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]: Returns: New list of segments """ - try: app = active_app.get() ansi_theme = app.ansi_theme diff --git a/src/textual/app.py b/src/textual/app.py index 362ea0c41d..60c2dde325 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -844,7 +844,6 @@ def __init__( @property def _enabled_filters(self) -> list[LineFilter]: - """Filters which are currently enabled.""" return [filter for filter in self._filters if filter.enabled] @property diff --git a/src/textual/filter.py b/src/textual/filter.py index 72a5382dfd..0d08292fde 100644 --- a/src/textual/filter.py +++ b/src/textual/filter.py @@ -244,8 +244,8 @@ def truecolor_style(self, style: Style, background: RichColor) -> Style: changed = False if (color := style.color) is not None: if color.triplet is None: - color = RichColor.from_rgb( - *color.get_truecolor(terminal_theme, foreground=True) + color = RichColor.from_triplet( + color.get_truecolor(terminal_theme, foreground=True) ) changed = True if style.dim: @@ -254,8 +254,8 @@ def truecolor_style(self, style: Style, background: RichColor) -> Style: changed = True if (bgcolor := style.bgcolor) is not None and bgcolor.triplet is None: - bgcolor = RichColor.from_rgb( - *bgcolor.get_truecolor(terminal_theme, foreground=False) + bgcolor = RichColor.from_triplet( + bgcolor.get_truecolor(terminal_theme, foreground=False) ) changed = True From e81471e0d2ff354f512d1e3cea9015a55c131bff Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 21 Aug 2025 08:26:54 +0100 Subject: [PATCH 4/7] style --- src/textual/_styles_cache.py | 4 ++-- src/textual/dom.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index 6a6104fb58..f19af27072 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -440,9 +440,9 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]: content_y = y - gutter.top if content_y < content_height: line = render_content_line(y - gutter.top) - line = line.adjust_cell_length(content_width) + line = line.adjust_cell_length(content_width, inner.rich_style) else: - line = [make_blank(content_width, inner.rich_style)] + line = Strip.blank(content_width, inner.rich_style) if inner: line = Segment.apply_style(line, inner.rich_style) diff --git a/src/textual/dom.py b/src/textual/dom.py index 9aca779fd0..5fc51da8be 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1165,7 +1165,7 @@ def _opacity_background_colors(self) -> tuple[Color, Color]: Returns: `(, )` """ - base_background = background = BLACK + base_background = background = Color(0, 0, 0, 0) opacity = 1.0 for node in reversed(self.ancestors_with_self): styles = node.styles @@ -1749,6 +1749,7 @@ def add_class(self, *class_names: str, update: bool = True) -> Self: check_identifiers("class name", *class_names) old_classes = self._classes.copy() self._classes.update(class_names) + self._nodes.updated() if old_classes == self._classes: return self if update: From 46b97533edaab547b18bfe909edd75052e825a1f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 21 Aug 2025 09:09:23 +0100 Subject: [PATCH 5/7] snapshots --- CHANGELOG.md | 2 + src/textual/_styles_cache.py | 8 +- src/textual/dom.py | 28 +- src/textual/scrollbar.py | 2 +- src/textual/widget.py | 11 +- src/textual/widgets/_input.py | 2 +- src/textual/widgets/_masked_input.py | 2 +- src/textual/widgets/_option_list.py | 10 +- src/textual/widgets/_text_area.py | 15 +- .../test_snapshots/test_add_separator.svg | 124 ++--- .../test_snapshots/test_compact.svg | 2 +- .../test_snapshots/test_disabled.svg | 2 +- .../test_escape_to_minimize.svg | 2 +- .../test_escape_to_minimize_disabled.svg | 122 ++--- ...est_escape_to_minimize_screen_override.svg | 2 +- .../test_focus_within_transparent.svg | 2 +- .../test_input_percentage_width.svg | 2 +- .../test_option_list_strings.svg | 122 ++--- .../test_scrollbar_thumb_height.svg | 120 ++--- .../test_selection_list_selections.svg | 2 +- .../test_selection_list_tuples.svg | 2 +- .../test_setting_transparency.svg | 2 +- .../test_split_segments_infinite_loop.svg | 118 ++--- .../test_text_area_alternate_screen.svg | 2 +- ...text_area_css_theme_updates_background.svg | 2 +- ...est_text_area_language_rendering[bash].svg | 446 +++++++++--------- ...test_text_area_language_rendering[css].svg | 2 +- .../test_text_area_language_rendering[go].svg | 324 ++++++------- ...xt_area_language_rendering[javascript].svg | 2 +- ...est_text_area_language_rendering[json].svg | 172 +++---- ...est_text_area_language_rendering[toml].svg | 152 +++--- ...est_text_area_language_rendering[yaml].svg | 200 ++++---- .../test_text_area_line_number_start.svg | 56 +-- ...t_text_area_read_only_cursor_rendering.svg | 2 +- ...t_area_selection_rendering[selection0].svg | 2 +- ...t_area_selection_rendering[selection1].svg | 2 +- ...t_area_selection_rendering[selection2].svg | 2 +- ...t_area_selection_rendering[selection3].svg | 2 +- ...t_area_selection_rendering[selection4].svg | 2 +- ...t_area_selection_rendering[selection5].svg | 2 +- .../test_text_area_themes[css].svg | 2 +- .../test_text_area_themes[dracula].svg | 2 +- .../test_text_area_themes[github_light].svg | 2 +- .../test_text_area_themes[monokai].svg | 2 +- .../test_text_area_themes[vscode_dark].svg | 2 +- .../test_textarea_line_highlight.svg | 2 +- .../test_textarea_placeholder.svg | 3 +- .../test_snapshots/test_textarea_select.svg | 2 +- .../test_textarea_suggestion.svg | 2 +- .../test_textual_dev_border_preview.svg | 2 +- .../snapshot_apps/scrollbar_thumb_height.py | 4 +- tests/test_styles_cache.py | 35 -- 52 files changed, 1059 insertions(+), 1077 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c79e2fc4f4..260557ecd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,11 +20,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `OptionList.set_options` https://github.com/Textualize/textual/pull/6048 - Added `TextArea.suggestion` https://github.com/Textualize/textual/pull/6048 - Added `TextArea.placeholder` https://github.com/Textualize/textual/pull/6048 +- Added `Widget.get_line_filters` ### Changed - Breaking change: The `renderable` property on the `Static` widget has been changed to `content`. https://github.com/Textualize/textual/pull/6041 - Breaking change: Renamed `Label` constructor argument `renderable` to `content` for consistency https://github.com/Textualize/textual/pull/6045 +- Breaking change: Optimization to line API to avoid applying background styles to widget content. In practice this means that you can no longer rely on blank Segments automatically getting the background color. # [5.3.0] - 2025-08-07 diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index f19af27072..646b56321a 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -108,7 +108,7 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]: border_title = widget._border_title border_subtitle = widget._border_subtitle - base_background, background = widget._opacity_background_colors + base_background, background = widget.background_colors styles = widget.styles strips = self.render( styles, @@ -116,7 +116,7 @@ def render_widget(self, widget: Widget, crop: Region) -> list[Strip]: base_background, background, widget.render_line, - widget.app._enabled_filters, + widget.get_line_filters(), ( None if border_title is None @@ -417,7 +417,7 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]: elif (pad_top and y < gutter.top) or ( pad_bottom and y >= height - gutter.bottom ): - background_rich_style = from_color(bgcolor=background.rich_color) + background_rich_style = inner.rich_style left_style = Style( foreground=base_background + border_left_color.multiply_alpha(opacity) ) @@ -444,8 +444,6 @@ def post(segments: Iterable[Segment]) -> Iterable[Segment]: else: line = Strip.blank(content_width, inner.rich_style) - if inner: - line = Segment.apply_style(line, inner.rich_style) if (text_opacity := styles.text_opacity) != 1.0: line = TextOpacity.process_segments(line, text_opacity, ansi_theme) line = line_post(line_pad(line, pad_left, pad_right, inner.rich_style)) diff --git a/src/textual/dom.py b/src/textual/dom.py index 5fc51da8be..adf3f78b1d 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1144,22 +1144,22 @@ def _get_subtitle_style_information( VisualStyle.from_rich_style(styles.border_subtitle_style), ) - @property - def background_colors(self) -> tuple[Color, Color]: - """The background color and the color of the parent's background. - - Returns: - `(, )` - """ - base_background = background = BLACK - for node in reversed(self.ancestors_with_self): - styles = node.styles - base_background = background - background += styles.background.tint(styles.background_tint) - return (base_background, background) + # @property + # def background_colors(self) -> tuple[Color, Color]: + # """The background color and the color of the parent's background. + + # Returns: + # `(, )` + # """ + # base_background = background = BLACK + # for node in reversed(self.ancestors_with_self): + # styles = node.styles + # base_background = background + # background += styles.background.tint(styles.background_tint) + # return (base_background, background) @property - def _opacity_background_colors(self) -> tuple[Color, Color]: + def background_colors(self) -> tuple[Color, Color]: """Background colors adjusted for opacity. Returns: diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index be83250928..ef039148c5 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -287,7 +287,7 @@ def render(self) -> RenderableType: background = styles.scrollbar_background color = styles.scrollbar_color if background.a < 1: - base_background, _ = self.parent._opacity_background_colors + base_background, _ = self.parent.background_colors background = base_background + background color = background + color scrollbar_style = Style.from_color(color.rich_color, background.rich_color) diff --git a/src/textual/widget.py b/src/textual/widget.py index 13135b8d01..f3d3a481af 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -94,6 +94,7 @@ if TYPE_CHECKING: from textual.app import App, ComposeResult from textual.css.query import QueryType + from textual.filter import LineFilter from textual.message_pump import MessagePump from textual.scrollbar import ( ScrollBar, @@ -674,6 +675,14 @@ def text_selection(self) -> Selection | None: """Text selection information, or `None` if no text is selected in this widget.""" return self.screen.selections.get(self, None) + def get_line_filters(self) -> Sequence[LineFilter]: + """Get the line filters enabled for this widget. + + Returns: + A sequence of LineFilter instances. + """ + return self.app._enabled_filters + def preflight_checks(self) -> None: """Called in debug mode to do preflight checks. @@ -4088,7 +4097,7 @@ def render_line(self, y: int) -> Strip: try: line = self._render_cache.lines[y] except IndexError: - line = Strip.blank(self.size.width, self.rich_style) + line = Strip.blank(self.size.width, self.visual_style.rich_style) return line diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py index 7933c97ebb..c56234f9c1 100644 --- a/src/textual/widgets/_input.py +++ b/src/textual/widgets/_input.py @@ -601,7 +601,7 @@ def is_valid(self) -> bool: def render_line(self, y: int) -> Strip: if y != 0: - return Strip.blank(self.size.width) + return Strip.blank(self.size.width, self.rich_style) console = self.app.console console_options = self.app.console_options diff --git a/src/textual/widgets/_masked_input.py b/src/textual/widgets/_masked_input.py index 8f4c4931dd..a48ef9b60b 100644 --- a/src/textual/widgets/_masked_input.py +++ b/src/textual/widgets/_masked_input.py @@ -553,7 +553,7 @@ def set_classes() -> None: def render_line(self, y: int) -> Strip: if y != 0: - return Strip.blank(self.size.width) + return Strip.blank(self.size.width, self.rich_style) result = self._value width = self.content_size.width diff --git a/src/textual/widgets/_option_list.py b/src/textual/widgets/_option_list.py index 7a40a61fcb..a7e8ddd379 100644 --- a/src/textual/widgets/_option_list.py +++ b/src/textual/widgets/_option_list.py @@ -875,7 +875,10 @@ def render_line(self, y: int) -> Strip: option_index, line_offset = self._lines[line_number] option = self.options[option_index] except IndexError: - return Strip.blank(self.scrollable_content_region.width) + return Strip.blank( + self.scrollable_content_region.width, + self.get_visual_style("option-list--option").rich_style, + ) mouse_over = self._mouse_hovering_over == option_index component_class = "" @@ -895,7 +898,10 @@ def render_line(self, y: int) -> Strip: try: strip = strips[line_offset] except IndexError: - return Strip.blank(self.scrollable_content_region.width) + return Strip.blank( + self.scrollable_content_region.width, + self.get_visual_style("option-list--option").rich_style, + ) return strip def validate_highlighted(self, highlighted: int | None) -> int | None: diff --git a/src/textual/widgets/_text_area.py b/src/textual/widgets/_text_area.py index 4b6e4fa98f..50b1462f4b 100644 --- a/src/textual/widgets/_text_area.py +++ b/src/textual/widgets/_text_area.py @@ -1264,6 +1264,11 @@ def _render_line(self, y: int) -> Strip: A rendered line. """ theme = self._theme + base_style = ( + theme.base_style + if theme and theme.base_style is not None + else self.rich_style + ) wrapped_document = self.wrapped_document scroll_x, scroll_y = self.scroll_offset @@ -1275,7 +1280,7 @@ def _render_line(self, y: int) -> Strip: out_of_bounds = y_offset >= wrapped_document.height if out_of_bounds: - return Strip.blank(self.size.width) + return Strip.blank(self.size.width, base_style) # Get the line corresponding to this offset try: @@ -1284,7 +1289,7 @@ def _render_line(self, y: int) -> Strip: line_info = None if line_info is None: - return Strip.blank(self.size.width) + return Strip.blank(self.size.width, base_style) line_index, section_offset = line_info @@ -1474,11 +1479,7 @@ def _render_line(self, y: int) -> Strip: text_strip = text_strip.extend_cell_length(target_width, line_style) strip = Strip.join([Strip(gutter, cell_length=gutter_width), text_strip]) - return strip.apply_style( - theme.base_style - if theme and theme.base_style is not None - else self.rich_style - ) + return strip.apply_style(base_style) @property def text(self) -> str: diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_add_separator.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_add_separator.svg index 9ec842e23b..7b723850da 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_add_separator.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_add_separator.svg @@ -19,138 +19,138 @@ font-weight: 700; } - .terminal-3007591199-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3007591199-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3007591199-r1 { fill: #2d2d2d } -.terminal-3007591199-r2 { fill: #121212 } -.terminal-3007591199-r3 { fill: #191919 } -.terminal-3007591199-r4 { fill: #c5c8c6 } -.terminal-3007591199-r5 { fill: #272727;font-weight: bold } -.terminal-3007591199-r6 { fill: #e0e0e0 } -.terminal-3007591199-r7 { fill: #0d0d0d } -.terminal-3007591199-r8 { fill: #3b3b3b } + .terminal-r1 { fill: #2d2d2d } +.terminal-r2 { fill: #121212 } +.terminal-r3 { fill: #191919 } +.terminal-r4 { fill: #c5c8c6 } +.terminal-r5 { fill: #272727;font-weight: bold } +.terminal-r6 { fill: #e0e0e0 } +.terminal-r7 { fill: #0d0d0d } +.terminal-r8 { fill: #3b3b3b } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - FocusTest + FocusTest - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Add This is option 1 -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁──────────────────────────────────────────────────────────── -This is option 3 - - - - - - - - - - - - - - - - - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Add This is option 1 +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁──────────────────────────────────────────────────────────── +This is option 3 + + + + + + + + + + + + + + + + + + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_compact.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_compact.svg index 46754508c2..5684f2e9a4 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_compact.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_compact.svg @@ -128,7 +128,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Bar   Foo world                                    diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_disabled.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_disabled.svg index 41b53732e3..c9c3bf6db3 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_disabled.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_disabled.svg @@ -128,7 +128,7 @@ - + Labels don't have a disabled state I am disabled diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize.svg index a854da747c..20b67b0831 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize.svg @@ -125,7 +125,7 @@ - + diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize_disabled.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize_disabled.svg index d2dd5fe89a..28a4c23567 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize_disabled.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize_disabled.svg @@ -19,137 +19,137 @@ font-weight: 700; } - .terminal-2497035206-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2497035206-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2497035206-r1 { fill: #121212 } -.terminal-2497035206-r2 { fill: #0178d4 } -.terminal-2497035206-r3 { fill: #c5c8c6 } -.terminal-2497035206-r4 { fill: #c2c2bf } -.terminal-2497035206-r5 { fill: #272822 } -.terminal-2497035206-r6 { fill: #f8f8f2 } -.terminal-2497035206-r7 { fill: #90908a } + .terminal-r1 { fill: #121212 } +.terminal-r2 { fill: #0178d4 } +.terminal-r3 { fill: #c5c8c6 } +.terminal-r4 { fill: #c2c2bf } +.terminal-r5 { fill: #272822 } +.terminal-r6 { fill: #f8f8f2 } +.terminal-r7 { fill: #90908a } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TextAreaExample + TextAreaExample - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -1     def hello(name):                                                      -2          print("hello" + name)                                             -3   -4      def goodbye(name):                                                    -5          print("goodbye" + name)                                           -6   - - - - - - - - - - - - - - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +1     def hello(name):                                                      +2          print("hello" + name)                                             +3   +4      def goodbye(name):                                                    +5          print("goodbye" + name)                                           +6   + + + + + + + + + + + + + + + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize_screen_override.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize_screen_override.svg index a854da747c..20b67b0831 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize_screen_override.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_escape_to_minimize_screen_override.svg @@ -125,7 +125,7 @@ - + diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_focus_within_transparent.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_focus_within_transparent.svg index 3d0312c1e8..3a3a3fe2af 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_focus_within_transparent.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_focus_within_transparent.svg @@ -123,7 +123,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ This is here to escape to diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_input_percentage_width.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_input_percentage_width.svg index f7fc7d756c..eef4c0c749 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_input_percentage_width.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_input_percentage_width.svg @@ -122,7 +122,7 @@ - + 01234567890123456789012345678901234567890123456789012345678901234567890123456789 ┌──────────────────────────────────────┐ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_option_list_strings.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_option_list_strings.svg index 70a6045b27..d0f0cd8eb5 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_option_list_strings.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_option_list_strings.svg @@ -19,137 +19,137 @@ font-weight: 700; } - .terminal-2124869530-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2124869530-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2124869530-r1 { fill: #c5c8c6 } -.terminal-2124869530-r2 { fill: #e0e0e0 } -.terminal-2124869530-r3 { fill: #121212 } -.terminal-2124869530-r4 { fill: #0178d4 } -.terminal-2124869530-r5 { fill: #ddedf9;font-weight: bold } -.terminal-2124869530-r6 { fill: #495259 } -.terminal-2124869530-r7 { fill: #ffa62b;font-weight: bold } + .terminal-r1 { fill: #c5c8c6 } +.terminal-r2 { fill: #e0e0e0 } +.terminal-r3 { fill: #121212 } +.terminal-r4 { fill: #0178d4 } +.terminal-r5 { fill: #ddedf9;font-weight: bold } +.terminal-r6 { fill: #495259 } +.terminal-r7 { fill: #ffa62b;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - OptionListApp + OptionListApp - - - - OptionListApp - - -▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -Aerilon -Aquaria -Canceron -Caprica -Gemenon -Leonis -Libran -Picon -Sagittaron -Scorpia -Tauron -Virgon - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - - -^p palette + + + + OptionListApp + + +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +Aerilon +Aquaria +Canceron +Caprica +Gemenon +Leonis +Libran +Picon +Sagittaron +Scorpia +Tauron +Virgon + + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + +^p palette diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_scrollbar_thumb_height.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_scrollbar_thumb_height.svg index 4ad91b6991..8f3740530d 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_scrollbar_thumb_height.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_scrollbar_thumb_height.svg @@ -19,136 +19,136 @@ font-weight: 700; } - .terminal-1003123087-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1003123087-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1003123087-r1 { fill: #c5c8c6 } -.terminal-1003123087-r2 { fill: #e0e0e0 } -.terminal-1003123087-r3 { fill: #ff0000 } -.terminal-1003123087-r4 { fill: #0053aa } -.terminal-1003123087-r5 { fill: #495259 } -.terminal-1003123087-r6 { fill: #ffa62b;font-weight: bold } + .terminal-r1 { fill: #c5c8c6 } +.terminal-r2 { fill: #e0e0e0 } +.terminal-r3 { fill: #ff0000 } +.terminal-r4 { fill: #0053aa } +.terminal-r5 { fill: #495259 } +.terminal-r6 { fill: #ffa62b;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - ScrollViewTester + ScrollViewTester - - - - ScrollViewTester -╭─ 1 ──────────────────────────────────────────────────────────────────────────╮ -Welcome to line 980                                                          -Welcome to line 981                                                          -Welcome to line 982                                                          -Welcome to line 983                                                          -Welcome to line 984                                                          -Welcome to line 985                                                          -Welcome to line 986                                                          -Welcome to line 987                                                          -Welcome to line 988                                                          -Welcome to line 989                                                          -Welcome to line 990                                                          -Welcome to line 991                                                          -Welcome to line 992                                                          -Welcome to line 993                                                          -Welcome to line 994                                                          -Welcome to line 995                                                          -Welcome to line 996                                                          -Welcome to line 997                                                          -Welcome to line 998                                                          -Welcome to line 999                                                          -╰──────────────────────────────────────────────────────────────────────────────╯ -^p palette + + + + ScrollViewTester +╭─ 1 ──────────────────────────────────────────────────────────────────────────╮ +Welcome to line 980 +Welcome to line 981 +Welcome to line 982 +Welcome to line 983 +Welcome to line 984 +Welcome to line 985 +Welcome to line 986 +Welcome to line 987 +Welcome to line 988 +Welcome to line 989 +Welcome to line 990 +Welcome to line 991 +Welcome to line 992 +Welcome to line 993 +Welcome to line 994 +Welcome to line 995 +Welcome to line 996 +Welcome to line 997 +Welcome to line 998 +Welcome to line 999 +╰──────────────────────────────────────────────────────────────────────────────╯ +^p palette diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_selection_list_selections.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_selection_list_selections.svg index 74df41d4a5..d88e473a26 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_selection_list_selections.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_selection_list_selections.svg @@ -126,7 +126,7 @@ - + SelectionListApp diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_selection_list_tuples.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_selection_list_tuples.svg index 74df41d4a5..d88e473a26 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_selection_list_tuples.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_selection_list_tuples.svg @@ -126,7 +126,7 @@ - + SelectionListApp diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_setting_transparency.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_setting_transparency.svg index f4285cfdc5..4ad4afc4db 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_setting_transparency.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_setting_transparency.svg @@ -123,7 +123,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Baseline normal TextArea, not transparent      diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg index d41e3b4475..593a61c7d7 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg @@ -19,135 +19,135 @@ font-weight: 700; } - .terminal-4224541166-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-4224541166-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-4224541166-r1 { fill: #121212 } -.terminal-4224541166-r2 { fill: #0178d4 } -.terminal-4224541166-r3 { fill: #c5c8c6 } -.terminal-4224541166-r4 { fill: #959595;font-weight: bold } -.terminal-4224541166-r5 { fill: #e0e0e0 } + .terminal-r1 { fill: #121212 } +.terminal-r2 { fill: #0178d4 } +.terminal-r3 { fill: #c5c8c6 } +.terminal-r4 { fill: #959595;font-weight: bold } +.terminal-r5 { fill: #e0e0e0 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - CodeApp + CodeApp - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -1  x - - - - - - - - - - - - - - - - - - - - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +1  x + + + + + + + + + + + + + + + + + + + + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_alternate_screen.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_alternate_screen.svg index 4dd6b583b8..ff46a2d512 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_alternate_screen.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_alternate_screen.svg @@ -79,7 +79,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ foo                                          diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_css_theme_updates_background.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_css_theme_updates_background.svg index cc32704154..a00b088b78 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_css_theme_updates_background.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_css_theme_updates_background.svg @@ -122,7 +122,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ This TextArea theme is always `css`.                                         diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[bash].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[bash].svg index fac1d695c7..054b8e46dc 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[bash].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[bash].svg @@ -19,457 +19,457 @@ font-weight: 700; } - .terminal-1868575305-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-1868575305-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-1868575305-r1 { fill: #121212 } -.terminal-1868575305-r2 { fill: #0178d4 } -.terminal-1868575305-r3 { fill: #c5c8c6 } -.terminal-1868575305-r4 { fill: #c2c2bf } -.terminal-1868575305-r5 { fill: #272822 } -.terminal-1868575305-r6 { fill: #75715e } -.terminal-1868575305-r7 { fill: #f8f8f2 } -.terminal-1868575305-r8 { fill: #90908a } -.terminal-1868575305-r9 { fill: #e6db74 } -.terminal-1868575305-r10 { fill: #a6e22e } -.terminal-1868575305-r11 { fill: #f92672 } + .terminal-r1 { fill: #121212 } +.terminal-r2 { fill: #0178d4 } +.terminal-r3 { fill: #c5c8c6 } +.terminal-r4 { fill: #c2c2bf } +.terminal-r5 { fill: #272822 } +.terminal-r6 { fill: #75715e } +.terminal-r7 { fill: #f8f8f2 } +.terminal-r8 { fill: #90908a } +.terminal-r9 { fill: #e6db74 } +.terminal-r10 { fill: #a6e22e } +.terminal-r11 { fill: #f92672 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TextAreaSnapshot + TextAreaSnapshot - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -  1  #!/bin/bash -  2   -  3  # Variables -  4  name="John" -  5  age=30                                                                  -  6  is_student=true                                                         -  7   -  8  # Printing variables -  9  echo"Hello, $name! You are $age years old." - 10   - 11  # Conditional statements - 12  if [[ $age -ge 18 && $is_student == true ]]; then - 13  echo"You are an adult student." - 14  elif [[ $age -ge 18 ]]; then - 15  echo"You are an adult." - 16  else - 17  echo"You are a minor." - 18  fi - 19   - 20  # Arrays - 21  numbers=(1 2 3 4 5)                                                     - 22  echo"Numbers: ${numbers[@]}" - 23   - 24  # Loops - 25  for num in"${numbers[@]}"do - 26  echo"Number: $num" - 27  done - 28   - 29  # Functions - 30  greet() {                                                               - 31    local name=$1                                                         - 32  echo"Hello, $name!" - 33  }                                                                       - 34  greet"Alice" - 35   - 36  # Command substitution - 37  current_date=$(date +%Y-%m-%d)                                          - 38  echo"Current date: $current_date" - 39   - 40  # File operations - 41  touch file.txt                                                          - 42  echo"Some content" > file.txt                                          - 43  cat file.txt                                                            - 44   - 45  # Conditionals with file checks - 46  if [[ -f file.txt ]]; then - 47  echo"file.txt exists." - 48  else - 49  echo"file.txt does not exist." - 50  fi - 51   - 52  # Case statement - 53  case $age in - 54    18)                                                                   - 55  echo"You are 18 years old." - 56      ;;                                                                  - 57    30)                                                                   - 58  echo"You are 30 years old." - 59      ;;                                                                  - 60    *)                                                                    - 61  echo"You are neither 18 nor 30 years old." - 62      ;;                                                                  - 63  esac - 64   - 65  # While loop - 66  counter=0                                                               - 67  while [[ $counter -lt 5 ]]; do - 68  echo"Counter: $counter" - 69    ((counter++))                                                         - 70  done - 71   - 72  # Until loop - 73  until [[ $counter -eq 0 ]]; do - 74  echo"Counter: $counter" - 75    ((counter--))                                                         - 76  done - 77   - 78  # Heredoc - 79  cat << EOF - 80  This is a heredoc.  - 81  It allows you to write multiple lines of text.  - 82  EOF  - 83   - 84  # Redirection - 85  ls > file_list.txt                                                      - 86  grep"file" file_list.txt > filtered_list.txt                           - 87   - 88  # Pipes - 89  cat file_list.txt | wc -l                                               - 90   - 91  # Arithmetic operations - 92  result=$((10 + 5))                                                      - 93  echo"Result: $result" - 94   - 95  # Exporting variables - 96  export DB_PASSWORD="secret" - 97   - 98  # Sourcing external files - 99  source config.sh                                                        -100   - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +  1  #!/bin/bash +  2   +  3  # Variables +  4  name="John" +  5  age=30                                                                  +  6  is_student=true                                                         +  7   +  8  # Printing variables +  9  echo"Hello, $name! You are $age years old." + 10   + 11  # Conditional statements + 12  if [[ $age -ge 18 && $is_student == true ]]; then + 13  echo"You are an adult student." + 14  elif [[ $age -ge 18 ]]; then + 15  echo"You are an adult." + 16  else + 17  echo"You are a minor." + 18  fi + 19   + 20  # Arrays + 21  numbers=(1 2 3 4 5)                                                     + 22  echo"Numbers: ${numbers[@]}" + 23   + 24  # Loops + 25  for num in"${numbers[@]}"do + 26  echo"Number: $num" + 27  done + 28   + 29  # Functions + 30  greet() {                                                               + 31    local name=$1                                                         + 32  echo"Hello, $name!" + 33  }                                                                       + 34  greet"Alice" + 35   + 36  # Command substitution + 37  current_date=$(date +%Y-%m-%d)                                          + 38  echo"Current date: $current_date" + 39   + 40  # File operations + 41  touch file.txt                                                          + 42  echo"Some content" > file.txt                                          + 43  cat file.txt                                                            + 44   + 45  # Conditionals with file checks + 46  if [[ -f file.txt ]]; then + 47  echo"file.txt exists." + 48  else + 49  echo"file.txt does not exist." + 50  fi + 51   + 52  # Case statement + 53  case $age in + 54    18)                                                                   + 55  echo"You are 18 years old." + 56      ;;                                                                  + 57    30)                                                                   + 58  echo"You are 30 years old." + 59      ;;                                                                  + 60    *)                                                                    + 61  echo"You are neither 18 nor 30 years old." + 62      ;;                                                                  + 63  esac + 64   + 65  # While loop + 66  counter=0                                                               + 67  while [[ $counter -lt 5 ]]; do + 68  echo"Counter: $counter" + 69    ((counter++))                                                         + 70  done + 71   + 72  # Until loop + 73  until [[ $counter -eq 0 ]]; do + 74  echo"Counter: $counter" + 75    ((counter--))                                                         + 76  done + 77   + 78  # Heredoc + 79  cat << EOF + 80  This is a heredoc.  + 81  It allows you to write multiple lines of text.  + 82  EOF  + 83   + 84  # Redirection + 85  ls > file_list.txt                                                      + 86  grep"file" file_list.txt > filtered_list.txt                           + 87   + 88  # Pipes + 89  cat file_list.txt | wc -l                                               + 90   + 91  # Arithmetic operations + 92  result=$((10 + 5))                                                      + 93  echo"Result: $result" + 94   + 95  # Exporting variables + 96  export DB_PASSWORD="secret" + 97   + 98  # Sourcing external files + 99  source config.sh                                                        +100   + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[css].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[css].svg index 1d61cc6b0a..119172b454 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[css].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[css].svg @@ -270,7 +270,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔  1  /* This is a comment in CSS */ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[go].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[go].svg index 9cf4d393b6..8d6d0a83fa 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[go].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[go].svg @@ -19,334 +19,334 @@ font-weight: 700; } - .terminal-716815621-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-716815621-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-716815621-r1 { fill: #121212 } -.terminal-716815621-r2 { fill: #0178d4 } -.terminal-716815621-r3 { fill: #c5c8c6 } -.terminal-716815621-r4 { fill: #c2c2bf } -.terminal-716815621-r5 { fill: #272822 } -.terminal-716815621-r6 { fill: #f92672 } -.terminal-716815621-r7 { fill: #f8f8f2 } -.terminal-716815621-r8 { fill: #90908a } -.terminal-716815621-r9 { fill: #e6db74 } -.terminal-716815621-r10 { fill: #ae81ff } -.terminal-716815621-r11 { fill: #a6e22e } -.terminal-716815621-r12 { fill: #66d9ef;font-style: italic; } + .terminal-r1 { fill: #121212 } +.terminal-r2 { fill: #0178d4 } +.terminal-r3 { fill: #c5c8c6 } +.terminal-r4 { fill: #c2c2bf } +.terminal-r5 { fill: #272822 } +.terminal-r6 { fill: #f92672 } +.terminal-r7 { fill: #f8f8f2 } +.terminal-r8 { fill: #90908a } +.terminal-r9 { fill: #e6db74 } +.terminal-r10 { fill: #ae81ff } +.terminal-r11 { fill: #a6e22e } +.terminal-r12 { fill: #66d9ef;font-style: italic; } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TextAreaSnapshot + TextAreaSnapshot - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - 1  package main                                                             - 2   - 3  import (                                                                 - 4  "fmt" - 5  "math" - 6  "strings" - 7  )                                                                        - 8   - 9  const PI = 3.14159 -10   -11  typeShapeinterface {                                                   -12      Area() float64 -13  }                                                                        -14   -15  typeCirclestruct {                                                     -16      Radius float64 -17  }                                                                        -18   -19  func (c Circle) Area() float64 {                                         -20  return PI * c.Radius * c.Radius                                      -21  }                                                                        -22   -23  funcmain() {                                                            -24  var name string = "John" -25      age := 30 -26      isStudent := true -27   -28      fmt.Printf("Hello, %s! You are %d years old.", name, age)            -29   -30  if age >= 18 && isStudent {                                          -31          fmt.Println("You are an adult student.")                         -32      } elseif age >= 18 {                                                -33          fmt.Println("You are an adult.")                                 -34      } else {                                                             -35          fmt.Println("You are a minor.")                                  -36      }                                                                    -37   -38      numbers := []int{12345}                                      -39      sum := 0 -40  for _, num := range numbers {                                        -41          sum += num                                                       -42      }                                                                    -43      fmt.Printf("The sum is: %d", sum)                                    -44   -45      message := "Hello, World!" -46      uppercaseMessage := strings.ToUpper(message)                         -47      fmt.Println(uppercaseMessage)                                        -48   -49      circle := Circle{Radius: 5}                                          -50      fmt.Printf("Circle area: %.2f", circle.Area())                       -51   -52      result := factorial(5)                                               -53      fmt.Printf("Factorial of 5: %d", result)                             -54   -55  defer fmt.Println("Program finished.")                               -56   -57      sqrt := func(x float64float64 {                                    -58  return math.Sqrt(x)                                              -59      }                                                                    -60      fmt.Printf("Square root of 16: %.2f"sqrt(16))                      -61  }                                                                        -62   -63  funcfactorial(n intint {                                              -64  if n == 0 {                                                          -65  return1 -66      }                                                                    -67  return n * factorial(n-1)                                            -68  }                                                                        -69   - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + 1  package main                                                             + 2   + 3  import (                                                                 + 4  "fmt" + 5  "math" + 6  "strings" + 7  )                                                                        + 8   + 9  const PI = 3.14159 +10   +11  typeShapeinterface {                                                   +12      Area() float64 +13  }                                                                        +14   +15  typeCirclestruct {                                                     +16      Radius float64 +17  }                                                                        +18   +19  func (c Circle) Area() float64 {                                         +20  return PI * c.Radius * c.Radius                                      +21  }                                                                        +22   +23  funcmain() {                                                            +24  var name string = "John" +25      age := 30 +26      isStudent := true +27   +28      fmt.Printf("Hello, %s! You are %d years old.", name, age)            +29   +30  if age >= 18 && isStudent {                                          +31          fmt.Println("You are an adult student.")                         +32      } elseif age >= 18 {                                                +33          fmt.Println("You are an adult.")                                 +34      } else {                                                             +35          fmt.Println("You are a minor.")                                  +36      }                                                                    +37   +38      numbers := []int{12345}                                      +39      sum := 0 +40  for _, num := range numbers {                                        +41          sum += num                                                       +42      }                                                                    +43      fmt.Printf("The sum is: %d", sum)                                    +44   +45      message := "Hello, World!" +46      uppercaseMessage := strings.ToUpper(message)                         +47      fmt.Println(uppercaseMessage)                                        +48   +49      circle := Circle{Radius: 5}                                          +50      fmt.Printf("Circle area: %.2f", circle.Area())                       +51   +52      result := factorial(5)                                               +53      fmt.Printf("Factorial of 5: %d", result)                             +54   +55  defer fmt.Println("Program finished.")                               +56   +57      sqrt := func(x float64float64 {                                    +58  return math.Sqrt(x)                                              +59      }                                                                    +60      fmt.Printf("Square root of 16: %.2f"sqrt(16))                      +61  }                                                                        +62   +63  funcfactorial(n intint {                                              +64  if n == 0 {                                                          +65  return1 +66      }                                                                    +67  return n * factorial(n-1)                                            +68  }                                                                        +69   + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[javascript].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[javascript].svg index 210ce01926..dcd5989da5 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[javascript].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[javascript].svg @@ -301,7 +301,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔  1  // Variable declarations diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[json].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[json].svg index d684ae0aa1..71aed4ec77 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[json].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[json].svg @@ -19,182 +19,182 @@ font-weight: 700; } - .terminal-3328771125-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3328771125-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3328771125-r1 { fill: #121212 } -.terminal-3328771125-r2 { fill: #0178d4 } -.terminal-3328771125-r3 { fill: #c5c8c6 } -.terminal-3328771125-r4 { fill: #c2c2bf } -.terminal-3328771125-r5 { fill: #272822;font-weight: bold } -.terminal-3328771125-r6 { fill: #f8f8f2 } -.terminal-3328771125-r7 { fill: #90908a } -.terminal-3328771125-r8 { fill: #f92672;font-weight: bold } -.terminal-3328771125-r9 { fill: #e6db74 } -.terminal-3328771125-r10 { fill: #ae81ff } -.terminal-3328771125-r11 { fill: #66d9ef;font-style: italic; } -.terminal-3328771125-r12 { fill: #f8f8f2;font-weight: bold } + .terminal-r1 { fill: #121212 } +.terminal-r2 { fill: #0178d4 } +.terminal-r3 { fill: #c5c8c6 } +.terminal-r4 { fill: #c2c2bf } +.terminal-r5 { fill: #272822;font-weight: bold } +.terminal-r6 { fill: #f8f8f2 } +.terminal-r7 { fill: #90908a } +.terminal-r8 { fill: #f92672;font-weight: bold } +.terminal-r9 { fill: #e6db74 } +.terminal-r10 { fill: #ae81ff } +.terminal-r11 { fill: #66d9ef;font-style: italic; } +.terminal-r12 { fill: #f8f8f2;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TextAreaSnapshot + TextAreaSnapshot - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - 1  { - 2  "name""John Doe",                                                  - 3  "age"30,                                                           - 4  "isStudent"false,                                                  - 5  "address": {                                                         - 6  "street""123 Main St",                                         - 7  "city""Anytown",                                               - 8  "state""CA",                                                   - 9  "zip""12345" -10      },                                                                   -11  "phoneNumbers": [                                                    -12          {                                                                -13  "type""home",                                              -14  "number""555-555-1234" -15          },                                                               -16          {                                                                -17  "type""work",                                              -18  "number""555-555-5678" -19          }                                                                -20      ],                                                                   -21  "hobbies": ["reading""hiking""swimming"],                        -22  "pets": [                                                            -23          {                                                                -24  "type""dog",                                               -25  "name""Fido" -26          },                                                               -27      ],                                                                   -28  "graduationYear"null -29  } -30   -31   - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + 1  { + 2  "name""John Doe",                                                  + 3  "age"30,                                                           + 4  "isStudent"false,                                                  + 5  "address": {                                                         + 6  "street""123 Main St",                                         + 7  "city""Anytown",                                               + 8  "state""CA",                                                   + 9  "zip""12345" +10      },                                                                   +11  "phoneNumbers": [                                                    +12          {                                                                +13  "type""home",                                              +14  "number""555-555-1234" +15          },                                                               +16          {                                                                +17  "type""work",                                              +18  "number""555-555-5678" +19          }                                                                +20      ],                                                                   +21  "hobbies": ["reading""hiking""swimming"],                        +22  "pets": [                                                            +23          {                                                                +24  "type""dog",                                               +25  "name""Fido" +26          },                                                               +27      ],                                                                   +28  "graduationYear"null +29  } +30   +31   + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[toml].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[toml].svg index ceb6cb5983..f2a96926cf 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[toml].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[toml].svg @@ -19,162 +19,162 @@ font-weight: 700; } - .terminal-3154393304-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3154393304-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3154393304-r1 { fill: #121212 } -.terminal-3154393304-r2 { fill: #0178d4 } -.terminal-3154393304-r3 { fill: #c5c8c6 } -.terminal-3154393304-r4 { fill: #c2c2bf } -.terminal-3154393304-r5 { fill: #272822 } -.terminal-3154393304-r6 { fill: #75715e } -.terminal-3154393304-r7 { fill: #f8f8f2 } -.terminal-3154393304-r8 { fill: #90908a } -.terminal-3154393304-r9 { fill: #f92672 } -.terminal-3154393304-r10 { fill: #e6db74 } -.terminal-3154393304-r11 { fill: #ae81ff } -.terminal-3154393304-r12 { fill: #66d9ef;font-style: italic; } + .terminal-r1 { fill: #121212 } +.terminal-r2 { fill: #0178d4 } +.terminal-r3 { fill: #c5c8c6 } +.terminal-r4 { fill: #c2c2bf } +.terminal-r5 { fill: #272822 } +.terminal-r6 { fill: #75715e } +.terminal-r7 { fill: #f8f8f2 } +.terminal-r8 { fill: #90908a } +.terminal-r9 { fill: #f92672 } +.terminal-r10 { fill: #e6db74 } +.terminal-r11 { fill: #ae81ff } +.terminal-r12 { fill: #66d9ef;font-style: italic; } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TextAreaSnapshot + TextAreaSnapshot - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - 1  # This is a comment in TOML - 2   - 3  string = "Hello, world!" - 4  integer = 42 - 5  float = 3.14 - 6  boolean = true - 7  datetime = 1979-05-27T07:32:00Z - 8   - 9  fruits = ["apple""banana""cherry"]                                   -10   -11  [address]                                                                -12  street = "123 Main St" -13  city = "Anytown" -14  state = "CA" -15  zip = "12345" -16   -17  [person.john]                                                            -18  name = "John Doe" -19  age = 28 -20  is_student = false -21   -22   -23  [[animals]]                                                              -24  name = "Fido" -25  type = "dog" -26   - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + 1  # This is a comment in TOML + 2   + 3  string = "Hello, world!" + 4  integer = 42 + 5  float = 3.14 + 6  boolean = true + 7  datetime = 1979-05-27T07:32:00Z + 8   + 9  fruits = ["apple""banana""cherry"]                                   +10   +11  [address]                                                                +12  street = "123 Main St" +13  city = "Anytown" +14  state = "CA" +15  zip = "12345" +16   +17  [person.john]                                                            +18  name = "John Doe" +19  age = 28 +20  is_student = false +21   +22   +23  [[animals]]                                                              +24  name = "Fido" +25  type = "dog" +26   + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[yaml].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[yaml].svg index f12b6d629f..f77012a12a 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[yaml].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_language_rendering[yaml].svg @@ -19,210 +19,210 @@ font-weight: 700; } - .terminal-2714046411-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2714046411-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2714046411-r1 { fill: #121212 } -.terminal-2714046411-r2 { fill: #0178d4 } -.terminal-2714046411-r3 { fill: #c5c8c6 } -.terminal-2714046411-r4 { fill: #c2c2bf } -.terminal-2714046411-r5 { fill: #272822 } -.terminal-2714046411-r6 { fill: #75715e } -.terminal-2714046411-r7 { fill: #f8f8f2 } -.terminal-2714046411-r8 { fill: #90908a } -.terminal-2714046411-r9 { fill: #f92672;font-weight: bold } -.terminal-2714046411-r10 { fill: #e6db74 } -.terminal-2714046411-r11 { fill: #ae81ff } -.terminal-2714046411-r12 { fill: #66d9ef;font-style: italic; } + .terminal-r1 { fill: #121212 } +.terminal-r2 { fill: #0178d4 } +.terminal-r3 { fill: #c5c8c6 } +.terminal-r4 { fill: #c2c2bf } +.terminal-r5 { fill: #272822 } +.terminal-r6 { fill: #75715e } +.terminal-r7 { fill: #f8f8f2 } +.terminal-r8 { fill: #90908a } +.terminal-r9 { fill: #f92672;font-weight: bold } +.terminal-r10 { fill: #e6db74 } +.terminal-r11 { fill: #ae81ff } +.terminal-r12 { fill: #66d9ef;font-style: italic; } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - TextAreaSnapshot + TextAreaSnapshot - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - 1  # This is a comment in YAML - 2   - 3  # Scalars - 4  string"Hello, world!" - 5  integer42 - 6  float3.14 - 7  booleantrue - 8   - 9  # Sequences (Arrays) -10  fruits:                                                                  -11    - Apple -12    - Banana -13    - Cherry -14   -15  # Nested sequences -16  persons:                                                                 -17    - nameJohn -18  age28 -19  is_studentfalse -20    - nameJane -21  age22 -22  is_studenttrue -23   -24  # Mappings (Dictionaries) -25  address:                                                                 -26  street123 Main St -27  cityAnytown -28  stateCA -29  zip'12345' -30   -31  # Multiline string -32  description: | -33    This is a multiline  -34    string in YAML. -35   -36  # Inline and nested collections -37  colors: { redFF0000green00FF00blue0000FF }                     -38   - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + 1  # This is a comment in YAML + 2   + 3  # Scalars + 4  string"Hello, world!" + 5  integer42 + 6  float3.14 + 7  booleantrue + 8   + 9  # Sequences (Arrays) +10  fruits:                                                                  +11    - Apple +12    - Banana +13    - Cherry +14   +15  # Nested sequences +16  persons:                                                                 +17    - nameJohn +18  age28 +19  is_studentfalse +20    - nameJane +21  age22 +22  is_studenttrue +23   +24  # Mappings (Dictionaries) +25  address:                                                                 +26  street123 Main St +27  cityAnytown +28  stateCA +29  zip'12345' +30   +31  # Multiline string +32  description: | +33    This is a multiline  +34    string in YAML. +35   +36  # Inline and nested collections +37  colors: { redFF0000green00FF00blue0000FF }                     +38   + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_line_number_start.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_line_number_start.svg index b6b05e80b3..bb06597fe6 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_line_number_start.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_line_number_start.svg @@ -19,72 +19,72 @@ font-weight: 700; } - .terminal-2299821873-matrix { + .terminal-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2299821873-title { + .terminal-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2299821873-r1 { fill: #121212 } -.terminal-2299821873-r2 { fill: #0178d4 } -.terminal-2299821873-r3 { fill: #c5c8c6 } -.terminal-2299821873-r4 { fill: #959595;font-weight: bold } -.terminal-2299821873-r5 { fill: #e0e0e0 } -.terminal-2299821873-r6 { fill: #6b6b6b } + .terminal-r1 { fill: #121212 } +.terminal-r2 { fill: #0178d4 } +.terminal-r3 { fill: #c5c8c6 } +.terminal-r4 { fill: #959595;font-weight: bold } +.terminal-r5 { fill: #e0e0e0 } +.terminal-r6 { fill: #6b6b6b } - + - + - + - + - + - + - + - + - LineNumbersReactive + LineNumbersReactive - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - 9999  Foo                   -10000  Bar                   -10001  Baz                   -10002   - - -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + 9999  Foo                   +10000  Bar                   +10001  Baz                   +10002   + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_read_only_cursor_rendering.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_read_only_cursor_rendering.svg index 86db6b1030..fdee390c94 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_read_only_cursor_rendering.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_read_only_cursor_rendering.svg @@ -65,7 +65,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ 1  Hello, world!           diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection0].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection0].svg index 025391ca71..fae18d7d19 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection0].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection0].svg @@ -75,7 +75,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ I am a line. diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection1].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection1].svg index 914e6127c0..09a948c40b 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection1].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection1].svg @@ -75,7 +75,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ I am a line. diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection2].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection2].svg index d912b7920a..6061000617 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection2].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection2].svg @@ -75,7 +75,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ I am a line. diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection3].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection3].svg index 456199a1db..e3d27c99d9 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection3].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection3].svg @@ -75,7 +75,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ I am a line. diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection4].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection4].svg index ef5f467e75..aacff2e411 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection4].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection4].svg @@ -74,7 +74,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ I am a line.               diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection5].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection5].svg index 54a9c7edcb..ed8b4ada7d 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection5].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_selection_rendering[selection5].svg @@ -74,7 +74,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ I am a line.               diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[css].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[css].svg index 1780ec285f..08791ecdff 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[css].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[css].svg @@ -84,7 +84,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ 1  defhello(name): diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[dracula].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[dracula].svg index c46632b650..220970861b 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[dracula].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[dracula].svg @@ -83,7 +83,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ 1  defhello(name): diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[github_light].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[github_light].svg index 0b33d27723..8cd04bfe5c 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[github_light].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[github_light].svg @@ -86,7 +86,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ 1  defhello(name): diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[monokai].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[monokai].svg index 3e94f22d02..efc56b0916 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[monokai].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[monokai].svg @@ -84,7 +84,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ 1  defhello(name): diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[vscode_dark].svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[vscode_dark].svg index 43710bef03..cac4ae727f 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[vscode_dark].svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_text_area_themes[vscode_dark].svg @@ -83,7 +83,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ 1  defhello(name): diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_line_highlight.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_line_highlight.svg index 9a97470459..0cbdc39137 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_line_highlight.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_line_highlight.svg @@ -121,7 +121,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Hello                                                                        diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_placeholder.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_placeholder.svg index e8b52162e3..2ef1ed80ed 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_placeholder.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_placeholder.svg @@ -36,6 +36,7 @@ .terminal-r2 { fill: #0178d4 } .terminal-r3 { fill: #c5c8c6 } .terminal-r4 { fill: #787878 } +.terminal-r5 { fill: #e0e0e0 } @@ -121,7 +122,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Your text here diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_select.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_select.svg index 984cc0ec07..3505eff14c 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_select.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_select.svg @@ -121,7 +121,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Hello, World! Hello, World! Hello, World! Hello, World! Hello, World!  diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_suggestion.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_suggestion.svg index 8554cd3c0e..9fa7829362 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_suggestion.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_textarea_suggestion.svg @@ -122,7 +122,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ Hello, World! diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_textual_dev_border_preview.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_textual_dev_border_preview.svg index 2351d47355..b30bed6d3d 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_textual_dev_border_preview.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_textual_dev_border_preview.svg @@ -123,7 +123,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ascii diff --git a/tests/snapshot_tests/snapshot_apps/scrollbar_thumb_height.py b/tests/snapshot_tests/snapshot_apps/scrollbar_thumb_height.py index 148e2443f1..bb4510df45 100644 --- a/tests/snapshot_tests/snapshot_apps/scrollbar_thumb_height.py +++ b/tests/snapshot_tests/snapshot_apps/scrollbar_thumb_height.py @@ -16,7 +16,7 @@ def __init__(self, height: int, border_title: str) -> None: def render_line(self, y: int) -> Strip: return Strip( [ - Segment(f"Welcome to line {self.scroll_offset.y + y}"), + Segment(f"Welcome to line {self.scroll_offset.y + y}", self.rich_style), ] ) @@ -33,7 +33,7 @@ class ScrollViewTester(App[None]): def compose(self) -> ComposeResult: yield Header() - yield TestScrollView(height=1000, border_title=f"1") + yield TestScrollView(height=1000, border_title="1") yield Footer() def on_ready(self) -> None: diff --git a/tests/test_styles_cache.py b/tests/test_styles_cache.py index a94cfa682f..83d2ca00fb 100644 --- a/tests/test_styles_cache.py +++ b/tests/test_styles_cache.py @@ -1,7 +1,6 @@ from __future__ import annotations from rich.segment import Segment -from rich.style import Style from textual._styles_cache import StylesCache from textual.color import Color @@ -25,40 +24,6 @@ def test_set_dirty(): assert not cache.is_dirty(6) -def test_no_styles(): - """Test that empty style returns the content un-altered""" - content = [ - Strip([Segment("foo")]), - Strip([Segment("bar")]), - Strip([Segment("baz")]), - ] - styles = Styles() - cache = StylesCache() - lines = cache.render( - styles, - Size(3, 3), - Color.parse("blue"), - Color.parse("green"), - content.__getitem__, - [], - None, - None, - content_size=Size(3, 3), - ) - style = Style.from_color(bgcolor=Color.parse("green").rich_color) - - expected = [ - Strip([Segment("foo", style)], 3), - Strip([Segment("bar", style)], 3), - Strip([Segment("baz", style)], 3), - ] - - print(lines[0]) - print(expected[0]) - - assert lines == expected - - def test_border(): content = [ Strip([Segment("foo")]), From 61e53c5bff4aee0d070d4ceb0e2448ab24c48710 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 21 Aug 2025 09:18:26 +0100 Subject: [PATCH 6/7] get_line_filters --- CHANGELOG.md | 2 +- src/textual/app.py | 8 ++++++-- src/textual/dom.py | 14 -------------- src/textual/widget.py | 4 ++-- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 260557ecd8..963e715032 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `OptionList.set_options` https://github.com/Textualize/textual/pull/6048 - Added `TextArea.suggestion` https://github.com/Textualize/textual/pull/6048 - Added `TextArea.placeholder` https://github.com/Textualize/textual/pull/6048 -- Added `Widget.get_line_filters` +- Added `Widget.get_line_filters` and `App.get_line_filters` https://github.com/Textualize/textual/pull/6057 ### Changed diff --git a/src/textual/app.py b/src/textual/app.py index 60c2dde325..c3b6b5e9a4 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -842,8 +842,12 @@ def __init__( ) ) - @property - def _enabled_filters(self) -> list[LineFilter]: + def get_line_filters(self) -> Sequence[LineFilter]: + """Get currently enabled line filters. + + Returns: + A list of [LineFilter][textual.filters.LineFilter] instances. + """ return [filter for filter in self._filters if filter.enabled] @property diff --git a/src/textual/dom.py b/src/textual/dom.py index adf3f78b1d..b7c832f7a7 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1144,20 +1144,6 @@ def _get_subtitle_style_information( VisualStyle.from_rich_style(styles.border_subtitle_style), ) - # @property - # def background_colors(self) -> tuple[Color, Color]: - # """The background color and the color of the parent's background. - - # Returns: - # `(, )` - # """ - # base_background = background = BLACK - # for node in reversed(self.ancestors_with_self): - # styles = node.styles - # base_background = background - # background += styles.background.tint(styles.background_tint) - # return (base_background, background) - @property def background_colors(self) -> tuple[Color, Color]: """Background colors adjusted for opacity. diff --git a/src/textual/widget.py b/src/textual/widget.py index f3d3a481af..51dbac0c8d 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -679,9 +679,9 @@ def get_line_filters(self) -> Sequence[LineFilter]: """Get the line filters enabled for this widget. Returns: - A sequence of LineFilter instances. + A sequence of [LineFilter][textual.filters.LineFilter] instances. """ - return self.app._enabled_filters + return self.app.get_line_filters() def preflight_checks(self) -> None: """Called in debug mode to do preflight checks. From 515b82fa28600afb938101681217322425417b18 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 21 Aug 2025 10:41:04 +0100 Subject: [PATCH 7/7] superfluous --- src/textual/dom.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/textual/dom.py b/src/textual/dom.py index b7c832f7a7..11d649ff03 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1735,7 +1735,6 @@ def add_class(self, *class_names: str, update: bool = True) -> Self: check_identifiers("class name", *class_names) old_classes = self._classes.copy() self._classes.update(class_names) - self._nodes.updated() if old_classes == self._classes: return self if update: