ratatui_ruby 0.9.1 → 0.10.1
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 +4 -4
- data/.builds/ruby-3.2.yml +1 -1
- data/.builds/ruby-3.3.yml +1 -1
- data/.builds/ruby-3.4.yml +1 -1
- data/.builds/ruby-4.0.0.yml +1 -1
- data/AGENTS.md +2 -1
- data/CHANGELOG.md +113 -0
- data/README.md +17 -0
- data/REUSE.toml +5 -0
- data/Rakefile +1 -1
- data/Steepfile +49 -0
- data/doc/concepts/debugging.md +401 -0
- data/doc/getting_started/quickstart.md +8 -3
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_color_picker.png +0 -0
- data/doc/images/app_debugging_showcase.gif +0 -0
- data/doc/images/app_debugging_showcase.png +0 -0
- data/doc/images/app_login_form.png +0 -0
- data/doc/images/app_stateful_interaction.png +0 -0
- data/doc/images/verify_quickstart_dsl.png +0 -0
- data/doc/images/verify_quickstart_layout.png +0 -0
- data/doc/images/verify_quickstart_lifecycle.png +0 -0
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_barchart.png +0 -0
- data/doc/images/widget_block.png +0 -0
- data/doc/images/widget_box.png +0 -0
- data/doc/images/widget_calendar.png +0 -0
- data/doc/images/widget_canvas.png +0 -0
- data/doc/images/widget_cell.png +0 -0
- data/doc/images/widget_center.png +0 -0
- data/doc/images/widget_chart.png +0 -0
- data/doc/images/widget_gauge.png +0 -0
- data/doc/images/widget_layout_split.png +0 -0
- data/doc/images/widget_line_gauge.png +0 -0
- data/doc/images/widget_list.png +0 -0
- data/doc/images/widget_map.png +0 -0
- data/doc/images/widget_overlay.png +0 -0
- data/doc/images/widget_popup.png +0 -0
- data/doc/images/widget_ratatui_logo.png +0 -0
- data/doc/images/widget_ratatui_mascot.png +0 -0
- data/doc/images/widget_rect.png +0 -0
- data/doc/images/widget_render.png +0 -0
- data/doc/images/widget_rich_text.png +0 -0
- data/doc/images/widget_scroll_text.png +0 -0
- data/doc/images/widget_scrollbar.png +0 -0
- data/doc/images/widget_sparkline.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/images/widget_table.png +0 -0
- data/doc/images/widget_tabs.png +0 -0
- data/doc/images/widget_text_width.png +0 -0
- data/doc/troubleshooting/async.md +4 -0
- data/examples/app_debugging_showcase/README.md +119 -0
- data/examples/app_debugging_showcase/app.rb +318 -0
- data/examples/widget_canvas/app.rb +19 -14
- data/examples/widget_gauge/app.rb +18 -3
- data/examples/widget_layout_split/app.rb +10 -4
- data/examples/widget_list/app.rb +22 -6
- data/examples/widget_rect/app.rb +7 -6
- data/examples/widget_rich_text/app.rb +62 -37
- data/examples/widget_style_colors/app.rb +26 -47
- data/examples/widget_table/app.rb +28 -5
- data/examples/widget_text_width/app.rb +6 -4
- data/ext/ratatui_ruby/Cargo.lock +48 -1
- data/ext/ratatui_ruby/Cargo.toml +6 -2
- 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 +15 -14
- data/ext/ratatui_ruby/src/lib.rs +56 -0
- data/ext/ratatui_ruby/src/rendering.rs +3 -1
- data/ext/ratatui_ruby/src/style.rs +48 -21
- data/ext/ratatui_ruby/src/terminal.rs +40 -9
- data/ext/ratatui_ruby/src/text.rs +21 -9
- data/ext/ratatui_ruby/src/widgets/chart.rs +2 -1
- data/ext/ratatui_ruby/src/widgets/layout.rs +90 -2
- data/ext/ratatui_ruby/src/widgets/list.rs +6 -5
- data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
- data/ext/ratatui_ruby/src/widgets/table.rs +7 -6
- data/ext/ratatui_ruby/src/widgets/table_state.rs +55 -0
- data/ext/ratatui_ruby/src/widgets/tabs.rs +3 -2
- data/lib/ratatui_ruby/buffer/cell.rb +25 -15
- data/lib/ratatui_ruby/buffer.rb +134 -2
- data/lib/ratatui_ruby/cell.rb +13 -5
- data/lib/ratatui_ruby/debug.rb +215 -0
- data/lib/ratatui_ruby/event/key.rb +3 -2
- data/lib/ratatui_ruby/event.rb +1 -1
- data/lib/ratatui_ruby/layout/constraint.rb +49 -0
- data/lib/ratatui_ruby/layout/layout.rb +119 -13
- data/lib/ratatui_ruby/layout/position.rb +55 -0
- data/lib/ratatui_ruby/layout/rect.rb +188 -0
- data/lib/ratatui_ruby/layout/size.rb +55 -0
- data/lib/ratatui_ruby/layout.rb +4 -0
- data/lib/ratatui_ruby/style/color.rb +149 -0
- data/lib/ratatui_ruby/style/style.rb +51 -4
- data/lib/ratatui_ruby/style.rb +2 -0
- data/lib/ratatui_ruby/symbols.rb +435 -0
- data/lib/ratatui_ruby/synthetic_events.rb +1 -1
- data/lib/ratatui_ruby/table_state.rb +51 -0
- data/lib/ratatui_ruby/terminal_lifecycle.rb +2 -1
- data/lib/ratatui_ruby/test_helper/event_injection.rb +6 -1
- data/lib/ratatui_ruby/test_helper.rb +9 -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/canvas_factories.rb +103 -0
- data/lib/ratatui_ruby/tui/core.rb +13 -2
- data/lib/ratatui_ruby/tui/layout_factories.rb +50 -3
- data/lib/ratatui_ruby/tui/state_factories.rb +42 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +40 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +135 -60
- data/lib/ratatui_ruby/tui.rb +22 -1
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +30 -20
- data/lib/ratatui_ruby/widgets/block.rb +14 -6
- data/lib/ratatui_ruby/widgets/calendar.rb +2 -0
- data/lib/ratatui_ruby/widgets/canvas.rb +56 -0
- data/lib/ratatui_ruby/widgets/cell.rb +2 -0
- data/lib/ratatui_ruby/widgets/center.rb +2 -0
- data/lib/ratatui_ruby/widgets/chart.rb +6 -0
- data/lib/ratatui_ruby/widgets/clear.rb +2 -0
- data/lib/ratatui_ruby/widgets/coerceable_widget.rb +77 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +2 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +61 -3
- data/lib/ratatui_ruby/widgets/line_gauge.rb +66 -4
- data/lib/ratatui_ruby/widgets/list.rb +87 -3
- data/lib/ratatui_ruby/widgets/list_item.rb +2 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +2 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +4 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +2 -0
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +2 -0
- data/lib/ratatui_ruby/widgets/row.rb +45 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +2 -0
- data/lib/ratatui_ruby/widgets/shape/label.rb +2 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +21 -13
- data/lib/ratatui_ruby/widgets/table.rb +13 -3
- data/lib/ratatui_ruby/widgets/tabs.rb +6 -4
- data/lib/ratatui_ruby/widgets.rb +1 -0
- data/lib/ratatui_ruby.rb +42 -11
- data/sig/examples/app_all_events/model/app_model.rbs +23 -0
- data/sig/examples/app_all_events/model/event_entry.rbs +15 -8
- data/sig/examples/app_all_events/model/timestamp.rbs +1 -1
- data/sig/examples/app_all_events/view.rbs +1 -1
- data/sig/examples/app_stateful_interaction/app.rbs +5 -5
- data/sig/examples/widget_block_demo/app.rbs +6 -6
- data/sig/manifest.yaml +5 -0
- data/sig/patches/data.rbs +26 -0
- data/sig/patches/debugger__.rbs +8 -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 +68 -8
- data/sig/ratatui_ruby/frame.rbs +4 -4
- data/sig/ratatui_ruby/interfaces.rbs +25 -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/output_guard.rbs +23 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +84 -5
- data/sig/ratatui_ruby/rect.rbs +17 -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 +21 -0
- data/sig/ratatui_ruby/table_state.rbs +6 -0
- data/sig/ratatui_ruby/terminal_lifecycle.rbs +31 -0
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +2 -2
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +22 -3
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +8 -1
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +7 -3
- 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 +1 -1
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +23 -5
- data/sig/ratatui_ruby/tui/core.rbs +2 -2
- data/sig/ratatui_ruby/tui/layout_factories.rbs +16 -2
- data/sig/ratatui_ruby/tui/state_factories.rbs +8 -3
- data/sig/ratatui_ruby/tui/style_factories.rbs +3 -1
- data/sig/ratatui_ruby/tui/text_factories.rbs +7 -4
- data/sig/ratatui_ruby/tui/widget_factories.rbs +123 -30
- 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/{schema/list_item.rbs → widgets.rbs} +4 -4
- data/tasks/steep.rake +11 -0
- metadata +80 -63
- data/doc/contributors/v1.0.0_blockers.md +0 -870
- data/doc/troubleshooting/debugging.md +0 -101
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +0 -47
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +0 -25
- data/lib/ratatui_ruby/schema/bar_chart.rb +0 -287
- data/lib/ratatui_ruby/schema/block.rb +0 -198
- data/lib/ratatui_ruby/schema/calendar.rb +0 -84
- data/lib/ratatui_ruby/schema/canvas.rb +0 -239
- data/lib/ratatui_ruby/schema/center.rb +0 -67
- data/lib/ratatui_ruby/schema/chart.rb +0 -159
- data/lib/ratatui_ruby/schema/clear.rb +0 -62
- data/lib/ratatui_ruby/schema/constraint.rb +0 -151
- data/lib/ratatui_ruby/schema/cursor.rb +0 -50
- data/lib/ratatui_ruby/schema/gauge.rb +0 -72
- data/lib/ratatui_ruby/schema/layout.rb +0 -122
- data/lib/ratatui_ruby/schema/line_gauge.rb +0 -80
- data/lib/ratatui_ruby/schema/list.rb +0 -135
- data/lib/ratatui_ruby/schema/list_item.rb +0 -51
- data/lib/ratatui_ruby/schema/overlay.rb +0 -51
- data/lib/ratatui_ruby/schema/paragraph.rb +0 -107
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +0 -31
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +0 -36
- data/lib/ratatui_ruby/schema/rect.rb +0 -174
- data/lib/ratatui_ruby/schema/row.rb +0 -76
- data/lib/ratatui_ruby/schema/scrollbar.rb +0 -143
- data/lib/ratatui_ruby/schema/shape/label.rb +0 -76
- data/lib/ratatui_ruby/schema/sparkline.rb +0 -142
- data/lib/ratatui_ruby/schema/style.rb +0 -97
- data/lib/ratatui_ruby/schema/table.rb +0 -141
- data/lib/ratatui_ruby/schema/tabs.rb +0 -85
- data/lib/ratatui_ruby/schema/text.rb +0 -217
- data/sig/examples/app_all_events/model/events.rbs +0 -15
- data/sig/examples/app_all_events/view_state.rbs +0 -21
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +0 -22
- data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +0 -19
- data/sig/ratatui_ruby/schema/bar_chart.rbs +0 -38
- data/sig/ratatui_ruby/schema/block.rbs +0 -18
- data/sig/ratatui_ruby/schema/calendar.rbs +0 -23
- data/sig/ratatui_ruby/schema/canvas.rbs +0 -81
- data/sig/ratatui_ruby/schema/center.rbs +0 -17
- data/sig/ratatui_ruby/schema/chart.rbs +0 -39
- data/sig/ratatui_ruby/schema/constraint.rbs +0 -30
- data/sig/ratatui_ruby/schema/cursor.rbs +0 -16
- data/sig/ratatui_ruby/schema/draw.rbs +0 -33
- data/sig/ratatui_ruby/schema/gauge.rbs +0 -23
- data/sig/ratatui_ruby/schema/layout.rbs +0 -27
- data/sig/ratatui_ruby/schema/line_gauge.rbs +0 -24
- data/sig/ratatui_ruby/schema/list.rbs +0 -28
- data/sig/ratatui_ruby/schema/overlay.rbs +0 -15
- data/sig/ratatui_ruby/schema/paragraph.rbs +0 -20
- data/sig/ratatui_ruby/schema/ratatui_logo.rbs +0 -14
- data/sig/ratatui_ruby/schema/ratatui_mascot.rbs +0 -17
- data/sig/ratatui_ruby/schema/rect.rbs +0 -48
- data/sig/ratatui_ruby/schema/row.rbs +0 -28
- data/sig/ratatui_ruby/schema/scrollbar.rbs +0 -42
- data/sig/ratatui_ruby/schema/sparkline.rbs +0 -22
- data/sig/ratatui_ruby/schema/style.rbs +0 -19
- data/sig/ratatui_ruby/schema/table.rbs +0 -32
- data/sig/ratatui_ruby/schema/tabs.rbs +0 -21
- data/sig/ratatui_ruby/schema/text.rbs +0 -31
- /data/lib/ratatui_ruby/{schema/draw.rb → draw.rb} +0 -0
|
@@ -69,6 +69,40 @@ impl RubyTableState {
|
|
|
69
69
|
self.inner.borrow_mut().scroll_up_by(amount);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/// Returns the currently selected cell as (row, column) tuple.
|
|
73
|
+
/// Returns None if either row or column is not selected.
|
|
74
|
+
pub fn selected_cell(&self) -> Option<(usize, usize)> {
|
|
75
|
+
self.inner.borrow().selected_cell()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Selects the next column or the first one if no column is selected.
|
|
79
|
+
pub fn select_next_column(&self) {
|
|
80
|
+
self.inner.borrow_mut().select_next_column();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// Selects the previous column or the last one if no column is selected.
|
|
84
|
+
pub fn select_previous_column(&self) {
|
|
85
|
+
self.inner.borrow_mut().select_previous_column();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// Selects the first column.
|
|
89
|
+
pub fn select_first_column(&self) {
|
|
90
|
+
self.inner.borrow_mut().select_first_column();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// Selects the last column.
|
|
94
|
+
pub fn select_last_column(&self) {
|
|
95
|
+
self.inner.borrow_mut().select_last_column();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Creates a new `RubyTableState` with a selected cell (row, column).
|
|
99
|
+
pub fn with_selected_cell(cell: Option<(usize, usize)>) -> Self {
|
|
100
|
+
let state = TableState::default().with_selected_cell(cell);
|
|
101
|
+
Self {
|
|
102
|
+
inner: RefCell::new(state),
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
72
106
|
/// Borrows the inner `TableState` mutably for rendering.
|
|
73
107
|
pub fn borrow_mut(&self) -> std::cell::RefMut<'_, TableState> {
|
|
74
108
|
self.inner.borrow_mut()
|
|
@@ -79,6 +113,10 @@ impl RubyTableState {
|
|
|
79
113
|
pub fn register(ruby: &Ruby, module: magnus::RModule) -> Result<(), Error> {
|
|
80
114
|
let class = module.define_class("TableState", ruby.class_object())?;
|
|
81
115
|
class.define_singleton_method("new", function!(RubyTableState::new, 1))?;
|
|
116
|
+
class.define_singleton_method(
|
|
117
|
+
"with_selected_cell",
|
|
118
|
+
function!(RubyTableState::with_selected_cell, 1),
|
|
119
|
+
)?;
|
|
82
120
|
class.define_method("select", method!(RubyTableState::select, 1))?;
|
|
83
121
|
class.define_method("selected", method!(RubyTableState::selected, 0))?;
|
|
84
122
|
class.define_method("select_column", method!(RubyTableState::select_column, 1))?;
|
|
@@ -86,6 +124,23 @@ pub fn register(ruby: &Ruby, module: magnus::RModule) -> Result<(), Error> {
|
|
|
86
124
|
"selected_column",
|
|
87
125
|
method!(RubyTableState::selected_column, 0),
|
|
88
126
|
)?;
|
|
127
|
+
class.define_method("selected_cell", method!(RubyTableState::selected_cell, 0))?;
|
|
128
|
+
class.define_method(
|
|
129
|
+
"select_next_column",
|
|
130
|
+
method!(RubyTableState::select_next_column, 0),
|
|
131
|
+
)?;
|
|
132
|
+
class.define_method(
|
|
133
|
+
"select_previous_column",
|
|
134
|
+
method!(RubyTableState::select_previous_column, 0),
|
|
135
|
+
)?;
|
|
136
|
+
class.define_method(
|
|
137
|
+
"select_first_column",
|
|
138
|
+
method!(RubyTableState::select_first_column, 0),
|
|
139
|
+
)?;
|
|
140
|
+
class.define_method(
|
|
141
|
+
"select_last_column",
|
|
142
|
+
method!(RubyTableState::select_last_column, 0),
|
|
143
|
+
)?;
|
|
89
144
|
class.define_method("offset", method!(RubyTableState::offset, 0))?;
|
|
90
145
|
class.define_method("scroll_down_by", method!(RubyTableState::scroll_down_by, 1))?;
|
|
91
146
|
class.define_method("scroll_up_by", method!(RubyTableState::scroll_up_by, 1))?;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
+
use crate::errors::type_error_with_context;
|
|
4
5
|
use crate::style::parse_block;
|
|
5
6
|
use crate::text::{parse_line, parse_span};
|
|
6
7
|
use bumpalo::Bump;
|
|
@@ -59,7 +60,7 @@ fn create_tabs(node: Value, bump: &Bump) -> Result<Tabs<'_>, Error> {
|
|
|
59
60
|
let padding_right_val: Value = node.funcall("padding_right", ())?;
|
|
60
61
|
|
|
61
62
|
let titles_array = magnus::RArray::from_value(titles_val)
|
|
62
|
-
.ok_or_else(||
|
|
63
|
+
.ok_or_else(|| type_error_with_context(&ruby, "expected array for titles", titles_val))?;
|
|
63
64
|
|
|
64
65
|
let mut titles = Vec::new();
|
|
65
66
|
for i in 0..titles_array.len() {
|
|
@@ -117,7 +118,7 @@ pub fn width(node: Value) -> Result<usize, Error> {
|
|
|
117
118
|
let padding_right: usize = node.funcall("padding_right", ())?;
|
|
118
119
|
|
|
119
120
|
let titles_array = magnus::RArray::from_value(titles_val)
|
|
120
|
-
.ok_or_else(||
|
|
121
|
+
.ok_or_else(|| type_error_with_context(&ruby, "expected array for titles", titles_val))?;
|
|
121
122
|
|
|
122
123
|
let mut total_width = padding_left + padding_right;
|
|
123
124
|
|
|
@@ -46,6 +46,11 @@ module RatatuiRuby
|
|
|
46
46
|
# The background color of the cell (e.g., :black, nil).
|
|
47
47
|
attr_reader :bg
|
|
48
48
|
|
|
49
|
+
# The underline color of the cell.
|
|
50
|
+
#
|
|
51
|
+
# Distinct from foreground color. Some terminals support colored underlines.
|
|
52
|
+
attr_reader :underline_color
|
|
53
|
+
|
|
49
54
|
# The list of active modifiers (e.g., ["bold", "italic"]).
|
|
50
55
|
attr_reader :modifiers
|
|
51
56
|
|
|
@@ -64,7 +69,7 @@ module RatatuiRuby
|
|
|
64
69
|
# SPDX-SnippetEnd
|
|
65
70
|
#++
|
|
66
71
|
def self.empty
|
|
67
|
-
new(symbol: " ", fg: nil, bg: nil, modifiers: [])
|
|
72
|
+
new(symbol: " ", fg: nil, bg: nil, underline_color: nil, modifiers: [])
|
|
68
73
|
end
|
|
69
74
|
|
|
70
75
|
# Returns a default cell (alias for empty).
|
|
@@ -102,7 +107,7 @@ module RatatuiRuby
|
|
|
102
107
|
# SPDX-SnippetEnd
|
|
103
108
|
#++
|
|
104
109
|
def self.symbol(symbol)
|
|
105
|
-
new(symbol:, fg: nil, bg: nil, modifiers: [])
|
|
110
|
+
new(symbol:, fg: nil, bg: nil, underline_color: nil, modifiers: [])
|
|
106
111
|
end
|
|
107
112
|
|
|
108
113
|
# Alias for Rubyists who prefer a shorter name.
|
|
@@ -115,58 +120,61 @@ module RatatuiRuby
|
|
|
115
120
|
# [symbol] String (single character). Aliased as <tt>char:</tt>.
|
|
116
121
|
# [fg] Symbol or String (nullable).
|
|
117
122
|
# [bg] Symbol or String (nullable).
|
|
118
|
-
# [
|
|
119
|
-
|
|
123
|
+
# [underline_color] Symbol or String (nullable).
|
|
124
|
+
# [modifiers] Array of Strings, Symbols, or any object responding to to_sym or to_s.
|
|
125
|
+
# Normalized to Symbols for consistent output.
|
|
126
|
+
def initialize(symbol: nil, char: nil, fg: nil, bg: nil, underline_color: nil, modifiers: [])
|
|
120
127
|
@symbol = (symbol || char || " ").freeze
|
|
121
128
|
@fg = fg&.freeze
|
|
122
129
|
@bg = bg&.freeze
|
|
123
|
-
@
|
|
130
|
+
@underline_color = underline_color&.freeze
|
|
131
|
+
@modifiers = modifiers.map { |m| m.respond_to?(:to_sym) ? m.to_sym : m.to_s.to_sym }.freeze
|
|
124
132
|
freeze
|
|
125
133
|
end
|
|
126
134
|
|
|
127
135
|
# Returns true if the cell has the bold modifier.
|
|
128
136
|
def bold?
|
|
129
|
-
modifiers.include?(
|
|
137
|
+
modifiers.include?(:bold)
|
|
130
138
|
end
|
|
131
139
|
|
|
132
140
|
# Returns true if the cell has the dim modifier.
|
|
133
141
|
def dim?
|
|
134
|
-
modifiers.include?(
|
|
142
|
+
modifiers.include?(:dim)
|
|
135
143
|
end
|
|
136
144
|
|
|
137
145
|
# Returns true if the cell has the italic modifier.
|
|
138
146
|
def italic?
|
|
139
|
-
modifiers.include?(
|
|
147
|
+
modifiers.include?(:italic)
|
|
140
148
|
end
|
|
141
149
|
|
|
142
150
|
# Returns true if the cell has the underlined modifier.
|
|
143
151
|
def underlined?
|
|
144
|
-
modifiers.include?(
|
|
152
|
+
modifiers.include?(:underlined)
|
|
145
153
|
end
|
|
146
154
|
|
|
147
155
|
# Returns true if the cell has the slow_blink modifier.
|
|
148
156
|
def slow_blink?
|
|
149
|
-
modifiers.include?(
|
|
157
|
+
modifiers.include?(:slow_blink)
|
|
150
158
|
end
|
|
151
159
|
|
|
152
160
|
# Returns true if the cell has the rapid_blink modifier.
|
|
153
161
|
def rapid_blink?
|
|
154
|
-
modifiers.include?(
|
|
162
|
+
modifiers.include?(:rapid_blink)
|
|
155
163
|
end
|
|
156
164
|
|
|
157
165
|
# Returns true if the cell has the reversed modifier.
|
|
158
166
|
def reversed?
|
|
159
|
-
modifiers.include?(
|
|
167
|
+
modifiers.include?(:reversed)
|
|
160
168
|
end
|
|
161
169
|
|
|
162
170
|
# Returns true if the cell has the hidden modifier.
|
|
163
171
|
def hidden?
|
|
164
|
-
modifiers.include?(
|
|
172
|
+
modifiers.include?(:hidden)
|
|
165
173
|
end
|
|
166
174
|
|
|
167
175
|
# Returns true if the cell has the crossed_out modifier.
|
|
168
176
|
def crossed_out?
|
|
169
|
-
modifiers.include?(
|
|
177
|
+
modifiers.include?(:crossed_out)
|
|
170
178
|
end
|
|
171
179
|
|
|
172
180
|
# Checks equality with another Cell.
|
|
@@ -175,6 +183,7 @@ module RatatuiRuby
|
|
|
175
183
|
char == other.char &&
|
|
176
184
|
fg == other.fg &&
|
|
177
185
|
bg == other.bg &&
|
|
186
|
+
underline_color == other.underline_color &&
|
|
178
187
|
modifiers == other.modifiers
|
|
179
188
|
end
|
|
180
189
|
|
|
@@ -183,6 +192,7 @@ module RatatuiRuby
|
|
|
183
192
|
parts = ["symbol=#{symbol.inspect}"]
|
|
184
193
|
parts << "fg=#{fg.inspect}" if fg
|
|
185
194
|
parts << "bg=#{bg.inspect}" if bg
|
|
195
|
+
parts << "underline_color=#{underline_color.inspect}" if underline_color
|
|
186
196
|
parts << "modifiers=#{modifiers.inspect}" unless modifiers.empty?
|
|
187
197
|
"#<#{self.class} #{parts.join(' ')}>"
|
|
188
198
|
end
|
|
@@ -195,7 +205,7 @@ module RatatuiRuby
|
|
|
195
205
|
# Support for pattern matching.
|
|
196
206
|
# Supports both <tt>:symbol</tt> and <tt>:char</tt> keys.
|
|
197
207
|
def deconstruct_keys(keys)
|
|
198
|
-
{ symbol:, char: symbol, fg:, bg:, modifiers: }
|
|
208
|
+
{ symbol:, char: symbol, fg:, bg:, underline_color:, modifiers: }
|
|
199
209
|
end
|
|
200
210
|
end
|
|
201
211
|
end
|
data/lib/ratatui_ruby/buffer.rb
CHANGED
|
@@ -8,9 +8,141 @@
|
|
|
8
8
|
module RatatuiRuby
|
|
9
9
|
# Buffer primitives for terminal cell inspection.
|
|
10
10
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
11
|
+
# Widgets render to an intermediate buffer, not directly to the terminal.
|
|
12
|
+
# Testing and debugging require access to buffer state.
|
|
13
|
+
#
|
|
14
|
+
# This module mirrors +ratatui::buffer+ and provides query methods
|
|
15
|
+
# for inspecting buffer contents, converting between coordinates and indices,
|
|
16
|
+
# and retrieving individual cells.
|
|
17
|
+
#
|
|
18
|
+
# Use it in tests to verify rendered output or in debugging to inspect state.
|
|
13
19
|
module Buffer
|
|
20
|
+
class << self
|
|
21
|
+
# Converts a position to a linear buffer index.
|
|
22
|
+
#
|
|
23
|
+
# Buffers store cells in a flat array, row by row. Widget code
|
|
24
|
+
# works with (x, y) coordinates. Bridging these representations
|
|
25
|
+
# requires index translation.
|
|
26
|
+
#
|
|
27
|
+
# The index is calculated as <tt>y * width + x</tt>.
|
|
28
|
+
#
|
|
29
|
+
# === Example
|
|
30
|
+
#
|
|
31
|
+
#--
|
|
32
|
+
# SPDX-SnippetBegin
|
|
33
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
34
|
+
# SPDX-License-Identifier: MIT-0
|
|
35
|
+
#++
|
|
36
|
+
# # In a 10-wide buffer, position (3, 2) maps to index 23
|
|
37
|
+
# Buffer.index_of(3, 2) # => 23
|
|
38
|
+
#--
|
|
39
|
+
# SPDX-SnippetEnd
|
|
40
|
+
#++
|
|
41
|
+
#
|
|
42
|
+
# [x] Column (0-indexed from left).
|
|
43
|
+
# [y] Row (0-indexed from top).
|
|
44
|
+
#
|
|
45
|
+
# Returns the linear index (Integer).
|
|
46
|
+
def index_of(x, y)
|
|
47
|
+
area = RatatuiRuby._get_terminal_area
|
|
48
|
+
(y * area["width"]) + x
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Converts a linear buffer index to position coordinates.
|
|
52
|
+
#
|
|
53
|
+
# Inverse of +index_of+. When iterating over buffer content
|
|
54
|
+
# by index, use this to recover the original coordinates.
|
|
55
|
+
#
|
|
56
|
+
# === Example
|
|
57
|
+
#
|
|
58
|
+
#--
|
|
59
|
+
# SPDX-SnippetBegin
|
|
60
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
61
|
+
# SPDX-License-Identifier: MIT-0
|
|
62
|
+
#++
|
|
63
|
+
# # In a 10-wide buffer, index 23 maps to position (3, 2)
|
|
64
|
+
# Buffer.pos_of(23) # => [3, 2]
|
|
65
|
+
#--
|
|
66
|
+
# SPDX-SnippetEnd
|
|
67
|
+
#++
|
|
68
|
+
#
|
|
69
|
+
# [index] Linear buffer index (Integer).
|
|
70
|
+
#
|
|
71
|
+
# Returns <tt>[x, y]</tt> coordinates.
|
|
72
|
+
def pos_of(index)
|
|
73
|
+
area = RatatuiRuby._get_terminal_area
|
|
74
|
+
width = area["width"]
|
|
75
|
+
x = index % width
|
|
76
|
+
y = index / width
|
|
77
|
+
[x, y]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns the Cell at the specified position.
|
|
81
|
+
#
|
|
82
|
+
# Tests assert on cell contents. This method provides direct
|
|
83
|
+
# access without iterating the entire buffer.
|
|
84
|
+
#
|
|
85
|
+
# Delegates to +RatatuiRuby.get_cell_at+.
|
|
86
|
+
#
|
|
87
|
+
# === Example
|
|
88
|
+
#
|
|
89
|
+
#--
|
|
90
|
+
# SPDX-SnippetBegin
|
|
91
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
92
|
+
# SPDX-License-Identifier: MIT-0
|
|
93
|
+
#++
|
|
94
|
+
# cell = Buffer.get(0, 0)
|
|
95
|
+
# assert_equal "H", cell.char
|
|
96
|
+
#--
|
|
97
|
+
# SPDX-SnippetEnd
|
|
98
|
+
#++
|
|
99
|
+
#
|
|
100
|
+
# [x] Column (0-indexed from left).
|
|
101
|
+
# [y] Row (0-indexed from top).
|
|
102
|
+
#
|
|
103
|
+
# Returns a Buffer::Cell containing the character and style at that position.
|
|
104
|
+
def get(x, y)
|
|
105
|
+
RatatuiRuby.get_cell_at(x, y)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Returns all cells in the buffer as a flat array.
|
|
109
|
+
#
|
|
110
|
+
# Snapshot testing compares entire buffer states. Manually
|
|
111
|
+
# iterating coordinates is verbose and error-prone.
|
|
112
|
+
#
|
|
113
|
+
# This method returns every cell, ordered row by row
|
|
114
|
+
# (top to bottom, left to right).
|
|
115
|
+
#
|
|
116
|
+
# === Example
|
|
117
|
+
#
|
|
118
|
+
#--
|
|
119
|
+
# SPDX-SnippetBegin
|
|
120
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
121
|
+
# SPDX-License-Identifier: MIT-0
|
|
122
|
+
#++
|
|
123
|
+
# cells = Buffer.content
|
|
124
|
+
# cells.each { |cell| puts cell.char }
|
|
125
|
+
#--
|
|
126
|
+
# SPDX-SnippetEnd
|
|
127
|
+
#++
|
|
128
|
+
#
|
|
129
|
+
# Returns an Array of Buffer::Cell objects.
|
|
130
|
+
def content
|
|
131
|
+
area = RatatuiRuby._get_terminal_area
|
|
132
|
+
width = area["width"]
|
|
133
|
+
height = area["height"]
|
|
134
|
+
cells = [] #: Array[Buffer::Cell]
|
|
135
|
+
(0...height).each do |y|
|
|
136
|
+
(0...width).each do |x|
|
|
137
|
+
cells << RatatuiRuby.get_cell_at(x, y)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
cells
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Ruby-idiomatic alias (TIMTOWTDI)
|
|
144
|
+
alias [] get
|
|
145
|
+
end
|
|
14
146
|
end
|
|
15
147
|
end
|
|
16
148
|
|
data/lib/ratatui_ruby/cell.rb
CHANGED
|
@@ -45,8 +45,12 @@ module RatatuiRuby
|
|
|
45
45
|
# The background color of the cell (e.g., :black, nil).
|
|
46
46
|
attr_reader :bg
|
|
47
47
|
|
|
48
|
+
# The underline color of the cell.
|
|
49
|
+
#
|
|
50
|
+
# Distinct from foreground color. Some terminals support colored underlines.
|
|
51
|
+
attr_reader :underline_color
|
|
52
|
+
|
|
48
53
|
# The list of active modifiers (e.g., ["bold", "italic"]).
|
|
49
|
-
attr_reader :modifiers
|
|
50
54
|
|
|
51
55
|
# Returns an empty cell (space character, no styles).
|
|
52
56
|
#
|
|
@@ -63,7 +67,7 @@ module RatatuiRuby
|
|
|
63
67
|
# SPDX-SnippetEnd
|
|
64
68
|
#++
|
|
65
69
|
def self.empty
|
|
66
|
-
new(symbol: " ", fg: nil, bg: nil, modifiers: [])
|
|
70
|
+
new(symbol: " ", fg: nil, bg: nil, underline_color: nil, modifiers: [])
|
|
67
71
|
end
|
|
68
72
|
|
|
69
73
|
# Returns a default cell (alias for empty).
|
|
@@ -101,7 +105,7 @@ module RatatuiRuby
|
|
|
101
105
|
# SPDX-SnippetEnd
|
|
102
106
|
#++
|
|
103
107
|
def self.symbol(symbol)
|
|
104
|
-
new(symbol:, fg: nil, bg: nil, modifiers: [])
|
|
108
|
+
new(symbol:, fg: nil, bg: nil, underline_color: nil, modifiers: [])
|
|
105
109
|
end
|
|
106
110
|
|
|
107
111
|
# Alias for Rubyists who prefer a shorter name.
|
|
@@ -114,11 +118,13 @@ module RatatuiRuby
|
|
|
114
118
|
# [symbol] String (single character). Aliased as <tt>char:</tt>.
|
|
115
119
|
# [fg] Symbol or String (nullable).
|
|
116
120
|
# [bg] Symbol or String (nullable).
|
|
121
|
+
# [underline_color] Symbol or String (nullable).
|
|
117
122
|
# [modifiers] Array of Strings.
|
|
118
|
-
def initialize(symbol: nil, char: nil, fg: nil, bg: nil, modifiers: [])
|
|
123
|
+
def initialize(symbol: nil, char: nil, fg: nil, bg: nil, underline_color: nil, modifiers: [])
|
|
119
124
|
@symbol = (symbol || char || " ").freeze
|
|
120
125
|
@fg = fg&.freeze
|
|
121
126
|
@bg = bg&.freeze
|
|
127
|
+
@underline_color = underline_color&.freeze
|
|
122
128
|
@modifiers = modifiers.map(&:freeze).freeze
|
|
123
129
|
freeze
|
|
124
130
|
end
|
|
@@ -174,6 +180,7 @@ module RatatuiRuby
|
|
|
174
180
|
char == other.char &&
|
|
175
181
|
fg == other.fg &&
|
|
176
182
|
bg == other.bg &&
|
|
183
|
+
underline_color == other.underline_color &&
|
|
177
184
|
modifiers == other.modifiers
|
|
178
185
|
end
|
|
179
186
|
|
|
@@ -182,6 +189,7 @@ module RatatuiRuby
|
|
|
182
189
|
parts = ["symbol=#{symbol.inspect}"]
|
|
183
190
|
parts << "fg=#{fg.inspect}" if fg
|
|
184
191
|
parts << "bg=#{bg.inspect}" if bg
|
|
192
|
+
parts << "underline_color=#{underline_color.inspect}" if underline_color
|
|
185
193
|
parts << "modifiers=#{modifiers.inspect}" unless modifiers.empty?
|
|
186
194
|
"#<#{self.class} #{parts.join(' ')}>"
|
|
187
195
|
end
|
|
@@ -194,7 +202,7 @@ module RatatuiRuby
|
|
|
194
202
|
# Support for pattern matching.
|
|
195
203
|
# Supports both <tt>:symbol</tt> and <tt>:char</tt> keys.
|
|
196
204
|
def deconstruct_keys(keys)
|
|
197
|
-
{ symbol:, char: symbol, fg:, bg:, modifiers: }
|
|
205
|
+
{ symbol:, char: symbol, fg:, bg:, underline_color:, modifiers: }
|
|
198
206
|
end
|
|
199
207
|
end
|
|
200
208
|
end
|
|
@@ -0,0 +1,215 @@
|
|
|
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
|
+
##
|
|
10
|
+
# Debug mode control for RatatuiRuby.
|
|
11
|
+
#
|
|
12
|
+
# TUI applications are hard to debug. Rust panics show cryptic stack traces.
|
|
13
|
+
# Ruby exceptions lack Rust context.
|
|
14
|
+
#
|
|
15
|
+
# This module controls debug visibility. Enable Rust backtraces only, or
|
|
16
|
+
# enable full debug mode for both Rust and Ruby-side features.
|
|
17
|
+
#
|
|
18
|
+
# == Activation Methods
|
|
19
|
+
#
|
|
20
|
+
# Three ways to enable debug features:
|
|
21
|
+
#
|
|
22
|
+
# [<tt>RUST_BACKTRACE=1</tt>] Rust backtraces only (no Ruby-side debug).
|
|
23
|
+
# [<tt>RR_DEBUG=1</tt>] Full debug mode (backtraces + Ruby features).
|
|
24
|
+
# [<tt>include RatatuiRuby::TestHelper</tt>] Auto-enables debug mode.
|
|
25
|
+
#
|
|
26
|
+
# === Example
|
|
27
|
+
#
|
|
28
|
+
#--
|
|
29
|
+
# SPDX-SnippetBegin
|
|
30
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
31
|
+
# SPDX-License-Identifier: MIT-0
|
|
32
|
+
#++
|
|
33
|
+
# # Programmatic activation
|
|
34
|
+
# RatatuiRuby::Debug.enable!
|
|
35
|
+
#
|
|
36
|
+
# # Or use the convenience alias
|
|
37
|
+
# RatatuiRuby.debug_mode!
|
|
38
|
+
#
|
|
39
|
+
#--
|
|
40
|
+
# SPDX-SnippetEnd
|
|
41
|
+
#++
|
|
42
|
+
module Debug
|
|
43
|
+
@rust_backtrace_enabled = false
|
|
44
|
+
@debug_mode_enabled = false
|
|
45
|
+
|
|
46
|
+
class << self
|
|
47
|
+
##
|
|
48
|
+
# Enables Rust backtraces only.
|
|
49
|
+
#
|
|
50
|
+
# Call this to get meaningful stack traces when Rust panics.
|
|
51
|
+
# Does not enable Ruby-side debug features.
|
|
52
|
+
#
|
|
53
|
+
# Safe to call multiple times; subsequent calls are no-ops.
|
|
54
|
+
def enable_rust_backtrace!
|
|
55
|
+
return if @rust_backtrace_enabled
|
|
56
|
+
|
|
57
|
+
@rust_backtrace_enabled = true
|
|
58
|
+
RatatuiRuby.__send__(:_enable_rust_backtrace)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Enables full debug mode.
|
|
63
|
+
#
|
|
64
|
+
# Activates Rust backtraces plus any Ruby-side debug features.
|
|
65
|
+
# Optionally enables remote debugging via the debug gem.
|
|
66
|
+
#
|
|
67
|
+
# Safe to call multiple times; subsequent calls are no-ops.
|
|
68
|
+
#
|
|
69
|
+
# [source] <tt>:env</tt> if called from RR_DEBUG env var,
|
|
70
|
+
#--
|
|
71
|
+
# SPDX-SnippetBegin
|
|
72
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
73
|
+
# SPDX-License-Identifier: MIT-0
|
|
74
|
+
#++
|
|
75
|
+
# <tt>:test</tt> from TestHelper (skips remote debugging),
|
|
76
|
+
# <tt>:programmatic</tt> otherwise.
|
|
77
|
+
#--
|
|
78
|
+
# SPDX-SnippetEnd
|
|
79
|
+
#++
|
|
80
|
+
def enable!(source: :programmatic)
|
|
81
|
+
return @socket_path if @debug_mode_enabled
|
|
82
|
+
|
|
83
|
+
@debug_mode_enabled = true
|
|
84
|
+
enable_rust_backtrace!
|
|
85
|
+
|
|
86
|
+
# Tests don't need remote debugging — it would cause hangs
|
|
87
|
+
return if source == :test
|
|
88
|
+
|
|
89
|
+
@remote_debugging_mode = (source == :env) ? :open : :open_nonstop
|
|
90
|
+
@socket_path = enable_remote_debugging!
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# rubocop:disable Lint/Debugger -- intentional debug gem integration
|
|
94
|
+
private def enable_remote_debugging!
|
|
95
|
+
# Suppress the "Debugger can attach via..." message that corrupts TUI displays
|
|
96
|
+
# Only suppress for programmatic activation; RR_DEBUG=1 users need to see it
|
|
97
|
+
old_log_level = ENV["RUBY_DEBUG_LOG_LEVEL"]
|
|
98
|
+
ENV["RUBY_DEBUG_LOG_LEVEL"] = "ERROR" if @remote_debugging_mode == :open_nonstop
|
|
99
|
+
|
|
100
|
+
case @remote_debugging_mode
|
|
101
|
+
when :open
|
|
102
|
+
# Stop at load so user can read socket path before TUI enters raw mode
|
|
103
|
+
ENV["RUBY_DEBUG_STOP_AT_LOAD"] = "1"
|
|
104
|
+
require "debug/open"
|
|
105
|
+
when :open_nonstop
|
|
106
|
+
require "debug/open_nonstop"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Restore log level after require (the require is what prints the message)
|
|
110
|
+
ENV["RUBY_DEBUG_LOG_LEVEL"] = old_log_level if @remote_debugging_mode == :open_nonstop
|
|
111
|
+
|
|
112
|
+
# Return the socket path so apps can display it
|
|
113
|
+
::DEBUGGER__.create_unix_domain_socket_name
|
|
114
|
+
rescue NameError
|
|
115
|
+
# Windows uses TCP/IP, not Unix sockets — DEBUGGER__ might not have this method
|
|
116
|
+
nil
|
|
117
|
+
# rubocop:enable Lint/Debugger
|
|
118
|
+
rescue LoadError
|
|
119
|
+
return unless @remote_debugging_mode == :open
|
|
120
|
+
|
|
121
|
+
raise LoadError,
|
|
122
|
+
"RR_DEBUG=1 requires the 'debug' gem for remote debugging. " \
|
|
123
|
+
"Add `gem 'debug'` to your Gemfile or install it with `gem install debug`."
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
##
|
|
127
|
+
# Returns whether full debug mode is enabled.
|
|
128
|
+
public def enabled?
|
|
129
|
+
@debug_mode_enabled
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
##
|
|
133
|
+
# Returns whether Rust backtraces are enabled.
|
|
134
|
+
public def rust_backtrace_enabled?
|
|
135
|
+
@rust_backtrace_enabled
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
##
|
|
139
|
+
# Returns the remote debugging mode for debug gem integration.
|
|
140
|
+
#
|
|
141
|
+
# TUI apps run in raw terminal mode, making interactive debugging
|
|
142
|
+
# impossible. The debug gem's remote debugging feature lets you
|
|
143
|
+
# attach from another terminal via UNIX socket.
|
|
144
|
+
#
|
|
145
|
+
# Returns one of:
|
|
146
|
+
# <tt>:open</tt> Stop at program start, wait for debugger attach.
|
|
147
|
+
# Activated when <tt>RR_DEBUG=1</tt> is set at startup.
|
|
148
|
+
# <tt>:open_nonstop</tt> Continue running, attach whenever ready.
|
|
149
|
+
# Activated when <tt>enable!</tt> is called programmatically.
|
|
150
|
+
# <tt>nil</tt> No remote debugging configured.
|
|
151
|
+
public def remote_debugging_mode
|
|
152
|
+
@remote_debugging_mode
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
##
|
|
156
|
+
# Triggers a Rust panic for backtrace verification.
|
|
157
|
+
#
|
|
158
|
+
# Debugging TUI apps is hard. Rust errors lack context. You want to
|
|
159
|
+
# confirm <tt>RUST_BACKTRACE=1</tt> actually shows stack traces before
|
|
160
|
+
# hitting a real bug.
|
|
161
|
+
#
|
|
162
|
+
# This method deliberately panics. The panic hook catches it and prints
|
|
163
|
+
# the Rust backtrace to stderr. If you see stack frames, your setup works.
|
|
164
|
+
#
|
|
165
|
+
# <b>WARNING</b>: Crashes your process. Use only for debugging.
|
|
166
|
+
#
|
|
167
|
+
# === Example
|
|
168
|
+
#
|
|
169
|
+
#--
|
|
170
|
+
# SPDX-SnippetBegin
|
|
171
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
172
|
+
# SPDX-License-Identifier: MIT-0
|
|
173
|
+
#++
|
|
174
|
+
# RUST_BACKTRACE=1 ruby -e 'require "ratatui_ruby"; RatatuiRuby::Debug.test_panic!'
|
|
175
|
+
#--
|
|
176
|
+
# SPDX-SnippetEnd
|
|
177
|
+
#++
|
|
178
|
+
public def test_panic!
|
|
179
|
+
RatatuiRuby.__send__(:_test_panic)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
##
|
|
183
|
+
# Temporarily suppresses Ruby-side debug mode checks.
|
|
184
|
+
#
|
|
185
|
+
# Rust backtraces remain enabled if previously activated; only
|
|
186
|
+
# Ruby-side features (like unknown-key errors) are suppressed
|
|
187
|
+
# within the block.
|
|
188
|
+
#
|
|
189
|
+
# === Example
|
|
190
|
+
#
|
|
191
|
+
#--
|
|
192
|
+
# SPDX-SnippetBegin
|
|
193
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
194
|
+
# SPDX-License-Identifier: MIT-0
|
|
195
|
+
#++
|
|
196
|
+
# RatatuiRuby::Debug.suppress_debug_mode do
|
|
197
|
+
# tui.table({ unknown_key: 1 }) # Does not raise
|
|
198
|
+
# end
|
|
199
|
+
#--
|
|
200
|
+
# SPDX-SnippetEnd
|
|
201
|
+
#++
|
|
202
|
+
public def suppress_debug_mode
|
|
203
|
+
old_value = @debug_mode_enabled
|
|
204
|
+
@debug_mode_enabled = false
|
|
205
|
+
yield
|
|
206
|
+
ensure
|
|
207
|
+
@debug_mode_enabled = old_value
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Auto-enable based on environment variables
|
|
214
|
+
RatatuiRuby::Debug.enable_rust_backtrace! if ENV["RUST_BACKTRACE"]
|
|
215
|
+
RatatuiRuby::Debug.enable!(source: :env) if ENV["RR_DEBUG"]
|
|
@@ -330,9 +330,10 @@ module RatatuiRuby
|
|
|
330
330
|
#--
|
|
331
331
|
# SPDX-SnippetEnd
|
|
332
332
|
#++
|
|
333
|
-
def method_missing(name, *args, &block)
|
|
333
|
+
def method_missing(name, *args, **kwargs, &block)
|
|
334
334
|
if name.to_s.end_with?("?")
|
|
335
|
-
|
|
335
|
+
name_str = name.to_s
|
|
336
|
+
key_name = name_str.chop # Returns String, never nil for non-empty string
|
|
336
337
|
key_sym = key_name.to_sym
|
|
337
338
|
|
|
338
339
|
# Fast path: Exact match (e.g., media_pause? for media_pause)
|
data/lib/ratatui_ruby/event.rb
CHANGED
|
@@ -118,7 +118,7 @@ module RatatuiRuby
|
|
|
118
118
|
|
|
119
119
|
# Responds to dynamic predicate methods for key checks.
|
|
120
120
|
# All non-Key events return false for any key predicate.
|
|
121
|
-
def method_missing(name, *args, &block)
|
|
121
|
+
def method_missing(name, *args, **kwargs, &block)
|
|
122
122
|
if name.to_s.end_with?("?")
|
|
123
123
|
false
|
|
124
124
|
else
|