fatty 0.99.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.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +2 -0
  3. data/.simplecov +23 -0
  4. data/.yardopts +4 -0
  5. data/CHANGELOG.md +34 -0
  6. data/CHANGELOG.org +38 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +31 -0
  9. data/README.org +166 -0
  10. data/Rakefile +15 -0
  11. data/TODO.org +163 -0
  12. data/examples/markdown/native-markdown.md +370 -0
  13. data/examples/markdown/ox-gfm-markdown.md +373 -0
  14. data/examples/markdown/ox-gfm-markdown.org +376 -0
  15. data/exe/fatty +275 -0
  16. data/fatty.gemspec +42 -0
  17. data/lib/fatty/accept_env.rb +32 -0
  18. data/lib/fatty/action.rb +103 -0
  19. data/lib/fatty/action_environment.rb +42 -0
  20. data/lib/fatty/actionable.rb +73 -0
  21. data/lib/fatty/alert.rb +93 -0
  22. data/lib/fatty/ansi/renderer.rb +168 -0
  23. data/lib/fatty/ansi.rb +352 -0
  24. data/lib/fatty/colors/color.rb +379 -0
  25. data/lib/fatty/colors/pairs.rb +73 -0
  26. data/lib/fatty/colors/palette.rb +73 -0
  27. data/lib/fatty/colors/rgb.txt +788 -0
  28. data/lib/fatty/colors.rb +5 -0
  29. data/lib/fatty/config.rb +86 -0
  30. data/lib/fatty/config_files/config.yml +50 -0
  31. data/lib/fatty/config_files/help.md +120 -0
  32. data/lib/fatty/config_files/help.org +124 -0
  33. data/lib/fatty/config_files/keybindings.yml +49 -0
  34. data/lib/fatty/config_files/keydefs.yml +23 -0
  35. data/lib/fatty/config_files/themes/mono.yml +76 -0
  36. data/lib/fatty/config_files/themes/nordic.yml +77 -0
  37. data/lib/fatty/config_files/themes/solarized_dark.yml +77 -0
  38. data/lib/fatty/config_files/themes/terminal.yml +90 -0
  39. data/lib/fatty/config_files/themes/wordperfect.yml +77 -0
  40. data/lib/fatty/config_files/themes/wordperfect_light.yml +77 -0
  41. data/lib/fatty/core_ext/string.rb +21 -0
  42. data/lib/fatty/core_ext.rb +3 -0
  43. data/lib/fatty/counter.rb +81 -0
  44. data/lib/fatty/curses/context.rb +279 -0
  45. data/lib/fatty/curses/curses_coder.rb +684 -0
  46. data/lib/fatty/curses/event_source.rb +230 -0
  47. data/lib/fatty/curses/key_decoder.rb +183 -0
  48. data/lib/fatty/curses/patch.rb +116 -0
  49. data/lib/fatty/curses/window_styling.rb +32 -0
  50. data/lib/fatty/curses.rb +16 -0
  51. data/lib/fatty/env.rb +100 -0
  52. data/lib/fatty/help.rb +41 -0
  53. data/lib/fatty/history/entry.rb +71 -0
  54. data/lib/fatty/history.rb +289 -0
  55. data/lib/fatty/input_buffer.rb +998 -0
  56. data/lib/fatty/input_field.rb +507 -0
  57. data/lib/fatty/key_event.rb +342 -0
  58. data/lib/fatty/key_map.rb +392 -0
  59. data/lib/fatty/keymaps/emacs.rb +189 -0
  60. data/lib/fatty/log_formats/json.rb +47 -0
  61. data/lib/fatty/log_formats/text.rb +67 -0
  62. data/lib/fatty/logger.rb +142 -0
  63. data/lib/fatty/markdown/ansi_renderer.rb +373 -0
  64. data/lib/fatty/markdown/render.rb +22 -0
  65. data/lib/fatty/markdown.rb +4 -0
  66. data/lib/fatty/menu_env.rb +22 -0
  67. data/lib/fatty/mouse_event.rb +32 -0
  68. data/lib/fatty/output_buffer.rb +78 -0
  69. data/lib/fatty/pager.rb +801 -0
  70. data/lib/fatty/prompt.rb +40 -0
  71. data/lib/fatty/renderer/curses.rb +697 -0
  72. data/lib/fatty/renderer/truecolor.rb +607 -0
  73. data/lib/fatty/renderer.rb +419 -0
  74. data/lib/fatty/screen.rb +96 -0
  75. data/lib/fatty/search.rb +43 -0
  76. data/lib/fatty/session/alert_session.rb +52 -0
  77. data/lib/fatty/session/input_session.rb +99 -0
  78. data/lib/fatty/session/isearch_session.rb +172 -0
  79. data/lib/fatty/session/keytest_session.rb +236 -0
  80. data/lib/fatty/session/modal_session.rb +61 -0
  81. data/lib/fatty/session/output_session.rb +105 -0
  82. data/lib/fatty/session/popup_session.rb +540 -0
  83. data/lib/fatty/session/prompt_session.rb +157 -0
  84. data/lib/fatty/session/search_session.rb +136 -0
  85. data/lib/fatty/session/shell_session.rb +566 -0
  86. data/lib/fatty/session.rb +173 -0
  87. data/lib/fatty/sessions.rb +14 -0
  88. data/lib/fatty/terminal/popup_owner.rb +26 -0
  89. data/lib/fatty/terminal/progress.rb +374 -0
  90. data/lib/fatty/terminal.rb +1067 -0
  91. data/lib/fatty/themes/loader.rb +136 -0
  92. data/lib/fatty/themes/manager.rb +71 -0
  93. data/lib/fatty/themes/registry.rb +64 -0
  94. data/lib/fatty/themes/resolver.rb +224 -0
  95. data/lib/fatty/themes/themes.rb +131 -0
  96. data/lib/fatty/themes.rb +6 -0
  97. data/lib/fatty/version.rb +5 -0
  98. data/lib/fatty/view/alert_view.rb +14 -0
  99. data/lib/fatty/view/cursor_view.rb +18 -0
  100. data/lib/fatty/view/input_view.rb +9 -0
  101. data/lib/fatty/view/output_view.rb +9 -0
  102. data/lib/fatty/view/status_view.rb +14 -0
  103. data/lib/fatty/view.rb +33 -0
  104. data/lib/fatty/viewport.rb +90 -0
  105. data/lib/fatty/views.rb +9 -0
  106. data/lib/fatty.rb +55 -0
  107. data/sig/fatty.rbs +4 -0
  108. metadata +250 -0
@@ -0,0 +1,376 @@
1
+ #+TITLE: Markdown Demonstration
2
+ #+OPTIONS: toc:0
3
+
4
+ * H1 heading should be red on yellow and bold
5
+
6
+ ** H2 heading should be black on pink and bold
7
+
8
+ *** H3 heading should be magenta on lightyellow and underlined
9
+
10
+ This paragraph has *strong text*, /emphasized text/, ~inline code~,
11
+ ==highlighted text==, and [fatty on github](https://github.com/ddoherty03/fatty).
12
+
13
+ Autolink: https://github.com/ddoherty03/fat_config
14
+
15
+ #+begin_quote
16
+ This block quote should have a red quote gutter.
17
+ The quoted text itself can stay normal output for now.
18
+ #+end_quote
19
+
20
+ #+begin_src ruby
21
+ # This ruby block. Has comments.
22
+ # Two lines, in fact.
23
+ def hello(name)
24
+ puts "hello #{name}"
25
+ end
26
+ #+end_src
27
+
28
+ | Feature | Expected style |
29
+ |--------------+---------------------|
30
+ | Header cells | black on lightgreen |
31
+ | Body cells | normal output |
32
+
33
+ * Carnival of Constructs
34
+ These are samples of the kinds of constructs the ANSI renderer for =fatty= are
35
+ capable of.
36
+
37
+ ** Unordered Lists
38
+
39
+ - builtin commands are handled by the =fatty= demo, Four score and seven years
40
+ ago our fathers brought forth on this continent, a new nation, conceived in
41
+ Liberty, and dedicated to the proposition that all men are created equal.
42
+ - other commands are handled by =/bin/sh=
43
+ - keybindings are listed below
44
+
45
+ ** Ordered Lists
46
+
47
+ 1. builtin commands are handled by the =fatty= demo, Four score and seven years
48
+ ago our fathers brought forth on this continent, a new nation, conceived in
49
+ Liberty, and dedicated to the proposition that all men are created equal.
50
+ 2. other commands are handled by =/bin/sh=
51
+ 3. keybindings are listed below
52
+
53
+ Some regular text to break things up.
54
+
55
+ 1. Parent ordered item
56
+ 1. Child ordered item
57
+ 2. Another child ordered item
58
+ 2. Back to parent ordered item
59
+
60
+ ** Description Lists
61
+
62
+ - bold :: *this text is bold*
63
+ - italic :: /this text is italic/
64
+ - code :: ~this text is code~
65
+ - verbatim :: =this text is verbatim=
66
+ - strike :: +this text is strike-through+
67
+ - underline :: _this text is underlined_
68
+
69
+ ** A Code Block
70
+ #+begin_src ruby
71
+ def wrap(text, first_prefix: "", rest_prefix: first_prefix)
72
+ width = [@width.to_i, 20].max
73
+
74
+ words = text.to_s.split(/\s+/)
75
+ lines = []
76
+ line = +""
77
+
78
+ words.each do |word|
79
+ prefix = lines.empty? ? first_prefix : rest_prefix
80
+ available = width - Fatty::Ansi.visible_length(prefix)
81
+ available = 20 if available < 20
82
+
83
+ candidate = line.empty? ? word : "#{line} #{word}"
84
+
85
+ if Fatty::Ansi.visible_length(candidate) > available && !line.empty?
86
+ lines << "#{prefix}#{line}"
87
+ line = word.dup
88
+ else
89
+ line = candidate
90
+ end
91
+ end
92
+
93
+ unless line.empty?
94
+ prefix = lines.empty? ? first_prefix : rest_prefix
95
+ lines << "#{prefix}#{line}"
96
+ end
97
+
98
+ lines.join("\n")
99
+ end
100
+ #+end_src
101
+
102
+ ** Nested lists
103
+
104
+ - Parent item
105
+ + Child item
106
+ + Another child
107
+ - Back to parent
108
+
109
+ ** Links
110
+
111
+ [Fatty on GitHub](https://github.com/ddoherty03/fatty)
112
+
113
+ ** Block quotes
114
+
115
+ #+begin_quote
116
+ Four score and seven years ago our fathers brought forth on this continent,
117
+ a new nation, conceived in Liberty, and dedicated to the proposition that
118
+ all men are created equal.
119
+
120
+ Now we are engaged in a great civil war, testing whether that nation, or any
121
+ nation so conceived and so dedicated, can long endure. We are met on a
122
+ great battle-field of that war. We have come to dedicate a portion of that
123
+ field, as a final resting place for those who here gave their lives that
124
+ that nation might live. It is altogether fitting and proper that we should
125
+ do this.
126
+
127
+ But, in a larger sense, we can not dedicate---we can not consecrate---we can
128
+ not hallow---this ground. The brave men, living and dead, who struggled
129
+ here, have consecrated it, far above our poor power to add or detract. The
130
+ world will little note, nor long remember what we say here, but it can never
131
+ forget what they did here. It is for us the living, rather, to be dedicated
132
+ here to the unfinished work which they who fought here have thus far so
133
+ nobly advanced. It is rather for us to be here dedicated to the great task
134
+ remaining before us---that from these honored dead we take increased
135
+ devotion to that cause for which they gave the last full measure of
136
+ devotion---that we here highly resolve that these dead shall not have died
137
+ in vain---that this nation, under God, shall have a new birth of
138
+ freedom---and that government of the people, by the people, for the people,
139
+ shall not perish from the earth.
140
+ #+end_quote
141
+
142
+ ** Tables with styled cells
143
+
144
+ | Feature | Example |
145
+ |---------+---------------|
146
+ | bold | *bold cell* |
147
+ | code | ~code cell~ |
148
+ | strike | +strike cell+ |
149
+
150
+ ** Horizontal Rule
151
+ This should be a horizontal rule, but YMMV.
152
+
153
+ -----
154
+
155
+ ** Forced Line Break
156
+
157
+ This line has a forced line break<br>
158
+ and this should appear on the next line.
159
+
160
+ ** Fenced code with various languages
161
+
162
+ Here is the Fibonacci number computed recursively in various languages.
163
+
164
+ *** Ruby
165
+
166
+ #+begin_src ruby
167
+ def fib(n)
168
+ return n if n < 2
169
+
170
+ fib(n - 1) + fib(n - 2)
171
+ end
172
+ puts fib(10)
173
+ #+end_src
174
+
175
+ *** Python
176
+
177
+ #+begin_src python
178
+ def fib(n):
179
+ if n < 2:
180
+ return n
181
+
182
+ return fib(n - 1) + fib(n - 2)
183
+
184
+ print(fib(10))
185
+
186
+ #+end_src
187
+
188
+ *** C
189
+
190
+ #+begin_src c
191
+ #include <stdio.h>
192
+
193
+ int fib(int n)
194
+ {
195
+ if (n < 2) {
196
+ return n;
197
+ }
198
+
199
+ return fib(n - 1) + fib(n - 2);
200
+ }
201
+
202
+ int main(void)
203
+ {
204
+ printf("%d\n", fib(10));
205
+ return 0;
206
+ }
207
+ #+end_src
208
+
209
+ *** C++
210
+
211
+ #+begin_src cpp
212
+ #include <iostream>
213
+
214
+ int fib(int n)
215
+ {
216
+ if (n < 2) {
217
+ return n;
218
+ }
219
+
220
+ return fib(n - 1) + fib(n - 2);
221
+ }
222
+
223
+ int main()
224
+ {
225
+ std::cout << fib(10) << std::endl;
226
+ }
227
+ #+end_src
228
+
229
+ *** Go
230
+
231
+ #+begin_src go
232
+ package main
233
+
234
+ import "fmt"
235
+
236
+ func fib(n int) int {
237
+ if n < 2 {
238
+ return n
239
+ }
240
+
241
+ return fib(n-1) + fib(n-2)
242
+ }
243
+
244
+ func main() {
245
+ fmt.Println(fib(10))
246
+ }
247
+ #+end_src
248
+
249
+ *** Rust
250
+
251
+ #+begin_src rust
252
+ fn fib(n: u32) -> u32 {
253
+ if n < 2 {
254
+ n
255
+ } else {
256
+ fib(n - 1) + fib(n - 2)
257
+ }
258
+ }
259
+
260
+ fn main() {
261
+ println!("{}", fib(10));
262
+ }
263
+ #+end_src
264
+
265
+ *** PHP
266
+
267
+ #+begin_src php
268
+ <?php
269
+
270
+ function fib($n)
271
+ {
272
+ if ($n < 2) {
273
+ return $n;
274
+ }
275
+
276
+ return fib($n - 1) + fib($n - 2);
277
+ }
278
+
279
+ echo fib(10) . PHP_EOL;
280
+ #+end_src
281
+
282
+ *** JavaScript
283
+
284
+ #+begin_src javascript
285
+ function fib(n) {
286
+ if (n < 2) {
287
+ return n;
288
+ }
289
+
290
+ return fib(n - 1) + fib(n - 2);
291
+ }
292
+
293
+ console.log(fib(10));
294
+ #+end_src
295
+
296
+ *** TypeScript
297
+
298
+ #+begin_src typescript
299
+ function fib(n: number): number {
300
+ if (n < 2) {
301
+ return n;
302
+ }
303
+
304
+ return fib(n - 1) + fib(n - 2);
305
+ }
306
+
307
+ console.log(fib(10));
308
+ #+end_src
309
+
310
+ *** Java
311
+
312
+ #+begin_src java
313
+ public class Fib {
314
+ static int fib(int n) {
315
+ if (n < 2) {
316
+ return n;
317
+ }
318
+
319
+ return fib(n - 1) + fib(n - 2);
320
+ }
321
+
322
+ public static void main(String[] args) {
323
+ System.out.println(fib(10));
324
+ }
325
+ }
326
+ #+end_src
327
+
328
+ *** Shell
329
+
330
+ #+begin_src bash
331
+ fib() {
332
+ if [ "$1" -lt 2 ]; then
333
+ echo "$1"
334
+ else
335
+ a=$(fib $(($1 - 1)))
336
+ b=$(fib $(($1 - 2)))
337
+ echo $((a + b))
338
+ fi
339
+ }
340
+
341
+ fib 10
342
+ #+end_src
343
+
344
+ *** Haskell
345
+
346
+ #+begin_src haskell
347
+ fib :: Int -> Int
348
+ fib n
349
+ | n < 2 = n
350
+ | otherwise = fib (n - 1) + fib (n - 2)
351
+
352
+ main :: IO ()
353
+ main = print (fib 10)
354
+ #+end_src
355
+
356
+ *** SQL (Recursive CTE)
357
+
358
+ #+begin_src sql
359
+ WITH RECURSIVE fib(n, a, b) AS (
360
+ SELECT 0, 0, 1
361
+ UNION ALL
362
+ SELECT n + 1, b, a + b
363
+ FROM fib
364
+ WHERE n < 10
365
+ )
366
+ SELECT a
367
+ FROM fib
368
+ ORDER BY n DESC
369
+ LIMIT 1;
370
+ #+end_src
371
+
372
+ *** Nonsense Language
373
+
374
+ #+begin_src madeuplang
375
+ this should still render as plain text
376
+ #+end_src
data/exe/fatty ADDED
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/fatty"
5
+ require "open3"
6
+ require "shellwords"
7
+
8
+ require "rainbow/refinement"
9
+ using Rainbow
10
+
11
+ prompt = Fatty::Prompt.new { "#{Dir.pwd}> " }
12
+ history_ctx_proc = -> { { pwd: Dir.pwd } }
13
+
14
+ on_accept_proc = lambda do |line, env|
15
+ session = env.session
16
+ terminal = env.terminal
17
+
18
+ cmd, *args = Shellwords.split(line)
19
+
20
+ case cmd
21
+ when "cd"
22
+ dir = args.first || ENV["HOME"]
23
+ dir = File.expand_path(dir)
24
+ Dir.chdir(dir)
25
+ when "choose"
26
+ choices =
27
+ if args.empty?
28
+ ["Choice A", "Choice B", "Choice C"]
29
+ else
30
+ args
31
+ end
32
+
33
+ choice = terminal.choose(
34
+ "Select one",
35
+ choices: choices,
36
+ initial_choice_idx: 0,
37
+ quit_value: nil,
38
+ )
39
+
40
+ if choice.nil?
41
+ session.append_output("choose: cancelled\n")
42
+ else
43
+ session.append_output("choose: selected #{choice.inspect}\n")
44
+ end
45
+ when "choosevals"
46
+ pairs =
47
+ if args.empty?
48
+ [["Choice A", 55], ["Choice B", 3.14159], ["Choice C", :done]]
49
+ elsif args.length.odd?
50
+ session.append_output("choosevals: needs even number of args\n")
51
+ next
52
+ else
53
+ args.each_slice(2).map { |label, value| [label, value] }
54
+ end
55
+
56
+ choice = terminal.choose(
57
+ "Select one",
58
+ choices: pairs,
59
+ initial_choice_idx: 1,
60
+ quit_value: nil,
61
+ )
62
+
63
+ if choice.nil?
64
+ session.append_output("choosevals: cancelled\n")
65
+ else
66
+ session.append_output("choosevals: selected #{choice.inspect}\n")
67
+ end
68
+ when "choose_multi"
69
+ choices =
70
+ if args.empty?
71
+ ["Choice A", "Choice B", "Choice C"]
72
+ else
73
+ args
74
+ end
75
+
76
+ choices = terminal.choose_multi(
77
+ "Select one or more",
78
+ choices: choices,
79
+ quit_value: nil,
80
+ )
81
+
82
+ if choices.nil?
83
+ session.append_output("choose_multi: cancelled\n")
84
+ else
85
+ session.append_output("choose_multi: selected #{choices.inspect}\n")
86
+ end
87
+ when "choosevals_multi"
88
+ pairs =
89
+ if args.empty?
90
+ pairs = [["Choice A", 55], ["Choice B", 3.14159], ["Choice C", :done]]
91
+ elsif args.length.odd?
92
+ session.append_output("choosevals_multi: needs even number of args\n")
93
+ next
94
+ else
95
+ pairs = args.each_slice(2).map { |label, value| [label, value] }
96
+ end
97
+
98
+ choices = terminal.choose_multi(
99
+ "Select one or more",
100
+ choices: pairs,
101
+ quit_value: nil,
102
+ )
103
+
104
+ if choices.nil?
105
+ session.append_output("choosevals_multi: cancelled\n")
106
+ else
107
+ session.append_output("choosevals_multi: selected #{choices.inspect}\n")
108
+ end
109
+ when "menu"
110
+ result = terminal.menu(
111
+ "Demo menu",
112
+ choices: {
113
+ "Write to output pane" => proc { |env|
114
+ env.output("menu: wrote this from MenuEnv#output\n")
115
+ :output_written
116
+ },
117
+
118
+ "Show status message" => proc { |env|
119
+ env.status("menu: status set from MenuEnv#status")
120
+ :status_written
121
+ },
122
+
123
+ "Return plain value" => :plain_value,
124
+ },
125
+ initial_choice_idx: 0,
126
+ quit_value: nil,
127
+ )
128
+ if result.nil?
129
+ session.append_output("menu: cancelled\n")
130
+ else
131
+ session.append_output("menu: result #{result.inspect}\n")
132
+ end
133
+ when "info"
134
+ message = args.empty? ? "Information message" : args.join(" ")
135
+ message = message.gsub(/ ?~~ ?/, "\n")
136
+ terminal.info(message)
137
+ session.append_output("info: #{message}\n")
138
+ when "good"
139
+ message = args.empty? ? "Success message" : args.join(" ")
140
+ message = message.gsub(/ ?~~ ?/, "\n")
141
+ terminal.good(message)
142
+ session.append_output("good: #{message}\n")
143
+ when "warn"
144
+ message = args.empty? ? "Danger Will Robinson!" : args.join(" ")
145
+ message = message.gsub(/ ?~~ ?/, "\n")
146
+ terminal.warn(message)
147
+ session.append_output("warn: #{message}\n")
148
+ when "oops"
149
+ message = args.empty? ? "Something went wrong" : args.join(" ")
150
+ message = message.gsub(/ ?~~ ?/, "\n")
151
+ terminal.oops(message)
152
+ session.append_output("oops: #{message}\n")
153
+ when "prompt"
154
+ question = args.empty? ? "Enter value:" : args.join(" ")
155
+ value = terminal.prompt(question, initial: "demo")
156
+ session.append_output("prompt: #{value.inspect}\n")
157
+ when "progress"
158
+ styles = %w[count percent simple_percent trail bar unicode_bar braille_bar spinner]
159
+ style_name = args.first
160
+ style =
161
+ if style_name && styles.include?(style_name)
162
+ args.shift.to_sym
163
+ else
164
+ :trail
165
+ end
166
+
167
+ total =
168
+ begin
169
+ args.first ? Integer(args.first) : nil
170
+ rescue StandardError
171
+ nil
172
+ end
173
+
174
+ if style != :spinner
175
+ total ||= 40
176
+ total = 1 if total < 1
177
+ elsif total && total < 1
178
+ total = 1
179
+ end
180
+
181
+ prog = env.progress(
182
+ label: "Demo progress",
183
+ total: total,
184
+ style: style,
185
+ )
186
+
187
+ if style == :spinner && total.nil?
188
+ 40.times do
189
+ prog.update(render: true)
190
+ sleep 0.1
191
+ end
192
+
193
+ prog.finish("Demo progress complete.", render: true)
194
+ session.append_output("progress: style=#{style} completed indeterminate demo\n")
195
+ else
196
+ total.times do |i|
197
+ indicator =
198
+ case i % 4
199
+ when 0 then "+".fatty_good
200
+ when 1 then "-".fatty_error
201
+ when 2 then "^".fatty_warn
202
+ else "."
203
+ end
204
+
205
+ prog.update(current: i + 1, indicator: indicator, render: true)
206
+ sleep 0.1
207
+ end
208
+ prog.finish("Demo progress complete.".green, render: true)
209
+ session.append_output("progress: style=#{style} completed #{total} steps\n")
210
+ end
211
+ when "keytest"
212
+ return [[:terminal, :push_modal, Fatty::KeyTestSession.new(owner: session)], [:send, :alert, :clear, {}]]
213
+ when 'markdown'
214
+ file = args.first || File.expand_path("../examples/markdown/native-markdown.md", __dir__)
215
+ text = File.read(File.expand_path(file))
216
+ env.markdown(text)
217
+ when 'markdown_org'
218
+ file = File.expand_path("../examples/markdown/ox-gfm-markdown.md", __dir__)
219
+ text = File.read(File.expand_path(file))
220
+ env.markdown(text)
221
+ when 'help'
222
+ env.markdown(Fatty::Help.text)
223
+ else
224
+ out, _ = Open3.capture2e(line)
225
+ session.append_output(out)
226
+ end
227
+ [[:send, :alert, :clear, {}]]
228
+ rescue Errno::ENOENT
229
+ [[:send, :alert, :show, { level: :error, message: "Command not found (#{line})" }]]
230
+ rescue SystemCallError => ex
231
+ [[:send, :alert, :show, { level: :error, message: ex.message }]]
232
+ end
233
+
234
+ cutes = []
235
+ ENV['PATH'].split(':').each do |dir|
236
+ next unless Dir.exist?(dir)
237
+
238
+ Dir.children(dir).each do |name|
239
+ next if name.match?(/\A\./)
240
+
241
+ path = File.join(dir, name)
242
+ cutes << name if File.file?(path) && File.executable?(path)
243
+ end
244
+ end
245
+ cutes = cutes.compact.sort_by(&:downcase).uniq
246
+
247
+ builtins = %w[
248
+ cd
249
+ choose
250
+ choosevals
251
+ choose_multi
252
+ choosevals_multi
253
+ help
254
+ info
255
+ good
256
+ keytest
257
+ markdown
258
+ markdown_org
259
+ menu
260
+ oops
261
+ progress
262
+ prompt
263
+ warn
264
+ ]
265
+ commands = (builtins + cutes).uniq
266
+ completion_proc = lambda do |_buffer|
267
+ commands.to_a
268
+ end
269
+
270
+ Fatty::Terminal.new(
271
+ prompt: prompt,
272
+ history_ctx: history_ctx_proc,
273
+ on_accept: on_accept_proc,
274
+ completion_proc: completion_proc,
275
+ ).go
data/fatty.gemspec ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/fatty/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "fatty"
7
+ spec.version = Fatty::VERSION
8
+ spec.authors = ["Daniel E. Doherty"]
9
+ spec.email = ["ded@ddoherty.net"]
10
+
11
+ spec.summary = "Stateful terminal UI with editable input and scrollback"
12
+ spec.description = <<~DESC
13
+ fatty is a pure Ruby curses-backed terminal interaction library with editable single-line input, scrollable output, and
14
+ programmable keybindings, inspired by modern shells like fish.
15
+ DESC
16
+ spec.homepage = "https://github.com/ddoherty03/fatty"
17
+ spec.license = "MIT"
18
+ spec.required_ruby_version = ">= 3.2.0"
19
+
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = "https://github.com/ddoherty03/fatty"
23
+
24
+ # Specify which files should be added to the gem when it is released.
25
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
26
+ spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
27
+ ls.readlines("\x0", chomp: true).reject do |f|
28
+ f.start_with?(*%w[bin/ Gemfile .gitignore .rspec spec/ .github/ .rubocop.yml])
29
+ end
30
+ end
31
+ spec.bindir = "exe"
32
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ["lib"]
34
+
35
+ spec.add_dependency "curses", "~> 1.4"
36
+ spec.add_dependency "fat_config", ">=0.8.0"
37
+ spec.add_dependency "unicode-display_width", "~> 2.5"
38
+ spec.add_dependency "yaml"
39
+ spec.add_dependency "rainbow"
40
+ spec.add_dependency "redcarpet"
41
+ spec.add_dependency "rouge"
42
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This class provides a wrapper around components that can be handed off to
4
+ # Terminal's on_accept method for access to the terminal, the session, and a
5
+ # Progress widget.
6
+ module Fatty
7
+ class AcceptEnv
8
+ attr_reader :session
9
+
10
+ def initialize(session:)
11
+ @session = session
12
+ @progress = nil
13
+ end
14
+
15
+ def terminal
16
+ session.terminal
17
+ end
18
+
19
+ def progress(**opts)
20
+ @progress ||= Fatty::Terminal::Progress.new(terminal: terminal, **opts)
21
+ end
22
+
23
+ def markdown(text)
24
+ md = Fatty::Markdown.render(
25
+ text,
26
+ width: terminal.screen.cols,
27
+ palette: terminal.renderer.palette,
28
+ )
29
+ session.append_output(md)
30
+ end
31
+ end
32
+ end