fatty 0.99.0 → 0.99.3
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/CHANGELOG.org +11 -29
- data/README.md +2207 -18
- data/README.org +2066 -104
- data/exe/fatty +236 -93
- data/help/help.md +233 -0
- data/lib/fatty/action.rb +3 -7
- data/lib/fatty/actionable.rb +2 -0
- data/lib/fatty/alert.rb +65 -39
- data/lib/fatty/ansi/renderer.rb +26 -6
- data/lib/fatty/ansi.rb +1 -0
- data/lib/fatty/api/alert.rb +15 -0
- data/lib/fatty/api/choose.rb +145 -0
- data/lib/fatty/api/environment.rb +12 -0
- data/lib/fatty/api/keytest.rb +11 -0
- data/lib/fatty/api/menu.rb +104 -0
- data/lib/fatty/api/output.rb +55 -0
- data/lib/fatty/api/progress.rb +24 -0
- data/lib/fatty/api/prompt.rb +77 -0
- data/lib/fatty/api/status.rb +55 -0
- data/lib/fatty/api.rb +38 -0
- data/lib/fatty/callback_environment.rb +74 -0
- data/lib/fatty/colors/color.rb +366 -232
- data/lib/fatty/colors/pairs.rb +2 -0
- data/lib/fatty/command.rb +78 -0
- data/lib/fatty/config.rb +227 -39
- data/lib/fatty/config_files/config.yml +4 -12
- data/lib/fatty/config_files/keydefs.yml +13 -23
- data/lib/fatty/config_files/themes/capuchin_monk.yml +140 -0
- data/lib/fatty/config_files/themes/catppuccin_latte.yml +140 -0
- data/lib/fatty/config_files/themes/catppuccin_mocha.yml +140 -0
- data/lib/fatty/config_files/themes/cyberpunk.yml +141 -0
- data/lib/fatty/config_files/themes/dracula.yml +140 -0
- data/lib/fatty/config_files/themes/everforest_dark.yml +140 -0
- data/lib/fatty/config_files/themes/gruvbox_dark.yml +140 -0
- data/lib/fatty/config_files/themes/gruvbox_light.yml +140 -0
- data/lib/fatty/config_files/themes/mono.yml +119 -47
- data/lib/fatty/config_files/themes/monokai.yml +140 -0
- data/lib/fatty/config_files/themes/nordic.yml +1 -0
- data/lib/fatty/config_files/themes/onedark.yml +140 -0
- data/lib/fatty/config_files/themes/solarized_dark.yml +1 -0
- data/lib/fatty/config_files/themes/solarized_light.yml +140 -0
- data/lib/fatty/config_files/themes/terminal.yml +67 -0
- data/lib/fatty/config_files/themes/tokyo_night.yml +140 -0
- data/lib/fatty/config_files/themes/wordperfect.yml +80 -9
- data/lib/fatty/core_ext/hash.rb +19 -0
- data/lib/fatty/core_ext/string.rb +19 -12
- data/lib/fatty/core_ext.rb +1 -0
- data/lib/fatty/curses/context.rb +86 -84
- data/lib/fatty/curses/event_source.rb +128 -40
- data/lib/fatty/curses/key_decoder.rb +77 -11
- data/lib/fatty/env.rb +21 -20
- data/lib/fatty/help.rb +1 -29
- data/lib/fatty/history/entry.rb +2 -0
- data/lib/fatty/history.rb +17 -14
- data/lib/fatty/input_buffer.rb +71 -47
- data/lib/fatty/input_field.rb +216 -149
- data/lib/fatty/key_event.rb +67 -66
- data/lib/fatty/key_map.rb +54 -16
- data/lib/fatty/keymaps/emacs.rb +40 -6
- data/lib/fatty/log_formats/json.rb +2 -0
- data/lib/fatty/log_formats/text.rb +2 -0
- data/lib/fatty/logger.rb +7 -2
- data/lib/fatty/markdown/ansi_renderer.rb +75 -6
- data/lib/fatty/markdown/render.rb +2 -3
- data/lib/fatty/mouse_event.rb +142 -2
- data/lib/fatty/output_buffer.rb +2 -0
- data/lib/fatty/pager.rb +158 -47
- data/lib/fatty/progress.rb +417 -0
- data/lib/fatty/renderer/curses.rb +340 -184
- data/lib/fatty/renderer/truecolor.rb +266 -163
- data/lib/fatty/renderer.rb +115 -97
- data/lib/fatty/screen.rb +2 -0
- data/lib/fatty/session/alert_session.rb +39 -21
- data/lib/fatty/session/isearch_session.rb +92 -38
- data/lib/fatty/session/keytest_session.rb +105 -85
- data/lib/fatty/session/modal_session.rb +38 -1
- data/lib/fatty/session/output_session.rb +472 -21
- data/lib/fatty/session/popup_session.rb +251 -134
- data/lib/fatty/session/prompt_session.rb +68 -26
- data/lib/fatty/session/search_session.rb +79 -31
- data/lib/fatty/session/shell_session.rb +383 -368
- data/lib/fatty/session/status_session.rb +167 -0
- data/lib/fatty/session.rb +95 -95
- data/lib/fatty/terminal/popup_owner.rb +14 -11
- data/lib/fatty/terminal.rb +515 -813
- data/lib/fatty/themes/loader.rb +4 -2
- data/lib/fatty/themes/manager.rb +4 -0
- data/lib/fatty/themes/registry.rb +2 -0
- data/lib/fatty/themes/resolver.rb +175 -162
- data/lib/fatty/version.rb +1 -1
- data/lib/fatty/viewport.rb +8 -1
- data/lib/fatty.rb +35 -7
- metadata +42 -40
- data/.envrc +0 -2
- data/.simplecov +0 -23
- data/.yardopts +0 -4
- data/CHANGELOG.md +0 -34
- data/Rakefile +0 -15
- data/TODO.org +0 -163
- data/examples/markdown/native-markdown.md +0 -370
- data/examples/markdown/ox-gfm-markdown.md +0 -373
- data/examples/markdown/ox-gfm-markdown.org +0 -376
- data/fatty.gemspec +0 -42
- data/lib/fatty/accept_env.rb +0 -32
- data/lib/fatty/config_files/help.md +0 -120
- data/lib/fatty/config_files/help.org +0 -124
- data/lib/fatty/config_files/themes/wordperfect_light.yml +0 -77
- data/lib/fatty/menu_env.rb +0 -22
- data/lib/fatty/session/input_session.rb +0 -99
- data/lib/fatty/sessions.rb +0 -14
- data/lib/fatty/terminal/progress.rb +0 -374
- data/lib/fatty/view/alert_view.rb +0 -14
- data/lib/fatty/view/cursor_view.rb +0 -18
- data/lib/fatty/view/input_view.rb +0 -9
- data/lib/fatty/view/output_view.rb +0 -9
- data/lib/fatty/view/status_view.rb +0 -14
- data/lib/fatty/view.rb +0 -33
- data/lib/fatty/views.rb +0 -9
- data/sig/fatty.rbs +0 -4
|
@@ -9,38 +9,26 @@ module Fatty
|
|
|
9
9
|
super
|
|
10
10
|
@ansi_renderer = Fatty::Ansi::Renderer.new
|
|
11
11
|
@pending_ansi_draws = []
|
|
12
|
+
@cursor_visible = true
|
|
12
13
|
end
|
|
13
14
|
|
|
14
|
-
def render_status(
|
|
15
|
-
state = status_state(
|
|
15
|
+
def render_status(status_session)
|
|
16
|
+
state = status_state(status_session)
|
|
16
17
|
return if state == @last_status_state
|
|
17
18
|
|
|
18
19
|
@last_status_state = state
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
text,
|
|
22
|
-
width: screen.status_rect.cols,
|
|
23
|
-
max_rows: screen.status_rect.rows,
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
screen.status_rect.rows.times do |idx|
|
|
27
|
-
queue_ansi_line(
|
|
28
|
-
row: screen.status_rect.row + idx,
|
|
29
|
-
col: screen.status_rect.col,
|
|
30
|
-
width: screen.status_rect.cols,
|
|
31
|
-
text: lines[idx].to_s,
|
|
32
|
-
role: role,
|
|
33
|
-
)
|
|
34
|
-
end
|
|
21
|
+
draw_status_lines(status_session)
|
|
35
22
|
end
|
|
36
23
|
|
|
37
|
-
def render_alert(
|
|
38
|
-
state = alert_state(
|
|
24
|
+
def render_alert(alert_session)
|
|
25
|
+
state = alert_state(alert_session)
|
|
39
26
|
return if state == @last_alert_state
|
|
40
27
|
|
|
28
|
+
alert = alert_session.current
|
|
41
29
|
@last_alert_state = state
|
|
42
30
|
text = alert ? alert.format : ""
|
|
43
|
-
role = alert ? alert.role : :alert_info
|
|
31
|
+
role = alert ? alert_role(alert.role) : :alert_info
|
|
44
32
|
|
|
45
33
|
queue_ansi_line(
|
|
46
34
|
row: screen.alert_rect.row,
|
|
@@ -51,18 +39,24 @@ module Fatty
|
|
|
51
39
|
)
|
|
52
40
|
end
|
|
53
41
|
|
|
54
|
-
def render_output(
|
|
55
|
-
lines = viewport.slice(
|
|
56
|
-
normalized = normalized_highlights(highlights)
|
|
42
|
+
def render_output(output_session, viewport: output_session.viewport)
|
|
43
|
+
lines = viewport.slice(output_session.visible_lines)
|
|
44
|
+
normalized = normalized_highlights(output_session.highlights)
|
|
45
|
+
line_number_width =
|
|
46
|
+
if output_session.line_numbers?
|
|
47
|
+
output_session.output.lines.length.to_s.length
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
curr = output_state(output_session, viewport: viewport)
|
|
51
|
+
return if curr == @last_output_state
|
|
57
52
|
|
|
58
|
-
|
|
53
|
+
draw_output_lines(
|
|
54
|
+
lines,
|
|
59
55
|
viewport: viewport,
|
|
60
|
-
lines: lines,
|
|
61
56
|
highlights: normalized,
|
|
57
|
+
line_number_width: line_number_width,
|
|
62
58
|
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
draw_output_lines(lines, viewport: viewport, highlights: normalized)
|
|
59
|
+
@last_pager_field_state = nil
|
|
66
60
|
@last_output_state = curr
|
|
67
61
|
end
|
|
68
62
|
|
|
@@ -85,12 +79,22 @@ module Fatty
|
|
|
85
79
|
)
|
|
86
80
|
end
|
|
87
81
|
|
|
82
|
+
def clear_input_field
|
|
83
|
+
@last_input_state = nil
|
|
84
|
+
queue_ansi_line(
|
|
85
|
+
row: screen.input_rect.row,
|
|
86
|
+
col: screen.input_rect.col,
|
|
87
|
+
width: screen.input_rect.cols,
|
|
88
|
+
text: "",
|
|
89
|
+
role: :input,
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
88
93
|
def render_pager_field(field, row:, role: :pager_status)
|
|
89
94
|
state = pager_field_state(field, row: row, role: role)
|
|
90
95
|
return if state == @last_pager_field_state
|
|
91
96
|
|
|
92
97
|
@last_pager_field_state = state
|
|
93
|
-
|
|
94
98
|
win = context.output_win
|
|
95
99
|
return unless win
|
|
96
100
|
|
|
@@ -98,7 +102,6 @@ module Fatty
|
|
|
98
102
|
return unless row0 && col0
|
|
99
103
|
|
|
100
104
|
cols = win.respond_to?(:maxx) ? win.maxx : @screen.cols
|
|
101
|
-
|
|
102
105
|
queue_ansi_segments_line(
|
|
103
106
|
row: row0 + row,
|
|
104
107
|
col: col0,
|
|
@@ -113,12 +116,14 @@ module Fatty
|
|
|
113
116
|
)
|
|
114
117
|
end
|
|
115
118
|
|
|
119
|
+
# Modal overlays, render_popup and render_prompt_popu are deliberately
|
|
120
|
+
# not state-cached.
|
|
121
|
+
#
|
|
122
|
+
# They must be restaged on every frame because they are drawn on top of the
|
|
123
|
+
# focused session. Even when the overlay's own state has not changed, the
|
|
124
|
+
# underlying session may have redrawn the area beneath it. Skipping the overlay
|
|
125
|
+
# render would let the underlying frame erase or visually punch through it.
|
|
116
126
|
def render_popup(session:)
|
|
117
|
-
state = popup_state(session)
|
|
118
|
-
return if state == @last_popup_state
|
|
119
|
-
|
|
120
|
-
@last_popup_state = state
|
|
121
|
-
|
|
122
127
|
win = session.win
|
|
123
128
|
return unless win
|
|
124
129
|
|
|
@@ -140,33 +145,43 @@ module Fatty
|
|
|
140
145
|
layout = PopupLayout.new(row: 0, width: inner_w)
|
|
141
146
|
row = render_popup_message(session: session, layout: layout)
|
|
142
147
|
|
|
143
|
-
counts_present = !!session.counts
|
|
144
|
-
input_row = inner_h - 1
|
|
145
|
-
counts_row = counts_present ? input_row - 1 : nil
|
|
146
|
-
|
|
147
148
|
# Draw the displayed items inside the window with a gutter to have an
|
|
148
149
|
# indicator of what is selected, if any.
|
|
149
|
-
|
|
150
|
+
filter_present = session.filter_present?
|
|
151
|
+
counts_present = session.counts_present?
|
|
152
|
+
input_row = filter_present ? inner_h - 1 : nil
|
|
153
|
+
counts_row =
|
|
154
|
+
if counts_present
|
|
155
|
+
filter_present ? input_row - 1 : inner_h - 1
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
list_end =
|
|
159
|
+
if counts_present
|
|
160
|
+
counts_row
|
|
161
|
+
elsif filter_present
|
|
162
|
+
input_row
|
|
163
|
+
else
|
|
164
|
+
inner_h
|
|
165
|
+
end
|
|
166
|
+
list_h = [list_end - row, 0].max
|
|
167
|
+
|
|
150
168
|
layout = PopupLayout.new(row: row, width: inner_w, height: list_h)
|
|
151
169
|
render_popup_items(session: session, layout: layout)
|
|
152
170
|
|
|
153
171
|
# Draw the "counts" line to indicate the total number of items, items
|
|
154
172
|
# selected, and items displayed.
|
|
155
173
|
layout = PopupLayout.new(row: counts_row, width: inner_w)
|
|
156
|
-
render_popup_counts(session: session, layout: layout) if counts_present
|
|
174
|
+
render_popup_counts(session: session, layout: layout) if session.counts_present?
|
|
157
175
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
176
|
+
if filter_present
|
|
177
|
+
# Draw the input field for the user to type narrowing selection
|
|
178
|
+
# queries.
|
|
179
|
+
layout = PopupLayout.new(row: input_row, width: inner_w)
|
|
180
|
+
render_popup_input_field(session: session, layout: layout)
|
|
181
|
+
end
|
|
162
182
|
end
|
|
163
183
|
|
|
164
184
|
def render_prompt_popup(session:)
|
|
165
|
-
state = prompt_popup_state(session)
|
|
166
|
-
return if state == @last_prompt_popup_state
|
|
167
|
-
|
|
168
|
-
@last_prompt_popup_state = state
|
|
169
|
-
|
|
170
185
|
win = session.win
|
|
171
186
|
return unless win
|
|
172
187
|
|
|
@@ -184,6 +199,7 @@ module Fatty
|
|
|
184
199
|
row = render_popup_message(session: session, layout: layout)
|
|
185
200
|
|
|
186
201
|
layout = PopupLayout.new(row: row, width: inner_w)
|
|
202
|
+
show_cursor
|
|
187
203
|
render_popup_input_field(session: session, layout: layout)
|
|
188
204
|
rescue RuntimeError => e
|
|
189
205
|
raise unless e.message.include?("closed window") ||
|
|
@@ -208,11 +224,27 @@ module Fatty
|
|
|
208
224
|
end
|
|
209
225
|
|
|
210
226
|
def restore_cursor(field)
|
|
227
|
+
return self unless @cursor_visible
|
|
228
|
+
|
|
211
229
|
rect = screen.input_rect
|
|
212
230
|
x = field.cursor_x.to_i.clamp(0, [rect.cols - 1, 0].max)
|
|
213
231
|
queue_ansi_cursor(row: rect.row, col: rect.col + x)
|
|
214
232
|
end
|
|
215
233
|
|
|
234
|
+
def show_cursor
|
|
235
|
+
@cursor_visible = true
|
|
236
|
+
::Curses.curs_set(1)
|
|
237
|
+
@ansi_renderer.write_ansi(@ansi_renderer.hide_cursor)
|
|
238
|
+
self
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def hide_cursor
|
|
242
|
+
@cursor_visible = false
|
|
243
|
+
::Curses.curs_set(0)
|
|
244
|
+
@ansi_renderer.write_ansi(@ansi_renderer.hide_cursor)
|
|
245
|
+
self
|
|
246
|
+
end
|
|
247
|
+
|
|
216
248
|
# In truecolor mode, curses windows must still have themed backgrounds.
|
|
217
249
|
# ncurses may repaint or expose its backing store during getch/doupdate,
|
|
218
250
|
# so we must keep the backing store visually consistent with ANSI output.
|
|
@@ -229,8 +261,28 @@ module Fatty
|
|
|
229
261
|
self
|
|
230
262
|
end
|
|
231
263
|
|
|
264
|
+
# Restore cursor into the output window at a specific *output-win* row.
|
|
265
|
+
# `row:` is 0..(screen.output_rect.rows-1), NOT an absolute screen row.
|
|
266
|
+
def restore_output_cursor(field, row:)
|
|
267
|
+
return self unless @cursor_visible
|
|
268
|
+
|
|
269
|
+
cols = @screen.cols
|
|
270
|
+
x = field.cursor_x.to_i
|
|
271
|
+
x = x.clamp(0, [cols - 1, 0].max)
|
|
272
|
+
row0 = @screen.output_rect.row
|
|
273
|
+
col0 = @screen.output_rect.col
|
|
274
|
+
cols = @screen.output_rect.cols
|
|
275
|
+
@pending_ansi_draws << {
|
|
276
|
+
type: :cursor,
|
|
277
|
+
row: row0 + row,
|
|
278
|
+
col: col0 + x.clamp(0, [cols - 1, 0].max),
|
|
279
|
+
}
|
|
280
|
+
end
|
|
281
|
+
|
|
232
282
|
private
|
|
233
283
|
|
|
284
|
+
# simplecov:disable
|
|
285
|
+
|
|
234
286
|
def sync_window_background(win, role)
|
|
235
287
|
return unless win
|
|
236
288
|
|
|
@@ -332,24 +384,32 @@ module Fatty
|
|
|
332
384
|
return unless win
|
|
333
385
|
|
|
334
386
|
items = session.displayed
|
|
335
|
-
|
|
387
|
+
selected = session.current
|
|
336
388
|
start = session.scroll_start(list_h: layout.height)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
line =
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
389
|
+
|
|
390
|
+
(0...layout.height).each do |offset|
|
|
391
|
+
item_index = start + offset
|
|
392
|
+
item_selected = item_index == selected
|
|
393
|
+
role = item_selected ? :popup_selection : :popup
|
|
394
|
+
|
|
395
|
+
line = popup_item_text(
|
|
396
|
+
session,
|
|
397
|
+
items: items,
|
|
398
|
+
item_index: item_index,
|
|
399
|
+
selected: item_selected,
|
|
400
|
+
width: layout.width,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
queue_ansi_popup_fill_row(
|
|
404
|
+
win: win,
|
|
405
|
+
inner_row: layout.row + offset,
|
|
406
|
+
width: layout.width,
|
|
407
|
+
role: role,
|
|
408
|
+
)
|
|
409
|
+
|
|
350
410
|
queue_ansi_popup_line(
|
|
351
411
|
win: win,
|
|
352
|
-
inner_row: row +
|
|
412
|
+
inner_row: layout.row + offset,
|
|
353
413
|
width: layout.width,
|
|
354
414
|
text: line,
|
|
355
415
|
role: role,
|
|
@@ -357,6 +417,21 @@ module Fatty
|
|
|
357
417
|
end
|
|
358
418
|
end
|
|
359
419
|
|
|
420
|
+
def popup_item_text(session, items:, item_index:, selected:, width:)
|
|
421
|
+
line = ""
|
|
422
|
+
|
|
423
|
+
if item_index < items.length
|
|
424
|
+
item = items[item_index]
|
|
425
|
+
gutter = session.gutter_for(item: item, selected: selected)
|
|
426
|
+
text = Fatty::Ansi.strip(item.to_s).tr("\r\n", " ")
|
|
427
|
+
available = [width - Fatty::Ansi.visible_length(gutter), 0].max
|
|
428
|
+
text = Fatty::Ansi.truncate_visible(text, available)
|
|
429
|
+
line = Fatty::Ansi.truncate_visible(gutter + text, width)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
line
|
|
433
|
+
end
|
|
434
|
+
|
|
360
435
|
def render_popup_counts(session:, layout:)
|
|
361
436
|
win = session.win
|
|
362
437
|
return unless win
|
|
@@ -378,6 +453,14 @@ module Fatty
|
|
|
378
453
|
|
|
379
454
|
row = win.origin[0] + 1 + layout.row
|
|
380
455
|
col = win.origin[1] + 1
|
|
456
|
+
|
|
457
|
+
queue_ansi_popup_fill_row(
|
|
458
|
+
win: win,
|
|
459
|
+
inner_row: layout.row,
|
|
460
|
+
width: layout.width,
|
|
461
|
+
role: :popup_input,
|
|
462
|
+
)
|
|
463
|
+
|
|
381
464
|
queue_ansi_segments_line(
|
|
382
465
|
row: row,
|
|
383
466
|
col: col,
|
|
@@ -390,6 +473,7 @@ module Fatty
|
|
|
390
473
|
),
|
|
391
474
|
fill_role: :popup_input,
|
|
392
475
|
)
|
|
476
|
+
|
|
393
477
|
cursor_x = session.field.cursor_x.to_i.clamp(0, [layout.width - 1, 0].max)
|
|
394
478
|
queue_ansi_cursor(row: row, col: col + cursor_x)
|
|
395
479
|
end
|
|
@@ -421,24 +505,50 @@ module Fatty
|
|
|
421
505
|
}
|
|
422
506
|
end
|
|
423
507
|
|
|
424
|
-
def draw_output_lines(lines, viewport:, highlights: nil)
|
|
508
|
+
def draw_output_lines(lines, viewport:, highlights: nil, line_number_width: nil)
|
|
425
509
|
rect = screen.output_rect
|
|
426
510
|
|
|
427
511
|
rect.rows.times do |y|
|
|
428
|
-
|
|
429
|
-
|
|
512
|
+
visible_line = lines[y]
|
|
513
|
+
text = visible_line&.text.to_s
|
|
514
|
+
abs_line = visible_line ? visible_line.number - 1 : nil
|
|
430
515
|
ranges = highlight_ranges_for_line(highlights, abs_line)
|
|
516
|
+
segments = []
|
|
517
|
+
|
|
518
|
+
if visible_line && line_number_width
|
|
519
|
+
number_text = visible_line.number.to_s.rjust(line_number_width, "0")
|
|
520
|
+
segments << {
|
|
521
|
+
text: "#{number_text}: ",
|
|
522
|
+
role: :line_number,
|
|
523
|
+
}
|
|
524
|
+
end
|
|
431
525
|
|
|
526
|
+
segments.concat(output_segments(text, ranges: ranges))
|
|
432
527
|
queue_ansi_segments_line(
|
|
433
528
|
row: rect.row + y,
|
|
434
529
|
col: rect.col,
|
|
435
530
|
width: rect.cols,
|
|
436
|
-
segments:
|
|
531
|
+
segments: segments,
|
|
437
532
|
fill_role: :output,
|
|
438
533
|
)
|
|
439
534
|
end
|
|
440
535
|
end
|
|
441
536
|
|
|
537
|
+
def draw_status_lines(status_session)
|
|
538
|
+
rect = screen.status_rect
|
|
539
|
+
lines = status_segment_lines(status_session)
|
|
540
|
+
|
|
541
|
+
rect.rows.times do |y|
|
|
542
|
+
queue_ansi_segments_line(
|
|
543
|
+
row: rect.row + y,
|
|
544
|
+
col: rect.col,
|
|
545
|
+
width: rect.cols,
|
|
546
|
+
segments: lines[y] || [],
|
|
547
|
+
fill_role: status_session.role,
|
|
548
|
+
)
|
|
549
|
+
end
|
|
550
|
+
end
|
|
551
|
+
|
|
442
552
|
def output_segments(line, ranges:)
|
|
443
553
|
plain = Fatty::Ansi.plain_text(line.to_s)
|
|
444
554
|
base_segments = []
|
|
@@ -495,113 +605,106 @@ module Fatty
|
|
|
495
605
|
|
|
496
606
|
out.reject { |seg| seg[:text].empty? }
|
|
497
607
|
end
|
|
498
|
-
end
|
|
499
608
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
cols = @screen.cols
|
|
504
|
-
|
|
505
|
-
x = field.cursor_x.to_i
|
|
506
|
-
x = x.clamp(0, [cols - 1, 0].max)
|
|
507
|
-
|
|
508
|
-
row0 = @screen.output_rect.row
|
|
509
|
-
col0 = @screen.output_rect.col
|
|
510
|
-
cols = @screen.output_rect.cols
|
|
511
|
-
|
|
512
|
-
@pending_ansi_draws << {
|
|
513
|
-
type: :cursor,
|
|
514
|
-
row: row0 + row,
|
|
515
|
-
col: col0 + x.clamp(0, [cols - 1, 0].max),
|
|
516
|
-
}
|
|
517
|
-
end
|
|
518
|
-
|
|
519
|
-
def queue_ansi_popup_line(win:, inner_row:, inner_col: 0, width:, text:, role:)
|
|
520
|
-
row, col = win.origin
|
|
521
|
-
return unless row && col
|
|
522
|
-
|
|
523
|
-
queue_ansi_line(
|
|
524
|
-
row: row + 1 + inner_row,
|
|
525
|
-
col: col + 1 + inner_col,
|
|
526
|
-
width: width,
|
|
527
|
-
text: text.to_s,
|
|
528
|
-
role: role,
|
|
529
|
-
)
|
|
530
|
-
end
|
|
531
|
-
|
|
532
|
-
def queue_ansi_segments_line(row:, col:, width:, segments:, fill_role: :output)
|
|
533
|
-
@pending_ansi_draws << {
|
|
534
|
-
type: :segments_line,
|
|
535
|
-
row: row,
|
|
536
|
-
col: col,
|
|
537
|
-
width: width,
|
|
538
|
-
segments: segments,
|
|
539
|
-
fill_role: fill_role,
|
|
540
|
-
}
|
|
541
|
-
nil
|
|
542
|
-
end
|
|
543
|
-
|
|
544
|
-
def queue_ansi_line(row:, col:, width:, text:, role: nil)
|
|
545
|
-
spec = palette[role] || {}
|
|
546
|
-
@pending_ansi_draws << {
|
|
547
|
-
row: row,
|
|
548
|
-
col: col,
|
|
549
|
-
width: width,
|
|
550
|
-
text: text,
|
|
551
|
-
role: role,
|
|
552
|
-
spec: spec,
|
|
553
|
-
}
|
|
554
|
-
end
|
|
555
|
-
|
|
556
|
-
def queue_ansi_rect(row:, col:, width:, height:, role:)
|
|
557
|
-
return if height <= 0 || width <= 0
|
|
609
|
+
def queue_ansi_popup_line(win:, inner_row:, inner_col: 0, width:, text:, role:)
|
|
610
|
+
row, col = win.origin
|
|
611
|
+
return unless row && col
|
|
558
612
|
|
|
559
|
-
height.times do |i|
|
|
560
613
|
queue_ansi_line(
|
|
561
|
-
row: row +
|
|
614
|
+
row: row + 1 + inner_row,
|
|
615
|
+
col: col + 1 + inner_col,
|
|
616
|
+
width: width,
|
|
617
|
+
text: text.to_s,
|
|
618
|
+
role: role,
|
|
619
|
+
)
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
def queue_ansi_segments_line(row:, col:, width:, segments:, fill_role: :output)
|
|
623
|
+
@pending_ansi_draws << {
|
|
624
|
+
type: :segments_line,
|
|
625
|
+
row: row,
|
|
562
626
|
col: col,
|
|
563
627
|
width: width,
|
|
628
|
+
segments: segments,
|
|
629
|
+
fill_role: fill_role,
|
|
630
|
+
}
|
|
631
|
+
nil
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
def queue_ansi_popup_fill_row(win:, inner_row:, inner_col: 0, width:, role:)
|
|
635
|
+
queue_ansi_popup_line(
|
|
636
|
+
win: win,
|
|
637
|
+
inner_row: inner_row,
|
|
638
|
+
inner_col: inner_col,
|
|
639
|
+
width: width,
|
|
564
640
|
text: " " * width,
|
|
565
641
|
role: role,
|
|
566
642
|
)
|
|
567
643
|
end
|
|
568
|
-
nil
|
|
569
|
-
end
|
|
570
644
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
col: draw[:col],
|
|
594
|
-
width: draw[:width],
|
|
595
|
-
text: draw[:text],
|
|
596
|
-
role: draw[:role],
|
|
597
|
-
palette: palette,
|
|
645
|
+
def queue_ansi_line(row:, col:, width:, text:, role: nil)
|
|
646
|
+
spec = palette[role] || {}
|
|
647
|
+
@pending_ansi_draws << {
|
|
648
|
+
row: row,
|
|
649
|
+
col: col,
|
|
650
|
+
width: width,
|
|
651
|
+
text: text,
|
|
652
|
+
role: role,
|
|
653
|
+
spec: spec,
|
|
654
|
+
}
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
def queue_ansi_rect(row:, col:, width:, height:, role:)
|
|
658
|
+
return if height <= 0 || width <= 0
|
|
659
|
+
|
|
660
|
+
height.times do |i|
|
|
661
|
+
queue_ansi_line(
|
|
662
|
+
row: row + i,
|
|
663
|
+
col: col,
|
|
664
|
+
width: width,
|
|
665
|
+
text: " " * width,
|
|
666
|
+
role: role,
|
|
598
667
|
)
|
|
599
668
|
end
|
|
669
|
+
nil
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
def flush_ansi_draws
|
|
673
|
+
Fatty.debug("flush_ansi_draws pending_count=#{@pending_ansi_draws.length}", tag: :render)
|
|
674
|
+
|
|
675
|
+
# Hide the cursor
|
|
676
|
+
@ansi_renderer.write_ansi("\e[?25l")
|
|
677
|
+
|
|
678
|
+
@pending_ansi_draws.each do |draw|
|
|
679
|
+
case draw[:type]
|
|
680
|
+
when :cursor
|
|
681
|
+
if @cursor_visible
|
|
682
|
+
@ansi_renderer.write_ansi("\e[#{draw[:row] + 1};#{draw[:col] + 1}H")
|
|
683
|
+
end
|
|
684
|
+
when :segments_line
|
|
685
|
+
@ansi_renderer.render_segments_line(
|
|
686
|
+
row: draw[:row],
|
|
687
|
+
col: draw[:col],
|
|
688
|
+
width: draw[:width],
|
|
689
|
+
segments: draw[:segments],
|
|
690
|
+
palette: palette,
|
|
691
|
+
fill_role: draw[:fill_role] || :output,
|
|
692
|
+
)
|
|
693
|
+
else
|
|
694
|
+
@ansi_renderer.render_line(
|
|
695
|
+
row: draw[:row],
|
|
696
|
+
col: draw[:col],
|
|
697
|
+
width: draw[:width],
|
|
698
|
+
text: draw[:text],
|
|
699
|
+
role: draw[:role],
|
|
700
|
+
palette: palette,
|
|
701
|
+
)
|
|
702
|
+
end
|
|
703
|
+
end
|
|
704
|
+
@pending_ansi_draws.clear
|
|
705
|
+
ensure
|
|
706
|
+
@ansi_renderer.write_ansi(@cursor_visible ? "\e[?25h" : "\e[?25l")
|
|
600
707
|
end
|
|
601
|
-
@pending_ansi_draws.clear
|
|
602
|
-
ensure
|
|
603
|
-
# Unhide the cursor
|
|
604
|
-
@ansi_renderer.write_ansi("\e[?25h")
|
|
605
708
|
end
|
|
606
709
|
end
|
|
607
710
|
end
|