ratatui_ruby 1.4.0-x86_64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +15 -0
- data/LICENSES/AGPL-3.0-or-later.txt +661 -0
- data/LICENSES/CC-BY-SA-4.0.txt +427 -0
- data/LICENSES/CC0-1.0.txt +121 -0
- data/LICENSES/LGPL-3.0-or-later.txt +304 -0
- data/LICENSES/MIT-0.txt +16 -0
- data/LICENSES/MIT.txt +21 -0
- data/REUSE.toml +42 -0
- data/exe/.gitkeep +0 -0
- data/ext/ratatui_ruby/.cargo/config.toml +13 -0
- data/ext/ratatui_ruby/.gitignore +4 -0
- data/ext/ratatui_ruby/Cargo.lock +1737 -0
- data/ext/ratatui_ruby/Cargo.toml +24 -0
- data/ext/ratatui_ruby/clippy.toml +7 -0
- data/ext/ratatui_ruby/extconf.rb +21 -0
- data/ext/ratatui_ruby/src/color.rs +82 -0
- data/ext/ratatui_ruby/src/errors.rs +28 -0
- data/ext/ratatui_ruby/src/events.rs +700 -0
- data/ext/ratatui_ruby/src/frame.rs +241 -0
- data/ext/ratatui_ruby/src/lib.rs +343 -0
- data/ext/ratatui_ruby/src/lib_header.rs +11 -0
- data/ext/ratatui_ruby/src/rendering.rs +158 -0
- data/ext/ratatui_ruby/src/string_width.rs +101 -0
- data/ext/ratatui_ruby/src/style.rs +469 -0
- data/ext/ratatui_ruby/src/terminal/capabilities.rs +46 -0
- data/ext/ratatui_ruby/src/terminal/init.rs +233 -0
- data/ext/ratatui_ruby/src/terminal/mod.rs +42 -0
- data/ext/ratatui_ruby/src/terminal/mutations.rs +158 -0
- data/ext/ratatui_ruby/src/terminal/queries.rs +231 -0
- data/ext/ratatui_ruby/src/terminal/query.rs +400 -0
- data/ext/ratatui_ruby/src/terminal/storage.rs +109 -0
- data/ext/ratatui_ruby/src/terminal/wrapper.rs +16 -0
- data/ext/ratatui_ruby/src/text.rs +225 -0
- data/ext/ratatui_ruby/src/widgets/barchart.rs +169 -0
- data/ext/ratatui_ruby/src/widgets/block.rs +41 -0
- data/ext/ratatui_ruby/src/widgets/calendar.rs +84 -0
- data/ext/ratatui_ruby/src/widgets/canvas.rs +183 -0
- data/ext/ratatui_ruby/src/widgets/center.rs +79 -0
- data/ext/ratatui_ruby/src/widgets/chart.rs +222 -0
- data/ext/ratatui_ruby/src/widgets/clear.rs +39 -0
- data/ext/ratatui_ruby/src/widgets/cursor.rs +32 -0
- data/ext/ratatui_ruby/src/widgets/gauge.rs +65 -0
- data/ext/ratatui_ruby/src/widgets/layout.rs +379 -0
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +100 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +378 -0
- data/ext/ratatui_ruby/src/widgets/list_state.rs +173 -0
- data/ext/ratatui_ruby/src/widgets/mod.rs +26 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +24 -0
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +87 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +55 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +214 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +127 -0
- data/ext/ratatui_ruby/src/widgets/table.rs +415 -0
- data/ext/ratatui_ruby/src/widgets/table_state.rs +203 -0
- data/ext/ratatui_ruby/src/widgets/tabs.rs +194 -0
- data/lib/ratatui_ruby/backend/window_size.rb +50 -0
- data/lib/ratatui_ruby/backend.rb +59 -0
- data/lib/ratatui_ruby/buffer/cell.rb +212 -0
- data/lib/ratatui_ruby/buffer.rb +149 -0
- data/lib/ratatui_ruby/cell.rb +208 -0
- data/lib/ratatui_ruby/debug.rb +215 -0
- data/lib/ratatui_ruby/draw.rb +63 -0
- data/lib/ratatui_ruby/event/focus_gained.rb +125 -0
- data/lib/ratatui_ruby/event/focus_lost.rb +127 -0
- data/lib/ratatui_ruby/event/key/character.rb +53 -0
- data/lib/ratatui_ruby/event/key/dwim.rb +301 -0
- data/lib/ratatui_ruby/event/key/media.rb +46 -0
- data/lib/ratatui_ruby/event/key/modifier.rb +107 -0
- data/lib/ratatui_ruby/event/key/navigation.rb +72 -0
- data/lib/ratatui_ruby/event/key/system.rb +47 -0
- data/lib/ratatui_ruby/event/key.rb +479 -0
- data/lib/ratatui_ruby/event/mouse.rb +291 -0
- data/lib/ratatui_ruby/event/none.rb +53 -0
- data/lib/ratatui_ruby/event/paste.rb +130 -0
- data/lib/ratatui_ruby/event/resize.rb +221 -0
- data/lib/ratatui_ruby/event/sync.rb +52 -0
- data/lib/ratatui_ruby/event.rb +163 -0
- data/lib/ratatui_ruby/frame.rb +257 -0
- data/lib/ratatui_ruby/labs/a11y.rb +182 -0
- data/lib/ratatui_ruby/labs/frame_a11y_capture.rb +50 -0
- data/lib/ratatui_ruby/labs.rb +47 -0
- data/lib/ratatui_ruby/layout/alignment.rb +91 -0
- data/lib/ratatui_ruby/layout/constraint.rb +337 -0
- data/lib/ratatui_ruby/layout/layout.rb +258 -0
- data/lib/ratatui_ruby/layout/position.rb +81 -0
- data/lib/ratatui_ruby/layout/rect.rb +733 -0
- data/lib/ratatui_ruby/layout/size.rb +62 -0
- data/lib/ratatui_ruby/layout.rb +29 -0
- data/lib/ratatui_ruby/list_state.rb +201 -0
- data/lib/ratatui_ruby/output_guard.rb +171 -0
- data/lib/ratatui_ruby/ratatui_ruby.so +0 -0
- data/lib/ratatui_ruby/scrollbar_state.rb +122 -0
- data/lib/ratatui_ruby/style/color.rb +149 -0
- data/lib/ratatui_ruby/style/style.rb +147 -0
- data/lib/ratatui_ruby/style.rb +19 -0
- data/lib/ratatui_ruby/symbols.rb +435 -0
- data/lib/ratatui_ruby/synthetic_events.rb +106 -0
- data/lib/ratatui_ruby/table_state.rb +251 -0
- data/lib/ratatui_ruby/terminal/capabilities.rb +316 -0
- data/lib/ratatui_ruby/terminal/viewport.rb +80 -0
- data/lib/ratatui_ruby/terminal.rb +66 -0
- data/lib/ratatui_ruby/terminal_lifecycle.rb +303 -0
- data/lib/ratatui_ruby/terminal_lifecycle.rb.bak +197 -0
- data/lib/ratatui_ruby/test_helper/event_injection.rb +241 -0
- data/lib/ratatui_ruby/test_helper/global_state.rb +111 -0
- data/lib/ratatui_ruby/test_helper/snapshot.rb +568 -0
- data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.ansi +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.txt +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.ansi +5 -0
- data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.txt +5 -0
- data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.ansi +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.txt +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/my_snapshot.txt +1 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.ansi +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.txt +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.ansi +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.txt +10 -0
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +449 -0
- data/lib/ratatui_ruby/test_helper/subprocess_timeout.rb +35 -0
- data/lib/ratatui_ruby/test_helper/terminal.rb +187 -0
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +86 -0
- data/lib/ratatui_ruby/test_helper.rb +115 -0
- data/lib/ratatui_ruby/text/line.rb +245 -0
- data/lib/ratatui_ruby/text/span.rb +158 -0
- data/lib/ratatui_ruby/text.rb +99 -0
- data/lib/ratatui_ruby/tui/buffer_factories.rb +22 -0
- data/lib/ratatui_ruby/tui/canvas_factories.rb +149 -0
- data/lib/ratatui_ruby/tui/core.rb +67 -0
- data/lib/ratatui_ruby/tui/layout_factories.rb +153 -0
- data/lib/ratatui_ruby/tui/state_factories.rb +77 -0
- data/lib/ratatui_ruby/tui/style_factories.rb +22 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +86 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +272 -0
- data/lib/ratatui_ruby/tui.rb +106 -0
- data/lib/ratatui_ruby/version.rb +12 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +51 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +29 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +308 -0
- data/lib/ratatui_ruby/widgets/block.rb +266 -0
- data/lib/ratatui_ruby/widgets/calendar.rb +88 -0
- data/lib/ratatui_ruby/widgets/canvas.rb +297 -0
- data/lib/ratatui_ruby/widgets/cell.rb +59 -0
- data/lib/ratatui_ruby/widgets/center.rb +71 -0
- data/lib/ratatui_ruby/widgets/chart.rb +172 -0
- data/lib/ratatui_ruby/widgets/clear.rb +66 -0
- data/lib/ratatui_ruby/widgets/coerceable_widget.rb +77 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +54 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +146 -0
- data/lib/ratatui_ruby/widgets/line_gauge.rb +158 -0
- data/lib/ratatui_ruby/widgets/list.rb +252 -0
- data/lib/ratatui_ruby/widgets/list_item.rb +55 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +55 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +113 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +35 -0
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +40 -0
- data/lib/ratatui_ruby/widgets/row.rb +123 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +147 -0
- data/lib/ratatui_ruby/widgets/shape/label.rb +80 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +153 -0
- data/lib/ratatui_ruby/widgets/table.rb +213 -0
- data/lib/ratatui_ruby/widgets/tabs.rb +91 -0
- data/lib/ratatui_ruby/widgets.rb +43 -0
- data/lib/ratatui_ruby.rb +555 -0
- data/sig/examples/app_all_events/app.rbs +11 -0
- data/sig/examples/app_all_events/model/app_model.rbs +23 -0
- data/sig/examples/app_all_events/model/event_entry.rbs +23 -0
- data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
- data/sig/examples/app_all_events/view/app_view.rbs +8 -0
- data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
- data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
- data/sig/examples/app_all_events/view/live_view.rbs +6 -0
- data/sig/examples/app_all_events/view/log_view.rbs +6 -0
- data/sig/examples/app_all_events/view.rbs +14 -0
- data/sig/examples/app_cli_rich_moments/app.rbs +12 -0
- data/sig/examples/app_color_picker/app.rbs +17 -0
- data/sig/examples/app_external_editor/app.rbs +12 -0
- data/sig/examples/app_login_form/app.rbs +11 -0
- data/sig/examples/app_stateful_interaction/app.rbs +39 -0
- data/sig/examples/verify_quickstart_dsl/app.rbs +17 -0
- data/sig/examples/verify_quickstart_lifecycle/app.rbs +17 -0
- data/sig/examples/verify_readme_usage/app.rbs +17 -0
- data/sig/examples/widget_block_demo/app.rbs +38 -0
- data/sig/examples/widget_box_demo/app.rbs +17 -0
- data/sig/examples/widget_calendar_demo/app.rbs +17 -0
- data/sig/examples/widget_cell_demo/app.rbs +17 -0
- data/sig/examples/widget_chart_demo/app.rbs +17 -0
- data/sig/examples/widget_gauge_demo/app.rbs +17 -0
- data/sig/examples/widget_layout_split/app.rbs +16 -0
- data/sig/examples/widget_line_gauge_demo/app.rbs +17 -0
- data/sig/examples/widget_list_demo/app.rbs +17 -0
- data/sig/examples/widget_map_demo/app.rbs +17 -0
- data/sig/examples/widget_popup_demo/app.rbs +17 -0
- data/sig/examples/widget_ratatui_logo_demo/app.rbs +17 -0
- data/sig/examples/widget_ratatui_mascot_demo/app.rbs +17 -0
- data/sig/examples/widget_rect/app.rbs +18 -0
- data/sig/examples/widget_render/app.rbs +16 -0
- data/sig/examples/widget_rich_text/app.rbs +17 -0
- data/sig/examples/widget_scroll_text/app.rbs +17 -0
- data/sig/examples/widget_scrollbar_demo/app.rbs +17 -0
- data/sig/examples/widget_sparkline_demo/app.rbs +16 -0
- data/sig/examples/widget_style_colors/app.rbs +20 -0
- data/sig/examples/widget_table_demo/app.rbs +17 -0
- data/sig/examples/widget_text_width/app.rbs +16 -0
- data/sig/generated/event_key_predicates.rbs +1348 -0
- data/sig/manifest.yaml +5 -0
- data/sig/patches/data.rbs +26 -0
- data/sig/patches/debugger__.rbs +8 -0
- data/sig/ratatui_ruby/backend/window_size.rbs +17 -0
- data/sig/ratatui_ruby/backend.rbs +12 -0
- data/sig/ratatui_ruby/buffer/cell.rbs +46 -0
- data/sig/ratatui_ruby/buffer.rbs +18 -0
- data/sig/ratatui_ruby/cell.rbs +44 -0
- data/sig/ratatui_ruby/clear.rbs +18 -0
- data/sig/ratatui_ruby/constraint.rbs +26 -0
- data/sig/ratatui_ruby/debug.rbs +45 -0
- data/sig/ratatui_ruby/draw.rbs +30 -0
- data/sig/ratatui_ruby/event.rbs +249 -0
- data/sig/ratatui_ruby/frame.rbs +23 -0
- data/sig/ratatui_ruby/interfaces.rbs +25 -0
- data/sig/ratatui_ruby/labs.rbs +90 -0
- data/sig/ratatui_ruby/layout/alignment.rbs +26 -0
- data/sig/ratatui_ruby/layout/constraint.rbs +39 -0
- data/sig/ratatui_ruby/layout/layout.rbs +45 -0
- data/sig/ratatui_ruby/layout/position.rbs +18 -0
- data/sig/ratatui_ruby/layout/rect.rbs +64 -0
- data/sig/ratatui_ruby/layout/size.rbs +18 -0
- data/sig/ratatui_ruby/list_state.rbs +23 -0
- data/sig/ratatui_ruby/output_guard.rbs +23 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +113 -0
- data/sig/ratatui_ruby/rect.rbs +17 -0
- data/sig/ratatui_ruby/scrollbar_state.rbs +24 -0
- data/sig/ratatui_ruby/session.rbs +93 -0
- data/sig/ratatui_ruby/style/color.rbs +22 -0
- data/sig/ratatui_ruby/style/style.rbs +29 -0
- data/sig/ratatui_ruby/symbols.rbs +141 -0
- data/sig/ratatui_ruby/synthetic_events.rbs +24 -0
- data/sig/ratatui_ruby/table_state.rbs +27 -0
- data/sig/ratatui_ruby/terminal/capabilities.rbs +38 -0
- data/sig/ratatui_ruby/terminal/viewport.rbs +33 -0
- data/sig/ratatui_ruby/terminal_lifecycle.rbs +39 -0
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +22 -0
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +37 -0
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +77 -0
- data/sig/ratatui_ruby/test_helper/terminal.rbs +20 -0
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +32 -0
- data/sig/ratatui_ruby/test_helper.rbs +18 -0
- data/sig/ratatui_ruby/text/line.rbs +27 -0
- data/sig/ratatui_ruby/text/span.rbs +23 -0
- data/sig/ratatui_ruby/text.rbs +12 -0
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +16 -0
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +38 -0
- data/sig/ratatui_ruby/tui/core.rbs +23 -0
- data/sig/ratatui_ruby/tui/layout_factories.rbs +39 -0
- data/sig/ratatui_ruby/tui/state_factories.rbs +23 -0
- data/sig/ratatui_ruby/tui/style_factories.rbs +18 -0
- data/sig/ratatui_ruby/tui/text_factories.rbs +23 -0
- data/sig/ratatui_ruby/tui/widget_factories.rbs +138 -0
- data/sig/ratatui_ruby/tui.rbs +25 -0
- data/sig/ratatui_ruby/version.rbs +12 -0
- data/sig/ratatui_ruby/widgets/bar_chart.rbs +95 -0
- data/sig/ratatui_ruby/widgets/block.rbs +51 -0
- data/sig/ratatui_ruby/widgets/calendar.rbs +45 -0
- data/sig/ratatui_ruby/widgets/canvas.rbs +95 -0
- data/sig/ratatui_ruby/widgets/chart.rbs +91 -0
- data/sig/ratatui_ruby/widgets/coerceable_widget.rbs +26 -0
- data/sig/ratatui_ruby/widgets/gauge.rbs +44 -0
- data/sig/ratatui_ruby/widgets/line_gauge.rbs +48 -0
- data/sig/ratatui_ruby/widgets/list.rbs +63 -0
- data/sig/ratatui_ruby/widgets/misc.rbs +158 -0
- data/sig/ratatui_ruby/widgets/paragraph.rbs +45 -0
- data/sig/ratatui_ruby/widgets/row.rbs +43 -0
- data/sig/ratatui_ruby/widgets/scrollbar.rbs +53 -0
- data/sig/ratatui_ruby/widgets/shape/label.rbs +37 -0
- data/sig/ratatui_ruby/widgets/sparkline.rbs +45 -0
- data/sig/ratatui_ruby/widgets/table.rbs +78 -0
- data/sig/ratatui_ruby/widgets/tabs.rbs +44 -0
- data/sig/ratatui_ruby/widgets.rbs +16 -0
- data/vendor/goodcop/base.yml +1047 -0
- metadata +729 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
require "timeout"
|
|
9
|
+
require "minitest/mock"
|
|
10
|
+
|
|
11
|
+
module RatatuiRuby
|
|
12
|
+
module TestHelper
|
|
13
|
+
##
|
|
14
|
+
# Terminal setup and buffer inspection for TUI tests.
|
|
15
|
+
#
|
|
16
|
+
# Testing TUIs against a real terminal is slow, flaky, and hard to automate.
|
|
17
|
+
# Initializing, cleaning up, and inspecting terminal state by hand is tedious.
|
|
18
|
+
#
|
|
19
|
+
# This mixin wraps a headless test terminal. It handles setup, teardown,
|
|
20
|
+
# and provides methods to query buffer content, cursor position, and cell styles.
|
|
21
|
+
#
|
|
22
|
+
# Use it to write fast, deterministic tests for your TUI applications.
|
|
23
|
+
#
|
|
24
|
+
# === Example
|
|
25
|
+
#
|
|
26
|
+
#--
|
|
27
|
+
# SPDX-SnippetBegin
|
|
28
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
29
|
+
# SPDX-License-Identifier: MIT-0
|
|
30
|
+
#++
|
|
31
|
+
# class MyTest < Minitest::Test
|
|
32
|
+
# include RatatuiRuby::TestHelper
|
|
33
|
+
#
|
|
34
|
+
# def test_rendering
|
|
35
|
+
# with_test_terminal(80, 24) do
|
|
36
|
+
# MyApp.new.run_once
|
|
37
|
+
# assert_includes buffer_content.join, "Hello"
|
|
38
|
+
# end
|
|
39
|
+
# end
|
|
40
|
+
# end
|
|
41
|
+
#--
|
|
42
|
+
# SPDX-SnippetEnd
|
|
43
|
+
#++
|
|
44
|
+
module Terminal
|
|
45
|
+
##
|
|
46
|
+
# Initializes a test terminal context with specified dimensions.
|
|
47
|
+
# Restores the original terminal state after the block executes.
|
|
48
|
+
#
|
|
49
|
+
# [width] Integer width of the test terminal (default: 80).
|
|
50
|
+
# [height] Integer height of the test terminal (default: 24).
|
|
51
|
+
# [timeout] Integer maximum execution time in seconds (default: 2). Pass <tt>nil</tt> to disable.
|
|
52
|
+
#
|
|
53
|
+
# === Example
|
|
54
|
+
#
|
|
55
|
+
#--
|
|
56
|
+
# SPDX-SnippetBegin
|
|
57
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
58
|
+
# SPDX-License-Identifier: MIT-0
|
|
59
|
+
#++
|
|
60
|
+
# with_test_terminal(120, 40) do
|
|
61
|
+
# # render and test your app
|
|
62
|
+
# end
|
|
63
|
+
#--
|
|
64
|
+
# SPDX-SnippetEnd
|
|
65
|
+
#++
|
|
66
|
+
def with_test_terminal(width = 80, height = 24, **opts)
|
|
67
|
+
# Defensive cleanup: reset any stale session state from previous test failures
|
|
68
|
+
RatatuiRuby.instance_variable_set(:@tui_session_active, false)
|
|
69
|
+
|
|
70
|
+
# Extract and resolve viewport
|
|
71
|
+
viewport_param = opts[:viewport]
|
|
72
|
+
viewport = case viewport_param
|
|
73
|
+
when nil then RatatuiRuby::Terminal::Viewport.fullscreen
|
|
74
|
+
when RatatuiRuby::Terminal::Viewport then viewport_param
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
RatatuiRuby.init_test_terminal(width, height, viewport.type.to_s, viewport.height)
|
|
78
|
+
# Flush any lingering events from previous tests
|
|
79
|
+
while (event = RatatuiRuby.poll_event) && !event.none?; end
|
|
80
|
+
|
|
81
|
+
RatatuiRuby.stub :init_terminal, nil do
|
|
82
|
+
RatatuiRuby.stub :restore_terminal, nil do
|
|
83
|
+
@_ratatui_test_terminal_active = true
|
|
84
|
+
timeout = opts.fetch(:timeout, 2)
|
|
85
|
+
if timeout
|
|
86
|
+
Timeout.timeout(timeout) do
|
|
87
|
+
yield
|
|
88
|
+
end
|
|
89
|
+
else
|
|
90
|
+
yield
|
|
91
|
+
end
|
|
92
|
+
ensure
|
|
93
|
+
@_ratatui_test_terminal_active = false
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
ensure
|
|
97
|
+
RatatuiRuby.restore_terminal
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
##
|
|
101
|
+
# Current content of the terminal buffer as an array of strings.
|
|
102
|
+
# Each string represents one row.
|
|
103
|
+
#
|
|
104
|
+
# === Example
|
|
105
|
+
#
|
|
106
|
+
#--
|
|
107
|
+
# SPDX-SnippetBegin
|
|
108
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
109
|
+
# SPDX-License-Identifier: MIT-0
|
|
110
|
+
#++
|
|
111
|
+
# buffer_content
|
|
112
|
+
# # => ["Row 1 text", "Row 2 text", ...]
|
|
113
|
+
#--
|
|
114
|
+
# SPDX-SnippetEnd
|
|
115
|
+
#++
|
|
116
|
+
def buffer_content
|
|
117
|
+
RatatuiRuby.get_buffer_content.split("\n")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
##
|
|
121
|
+
# Current cursor position as a hash with <tt>:x</tt> and <tt>:y</tt> keys.
|
|
122
|
+
#
|
|
123
|
+
# === Example
|
|
124
|
+
#
|
|
125
|
+
#--
|
|
126
|
+
# SPDX-SnippetBegin
|
|
127
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
128
|
+
# SPDX-License-Identifier: MIT-0
|
|
129
|
+
#++
|
|
130
|
+
# cursor_position
|
|
131
|
+
# # => { x: 0, y: 0 }
|
|
132
|
+
#--
|
|
133
|
+
# SPDX-SnippetEnd
|
|
134
|
+
#++
|
|
135
|
+
def cursor_position
|
|
136
|
+
x, y = RatatuiRuby.get_cursor_position
|
|
137
|
+
{ x:, y: }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
##
|
|
141
|
+
# Cell attributes at the given coordinates.
|
|
142
|
+
#
|
|
143
|
+
# Returns a hash with <tt>"symbol"</tt>, <tt>"fg"</tt>, and <tt>"bg"</tt> keys.
|
|
144
|
+
#
|
|
145
|
+
# [x] Integer column position (0-indexed).
|
|
146
|
+
# [y] Integer row position (0-indexed).
|
|
147
|
+
#
|
|
148
|
+
# === Example
|
|
149
|
+
#
|
|
150
|
+
#--
|
|
151
|
+
# SPDX-SnippetBegin
|
|
152
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
153
|
+
# SPDX-License-Identifier: MIT-0
|
|
154
|
+
#++
|
|
155
|
+
# get_cell(0, 0)
|
|
156
|
+
# # => { "symbol" => "H", "fg" => :red, "bg" => nil }
|
|
157
|
+
#--
|
|
158
|
+
# SPDX-SnippetEnd
|
|
159
|
+
#++
|
|
160
|
+
def get_cell(x, y)
|
|
161
|
+
RatatuiRuby.get_cell_at(x, y)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
##
|
|
165
|
+
# Prints the current buffer to STDOUT with full ANSI colors.
|
|
166
|
+
# Useful for debugging test failures.
|
|
167
|
+
#
|
|
168
|
+
# === Example
|
|
169
|
+
#
|
|
170
|
+
#--
|
|
171
|
+
# SPDX-SnippetBegin
|
|
172
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
173
|
+
# SPDX-License-Identifier: MIT-0
|
|
174
|
+
#++
|
|
175
|
+
# with_test_terminal do
|
|
176
|
+
# MyApp.new.render
|
|
177
|
+
# print_buffer # see exactly what would display
|
|
178
|
+
# end
|
|
179
|
+
#--
|
|
180
|
+
# SPDX-SnippetEnd
|
|
181
|
+
#++
|
|
182
|
+
def print_buffer
|
|
183
|
+
puts _render_buffer_with_ansi
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
module RatatuiRuby
|
|
9
|
+
module TestHelper
|
|
10
|
+
##
|
|
11
|
+
# Test doubles for view testing.
|
|
12
|
+
#
|
|
13
|
+
# View tests verify widget rendering without a real terminal. Real frames draw
|
|
14
|
+
# to the screen. Real rects come from terminal dimensions. Mocking both by hand
|
|
15
|
+
# is tedious.
|
|
16
|
+
#
|
|
17
|
+
# This mixin provides <tt>MockFrame</tt> to capture rendered widgets and
|
|
18
|
+
# <tt>StubRect</tt> to supply fixed dimensions.
|
|
19
|
+
#
|
|
20
|
+
# Use them to test view logic in isolation.
|
|
21
|
+
#
|
|
22
|
+
# === Example
|
|
23
|
+
#
|
|
24
|
+
#--
|
|
25
|
+
# SPDX-SnippetBegin
|
|
26
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
27
|
+
# SPDX-License-Identifier: MIT-0
|
|
28
|
+
#++
|
|
29
|
+
# frame = MockFrame.new
|
|
30
|
+
# area = StubRect.new(width: 60, height: 20)
|
|
31
|
+
# MyView.new.call(state, tui, frame, area)
|
|
32
|
+
#
|
|
33
|
+
# widget = frame.rendered_widgets.first[:widget]
|
|
34
|
+
# assert_equal "Dashboard", widget.block.title
|
|
35
|
+
#--
|
|
36
|
+
# SPDX-SnippetEnd
|
|
37
|
+
#++
|
|
38
|
+
module TestDoubles
|
|
39
|
+
##
|
|
40
|
+
# Mock frame for view tests.
|
|
41
|
+
#
|
|
42
|
+
# Captures widgets passed to <tt>render_widget</tt> for later inspection.
|
|
43
|
+
#
|
|
44
|
+
# === Example
|
|
45
|
+
#
|
|
46
|
+
#--
|
|
47
|
+
# SPDX-SnippetBegin
|
|
48
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
49
|
+
# SPDX-License-Identifier: MIT-0
|
|
50
|
+
#++
|
|
51
|
+
# frame = MockFrame.new
|
|
52
|
+
# View::Log.new.call(state, tui, frame, area)
|
|
53
|
+
# widget = frame.rendered_widgets.first[:widget]
|
|
54
|
+
# assert_equal "Event Log", widget.block.title
|
|
55
|
+
#--
|
|
56
|
+
# SPDX-SnippetEnd
|
|
57
|
+
#++
|
|
58
|
+
MockFrame = Data.define(:rendered_widgets) do
|
|
59
|
+
def initialize(rendered_widgets: [])
|
|
60
|
+
super
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def render_widget(widget, area)
|
|
64
|
+
rendered_widgets << { widget:, area: }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# Stub rect with fixed dimensions.
|
|
70
|
+
#
|
|
71
|
+
# [x] Integer left edge (default: 0).
|
|
72
|
+
# [y] Integer top edge (default: 0).
|
|
73
|
+
# [width] Integer width in cells (default: 80).
|
|
74
|
+
# [height] Integer height in cells (default: 24).
|
|
75
|
+
#
|
|
76
|
+
# === Example
|
|
77
|
+
#
|
|
78
|
+
# area = StubRect.new(width: 60, height: 20)
|
|
79
|
+
StubRect = Data.define(:x, :y, :width, :height) do
|
|
80
|
+
def initialize(x: 0, y: 0, width: 80, height: 24)
|
|
81
|
+
super
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
require "fileutils"
|
|
9
|
+
require_relative "test_helper/terminal"
|
|
10
|
+
require_relative "test_helper/snapshot"
|
|
11
|
+
require_relative "test_helper/event_injection"
|
|
12
|
+
require_relative "test_helper/style_assertions"
|
|
13
|
+
require_relative "test_helper/test_doubles"
|
|
14
|
+
require_relative "test_helper/global_state"
|
|
15
|
+
require_relative "test_helper/subprocess_timeout"
|
|
16
|
+
|
|
17
|
+
module RatatuiRuby
|
|
18
|
+
##
|
|
19
|
+
# Helpers for testing RatatuiRuby applications.
|
|
20
|
+
#
|
|
21
|
+
# Writing TUI tests by hand is tedious. You need a headless terminal, event
|
|
22
|
+
# injection, snapshot comparisons, and style assertions. Wiring all that up
|
|
23
|
+
# yourself is error-prone.
|
|
24
|
+
#
|
|
25
|
+
# This module bundles everything you need. Include it in your test class and
|
|
26
|
+
# start writing tests immediately.
|
|
27
|
+
#
|
|
28
|
+
# == Included Mixins
|
|
29
|
+
#
|
|
30
|
+
# [Terminal] Sets up a headless terminal and queries its buffer.
|
|
31
|
+
# [Snapshot] Compares the screen against stored reference files.
|
|
32
|
+
# [EventInjection] Simulates keypresses, mouse clicks, and resize events.
|
|
33
|
+
# [StyleAssertions] Checks foreground color, background color, and text modifiers.
|
|
34
|
+
# [TestDoubles] Provides mocks and stubs for testing views in isolation.
|
|
35
|
+
# [GlobalState] Provides with_argv and with_env helpers for testing global state access.
|
|
36
|
+
#
|
|
37
|
+
# == Example
|
|
38
|
+
#
|
|
39
|
+
#--
|
|
40
|
+
# SPDX-SnippetBegin
|
|
41
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
42
|
+
# SPDX-License-Identifier: MIT-0
|
|
43
|
+
#++
|
|
44
|
+
# require "ratatui_ruby/test_helper"
|
|
45
|
+
#
|
|
46
|
+
# class TestMyApp < Minitest::Test
|
|
47
|
+
# include RatatuiRuby::TestHelper
|
|
48
|
+
#
|
|
49
|
+
# def test_initial_render
|
|
50
|
+
# with_test_terminal(80, 24) do
|
|
51
|
+
# MyApp.new.run_once
|
|
52
|
+
# assert_snapshots("initial")
|
|
53
|
+
# end
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# def test_themes
|
|
57
|
+
# with_test_terminal do
|
|
58
|
+
# app = ThemeDemo.new
|
|
59
|
+
# app.run_once
|
|
60
|
+
# assert_rich_snapshot("default_theme")
|
|
61
|
+
#
|
|
62
|
+
# inject_key("t", modifiers: [:ctrl])
|
|
63
|
+
# app.run_once
|
|
64
|
+
# assert_rich_snapshot("dark_theme")
|
|
65
|
+
#
|
|
66
|
+
# inject_key("t", modifiers: [:ctrl])
|
|
67
|
+
# app.run_once
|
|
68
|
+
# assert_rich_snapshot("high_contrast_theme")
|
|
69
|
+
# end
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
# def test_highlighter_applies_selection_style
|
|
73
|
+
# with_test_terminal(40, 5) do
|
|
74
|
+
# RatatuiRuby.draw do |frame|
|
|
75
|
+
# highlighter = MyApp::UI::Highlighter.new(:yellow)
|
|
76
|
+
# highlighter.render_at(frame, 0, 2, "Selected Item")
|
|
77
|
+
# end
|
|
78
|
+
#
|
|
79
|
+
# assert_fg_color(:yellow, 0, 2)
|
|
80
|
+
# assert_bold(0, 2)
|
|
81
|
+
# end
|
|
82
|
+
# end
|
|
83
|
+
#
|
|
84
|
+
# def test_view_in_isolation
|
|
85
|
+
# frame = MockFrame.new
|
|
86
|
+
# area = StubRect.new(width: 60, height: 20)
|
|
87
|
+
#
|
|
88
|
+
# MyView.new.call(state, tui, frame, area)
|
|
89
|
+
#
|
|
90
|
+
# widget = frame.rendered_widgets.first[:widget]
|
|
91
|
+
# assert_equal "Dashboard", widget.block.title
|
|
92
|
+
# end
|
|
93
|
+
# end
|
|
94
|
+
#--
|
|
95
|
+
# SPDX-SnippetEnd
|
|
96
|
+
#++
|
|
97
|
+
module TestHelper
|
|
98
|
+
##
|
|
99
|
+
# Auto-enables debug mode when TestHelper is included.
|
|
100
|
+
#
|
|
101
|
+
# This ensures Rust backtraces are available in tests.
|
|
102
|
+
# Skips remote debugging since tests don't need it.
|
|
103
|
+
def self.included(base)
|
|
104
|
+
RatatuiRuby::Debug.enable!(source: :test)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
include Terminal
|
|
108
|
+
include Snapshot
|
|
109
|
+
include EventInjection
|
|
110
|
+
include StyleAssertions
|
|
111
|
+
include TestDoubles
|
|
112
|
+
include GlobalState
|
|
113
|
+
include SubprocessTimeout
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
module RatatuiRuby
|
|
9
|
+
module Text
|
|
10
|
+
# A sequence of styled spans.
|
|
11
|
+
#
|
|
12
|
+
# Words form sentences. Spans form lines.
|
|
13
|
+
#
|
|
14
|
+
# This class composes multiple {Span} objects into a single horizontal row of text.
|
|
15
|
+
# It handles the layout of rich text fragments within the flow of a paragraph.
|
|
16
|
+
#
|
|
17
|
+
# Use it to build multi-colored headers, status messages, or log entries.
|
|
18
|
+
#
|
|
19
|
+
# === Examples
|
|
20
|
+
#
|
|
21
|
+
#--
|
|
22
|
+
# SPDX-SnippetBegin
|
|
23
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long
|
|
24
|
+
# SPDX-License-Identifier: MIT-0
|
|
25
|
+
#++
|
|
26
|
+
# Text::Line.new(
|
|
27
|
+
# spans: [
|
|
28
|
+
# Text::Span.styled("User: ", Style.new(modifiers: [:bold])),
|
|
29
|
+
# Text::Span.styled("kerrick", Style.new(fg: :blue))
|
|
30
|
+
# ]
|
|
31
|
+
# )
|
|
32
|
+
#--
|
|
33
|
+
# SPDX-SnippetEnd
|
|
34
|
+
#++
|
|
35
|
+
class Line < Data.define(:spans, :alignment, :style)
|
|
36
|
+
##
|
|
37
|
+
# :attr_reader: spans
|
|
38
|
+
# Array of Span objects.
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
# :attr_reader: alignment
|
|
42
|
+
# Alignment within the container.
|
|
43
|
+
#
|
|
44
|
+
# <tt>:left</tt>, <tt>:center</tt>, or <tt>:right</tt>.
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# :attr_reader: style
|
|
48
|
+
# Line-level style applied to all spans.
|
|
49
|
+
#
|
|
50
|
+
# A Style object that sets colors/modifiers for the entire line.
|
|
51
|
+
|
|
52
|
+
# Creates a new Line.
|
|
53
|
+
#
|
|
54
|
+
# [spans] Array of Span objects (or Strings).
|
|
55
|
+
# [alignment] Symbol (optional).
|
|
56
|
+
# [style] Style object (optional).
|
|
57
|
+
def initialize(spans: [], alignment: nil, style: nil)
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Creates a simple line from a string.
|
|
62
|
+
#
|
|
63
|
+
# Text::Line.from_string("Hello")
|
|
64
|
+
def self.from_string(content, alignment: nil)
|
|
65
|
+
new(spans: [Span.new(content:, style: nil)], alignment:)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Calculates the display width of this line in terminal cells.
|
|
69
|
+
#
|
|
70
|
+
# Sums the widths of all span contents using the same unicode-aware
|
|
71
|
+
# algorithm as Text.width. Useful for layout calculations.
|
|
72
|
+
#
|
|
73
|
+
# === Examples
|
|
74
|
+
#
|
|
75
|
+
#--
|
|
76
|
+
# SPDX-SnippetBegin
|
|
77
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
78
|
+
# SPDX-License-Identifier: MIT-0
|
|
79
|
+
#++
|
|
80
|
+
# line = Text::Line.new(spans: [
|
|
81
|
+
# Text::Span.new(content: "Hello "),
|
|
82
|
+
# Text::Span.new(content: "世界")
|
|
83
|
+
# ])
|
|
84
|
+
# line.width # => 10 (6 ASCII + 4 CJK)
|
|
85
|
+
#
|
|
86
|
+
#--
|
|
87
|
+
# SPDX-SnippetEnd
|
|
88
|
+
#++
|
|
89
|
+
# Returns: Integer (number of terminal cells).
|
|
90
|
+
def width
|
|
91
|
+
RatatuiRuby::Text.width(spans.map { |s| s.content.to_s }.join)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Left-aligns this line of text.
|
|
95
|
+
#
|
|
96
|
+
# Convenience shortcut for <tt>alignment: :left</tt>. Setting the alignment of a Line
|
|
97
|
+
# overrides the alignment of its parent Text or Widget.
|
|
98
|
+
#
|
|
99
|
+
# === Example
|
|
100
|
+
#
|
|
101
|
+
#--
|
|
102
|
+
# SPDX-SnippetBegin
|
|
103
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
104
|
+
# SPDX-License-Identifier: MIT-0
|
|
105
|
+
#++
|
|
106
|
+
# line = Text::Line.new(spans: [Text::Span.new(content: "Hello")])
|
|
107
|
+
# aligned = line.left_aligned
|
|
108
|
+
# aligned.alignment # => :left
|
|
109
|
+
#
|
|
110
|
+
#--
|
|
111
|
+
# SPDX-SnippetEnd
|
|
112
|
+
#++
|
|
113
|
+
# Returns: Line.
|
|
114
|
+
def left_aligned
|
|
115
|
+
with(alignment: :left)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Center-aligns this line of text.
|
|
119
|
+
#
|
|
120
|
+
# Convenience shortcut for <tt>alignment: :center</tt>. Setting the alignment of a Line
|
|
121
|
+
# overrides the alignment of its parent Text or Widget.
|
|
122
|
+
#
|
|
123
|
+
# === Example
|
|
124
|
+
#
|
|
125
|
+
#--
|
|
126
|
+
# SPDX-SnippetBegin
|
|
127
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
128
|
+
# SPDX-License-Identifier: MIT-0
|
|
129
|
+
#++
|
|
130
|
+
# line = Text::Line.new(spans: [Text::Span.new(content: "Hello")])
|
|
131
|
+
# centered = line.centered
|
|
132
|
+
# centered.alignment # => :center
|
|
133
|
+
#
|
|
134
|
+
#--
|
|
135
|
+
# SPDX-SnippetEnd
|
|
136
|
+
#++
|
|
137
|
+
# Returns: Line.
|
|
138
|
+
def centered
|
|
139
|
+
with(alignment: :center)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Right-aligns this line of text.
|
|
143
|
+
#
|
|
144
|
+
# Convenience shortcut for <tt>alignment: :right</tt>. Setting the alignment of a Line
|
|
145
|
+
# overrides the alignment of its parent Text or Widget.
|
|
146
|
+
#
|
|
147
|
+
# === Example
|
|
148
|
+
#
|
|
149
|
+
#--
|
|
150
|
+
# SPDX-SnippetBegin
|
|
151
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
152
|
+
# SPDX-License-Identifier: MIT-0
|
|
153
|
+
#++
|
|
154
|
+
# line = Text::Line.new(spans: [Text::Span.new(content: "Hello")])
|
|
155
|
+
# aligned = line.right_aligned
|
|
156
|
+
# aligned.alignment # => :right
|
|
157
|
+
#
|
|
158
|
+
#--
|
|
159
|
+
# SPDX-SnippetEnd
|
|
160
|
+
#++
|
|
161
|
+
# Returns: Line.
|
|
162
|
+
def right_aligned
|
|
163
|
+
with(alignment: :right)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Adds a span to the line.
|
|
167
|
+
#
|
|
168
|
+
# Since Line is immutable (a Data subclass), this returns a new Line with the span appended.
|
|
169
|
+
# The original line remains unchanged.
|
|
170
|
+
#
|
|
171
|
+
# === Example
|
|
172
|
+
#
|
|
173
|
+
#--
|
|
174
|
+
# SPDX-SnippetBegin
|
|
175
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
176
|
+
# SPDX-License-Identifier: MIT-0
|
|
177
|
+
#++
|
|
178
|
+
# line = Text::Line.new(spans: [Text::Span.new(content: "Hello, ")])
|
|
179
|
+
# extended = line.push_span(Text::Span.new(content: "world!"))
|
|
180
|
+
# extended.spans.size # => 2
|
|
181
|
+
# line.spans.size # => 1 (original unchanged)
|
|
182
|
+
#
|
|
183
|
+
#--
|
|
184
|
+
# SPDX-SnippetEnd
|
|
185
|
+
#++
|
|
186
|
+
# [span] Span to append.
|
|
187
|
+
#
|
|
188
|
+
# Returns: Line.
|
|
189
|
+
def push_span(span)
|
|
190
|
+
with(spans: spans + [span])
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Patches the style of this line, adding modifiers from the given style.
|
|
194
|
+
#
|
|
195
|
+
# Applies <tt>patch_style</tt> to each span in the line. Use this when you want to layer
|
|
196
|
+
# styles on all spans without replacing their existing styles.
|
|
197
|
+
#
|
|
198
|
+
# === Example
|
|
199
|
+
#
|
|
200
|
+
#--
|
|
201
|
+
# SPDX-SnippetBegin
|
|
202
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
203
|
+
# SPDX-License-Identifier: MIT-0
|
|
204
|
+
#++
|
|
205
|
+
# line = Text::Line.new(spans: [Text::Span.new(content: "Hello")])
|
|
206
|
+
# styled = line.patch_style(Style::Style.new(fg: :red))
|
|
207
|
+
# styled.spans.first.style.fg # => :red
|
|
208
|
+
#
|
|
209
|
+
#--
|
|
210
|
+
# SPDX-SnippetEnd
|
|
211
|
+
#++
|
|
212
|
+
# [patch] Style::Style to merge onto each span.
|
|
213
|
+
#
|
|
214
|
+
# Returns: Line.
|
|
215
|
+
def patch_style(patch)
|
|
216
|
+
with(spans: spans.map { |s| s.patch_style(patch) })
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Resets the style of this line.
|
|
220
|
+
#
|
|
221
|
+
# Applies <tt>reset_style</tt> to each span in the line, clearing all styling.
|
|
222
|
+
#
|
|
223
|
+
# === Example
|
|
224
|
+
#
|
|
225
|
+
#--
|
|
226
|
+
# SPDX-SnippetBegin
|
|
227
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
228
|
+
# SPDX-License-Identifier: MIT-0
|
|
229
|
+
#++
|
|
230
|
+
# line = Text::Line.new(spans: [
|
|
231
|
+
# Text::Span.new(content: "styled", style: Style::Style.new(fg: :red))
|
|
232
|
+
# ])
|
|
233
|
+
# reset = line.reset_style
|
|
234
|
+
# reset.spans.first.style # => nil
|
|
235
|
+
#
|
|
236
|
+
#--
|
|
237
|
+
# SPDX-SnippetEnd
|
|
238
|
+
#++
|
|
239
|
+
# Returns: Line.
|
|
240
|
+
def reset_style
|
|
241
|
+
with(spans: spans.map(&:reset_style))
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|