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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.org +11 -29
  3. data/README.md +2207 -18
  4. data/README.org +2066 -104
  5. data/exe/fatty +236 -93
  6. data/help/help.md +233 -0
  7. data/lib/fatty/action.rb +3 -7
  8. data/lib/fatty/actionable.rb +2 -0
  9. data/lib/fatty/alert.rb +65 -39
  10. data/lib/fatty/ansi/renderer.rb +26 -6
  11. data/lib/fatty/ansi.rb +1 -0
  12. data/lib/fatty/api/alert.rb +15 -0
  13. data/lib/fatty/api/choose.rb +145 -0
  14. data/lib/fatty/api/environment.rb +12 -0
  15. data/lib/fatty/api/keytest.rb +11 -0
  16. data/lib/fatty/api/menu.rb +104 -0
  17. data/lib/fatty/api/output.rb +55 -0
  18. data/lib/fatty/api/progress.rb +24 -0
  19. data/lib/fatty/api/prompt.rb +77 -0
  20. data/lib/fatty/api/status.rb +55 -0
  21. data/lib/fatty/api.rb +38 -0
  22. data/lib/fatty/callback_environment.rb +74 -0
  23. data/lib/fatty/colors/color.rb +366 -232
  24. data/lib/fatty/colors/pairs.rb +2 -0
  25. data/lib/fatty/command.rb +78 -0
  26. data/lib/fatty/config.rb +227 -39
  27. data/lib/fatty/config_files/config.yml +4 -12
  28. data/lib/fatty/config_files/keydefs.yml +13 -23
  29. data/lib/fatty/config_files/themes/capuchin_monk.yml +140 -0
  30. data/lib/fatty/config_files/themes/catppuccin_latte.yml +140 -0
  31. data/lib/fatty/config_files/themes/catppuccin_mocha.yml +140 -0
  32. data/lib/fatty/config_files/themes/cyberpunk.yml +141 -0
  33. data/lib/fatty/config_files/themes/dracula.yml +140 -0
  34. data/lib/fatty/config_files/themes/everforest_dark.yml +140 -0
  35. data/lib/fatty/config_files/themes/gruvbox_dark.yml +140 -0
  36. data/lib/fatty/config_files/themes/gruvbox_light.yml +140 -0
  37. data/lib/fatty/config_files/themes/mono.yml +119 -47
  38. data/lib/fatty/config_files/themes/monokai.yml +140 -0
  39. data/lib/fatty/config_files/themes/nordic.yml +1 -0
  40. data/lib/fatty/config_files/themes/onedark.yml +140 -0
  41. data/lib/fatty/config_files/themes/solarized_dark.yml +1 -0
  42. data/lib/fatty/config_files/themes/solarized_light.yml +140 -0
  43. data/lib/fatty/config_files/themes/terminal.yml +67 -0
  44. data/lib/fatty/config_files/themes/tokyo_night.yml +140 -0
  45. data/lib/fatty/config_files/themes/wordperfect.yml +80 -9
  46. data/lib/fatty/core_ext/hash.rb +19 -0
  47. data/lib/fatty/core_ext/string.rb +19 -12
  48. data/lib/fatty/core_ext.rb +1 -0
  49. data/lib/fatty/curses/context.rb +86 -84
  50. data/lib/fatty/curses/event_source.rb +128 -40
  51. data/lib/fatty/curses/key_decoder.rb +77 -11
  52. data/lib/fatty/env.rb +21 -20
  53. data/lib/fatty/help.rb +1 -29
  54. data/lib/fatty/history/entry.rb +2 -0
  55. data/lib/fatty/history.rb +17 -14
  56. data/lib/fatty/input_buffer.rb +71 -47
  57. data/lib/fatty/input_field.rb +216 -149
  58. data/lib/fatty/key_event.rb +67 -66
  59. data/lib/fatty/key_map.rb +54 -16
  60. data/lib/fatty/keymaps/emacs.rb +40 -6
  61. data/lib/fatty/log_formats/json.rb +2 -0
  62. data/lib/fatty/log_formats/text.rb +2 -0
  63. data/lib/fatty/logger.rb +7 -2
  64. data/lib/fatty/markdown/ansi_renderer.rb +75 -6
  65. data/lib/fatty/markdown/render.rb +2 -3
  66. data/lib/fatty/mouse_event.rb +142 -2
  67. data/lib/fatty/output_buffer.rb +2 -0
  68. data/lib/fatty/pager.rb +158 -47
  69. data/lib/fatty/progress.rb +417 -0
  70. data/lib/fatty/renderer/curses.rb +340 -184
  71. data/lib/fatty/renderer/truecolor.rb +266 -163
  72. data/lib/fatty/renderer.rb +115 -97
  73. data/lib/fatty/screen.rb +2 -0
  74. data/lib/fatty/session/alert_session.rb +39 -21
  75. data/lib/fatty/session/isearch_session.rb +92 -38
  76. data/lib/fatty/session/keytest_session.rb +105 -85
  77. data/lib/fatty/session/modal_session.rb +38 -1
  78. data/lib/fatty/session/output_session.rb +472 -21
  79. data/lib/fatty/session/popup_session.rb +251 -134
  80. data/lib/fatty/session/prompt_session.rb +68 -26
  81. data/lib/fatty/session/search_session.rb +79 -31
  82. data/lib/fatty/session/shell_session.rb +383 -368
  83. data/lib/fatty/session/status_session.rb +167 -0
  84. data/lib/fatty/session.rb +95 -95
  85. data/lib/fatty/terminal/popup_owner.rb +14 -11
  86. data/lib/fatty/terminal.rb +515 -813
  87. data/lib/fatty/themes/loader.rb +4 -2
  88. data/lib/fatty/themes/manager.rb +4 -0
  89. data/lib/fatty/themes/registry.rb +2 -0
  90. data/lib/fatty/themes/resolver.rb +175 -162
  91. data/lib/fatty/version.rb +1 -1
  92. data/lib/fatty/viewport.rb +8 -1
  93. data/lib/fatty.rb +35 -7
  94. metadata +42 -40
  95. data/.envrc +0 -2
  96. data/.simplecov +0 -23
  97. data/.yardopts +0 -4
  98. data/CHANGELOG.md +0 -34
  99. data/Rakefile +0 -15
  100. data/TODO.org +0 -163
  101. data/examples/markdown/native-markdown.md +0 -370
  102. data/examples/markdown/ox-gfm-markdown.md +0 -373
  103. data/examples/markdown/ox-gfm-markdown.org +0 -376
  104. data/fatty.gemspec +0 -42
  105. data/lib/fatty/accept_env.rb +0 -32
  106. data/lib/fatty/config_files/help.md +0 -120
  107. data/lib/fatty/config_files/help.org +0 -124
  108. data/lib/fatty/config_files/themes/wordperfect_light.yml +0 -77
  109. data/lib/fatty/menu_env.rb +0 -22
  110. data/lib/fatty/session/input_session.rb +0 -99
  111. data/lib/fatty/sessions.rb +0 -14
  112. data/lib/fatty/terminal/progress.rb +0 -374
  113. data/lib/fatty/view/alert_view.rb +0 -14
  114. data/lib/fatty/view/cursor_view.rb +0 -18
  115. data/lib/fatty/view/input_view.rb +0 -9
  116. data/lib/fatty/view/output_view.rb +0 -9
  117. data/lib/fatty/view/status_view.rb +0 -14
  118. data/lib/fatty/view.rb +0 -33
  119. data/lib/fatty/views.rb +0 -9
  120. 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, views: [])
8
- super(keymap: keymap, views: views)
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
- total = @output.lines.length
68
- visible_h = pager_active? ? pager.page_height : @viewport.height
69
- bottom = [@viewport.top + visible_h, total].min
70
- pct =
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
- search = pager.search_label
76
- search = " [#{search}]" if search && !search.empty?
77
- if pct
78
- " #{pager.nav_arrow} --More-- #{bottom}/#{total} (#{pct}%)#{search} "
512
+ if pager.scrolling?
513
+ "--Scrolling--"
79
514
  else
80
- " #{pager.nav_arrow} --More-- #{bottom}#{search} "
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