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
@@ -4,85 +4,99 @@ require "tmpdir"
4
4
 
5
5
  module Fatty
6
6
  class KeyTestSession < Session
7
- def id = :keytest
7
+ attr_reader :output_session
8
8
 
9
- def initialize(owner: nil)
10
- super(keymap: nil, views: [])
11
- @owner = owner
9
+ def initialize(output_dir: Dir.tmpdir)
10
+ super(id: :keytest, keymap: nil)
11
+ @output_session = OutputSession.new
12
+ @path = nil
13
+ @suggestions = []
12
14
  end
13
15
 
16
+ #########################################################################################
17
+ # Session Protocol
18
+ #########################################################################################
19
+
14
20
  def init(terminal:)
15
- super
21
+ commands = super
16
22
  @suggestions = []
17
- @owner ||= terminal.focused_session
18
- show_quit_status
19
- force_scrolling_output!
20
- append <<~TEXT
21
- Key test mode
22
23
 
23
- Press keys to inspect Fatty's decoding and binding resolution.
24
- Press q, ESC, or C-g to leave key test mode.
25
-
26
- TEXT
27
- []
24
+ commands.concat(output_session.init(terminal: terminal))
25
+ commands << Command.terminal(:register_session, session: output_session)
26
+ commands << Command.session(output_session.id, :resize)
27
+ commands << Command.session(output_session.id, :set_mode, mode: :scrolling)
28
+ commands << Command.session(output_session.id, :begin_command)
29
+ commands << Command.session(
30
+ output_session.id,
31
+ :append,
32
+ text: <<~TEXT,
33
+ Key test mode
34
+
35
+ Press keys to inspect Fatty's decoding and binding resolution.
36
+ Press q, ESC, or C-g to leave key test mode.
37
+
38
+ TEXT
39
+ follow: true,
40
+ )
41
+ commands << show_quit_status_command
42
+ commands
28
43
  end
29
44
 
30
- def update_key(ev)
31
- if quit_key?(ev)
32
- append "Leaving key test mode.\n\n"
33
- return [[:terminal, :pop_modal]]
34
- end
35
-
36
- # append " TERM: #{terminal_name}\n"
37
- append ev.report
38
- snippet = ev.suggested_snippet(terminal_name)
39
- append snippet + "\n"
40
- @suggestions << snippet unless snippet.empty?
41
- show_quit_status
42
- []
45
+ def update(command)
46
+ log_update(command)
47
+ commands =
48
+ case command.action
49
+ when :key
50
+ ev = command.payload.fetch(:event)
51
+ if quit_key?(ev)
52
+ write_suggestions_commands + [
53
+ Command.session(output_session.id, :append, text: "Leaving key test mode.\n\n", follow: true),
54
+ Command.session(:status, :clear),
55
+ Command.terminal(:pop_modal),
56
+ Command.session(
57
+ :status,
58
+ :show,
59
+ text: "KeyTest suggestions written to #{@path}",
60
+ role: :good,
61
+ ),
62
+ ]
63
+ else
64
+ snippet = ev.suggested_snippet(terminal_name)
65
+ @suggestions << snippet unless snippet.empty?
66
+ text = [
67
+ ev.report,
68
+ "\nSuggested keybindings:\n",
69
+ snippet,
70
+ "\n",
71
+ ].join
72
+ [
73
+ Command.session(
74
+ output_session.id,
75
+ :append,
76
+ text: text,
77
+ follow: true,
78
+ mode: :scrolling),
79
+ show_quit_status_command,
80
+ ]
81
+ end
82
+ else
83
+ []
84
+ end
85
+ Array(commands)
43
86
  end
44
87
 
45
- def view(screen:, renderer:)
46
- # Intentionally blank. The underlying shell session renders the output
47
- # pane; this modal only captures keys and appends diagnostic text.
48
- nil
88
+ def view
89
+ output_session.view
49
90
  end
50
91
 
51
92
  def close
52
- write_suggestions!
53
- restore_owner_pager!
54
- terminal.clear_status if terminal.respond_to?(:clear_status)
93
+ output_session.close
55
94
  nil
56
95
  end
57
96
 
58
97
  private
59
98
 
60
- def append(text)
61
- return unless @owner&.respond_to?(:append_output)
62
-
63
- force_scrolling_output!
64
-
65
- before = @owner.output.lines.length
66
- @owner.append_output(text.to_s, follow: false)
67
-
68
- height = @owner.viewport.height.to_i
69
- added = @owner.output.lines.length - before
70
-
71
- if added >= height
72
- @owner.viewport.top = before
73
- @owner.viewport.clamp!(@owner.output.lines)
74
- else
75
- @owner.viewport.page_bottom(@owner.output.lines)
76
- end
77
-
78
- terminal.renderer.reset_frame_cache if terminal.respond_to?(:renderer)
79
- end
80
-
81
- def force_scrolling_output!
82
- if @owner&.respond_to?(:pager)
83
- @owner.pager.paging_to_scrolling
84
- end
85
- end
99
+ # simplecov:disable
86
100
 
87
101
  def quit_key?(ev)
88
102
  ev.key == :q ||
@@ -101,26 +115,38 @@ module Fatty
101
115
  env && env[:terminal] || ENV.fetch("TERM", "unknown")
102
116
  end
103
117
 
104
- def show_quit_status
105
- terminal.warn("KeyTest — press q, ESC, or C-g to quit (TERM: #{terminal_name})")
118
+ def show_quit_status_command
119
+ Command.session(
120
+ :status,
121
+ :show,
122
+ text: "KeyTest — press q, ESC, or C-g to quit (terminal: #{terminal_name})",
123
+ role: :warn,
124
+ )
106
125
  end
107
126
 
108
- def write_suggestions!
127
+ def write_suggestions_commands
109
128
  suggestions = @suggestions.uniq
110
- return if suggestions.empty?
111
-
112
- path = File.join(Dir.tmpdir, "fatty-keytest-#{Time.now.strftime('%Y%m%d-%H%M%S')}.yml")
113
-
114
- File.write(path, suggestion_file_text(suggestions))
115
-
116
- append <<~TEXT
117
- =======================================================
118
- KeyTest suggestions written to:
119
- #{path}
120
-
121
- TEXT
122
-
123
- terminal.good("KeyTest suggestions written to #{path}")
129
+ return [] if suggestions.empty?
130
+
131
+ @path = File.join(
132
+ Dir.tmpdir,
133
+ "fatty-keytest-#{Time.now.strftime('%Y%m%d-%H%M%S')}.yml",
134
+ )
135
+ File.write(@path, suggestion_file_text(suggestions))
136
+ [
137
+ Command.session(
138
+ output_session.id,
139
+ :append,
140
+ text: <<~TEXT,
141
+ =======================================================
142
+ KeyTest suggestions written to:
143
+ #{@path}
144
+
145
+ TEXT
146
+ follow: true,
147
+ mode: :scrolling,
148
+ ),
149
+ ]
124
150
  end
125
151
 
126
152
  def suggestion_file_text(suggestions)
@@ -195,7 +221,7 @@ module Fatty
195
221
 
196
222
  Mouse events may also be bound in keybindings.yml, for example:
197
223
 
198
- - mouse: <BUTTONNAME>
224
+ - button: <BUTTONNAME>
199
225
  context: paging
200
226
  action: page_up
201
227
 
@@ -226,11 +252,5 @@ module Fatty
226
252
  out << "\n"
227
253
  out
228
254
  end
229
-
230
- def restore_owner_pager!
231
- if @owner&.respond_to?(:pager)
232
- @owner.pager.quit_paging
233
- end
234
- end
235
255
  end
236
256
  end
@@ -5,6 +5,10 @@ module Fatty
5
5
  attr_reader :win
6
6
  private attr_writer :win
7
7
 
8
+ #########################################################################################
9
+ # Session Protocol
10
+ #########################################################################################
11
+
8
12
  def close
9
13
  Fatty.debug("#{self.class}#close: object_id=#{object_id}", tag: :session)
10
14
  old_win = win
@@ -18,6 +22,10 @@ module Fatty
18
22
  []
19
23
  end
20
24
 
25
+ private
26
+
27
+ # simplecov:disable
28
+
21
29
  def rebuild_windows!
22
30
  old_win = win
23
31
  self.win = nil
@@ -27,7 +35,8 @@ module Fatty
27
35
  rows = ::Curses.lines
28
36
  width, height = geometry(cols: cols, rows: rows)
29
37
  x = (cols - width) / 2
30
- y = (rows - height) / 2
38
+ input_row = renderer.screen.input_rect.row
39
+ y = [input_row - height - 1, 0].max
31
40
  self.win = ::Curses::Window.new(height, width, y, x)
32
41
  nil
33
42
  end
@@ -57,5 +66,33 @@ module Fatty
57
66
  def clamp_height(height, max_height:, min_height:)
58
67
  height.clamp(min_height, max_height)
59
68
  end
69
+
70
+ def safely_close_window(win)
71
+ return unless win
72
+
73
+ begin
74
+ win.erase
75
+ rescue RuntimeError => e
76
+ raise unless closed_window_error?(e)
77
+ end
78
+
79
+ begin
80
+ win.noutrefresh if win.respond_to?(:noutrefresh)
81
+ rescue RuntimeError => e
82
+ raise unless closed_window_error?(e)
83
+ end
84
+
85
+ begin
86
+ win.close
87
+ rescue RuntimeError => e
88
+ raise unless closed_window_error?(e)
89
+ end
90
+ nil
91
+ end
92
+
93
+ def closed_window_error?(error)
94
+ message = error.message
95
+ message.include?("closed window") || message.include?("already closed window")
96
+ end
60
97
  end
61
98
  end