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
|
@@ -2,20 +2,384 @@
|
|
|
2
2
|
|
|
3
3
|
module Fatty
|
|
4
4
|
class OutputSession < Session
|
|
5
|
+
VisibleLine = Data.define(:number, :text) do
|
|
6
|
+
def to_s
|
|
7
|
+
text.to_s
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
action_on :session
|
|
12
|
+
|
|
5
13
|
attr_reader :output, :viewport, :pager, :pager_field
|
|
6
14
|
|
|
7
|
-
def initialize(keymap: nil,
|
|
8
|
-
super(keymap: keymap
|
|
15
|
+
def initialize(id: nil, keymap: nil, history: nil)
|
|
16
|
+
super(keymap: keymap)
|
|
9
17
|
@output = Fatty::OutputBuffer.new(max_lines: 500_000)
|
|
10
18
|
@viewport = Fatty::Viewport.new(height: 10)
|
|
19
|
+
@history = history || Fatty::History.for_path(:default)
|
|
20
|
+
@line_numbers = false
|
|
21
|
+
@narrow_query = nil
|
|
22
|
+
@visible_lines = nil
|
|
11
23
|
mode = Fatty::Config.config.dig(:output, :mode)&.to_sym || :paging
|
|
12
24
|
@default_output_mode = mode
|
|
13
|
-
@pager = Fatty::Pager.new(output: @output, viewport: @viewport, mode: mode)
|
|
25
|
+
@pager = Fatty::Pager.new(output: @output, viewport: @viewport, mode: mode, lines: -> { visible_lines })
|
|
14
26
|
@pager_field = Fatty::InputField.new(prompt: -> { pager_status_prompt })
|
|
15
27
|
end
|
|
16
28
|
|
|
29
|
+
#########################################################################################
|
|
30
|
+
# Session Protocol
|
|
31
|
+
#########################################################################################
|
|
32
|
+
|
|
33
|
+
def init(terminal:)
|
|
34
|
+
super
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def update(command)
|
|
38
|
+
log_update(command)
|
|
39
|
+
payload = command.payload
|
|
40
|
+
commands =
|
|
41
|
+
case command.action
|
|
42
|
+
when :key
|
|
43
|
+
ev = payload.fetch(:event)
|
|
44
|
+
action, args = resolve_action(ev)
|
|
45
|
+
if action
|
|
46
|
+
apply_action(action, args, event: ev)
|
|
47
|
+
else
|
|
48
|
+
[]
|
|
49
|
+
end
|
|
50
|
+
when :append
|
|
51
|
+
case payload[:mode]
|
|
52
|
+
when :scrolling
|
|
53
|
+
pager.toggle_paging_mode if pager.mode == :paging
|
|
54
|
+
when :paging
|
|
55
|
+
pager.toggle_paging_mode if pager.mode == :scrolling
|
|
56
|
+
end
|
|
57
|
+
before = output.lines.length
|
|
58
|
+
append_output(
|
|
59
|
+
payload.fetch(:text, ""),
|
|
60
|
+
follow: payload.fetch(:follow, true),
|
|
61
|
+
)
|
|
62
|
+
reveal_appended_block(before) if payload[:scroll]
|
|
63
|
+
[]
|
|
64
|
+
when :set_mode
|
|
65
|
+
case payload.fetch(:mode).to_sym
|
|
66
|
+
when :scrolling
|
|
67
|
+
pager.set_to_scrolling
|
|
68
|
+
when :paging
|
|
69
|
+
pager.set_to_paging
|
|
70
|
+
end
|
|
71
|
+
[]
|
|
72
|
+
when :clear
|
|
73
|
+
reset_output!
|
|
74
|
+
[]
|
|
75
|
+
when :resize
|
|
76
|
+
resize_output!
|
|
77
|
+
[]
|
|
78
|
+
when :begin_command
|
|
79
|
+
reset_for_command!
|
|
80
|
+
pager.begin_command!(anchor: output.lines.length)
|
|
81
|
+
[]
|
|
82
|
+
when :finish_command
|
|
83
|
+
pager.finish_command!
|
|
84
|
+
[]
|
|
85
|
+
when :quit_paging
|
|
86
|
+
pager.quit
|
|
87
|
+
[Command.terminal(:refresh_layout)]
|
|
88
|
+
when :pager_search_set
|
|
89
|
+
update_pager_search_set(payload)
|
|
90
|
+
when :pager_search_step
|
|
91
|
+
update_pager_search_step(payload)
|
|
92
|
+
when :pager_search_cancel
|
|
93
|
+
pager.search_cancel!
|
|
94
|
+
[]
|
|
95
|
+
when :pager_search_commit
|
|
96
|
+
pager.search_commit!
|
|
97
|
+
[]
|
|
98
|
+
when :pager_isearch_update
|
|
99
|
+
update_pager_isearch_update(payload)
|
|
100
|
+
when :pager_isearch_step
|
|
101
|
+
update_pager_isearch_step(payload)
|
|
102
|
+
when :pager_isearch_cancel
|
|
103
|
+
pager.isearch_cancel!
|
|
104
|
+
[Command.session(:isearch, :isearch_set_failed, failed: false)]
|
|
105
|
+
when :pager_isearch_commit
|
|
106
|
+
update_pager_isearch_commit(payload)
|
|
107
|
+
when :prompt_result
|
|
108
|
+
apply_prompt_result(payload)
|
|
109
|
+
when :prompt_cancelled
|
|
110
|
+
[]
|
|
111
|
+
else
|
|
112
|
+
[]
|
|
113
|
+
end
|
|
114
|
+
Array(commands)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def view
|
|
118
|
+
Fatty.debug(
|
|
119
|
+
"OutputSession#view id=#{id} pager_active=#{pager_active?} output_rect=#{renderer.screen.output_rect.inspect}",
|
|
120
|
+
tag: :render,
|
|
121
|
+
)
|
|
122
|
+
if pager_active?
|
|
123
|
+
renderer.hide_cursor
|
|
124
|
+
renderer.render_output(self, viewport: pager_status_viewport(renderer.screen))
|
|
125
|
+
renderer.render_pager_field(
|
|
126
|
+
pager_field,
|
|
127
|
+
row: renderer.screen.output_rect.rows - 1,
|
|
128
|
+
role: :pager_status,
|
|
129
|
+
)
|
|
130
|
+
else
|
|
131
|
+
renderer.render_output(self)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def tick
|
|
136
|
+
dirty = false
|
|
137
|
+
if pager.autoscroll?
|
|
138
|
+
step = [(viewport.height * 3) / 4, 1].max
|
|
139
|
+
dirty ||= pager.autoscroll_step?(max_lines: step)
|
|
140
|
+
end
|
|
141
|
+
dirty
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
#########################################################################################
|
|
145
|
+
# Actions
|
|
146
|
+
#########################################################################################
|
|
147
|
+
|
|
148
|
+
desc "Search pager forward"
|
|
149
|
+
action :pager_search_forward do
|
|
150
|
+
regex = consume_search_regex_flag
|
|
151
|
+
pager.search_begin!
|
|
152
|
+
searcher = Fatty::SearchSession.new(direction: :forward, regex: regex, history: @history)
|
|
153
|
+
Command.terminal(:push_modal, session: searcher, owner: self)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
desc "Search pager backward"
|
|
157
|
+
action :pager_search_backward do
|
|
158
|
+
regex = consume_search_regex_flag
|
|
159
|
+
pager.search_begin!
|
|
160
|
+
searcher = Fatty::SearchSession.new(direction: :backward, regex: regex, history: @history)
|
|
161
|
+
Command.terminal(:push_modal, session: searcher, owner: self)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
desc "Search pager forward with regex"
|
|
165
|
+
action :pager_regex_search_forward do
|
|
166
|
+
pager.search_begin!
|
|
167
|
+
searcher = Fatty::SearchSession.new(direction: :forward, regex: true, history: @history)
|
|
168
|
+
Command.terminal(:push_modal, session: searcher, owner: self)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
desc "Search pager backward with regex"
|
|
172
|
+
action :pager_regex_search_backward do
|
|
173
|
+
pager.search_begin!
|
|
174
|
+
searcher = Fatty::SearchSession.new(direction: :backward, regex: true, history: @history)
|
|
175
|
+
Command.terminal(:push_modal, session: searcher, owner: self)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
desc "Start incremental pager search forward"
|
|
179
|
+
action :pager_isearch_forward do
|
|
180
|
+
pager.isearch_begin!
|
|
181
|
+
isearcher = Fatty::ISearchSession.new(direction: :forward, history: @history, last_pattern: pager.search_pattern)
|
|
182
|
+
Command.terminal(:push_modal, session: isearcher, owner: self)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
desc "Start incremental pager search backward"
|
|
186
|
+
action :pager_isearch_backward do
|
|
187
|
+
pager.isearch_begin!
|
|
188
|
+
isearcher = Fatty::ISearchSession.new(direction: :backward, history: @history, last_pattern: pager.search_pattern)
|
|
189
|
+
Command.terminal(:push_modal, session: isearcher, owner: self)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
desc "Repeat pager search forward"
|
|
193
|
+
action :pager_search_next do
|
|
194
|
+
handle_search_result(pager.search_repeat_next!)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
desc "Repeat pager search backward"
|
|
198
|
+
action :pager_search_prev do
|
|
199
|
+
handle_search_result(pager.search_repeat_prev!)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
desc "Toggle output line numbers"
|
|
203
|
+
action :toggle_line_numbers do
|
|
204
|
+
@line_numbers = !@line_numbers
|
|
205
|
+
[]
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
desc "Narrow output to matching lines"
|
|
209
|
+
action :narrow_output do
|
|
210
|
+
prompt = Fatty::PromptSession.new(
|
|
211
|
+
prompt: "Narrow: ",
|
|
212
|
+
title: "Narrow Output",
|
|
213
|
+
kind: :narrow_output,
|
|
214
|
+
history: @history,
|
|
215
|
+
history_ctx: :output_narrow,
|
|
216
|
+
)
|
|
217
|
+
Command.terminal(:push_modal, session: prompt, owner: self)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
desc "Widen output"
|
|
221
|
+
action :widen_output do
|
|
222
|
+
clear_narrowing!
|
|
223
|
+
[]
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
#########################################################################################
|
|
227
|
+
# Other public API methods
|
|
228
|
+
#########################################################################################
|
|
229
|
+
|
|
230
|
+
STATE_VIEWPORT = 0
|
|
231
|
+
STATE_LINES = 1
|
|
232
|
+
|
|
233
|
+
def state(viewport: @viewport)
|
|
234
|
+
[
|
|
235
|
+
viewport.state,
|
|
236
|
+
viewport.slice(visible_lines),
|
|
237
|
+
renderer.screen.output_rect.rows,
|
|
238
|
+
renderer.screen.output_rect.cols,
|
|
239
|
+
highlights,
|
|
240
|
+
renderer.theme_version,
|
|
241
|
+
line_numbers?,
|
|
242
|
+
]
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def visible_lines
|
|
246
|
+
@visible_lines ||=
|
|
247
|
+
if narrowed?
|
|
248
|
+
terms = narrow_query.split
|
|
249
|
+
|
|
250
|
+
output.lines.each_with_index.filter_map do |text, index|
|
|
251
|
+
visible_text = visible_output_text(text)
|
|
252
|
+
next unless terms.all? { |term| visible_text.include?(term) }
|
|
253
|
+
|
|
254
|
+
VisibleLine.new(number: index + 1, text: text)
|
|
255
|
+
end
|
|
256
|
+
else
|
|
257
|
+
output.lines.each_with_index.map do |text, index|
|
|
258
|
+
VisibleLine.new(number: index + 1, text: text)
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def visible_output_text(text)
|
|
264
|
+
Fatty::Ansi.segment(text.to_s).map(&:first).join
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def narrow_query
|
|
268
|
+
@narrow_query
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def narrowed?
|
|
272
|
+
!@narrow_query.nil?
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def apply_prompt_result(payload)
|
|
276
|
+
return [] unless payload[:kind].to_sym == :narrow_output
|
|
277
|
+
|
|
278
|
+
query = payload.fetch(:text, "").to_s
|
|
279
|
+
if query.empty?
|
|
280
|
+
clear_narrowing!
|
|
281
|
+
else
|
|
282
|
+
@narrow_query = query
|
|
283
|
+
invalidate_visible_lines!
|
|
284
|
+
viewport.top = 0
|
|
285
|
+
pager.clamp!
|
|
286
|
+
end
|
|
287
|
+
[]
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def clear_narrowing!
|
|
291
|
+
@narrow_query = nil
|
|
292
|
+
invalidate_visible_lines!
|
|
293
|
+
viewport.top = 0
|
|
294
|
+
pager.clamp!
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def line_numbers?
|
|
298
|
+
@line_numbers
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def incrementally_scrollable_from?(prev_state, curr_state)
|
|
302
|
+
prev_viewport = prev_state[STATE_VIEWPORT]
|
|
303
|
+
curr_viewport = curr_state[STATE_VIEWPORT]
|
|
304
|
+
prev_lines = prev_state[STATE_LINES]
|
|
305
|
+
curr_lines = curr_state[STATE_LINES]
|
|
306
|
+
|
|
307
|
+
delta = curr_viewport[0] - prev_viewport[0]
|
|
308
|
+
overlap = prev_lines.length - delta
|
|
309
|
+
|
|
310
|
+
delta.positive? &&
|
|
311
|
+
delta < curr_viewport[1] &&
|
|
312
|
+
overlap.positive? &&
|
|
313
|
+
prev_lines.drop(delta) == curr_lines.take(overlap)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def scroll_delta_from(prev_state, curr_state)
|
|
317
|
+
prev_viewport = prev_state[STATE_VIEWPORT]
|
|
318
|
+
curr_viewport = curr_state[STATE_VIEWPORT]
|
|
319
|
+
|
|
320
|
+
curr_viewport[0] - prev_viewport[0]
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# True when the pager is currently holding the screen (i.e., paging mode is
|
|
324
|
+
# active and the output is paused).
|
|
325
|
+
def pager_active?
|
|
326
|
+
@pager.active?
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def highlights
|
|
330
|
+
pager.search_visible_highlights(viewport: viewport)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
private
|
|
334
|
+
|
|
335
|
+
# simplecov:disable
|
|
336
|
+
|
|
337
|
+
def invalidate_visible_lines!
|
|
338
|
+
@visible_lines = nil
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def keymap_contexts
|
|
342
|
+
if pager.active?
|
|
343
|
+
[:paging, :terminal]
|
|
344
|
+
else
|
|
345
|
+
[:terminal]
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def apply_action(action, args, event:)
|
|
350
|
+
env = ActionEnvironment.new(
|
|
351
|
+
session: self,
|
|
352
|
+
event: event,
|
|
353
|
+
counter: counter,
|
|
354
|
+
pager: pager,
|
|
355
|
+
)
|
|
356
|
+
result = pager.act_on(action, *args, env: env)
|
|
357
|
+
normalize_action_result(result)
|
|
358
|
+
rescue Fatty::ActionError => e
|
|
359
|
+
[alert_cmd(:error, e.message, ev: event)]
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def reveal_appended_block(before)
|
|
363
|
+
added = output.lines.length - before
|
|
364
|
+
height = viewport.height.to_i
|
|
365
|
+
|
|
366
|
+
if added >= height
|
|
367
|
+
viewport.top = before
|
|
368
|
+
viewport.clamp!(output.lines)
|
|
369
|
+
else
|
|
370
|
+
viewport.page_bottom(output.lines)
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def pager_status_viewport(screen)
|
|
375
|
+
vp = pager_viewport.dup
|
|
376
|
+
vp.height = [screen.output_rect.rows - 1, 1].max
|
|
377
|
+
vp
|
|
378
|
+
end
|
|
379
|
+
|
|
17
380
|
def append_output(text, follow: true)
|
|
18
381
|
ntrim = @output.append(text.to_s)
|
|
382
|
+
invalidate_visible_lines!
|
|
19
383
|
@pager.on_append(ntrim: ntrim)
|
|
20
384
|
|
|
21
385
|
# When callers request follow behavior (scrolling mode),
|
|
@@ -29,8 +393,89 @@ module Fatty
|
|
|
29
393
|
ntrim
|
|
30
394
|
end
|
|
31
395
|
|
|
396
|
+
def update_pager_search_set(payload)
|
|
397
|
+
pattern = payload.fetch(:pattern, "").to_s
|
|
398
|
+
regex = !!payload[:regex]
|
|
399
|
+
direction = (payload[:direction] || :forward).to_sym
|
|
400
|
+
result = pager.search_set!(pattern: pattern, regex: regex, direction: direction)
|
|
401
|
+
|
|
402
|
+
if result[:status] == :moved
|
|
403
|
+
pager.search_commit!
|
|
404
|
+
else
|
|
405
|
+
pager.search_cancel!
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
handle_search_result(result)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def update_pager_search_step(payload)
|
|
412
|
+
pattern = payload.fetch(:pattern, nil)
|
|
413
|
+
regex = !!payload[:regex]
|
|
414
|
+
direction = (payload[:direction] || :forward).to_sym
|
|
415
|
+
|
|
416
|
+
result =
|
|
417
|
+
if pattern
|
|
418
|
+
pager.search_set!(pattern: pattern, regex: regex, direction: direction)
|
|
419
|
+
else
|
|
420
|
+
pager.search_step!(direction: direction)
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
handle_search_result(result)
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def update_pager_isearch_update(payload)
|
|
427
|
+
pattern = payload.fetch(:pattern, "").to_s
|
|
428
|
+
direction = (payload[:direction] || :forward).to_sym
|
|
429
|
+
result = pager.isearch_update!(pattern: pattern, direction: direction)
|
|
430
|
+
|
|
431
|
+
handle_search_result(result) <<
|
|
432
|
+
Command.session(:isearch, :isearch_set_failed, failed: result[:status] != :moved)
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def update_pager_isearch_step(payload)
|
|
436
|
+
direction = (payload[:direction] || :forward).to_sym
|
|
437
|
+
result = pager.isearch_step!(direction: direction)
|
|
438
|
+
|
|
439
|
+
handle_search_result(result) <<
|
|
440
|
+
Command.session(:isearch, :isearch_set_failed, failed: result[:status] != :moved)
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def update_pager_isearch_commit(payload)
|
|
444
|
+
pattern = payload.fetch(:pattern, "").to_s
|
|
445
|
+
direction = (payload[:direction] || :forward).to_sym
|
|
446
|
+
result = pager.isearch_accept!(pattern: pattern, direction: direction)
|
|
447
|
+
|
|
448
|
+
handle_search_result(result) <<
|
|
449
|
+
Command.session(:isearch, :isearch_set_failed, failed: false)
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def handle_search_result(result)
|
|
453
|
+
return [] unless result.is_a?(Hash)
|
|
454
|
+
|
|
455
|
+
case result[:status]
|
|
456
|
+
when :boundary
|
|
457
|
+
msg = result[:message].to_s
|
|
458
|
+
msg = "Search reached boundary" if msg.empty?
|
|
459
|
+
[alert_cmd(:info, msg)]
|
|
460
|
+
when :not_found
|
|
461
|
+
[alert_cmd(:info, "No matches")]
|
|
462
|
+
else
|
|
463
|
+
[]
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def consume_search_regex_flag
|
|
468
|
+
regex = false
|
|
469
|
+
if counter.digits.to_s == "4"
|
|
470
|
+
regex = true
|
|
471
|
+
counter.clear!
|
|
472
|
+
end
|
|
473
|
+
regex
|
|
474
|
+
end
|
|
475
|
+
|
|
32
476
|
def reset_output!
|
|
33
477
|
@output.lines.clear
|
|
478
|
+
invalidate_visible_lines!
|
|
34
479
|
@viewport.reset
|
|
35
480
|
end
|
|
36
481
|
|
|
@@ -42,16 +487,11 @@ module Fatty
|
|
|
42
487
|
|
|
43
488
|
def reset_for_command!
|
|
44
489
|
reset_output!
|
|
490
|
+
@narrow_query = nil
|
|
45
491
|
mode = @default_output_mode # Fatty::Config.config.dig(:output, :mode)&.to_sym || :paging
|
|
46
492
|
@pager.reset!(mode: mode)
|
|
47
493
|
end
|
|
48
494
|
|
|
49
|
-
# True when the pager is currently holding the screen (i.e., paging mode is
|
|
50
|
-
# active and the output is paused).
|
|
51
|
-
def pager_active?
|
|
52
|
-
@pager.reserve_prompt_row?
|
|
53
|
-
end
|
|
54
|
-
|
|
55
495
|
# When the pager is active, the last output row is reserved for the pager
|
|
56
496
|
# InputField. This returns the viewport to use for rendering the output
|
|
57
497
|
# content itself.
|
|
@@ -64,20 +504,31 @@ module Fatty
|
|
|
64
504
|
end
|
|
65
505
|
|
|
66
506
|
def pager_status_prompt
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if total.positive?
|
|
72
|
-
((bottom * 100.0) / total).round
|
|
73
|
-
end
|
|
507
|
+
visible_total = visible_lines.length
|
|
508
|
+
original_total = output.lines.length
|
|
509
|
+
visible_h = pager_active? ? pager.page_height : viewport.height
|
|
510
|
+
bottom = [viewport.top + visible_h, visible_total].min
|
|
74
511
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if pct
|
|
78
|
-
" #{pager.nav_arrow} --More-- #{bottom}/#{total} (#{pct}%)#{search} "
|
|
512
|
+
if pager.scrolling?
|
|
513
|
+
"--Scrolling--"
|
|
79
514
|
else
|
|
80
|
-
|
|
515
|
+
search = pager.search_label
|
|
516
|
+
pct =
|
|
517
|
+
if visible_total.positive?
|
|
518
|
+
((bottom * 100.0) / visible_total).round
|
|
519
|
+
end
|
|
520
|
+
narrow =
|
|
521
|
+
if narrowed?
|
|
522
|
+
" narrowed #{visible_total}/#{original_total}"
|
|
523
|
+
else
|
|
524
|
+
""
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
if pct
|
|
528
|
+
" #{pager.nav_arrow} --More-- #{bottom}/#{visible_total} (#{pct}%)#{narrow}#{search} "
|
|
529
|
+
else
|
|
530
|
+
" #{pager.nav_arrow} --More-- #{bottom}#{narrow}#{search} "
|
|
531
|
+
end
|
|
81
532
|
end
|
|
82
533
|
end
|
|
83
534
|
|