reflex-terminal 0.1.0
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/.doc/ext/reflex-terminal/native.cpp +19 -0
- data/.doc/ext/reflex-terminal/renderer.cpp +110 -0
- data/.doc/ext/reflex-terminal/terminal.cpp +530 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +12 -0
- data/.github/workflows/release-gem.yml +60 -0
- data/.github/workflows/tag.yml +35 -0
- data/.github/workflows/test.yml +46 -0
- data/.github/workflows/utils.rb +127 -0
- data/CONTRIBUTING.md +7 -0
- data/ChangeLog.md +6 -0
- data/Gemfile +5 -0
- data/LICENSE +21 -0
- data/README.md +100 -0
- data/Rakefile +44 -0
- data/VERSION +1 -0
- data/examples/terminal.rb +101 -0
- data/ext/reflex-terminal/defs.h +17 -0
- data/ext/reflex-terminal/extconf.rb +29 -0
- data/ext/reflex-terminal/native.cpp +19 -0
- data/ext/reflex-terminal/renderer.cpp +118 -0
- data/ext/reflex-terminal/terminal.cpp +572 -0
- data/include/reflex/terminal.h +233 -0
- data/include/reflex-terminal/defs.h +36 -0
- data/include/reflex-terminal/renderer.h +57 -0
- data/include/reflex-terminal/ruby/renderer.h +41 -0
- data/include/reflex-terminal/ruby/terminal.h +43 -0
- data/include/reflex-terminal/ruby.h +11 -0
- data/include/reflex-terminal.h +13 -0
- data/lib/reflex/bell_event.rb +15 -0
- data/lib/reflex/terminal.rb +168 -0
- data/lib/reflex/terminal_view.rb +268 -0
- data/lib/reflex-terminal/ext.rb +2 -0
- data/lib/reflex-terminal/extension.rb +41 -0
- data/lib/reflex-terminal.rb +7 -0
- data/reflex-terminal.gemspec +39 -0
- data/src/pty.cpp +236 -0
- data/src/renderer.cpp +365 -0
- data/src/terminal.cpp +1547 -0
- data/src/terminal.h +66 -0
- data/src/win32/pty.cpp +387 -0
- data/test/helper.rb +11 -0
- data/test/test_renderer.rb +113 -0
- data/test/test_terminal.rb +683 -0
- data/test/test_terminal_view.rb +182 -0
- metadata +152 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
require_relative 'helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TestTerminalView < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def view(*args, **kwargs)
|
|
7
|
+
Reflex::TerminalView.new(*args, **kwargs)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# a press on the cell at (x, y). Pointer#down is filled in by the window
|
|
11
|
+
# as it dispatches, so a hand-made event can only carry a press, not the
|
|
12
|
+
# drag that would reach back to one
|
|
13
|
+
def press(view, x, y, click_count: 1, modifiers: 0, button: Reflex::Pointer::MOUSE_LEFT)
|
|
14
|
+
position = Reflex::Point.new(
|
|
15
|
+
x * view.font.width('M'), y * view.font.height.ceil)
|
|
16
|
+
Reflex::PointerEvent.new Reflex::Pointer.new(
|
|
17
|
+
1, Reflex::Pointer::MOUSE | button,
|
|
18
|
+
Reflex::Pointer::DOWN, position, modifiers, click_count, false, 0)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def key_down(chars, code, modifiers = 0)
|
|
22
|
+
Reflex::KeyEvent.new Reflex::KeyEvent::DOWN, chars, code, modifiers, 0
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def terminal_view(text)
|
|
26
|
+
t = Reflex::Terminal.new 20, 4
|
|
27
|
+
t.feed text
|
|
28
|
+
t.update
|
|
29
|
+
[view(terminal: t), t]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_initialize()
|
|
33
|
+
v = view
|
|
34
|
+
assert_kind_of Reflex::View, v
|
|
35
|
+
assert_nil v.terminal
|
|
36
|
+
assert_equal Reflex::TerminalView::DEFAULT_FONT_SIZE, v.font_size
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_terminal()
|
|
40
|
+
t = Reflex::Terminal.new 80, 24
|
|
41
|
+
assert_same t, view(terminal: t).terminal
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_font()
|
|
45
|
+
size = Reflex::TerminalView::DEFAULT_FONT_SIZE
|
|
46
|
+
name = Reflex::TerminalView::DEFAULT_FONT_NAME
|
|
47
|
+
|
|
48
|
+
assert_include view(font: name) .font.name, name
|
|
49
|
+
assert_equal size, view(font: name) .font.size
|
|
50
|
+
assert_equal size, view(font: [name]) .font.size
|
|
51
|
+
assert_equal size, view(font: []) .font.size
|
|
52
|
+
assert_equal 20, view(font: [name, 20]).font.size
|
|
53
|
+
|
|
54
|
+
# an unknown name falls back to a proportional font, which would
|
|
55
|
+
# silently break the cell grid
|
|
56
|
+
font = view.font
|
|
57
|
+
assert_equal font.width('i'), font.width('W')
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def glyph_count(view)
|
|
61
|
+
view.instance_variable_get(:@renderer).glyph_count
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_on_update()
|
|
65
|
+
# a terminal handed over after it had stopped changing still has to be
|
|
66
|
+
# baked, or every glyph on it is drawn the slow way for good
|
|
67
|
+
v, t = terminal_view "hello\r\n"
|
|
68
|
+
assert_false t.update, 'nothing left for the update to report'
|
|
69
|
+
|
|
70
|
+
v.on_update nil
|
|
71
|
+
assert_operator glyph_count(v), :>, 0
|
|
72
|
+
|
|
73
|
+
# the atlas holds glyphs of one size, so a new font throws it away and
|
|
74
|
+
# the next update has to fill it again
|
|
75
|
+
v.font_size = 20
|
|
76
|
+
assert_equal 0, glyph_count(v)
|
|
77
|
+
v.on_update nil
|
|
78
|
+
assert_operator glyph_count(v), :>, 0
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test_on_key_down()
|
|
82
|
+
v, t = terminal_view "hello world\r\n"
|
|
83
|
+
v.on_pointer_down press(v, 0, 0, click_count: 2)
|
|
84
|
+
|
|
85
|
+
# a modifier arrives here on its own, and one held with command is a
|
|
86
|
+
# shortcut for the application to answer rather than something typed
|
|
87
|
+
v.on_key_down key_down('', Reflex::KEY_COMMAND, Reflex::MOD_COMMAND)
|
|
88
|
+
assert_true t.selection?
|
|
89
|
+
|
|
90
|
+
v.on_key_down key_down('c', Reflex::KEY_C, Reflex::MOD_COMMAND)
|
|
91
|
+
assert_true t.selection?
|
|
92
|
+
|
|
93
|
+
v.on_key_down key_down('a', Reflex::KEY_A)
|
|
94
|
+
assert_false t.selection?
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def test_on_pointer_down()
|
|
98
|
+
v, t = terminal_view "hello world\r\n"
|
|
99
|
+
|
|
100
|
+
v.on_pointer_down press(v, 0, 0, click_count: 2)
|
|
101
|
+
assert_equal 'hello', t.selected_text
|
|
102
|
+
|
|
103
|
+
v.on_pointer_down press(v, 6, 0, click_count: 2)# the column decides which word
|
|
104
|
+
assert_equal 'world', t.selected_text
|
|
105
|
+
|
|
106
|
+
v.on_pointer_down press(v, 6, 0, click_count: 3)
|
|
107
|
+
assert_equal 'hello world', t.selected_text
|
|
108
|
+
assert_true t.selection?
|
|
109
|
+
|
|
110
|
+
v.on_pointer_down press(v, 0, 0)
|
|
111
|
+
assert_false t.selection?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def test_on_pointer_down_right_button()
|
|
115
|
+
# right-clicking is how an application is asked what to do with what
|
|
116
|
+
# is already picked out, so taking it away first is no use to anyone
|
|
117
|
+
v, t = terminal_view "hello world\r\n"
|
|
118
|
+
v.on_pointer_down press(v, 0, 0, click_count: 2)
|
|
119
|
+
assert_equal 'hello', t.selected_text
|
|
120
|
+
|
|
121
|
+
v.on_pointer_down press(v, 0, 0, button: Reflex::Pointer::MOUSE_RIGHT)
|
|
122
|
+
assert_equal 'hello', t.selected_text
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def test_on_pointer_down_while_tracking()
|
|
126
|
+
v, t = terminal_view "hello world\r\n"
|
|
127
|
+
t.feed "\e[?1000h\e[?1006h"# normal tracking + SGR format
|
|
128
|
+
t.read_pending_input
|
|
129
|
+
|
|
130
|
+
v.on_pointer_down press(v, 0, 0, click_count: 2)
|
|
131
|
+
assert_false t.selection?, 'the child asked for the mouse'
|
|
132
|
+
assert_not_equal '', t.read_pending_input
|
|
133
|
+
|
|
134
|
+
v.on_pointer_down press(v, 0, 0, click_count: 2, modifiers: Reflex::MOD_SHIFT)
|
|
135
|
+
assert_equal 'hello', t.selected_text
|
|
136
|
+
assert_equal '', t.read_pending_input# taken by the selection
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def test_on_bell()
|
|
140
|
+
v, t = terminal_view "hello world\r\n"
|
|
141
|
+
bells = []
|
|
142
|
+
v.on(:bell) {|e| bells << e.count}
|
|
143
|
+
|
|
144
|
+
v.on_update nil
|
|
145
|
+
assert_equal [], bells
|
|
146
|
+
|
|
147
|
+
# a bell moves nothing on the screen, so it has to be picked up even
|
|
148
|
+
# when the update leaves nothing to draw
|
|
149
|
+
t.feed "\a\a"
|
|
150
|
+
v.on_update nil
|
|
151
|
+
assert_equal [2], bells
|
|
152
|
+
|
|
153
|
+
# answered once: the same bells do not come round again
|
|
154
|
+
v.on_update nil
|
|
155
|
+
assert_equal [2], bells
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def test_on_bell_with_a_rung_terminal()
|
|
159
|
+
# the bells a terminal rang before the view was attached to it are
|
|
160
|
+
# not the view's to answer
|
|
161
|
+
t = Reflex::Terminal.new 20, 4
|
|
162
|
+
t.feed "\a\a"
|
|
163
|
+
v = view terminal: t
|
|
164
|
+
|
|
165
|
+
bells = []
|
|
166
|
+
v.on(:bell) {|e| bells << e.count}
|
|
167
|
+
v.on_update nil
|
|
168
|
+
assert_equal [], bells
|
|
169
|
+
|
|
170
|
+
t.feed "\a"
|
|
171
|
+
v.on_update nil
|
|
172
|
+
assert_equal [1], bells
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def test_to_cell()
|
|
176
|
+
# clamped so that a drag leaving the view keeps selecting up to the edge
|
|
177
|
+
v, = terminal_view "hello world\r\n"
|
|
178
|
+
assert_equal [0, 0], v.send(:to_cell, -50, -50)
|
|
179
|
+
assert_equal [19, 3], v.send(:to_cell, 1e6, 1e6)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
end unless linux?# rays has no font lookup by name there
|
metadata
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: reflex-terminal
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- xordog
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-04 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: xot
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.4.0
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.4.0
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rucy
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 0.4.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 0.4.0
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rays
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 0.4.0
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 0.4.0
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: reflexion
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 0.6.0
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 0.6.0
|
|
69
|
+
description: Provides Reflex::Terminal, a headless terminal emulator model built on
|
|
70
|
+
libghostty-vt, and Reflex::TerminalView, a View that renders it in the Reflex GUI
|
|
71
|
+
toolkit.
|
|
72
|
+
email: xordog@gmail.com
|
|
73
|
+
executables: []
|
|
74
|
+
extensions:
|
|
75
|
+
- Rakefile
|
|
76
|
+
extra_rdoc_files:
|
|
77
|
+
- ".doc/ext/reflex-terminal/native.cpp"
|
|
78
|
+
- ".doc/ext/reflex-terminal/renderer.cpp"
|
|
79
|
+
- ".doc/ext/reflex-terminal/terminal.cpp"
|
|
80
|
+
files:
|
|
81
|
+
- ".doc/ext/reflex-terminal/native.cpp"
|
|
82
|
+
- ".doc/ext/reflex-terminal/renderer.cpp"
|
|
83
|
+
- ".doc/ext/reflex-terminal/terminal.cpp"
|
|
84
|
+
- ".github/PULL_REQUEST_TEMPLATE.md"
|
|
85
|
+
- ".github/workflows/release-gem.yml"
|
|
86
|
+
- ".github/workflows/tag.yml"
|
|
87
|
+
- ".github/workflows/test.yml"
|
|
88
|
+
- ".github/workflows/utils.rb"
|
|
89
|
+
- CONTRIBUTING.md
|
|
90
|
+
- ChangeLog.md
|
|
91
|
+
- Gemfile
|
|
92
|
+
- LICENSE
|
|
93
|
+
- README.md
|
|
94
|
+
- Rakefile
|
|
95
|
+
- VERSION
|
|
96
|
+
- examples/terminal.rb
|
|
97
|
+
- ext/reflex-terminal/defs.h
|
|
98
|
+
- ext/reflex-terminal/extconf.rb
|
|
99
|
+
- ext/reflex-terminal/native.cpp
|
|
100
|
+
- ext/reflex-terminal/renderer.cpp
|
|
101
|
+
- ext/reflex-terminal/terminal.cpp
|
|
102
|
+
- include/reflex-terminal.h
|
|
103
|
+
- include/reflex-terminal/defs.h
|
|
104
|
+
- include/reflex-terminal/renderer.h
|
|
105
|
+
- include/reflex-terminal/ruby.h
|
|
106
|
+
- include/reflex-terminal/ruby/renderer.h
|
|
107
|
+
- include/reflex-terminal/ruby/terminal.h
|
|
108
|
+
- include/reflex/terminal.h
|
|
109
|
+
- lib/reflex-terminal.rb
|
|
110
|
+
- lib/reflex-terminal/ext.rb
|
|
111
|
+
- lib/reflex-terminal/extension.rb
|
|
112
|
+
- lib/reflex/bell_event.rb
|
|
113
|
+
- lib/reflex/terminal.rb
|
|
114
|
+
- lib/reflex/terminal_view.rb
|
|
115
|
+
- reflex-terminal.gemspec
|
|
116
|
+
- src/pty.cpp
|
|
117
|
+
- src/renderer.cpp
|
|
118
|
+
- src/terminal.cpp
|
|
119
|
+
- src/terminal.h
|
|
120
|
+
- src/win32/pty.cpp
|
|
121
|
+
- test/helper.rb
|
|
122
|
+
- test/test_renderer.rb
|
|
123
|
+
- test/test_terminal.rb
|
|
124
|
+
- test/test_terminal_view.rb
|
|
125
|
+
homepage: https://github.com/xord/reflex-terminal
|
|
126
|
+
licenses:
|
|
127
|
+
- MIT
|
|
128
|
+
metadata: {}
|
|
129
|
+
post_install_message:
|
|
130
|
+
rdoc_options: []
|
|
131
|
+
require_paths:
|
|
132
|
+
- lib
|
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: 3.0.0
|
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
|
+
requirements:
|
|
140
|
+
- - ">="
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: '0'
|
|
143
|
+
requirements: []
|
|
144
|
+
rubygems_version: 3.4.19
|
|
145
|
+
signing_key:
|
|
146
|
+
specification_version: 4
|
|
147
|
+
summary: A terminal emulator view for Reflex.
|
|
148
|
+
test_files:
|
|
149
|
+
- test/helper.rb
|
|
150
|
+
- test/test_renderer.rb
|
|
151
|
+
- test/test_terminal.rb
|
|
152
|
+
- test/test_terminal_view.rb
|