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
data/README.org CHANGED
@@ -1,4 +1,4 @@
1
- #+BEGIN_EXPORT markdown
1
+ #+OPTIONS: toc:4 H:4
2
2
  #+PROPERTY: header-args:ruby :results value :colnames no :hlines yes :exports both :dir "./"
3
3
  #+PROPERTY: header-args:ruby+ :wrap example :session fatty_session :eval yes
4
4
  #+PROPERTY: header-args:ruby+ :prologue "$:.unshift('./lib') unless $:.first == './lib'; require 'fatty'"
@@ -6,74 +6,843 @@
6
6
  #+PROPERTY: header-args:sh :exports code :eval no
7
7
  #+PROPERTY: header-args:bash :exports code :eval no
8
8
 
9
- [![CI](https://github.com/ddoherty03/fatty/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/ddoherty03/fatty/actions/workflows/main.yml)
10
- #+END_EXPORT
9
+ * Introduction
10
+ ~Fatty~ aims to provide a full-featured command-line environment that provides:
11
11
 
12
+ 1. an emacs-like editing command-line input, including undo/redo and kill/yank
13
+ 2. a history facility,
14
+ 3. command completion,
15
+ 4. completion of partially-typed file path names,
16
+ 5. output into a paging environment,
17
+ 6. searching within the paged output,
18
+ 7. issuing messages to a "status" area separate from the output pane,
19
+ 8. binding keys to pre-defined actions,
20
+ 9. defining unrecognized key-codes as named keys,
21
+ 10. color themes that can be selected in real time,
22
+ 11. a way to define new themes,
23
+ 12. processing completed command lines with a callback procedure of your
24
+ choosing,
25
+ 13. several user-interface widgets such as selection popups, text prompts,
26
+ menus, and progress indicators,
27
+ 14. a way to render markdown to the output pane,
12
28
 
13
- #+PROPERTY: header-args:ruby :results value :colnames no :hlines yes :exports both :dir "./"
14
- #+PROPERTY: header-args:ruby+ :wrap example :session fatty_session :eval yes
15
- #+PROPERTY: header-args:ruby+ :prologue "$:.unshift('./lib') unless $:.first == './lib'; require 'fatty'"
16
- #+PROPERTY: header-args:ruby+ :ruby "bundle exec irb"
17
- #+PROPERTY: header-args:sh :exports code :eval no
18
- #+PROPERTY: header-args:bash :exports code :eval no
29
+ In other words, fatty allows you to write a terminal-based REPL of your choosing but takes
30
+ care of all the difficult parts.
19
31
 
20
- * Introduction
32
+ ~Fatty~ is written entirely in Ruby and was born from my frustrations at the
33
+ limitations of libraries like ~readline~ and ~reline~. It uses ~curses~ and
34
+ ~truecolor~ sequences for low-level rendering and is surprisingly snappy.
21
35
 
22
- * Reading Keys
36
+ ~Fatty~ is /not/ a terminal emulator but runs on top of one.
23
37
 
24
- Shift/Ctrl/Meta F-keys are normalized when ncurses provides distinct constants.
38
+ * Quick Start
39
+ ** Installing
40
+ ~Fatty~ is a ruby gem, so it can be installed with
25
41
 
26
- * Fatty Help
27
- ** The =fatty= demo command
42
+ #+begin_src sh :tangle no
43
+ $ gem install fatty
44
+ #+end_src
28
45
 
29
- When you =fatty=, it operates as a simple demo of the =fatty= gem by
30
- presenting an editable command-line using emacs key bindings. It has several
31
- builtin commands to demonstrate =fatty= features, and any command it does not
32
- recognized is handed off to the shell.
46
+ ** Trying it Out with the `fatty` Demo
47
+ Once installed, you can try out ~fatty~ with the included program called
48
+ ~fatty~. It allows you to easily exercise all of the important features of the
49
+ ~fatty~ library. At a normal shell prompt simply type ~fatty~, and it will
50
+ launch the ~fatty~ demo program.
33
51
 
34
- ** Builtin commands
52
+ Once inside ~fatty~ you will be prompted with a prompt that names your current
53
+ directory. Type ~help~ to get a summary of the builtin commands available to
54
+ you. If you type anything other than a builtin command, ~fatty~ attempts to
55
+ run it as a shell command and displays the output.
35
56
 
57
+ *** Builtin commands
36
58
  Here are the commands builtin to =fatty=
37
59
 
38
- | Command | Description |
39
- |-----------------------------+-------------------------------------------------------------------------|
40
- | help | Display this file on the output pane |
41
- | cd | Change the current directory used by the shell |
42
- | choose | Present a series of choices in a popup window |
43
- | choosevals | Also present choices in a popup window but return an associated value |
44
- | choose_multi | Present choices with a "checkbox" for selecting multiple values |
45
- | choosevals_multi | Also present a checkbox but return associated values |
46
- | menu | Present a menu of labeled routines to run |
47
- | info | Display an "info" message on the status line |
48
- | good | Display a "good" message colored to indicate success |
49
- | warn | Display a "warn" message colored to indicate caution |
50
- | oops | Display an "oops" message colored to indicate failure |
51
- | prompt | Popup a text box for entering a value in response to a prompt |
52
- | progress count <N> | Display an animated progress indicator counting up to 40 or the given N |
53
- | progress percent <N> | Same but also show the percent complete |
54
- | progress simple_percent <N> | Same but show only the percent complete |
55
- | progress trail | Show progress by displaying an "indicator" character for each step |
56
- | progress bar | Show progress by a filling bar using ASCII characters |
57
- | progress unicode_bar | Same, but using unicode characters |
58
- | progress braille_bar | Same, but using braille characters |
59
- | progress spinner | Animate a "spinner" showing a busy state |
60
- | markdown <file.md> | Render the markdown file to the output pane; defaults to a demo file |
61
- | keytest | Enter key diagnostic mode report keycodes, key names, and bindings |
62
-
63
-
64
- ** Keybindings
65
-
66
- The following tables explain the keybindings available in `fatty` in different
67
- contexts. Named keys are indicated by `:name` and key categories, such as
68
- `<digits>` are indicated with brackets.
60
+ | Command | Description |
61
+ |----------------------------+-------------------------------------------------------------------------|
62
+ | help | Display this file on the output pane |
63
+ | cd | Change the current directory used by the shell |
64
+ | choose | Present a series of choices in a popup window |
65
+ | choosevals | Also present choices in a popup window but return an associated value |
66
+ | choose_multi | Present choices with a "checkbox" for selecting multiple values |
67
+ | choosevals_multi | Also present a checkbox but return associated values |
68
+ | menu | Present a menu of labeled routines to run |
69
+ | info | Display an "info" message on the status line |
70
+ | good | Display a "good" message colored to indicate success |
71
+ | warn | Display a "warn" message colored to indicate caution |
72
+ | oops | Display an "oops" message colored to indicate failure |
73
+ | interrupt | Test fatty's interrupt handling |
74
+ | prompt | Popup a text box for entering a value in response to a prompt |
75
+ | progress count <N> | Display an animated progress indicator counting up to 40 or the given N |
76
+ | progress percent <N> | Same but also show the percent complete |
77
+ | progress count_percent <N> | Same but show both count and the percent complete |
78
+ | progress trail | Show progress by displaying an "indicator" character for each step |
79
+ | progress bar | Show progress by a filling bar using ASCII characters |
80
+ | progress unicode_bar | Same, but using unicode characters |
81
+ | progress braille_bar | Same, but using braille characters |
82
+ | progress spinner | Animate a "spinner" showing a busy state |
83
+ | markdown <file.md> | Render the markdown file to the output pane; defaults to a demo file |
84
+ | keytest | Enter key diagnostic mode to report keycodes, key names, and bindings |
85
+ | colors | Display ANSI, 256-color, and X11 color diagnostics |
69
86
 
70
- *** Input Context
87
+ *** Screenshots
88
+
89
+ **** Command line editing
90
+
91
+ Here is the command line, mid-edit showing a region selected and, in dim text
92
+ to the right, a predictive completion based on history.
93
+
94
+ #+CAPTION: Input editing showing region and predictive completion.
95
+ #+ATTR_HTML: :alt Fatty command line screenshot :width 900
96
+ [[file:images/input_editing.png]]
97
+
98
+ **** Searchable Output
99
+
100
+ While paging output, you can search for words, as here the user searches for
101
+ the word =tty= in the output. The current match is highlighted in yellow with
102
+ other matches highlighted in gray. The paging status line shows the search
103
+ term and the direction of search. The user can navigate for other matches in
104
+ the output using =n= and =N=.
105
+
106
+ #+CAPTION: Searching for instances of =tty= in the output.
107
+ #+ATTR_HTML: :alt Fatty search pane :width 900
108
+ [[file:images/search_output.png]]
109
+
110
+ **** Paging Markdown
111
+
112
+ The ~fatty~ demo running the =markdown= command and paging the output. It
113
+ shows =fatty's= ability to render markdown using ANSI codes, including
114
+ colorizing code blocks, as well as its paging interface.
115
+
116
+ #+CAPTION: Running the =fatty= demo =markdown= command.
117
+ #+ATTR_HTML: :alt Fatty terminal UI demo screenshot :width 900
118
+ [[file:images/page_markdown.png]]
119
+
120
+ **** Popup Selection
121
+ One of the many "widgets" available through =fatty= is the ability to present
122
+ the user with a set of choices to select from. After running the demo's
123
+ =choose "AAA" "BBB" "CCC" "DDD" "EEE"=
124
+
125
+ #+CAPTION: Running the =fatty= demo =choose= command.
126
+ #+ATTR_HTML: :alt Fatty terminal UI demo screenshot :width 900
127
+ [[file:images/choose_popup.png]]
128
+
129
+ * Usage
130
+ ** Launching a Fatty Terminal
131
+ *** =on_accept=
132
+ You can launch a ~Fatty~ terminal session that does arbitrary processing of an
133
+ edited command line and returns a String to be displayed on the output pane by
134
+ providing an =on_accept= proc as a parameter as shown in this file that we'll
135
+ call ~loudrev~:
136
+
137
+ #+begin_src ruby :tangle no
138
+ #! /usr/bin/env ruby
139
+ # -*- mode: ruby -*-
140
+
141
+ require 'fatty'
142
+
143
+ loud_reversal = lambda do |line|
144
+ line.upcase.reverse
145
+ end
146
+
147
+ Fatty::Terminal.new(
148
+ on_accept: loud_reversal,
149
+ ).go
150
+ #+end_src
151
+
152
+ Now you have an interactive application that allows you to type text and see
153
+ what it looks like when written backwards in uppercase letters. It's as easy
154
+ as that!
155
+
156
+ When a =fatty= application runs, =fatty= installs a few files if they do not
157
+ exist:
158
+
159
+ - a history file at =~/.fatty_history=
160
+ - a config directory at =~/.config/fatty= that contains
161
+ + =config.yml=, the main configuration file
162
+ + =keydefs.yml=, a file for associating keycodes emitted by the underlying
163
+ terminal with names when curses does not do so automatically,
164
+ + =keybindings.yml=, a file for associating named keys with actions above
165
+ and beyond the emacs bindings that are used by default, and
166
+ + =themes=, a directory of pre-defined theme definitions that you can choose
167
+ from and add to by adding your own themes.
168
+
169
+ **** Parameters to =on_accept=
170
+ The =on_accept= proc passed to an instance of =Fatty::Terminal= can take one
171
+ or two parameters: (1) =line=, the edited line as it exists when the user
172
+ types =RETURN= and (2) an optional callback parameter that you can use to
173
+ access the facilities of =fatty=.
174
+
175
+ **** The =line= parameter to =on_accept=
176
+ =Fatty= does not parse command lines for your application. When the user
177
+ accepts a =line=, =Fatty= passes the line to =on_accept= as text. It is up to
178
+ your callback to decide whether to strip whitespace, split words, interpret
179
+ quotes, parse options, recognize subcommands, or treat punctuation specially.
180
+
181
+ This keeps =Fatty= useful for many different kinds of REPLs. Some applications
182
+ want shell-like parsing. Others want to preserve the user's input exactly.
183
+
184
+ For shell-like parsing, Ruby's standard =Shellwords= library is often useful:
185
+
186
+ #+begin_src ruby :tangle no
187
+ #! /usr/bin/env ruby
188
+ # -*- mode: ruby -*-
189
+
190
+ require 'fatty'
191
+ require 'shellwords'
192
+
193
+ runner = lambda do |line, fatty|
194
+ words = Shellwords.split(line)
195
+
196
+ case words
197
+ when ["echo", *rest]
198
+ fatty.append("#{rest.join(" ")}\n")
199
+ when ["count", *rest]
200
+ fatty.append("#{rest.length}\n")
201
+ when []
202
+ fatty.status("Blank line", role: :info)
203
+ else
204
+ fatty.alert("Unknown command: #{words.first}", role: :warn)
205
+ end
206
+ rescue ArgumentError => e
207
+ fatty.alert("Could not parse line: #{e.message}", role: :error)
208
+ end
209
+
210
+ Fatty::Terminal.new(
211
+ prompt: "shellwords> ",
212
+ on_accept: runner,
213
+ ).go
214
+ #+end_src
215
+
216
+ **** The callback parameter to =on_accept=
217
+ The =on_accept= proc can have a second parameter named whatever you like that
218
+ serves as a callback into certain facilities provided by =fatty=. In this
219
+ README we use the name =fatty= for the callback parameter since it provides
220
+ access to the facilities provided by the =fatty= library. Here is a variation
221
+ of the prior example that also echoes the original typed string into the
222
+ so-called status area:
223
+
224
+ #+begin_src ruby :tangle no
225
+ #! /usr/bin/env ruby
226
+ # -*- mode: ruby -*-
227
+
228
+ require 'fatty'
229
+
230
+ loud_reversal = lambda do |line, fatty|
231
+ fatty.good(line)
232
+ line.upcase.reverse
233
+ end
234
+
235
+ Fatty::Terminal.new(
236
+ on_accept: loud_reversal,
237
+ ).go
238
+ #+end_src
239
+
240
+ The callback parameter responds to several methods that allow your
241
+ application to interact with the user. They are documented below.
242
+
243
+ **** Output ordering
244
+ The =on_accept= callback can send text to the output pane in three ways:
245
+
246
+ 1. by returning a value from the callback;
247
+ 2. by calling =append= or =markdown= on the callback environment; or
248
+ 3. by calling =append_now= on the callback environment.
249
+
250
+ A returned =String= is the simplest output mechanism. It is displayed after
251
+ the callback finishes:
252
+
253
+ #+begin_src ruby :tangle no
254
+ Fatty::Terminal.new(
255
+ on_accept: ->(line) { line.upcase.reverse },
256
+ ).go
257
+ #+end_src
258
+
259
+ Calls to =append= and =markdown= also queue output until the callback finishes.
260
+ If a callback both calls =append= or =markdown= and returns a =String=, the
261
+ queued output is displayed first and the returned string is displayed after it.
262
+
263
+ Calls to =append_now= are different. They append text and immediately render a
264
+ frame, so the user can see output while the callback is still running. Use
265
+ =append_now= for long-running callbacks that should stream progress to the
266
+ output pane.
267
+
268
+ At the end of the callback, =Fatty= finishes the command and updates the pager
269
+ state. Output produced with =append_now= may therefore appear during the
270
+ callback, while output from =append=, =markdown=, and the callback's return
271
+ value appears after the callback returns.
272
+
273
+ **** Long-running callbacks and interruption
274
+ The =on_accept= callback runs synchronously. While it is running, control does
275
+ not return to Fatty's normal event loop. A callback that performs lengthy work
276
+ should therefore provide explicit safe points at which the user may interrupt
277
+ it:
278
+
279
+ #+begin_src ruby :tangle no
280
+ runner = lambda do |_line, fatty|
281
+ records.find_each do |record|
282
+ process(record)
283
+ fatty.check_interrupt!
284
+ end
285
+ fatty.good("Processing complete.")
286
+ end
287
+ #+end_src
288
+
289
+ The =check_interrupt!= method performs a throttled, nonblocking input check. If
290
+ the user has typed C-c or C-g, it raises =Fatty::Interrupt=. Fatty catches that
291
+ exception at the callback boundary, finishes the current command cleanly, and
292
+ returns to the normal input loop.
293
+
294
+ Calls to =check_interrupt!= should be placed only at points where it is safe to
295
+ stop the application work. For example, they can go between records, batches,
296
+ files, or other complete units of work. Fatty does not interrupt callbacks
297
+ asynchronously and does not stop threads or raise exceptions at arbitrary
298
+ instructions.
299
+
300
+ Fast callbacks do not need to call =check_interrupt!=. Long-running callbacks
301
+ that already update a shared progress facility can often place the check there
302
+ so that individual commands do not need separate interruption logic.
303
+
304
+ Code that needs to report interruption before allowing Fatty to perform its
305
+ normal cleanup may rescue and re-raise the exception:
306
+
307
+ #+begin_src ruby :tangle no
308
+ begin
309
+ items.each do |item|
310
+ process(item)
311
+ fatty.check_interrupt!
312
+ end
313
+ rescue Fatty::Interrupt
314
+ fatty.warn("Processing interrupted.", replace: true)
315
+ raise
316
+ else
317
+ fatty.good("Processing complete.", replace: true)
318
+ end
319
+ #+end_src
320
+
321
+ **** ANSI colors in output
322
+ Text passed to =append=, =append_now=, =status=, and =alert= may contain ANSI
323
+ SGR color/style sequences. Fatty interprets those sequences relative to the
324
+ current theme role, so an ANSI reset returns to the active Fatty role rather
325
+ than to the terminal's physical default colors. =fatty= includes the nice
326
+ [[https://github.com/ku1ik/rainbow][=Rainbow= gem]] for colorizing text as a convenience.
327
+
328
+ *** Other parameters to =Terminal.new=
329
+ When building a REPL with =Terminal.new= you can also supply several other
330
+ parameters:
331
+
332
+ - =prompt:= :: a =String= or a zero-argument callable such as a =Proc= or
333
+ =lambda= that returns the prompt used for the input line to be edited.
334
+ Because it can be a callable, it can change after each run of =on_accept=;
335
+ if not given, it defaults to '> ';
336
+ - =app_name= :: the name for the "app" using the =fatty= library; this allows
337
+ app-specific configuration under =~/.config/fatty/apps/<app_name>= or a
338
+ different directory specified by =app_config_dir=;
339
+ - =app_config_dir= :: an alternative directory for app-specific configuration
340
+ instead of the default;
341
+ - =completion_proc= :: a callable that takes the current text of the input
342
+ field's buffer and returns an =Array= of possible completions at that point
343
+ in the input; by default, there is no completion proc;
344
+ - =history_path= :: a =String= for the file path where command history is
345
+ loaded from and saved. The default, =:default=, uses =history.file= from
346
+ Fatty's configuration, falling back to =~/.fatty_history= if no history file
347
+ is configured. Passing =nil= or =false= disables persistent history and
348
+ keeps history in memory only. Passing a string uses that path directly.
349
+ - =history_ctx= :: optional history context used to prefer relevant history
350
+ entries. It may be a hash or a callable returning a hash. The context is
351
+ stored in the history file with each accepted line and is used by history
352
+ navigation and history autosuggestions. Matching context entries are
353
+ preferred, but history is not strictly partitioned; Fatty can still fall
354
+ back to other entries. This is useful when the same application wants
355
+ different history behavior in different directories, ledgers, projects,
356
+ modes, or accounts.
357
+
358
+ For example, the =fatty= demo executable uses the current working directory as
359
+ history context:
360
+
361
+ #+begin_src ruby :tangle no
362
+ Fatty::Terminal.new(
363
+ history_ctx: -> { { pwd: Dir.pwd } },
364
+ on_accept: ->(line, env) {
365
+ # ...
366
+ },
367
+ ).go
368
+ #+end_src
369
+
370
+ With that setup, commands entered in =~/src/byr= are favored when the terminal
371
+ is again in =~/src/byr=, while commands entered elsewhere remain available as
372
+ fallback history.
373
+
374
+ ** The Callback API
375
+ Here are the details on the messages that you can send to the callback
376
+ parameter to the =on_accept= proc.
377
+
378
+ *** =append(text, follow: true)=
379
+ Add the given text to the output pane. After a full page of output is
380
+ produced, enter paging mode so the user can view the output at leisure and
381
+ search the output. If =follow= is true, keep the output pane's viewport
382
+ covering the last lines of output.
383
+
384
+ *** =append_now(text, follow: true, mode: nil)=
385
+ Like =append=, but display output as it is produced rather than wait for a
386
+ full page to be produced.
387
+
388
+ Setting the mode to =:scrolling= or =:paging= determines whether the output is
389
+ scrolled continuously or paused on each page of output.
390
+
391
+ *** =markdown(text)=
392
+ Render the given text as markdown source according to the current theme then
393
+ =append= the result to the output pane.
394
+
395
+ We will not rehearse here how to compose markdown, but one thing that is a bit
396
+ special with =fatty= is forcing a line break: you should use =<br>= to force a
397
+ line break in the middle of a line.
398
+
399
+ #+begin_example
400
+ This line has a forced line break <br> and this should appear on the next line.
401
+ #+end_example
402
+
403
+ will render as
404
+
405
+ #+begin_example
406
+ This line has a forced line break
407
+ and this should appear on the next line.
408
+ #+end_example
409
+
410
+ *** =status(text, role: :info)=
411
+ Display the text in the "status" area, the lines immediately above the input
412
+ field, using one of the following "roles":
413
+ - :good :: colored to indicate something affirming according to the theme,
414
+ usually something greenish;
415
+ - :info :: colored something neutral simply for informational purposes;
416
+ - :warn :: colored something to suggest caution, usually something in the
417
+ orange to yellow range;
418
+ - :error :: colored something to suggest danger, usually some tone of red.
419
+
420
+ *** =good(text, replace: false, render: true)=
421
+ Display the text in the status area with the role :good.
422
+
423
+ *** =info(text, replace: false, render: true)=
424
+ Display the text in the status area with the role :info.
425
+
426
+ *** =warn(text, replace: false, render: true)=
427
+ Display the text in the status area with the role :warn.
428
+
429
+ *** =error(text, replace: false, render: true)=
430
+ Display the text in the status area with the role :error.
431
+
432
+ *** =oops(text, replace: false, render: true)=
433
+ An alias for =error(text)=
434
+
435
+ *** =alert(text, role: :info)=
436
+ Display the text in the one-line alert panel just below the input field. In
437
+ the alert panel, the role only controls the foreground color, not the
438
+ background.
439
+
440
+ *** =prompt(prompt, initial: "", cancel_value: nil, history_key: nil, save_history: true)=
441
+ Display a popup dialog box that allows the user to type in any string and
442
+ return it. Combine the input line with a prompted-for string and shuffle
443
+ their letters together:
444
+
445
+ #+begin_src ruby :tangle no
446
+ #! /usr/bin/env -S ruby -Ilib
447
+ # -*- mode: ruby -*-
448
+
449
+ require 'fatty'
450
+
451
+ weaver = lambda do |line, fatty|
452
+ str = fatty.prompt("Secondary string to weave in with '#{line}'")
453
+ (line + str).downcase.gsub(/[^a-z]/, '').split('').shuffle.join
454
+ end
455
+
456
+ Fatty::Terminal.new(
457
+ on_accept: weaver,
458
+ prompt: "Primary String> "
459
+ ).go
460
+ #+end_src
461
+
462
+ You can pre-fill the prompt's input line by passing in an =initial:=
463
+ parameter.
464
+
465
+ The =prompt= input has its own history facility separate from the history in
466
+ the main input command line. You can turn it off by setting =save_history= to
467
+ =false=.
468
+
469
+ =prompt= returns whatever the user typed and returns =nil= if the user cancels
470
+ with C-c or C-g unless you specify an alternative =cancel_value=.
471
+
472
+ *** =check_interrupt!=
473
+ Provide a cooperative interruption point during a long-running =on_accept=
474
+ callback. Fatty periodically performs a nonblocking input check and raises
475
+ =Fatty::Interrupt= if the user has typed C-c or C-g.
476
+
477
+ The polling is throttled, so =check_interrupt!= may be called frequently. It
478
+ should nevertheless be called only at points where the callback can safely
479
+ stop its work.
480
+
481
+ Fatty catches =Fatty::Interrupt= at the callback boundary, finishes the current
482
+ command, restores the terminal state, and resumes the normal input loop.
483
+
484
+ *** =add_progress(label:, total: nil, style: :percent, role: :info, width: 40)=
485
+ Display a progress widget in the status area to show the user that the system
486
+ is working, not frozen. The =add_progress= method returns a =Progress= object
487
+ on which the =#update= method can be called to cause it to animate one step.
488
+ To the left of the widget the =label:= is displayed, followed by the widget.
489
+ For processes where the total size is known in advance, the =total:= parameter
490
+ indicates that size. The =role:= parameter controls the styling of the
491
+ widget. The =width:= parameter places a limit on the size of the widget.
492
+ =Fatty= supports several styles of progress widgets:
71
493
 
494
+ - :spinner :: a simple busy-wait indicator that just animates on every call;
495
+ - :count :: count up to =total= on each call of =update(current: <count>)=
496
+ - :percent :: count up to percent of =total= on each call of =update(current: <count>)=
497
+ - :count_percent :: count up and show percent of =total= on each call of =update(current: <count>)=
498
+ - :bar :: display a filling ASCII progress bar with percent and count
499
+ - :unicode_bar :: display a filling Unicode progress bar with percent and count
500
+ - :braille_bar :: display a filling progress bar with percent and count using
501
+ braille characters
502
+ - :trail :: display an "indicator" on each call of =update(indicator: <string>)=
503
+
504
+ **** Initialization =add_progress(label:, style: :percent, total: nil, role: :info, width: 40)=
505
+
506
+ - =label:= :: Text displayed before the progress widget, "Progress" by default.
507
+ - =style:= :: The style of the progress widget from one of those named above.
508
+ - =total:= :: For all styles except :trail and :spinner, the number that
509
+ represents the size of the task. As the =#update= calls increase the value
510
+ of =current= towards =total=, the widget indicates increasing completion.
511
+ - =role:= :: One of :good, :info, :warn, or :error to color the widget
512
+ according to the theme's idea of these roles. By default, :info.
513
+ - =width= :: The number of characters for the full widget display: it is only
514
+ relevant for the bar styles and the trail style; otherwise it is ignored.
515
+
516
+ **** Update =update(current: nil, indicator: nil, render: false)=
517
+ You update the progress widget by calling =#update= on the Progress object,
518
+ passing as the =current:= parameter a number that indicates progress so far.
519
+
520
+ #+begin_src ruby :tangle no
521
+ #! /usr/bin/env -S ruby -Ilib
522
+ # -*- mode: ruby -*-
523
+
524
+ require 'fatty'
525
+ require 'prime'
526
+
527
+ primer = lambda do |input, fatty|
528
+ if input.match?(/\A[1-9]\d*\z/)
529
+ k = input.to_i
530
+ prog = fatty.add_progress(label: "Thinking...", total: k, style: :bar)
531
+ out_line = +""
532
+ num_primes = 0
533
+ Prime.each do |p|
534
+ num_primes += 1
535
+ out = "#{p} "
536
+
537
+ if out_line.length + out.length > 100
538
+ fatty.append_now("#{out_line}\n", mode: :scrolling)
539
+ out_line.clear
540
+ end
541
+
542
+ out_line << out
543
+ prog.update(current: num_primes)
544
+ break if num_primes >= k
545
+ end
546
+ fatty.append_now("#{out_line}\n", mode: :scrolling) unless out_line.empty?
547
+ prog.finish("Done")
548
+ else
549
+ fatty.alert "Give me a positive integer", role: :error
550
+ end
551
+ end
552
+
553
+ Fatty::Terminal.new(
554
+ on_accept: primer,
555
+ prompt: 'How many primes? ',
556
+ ).go
557
+ #+end_src
558
+
559
+ For the trail-style progress, you can use an arbitrary string to indicate
560
+ progress by adding an =indicator:= parameter to the =#update= call.
561
+
562
+ Suppose, for example, you wanted to print a trail in which the last digit of
563
+ the prime is displayed to indicate progress and colored so it stands out:
564
+
565
+ #+begin_src ruby :tangle no
566
+ #! /usr/bin/env -S ruby -Ilib
567
+ # -*- mode: ruby -*-
568
+
569
+ require 'fatty'
570
+ require 'prime'
571
+ require "rainbow/refinement"
572
+
573
+ using Rainbow
574
+
575
+ primer = lambda do |input, fatty|
576
+ if input.match?(/\A[1-9]\d*\z/)
577
+ k = input.to_i
578
+ prog = fatty.add_progress(label: "Thinking...", total: k, style: :trail, width: 100)
579
+ out_line = +""
580
+ num_primes = 0
581
+ Prime.each do |p|
582
+ num_primes += 1
583
+ out = "#{p} "
584
+
585
+ if out_line.length + out.length > 100
586
+ fatty.append_now("#{out_line}\n", mode: :scrolling)
587
+ out_line.clear
588
+ end
589
+
590
+ out_line << out
591
+ last_dig = p.to_s.split('').last
592
+ sig =
593
+ case last_dig
594
+ when '1'
595
+ last_dig.red
596
+ when '3'
597
+ last_dig.blue
598
+ when '7'
599
+ last_dig.green
600
+ when '9'
601
+ last_dig.yellow
602
+ else
603
+ '!'
604
+ end
605
+ prog.update(current: num_primes, indicator: sig)
606
+ break if num_primes >= k
607
+ end
608
+ fatty.append_now("#{out_line}\n", mode: :scrolling) unless out_line.empty?
609
+ prog.finish("Done")
610
+ else
611
+ fatty.alert "Give me a positive integer", role: :error
612
+ end
613
+ end
614
+
615
+ Fatty::Terminal.new(
616
+ on_accept: primer,
617
+ prompt: 'How many primes? ',
618
+ ).go
619
+ #+end_src
620
+
621
+ **** Finish
622
+ As the prior examples illustrate, you can issue an ending message at the end
623
+ of the process by calling =#finish= on the Progress object.
624
+
625
+ **** Clear
626
+ And, if you have occasion, you can clear the Progress by calling =#clear= on
627
+ the Progress object.
628
+
629
+ *** =choose(prompt, choices:, initial_choice_idx: 0, cancel_value: nil)=
630
+ Present a set of =choices:=, which can be either:
631
+
632
+ - an =Array= of =String= choices, where the selected string is returned; or
633
+ - a =Hash= whose keys are converted to =String= labels and whose values are
634
+ returned.
635
+
636
+ =choose= returns:
637
+
638
+ - the selected =String= when =choices:= is an =Array=;
639
+ - the associated value when =choices:= is a =Hash=; or
640
+ - =cancel_value= when the user cancels with C-c or C-g.
641
+
642
+ The =prompt= String (by default "Choose") can guide the user about the purpose
643
+ of the choices.
644
+
645
+ If you want to set one of the choices as the initial choice, set
646
+ =initial_choice_idx:= to the Integer index of one of the choices.
647
+
648
+ If you want a value associated with the user's cancellation of the chooser
649
+ with C-c or C-g, set =cancel_value:= to that value.
650
+
651
+ With an Array of Strings:
652
+
653
+ #+begin_src ruby :tangle no
654
+ #! /usr/bin/env -S ruby -Ilib
655
+ # -*- mode: ruby -*-
656
+
657
+ require 'fatty'
658
+
659
+ rgb = lambda do |_line, fatty|
660
+ color = fatty.choose(
661
+ "Choose a color",
662
+ choices: ["red", "green", "blue"],
663
+ )
664
+ fatty.status("If you say so: #{Rainbow(color).send(color.to_sym)} it is!\n")
665
+ end
666
+
667
+ Fatty::Terminal.new(
668
+ prompt: "Hit RETURN to choose a color> ",
669
+ on_accept: rgb,
670
+ ).go
671
+ #+end_src
672
+
673
+ Or with a Hash:
674
+
675
+ #+begin_src ruby :tangle no
676
+ #! /usr/bin/env -S ruby -Ilib
677
+ # -*- mode: ruby -*-
678
+
679
+ require 'fatty'
680
+
681
+ nums = lambda do |_line, fatty|
682
+ const = fatty.choose(
683
+ "Choose a constant",
684
+ choices: { "Pi" => 3.14159, "Euler" => 2.718281828, "Golden Ratio" => 1.61803398875 }
685
+ )
686
+ fatty.status("Somewhere around #{const}\n")
687
+ end
688
+
689
+ Fatty::Terminal.new(
690
+ prompt: "Hit RETURN to choose a constant> ",
691
+ on_accept: nums,
692
+ ).go
693
+ #+end_src
694
+
695
+ *** =choose_multi(prompt, choices:, cancel_value: nil)=
696
+ Present a set of =choices:=, which can either be
697
+ - an Array whose items are converted to =Strings= and presented as choices
698
+ - a Hash whose keys converted to =Strings= and presented as choices
699
+
700
+ =choose_multi= returns
701
+ - a =Hash= whose keys and values are the user's
702
+ selections if =choices:= was an =Array= of =Strings=,
703
+ - a =Hash= whose keys and values are those selected from the =choices:= =Hash=
704
+ if =choices:= was an =Hash=,
705
+ - or the =cancel_value= if the user canceled the selection with C-c or C-g.
706
+
707
+ The =prompt= String (by default "Choose Many") can guide the user about the purpose
708
+ of the choices.
709
+
710
+ *** =confirm(prompt, yes_label: "Yes", no_label: "No", cancel_value: false)=
711
+ Present the user with a simple Yes/No choice using your choice of ways to
712
+ express "Yes" or "No" with =yes_label:= and =no_label:=.
713
+
714
+ =confirm= returns =true= for "Yes" and =false= for "No."
715
+
716
+ =confirm= will return =false= on cancellation with C-c or C-g unless you
717
+ provide an alternative =cancel_value:=, in which case it returns that.
718
+
719
+ *** =menu(prompt, choices:, initial_choice_idx: 0, cancel_value: nil)=
720
+ Present the user with a set of =choices:= representing actions to execute.
721
+
722
+ =choices:= is a =Hash= whose keys are labels presented to the user and whose
723
+ values are procs, lambdas, or other objects that respond to =#call=.
724
+
725
+ #+begin_src ruby :tangle no
726
+ #! /usr/bin/env -S ruby -Ilib
727
+ # -*- mode: ruby -*-
728
+
729
+ require 'fatty'
730
+
731
+ asker = lambda do |_line, fatty|
732
+ result = fatty.menu(
733
+ "Pick an action",
734
+ choices: {
735
+ "Self Echo" => ->(fatty, label) {
736
+ fatty.append("You chose #{label}\n")
737
+ :echoed
738
+ },
739
+ "Say Hello" => ->(fatty, _label) {
740
+ name = fatty.prompt("Name?")
741
+ fatty.append("Hello, #{name}\n")
742
+ :greeted
743
+ },
744
+ },
745
+ )
746
+ fatty.good(result)
747
+ end
748
+
749
+ Fatty::Terminal.new(
750
+ on_accept: asker,
751
+ prompt: "Type RETURN for some action> ",
752
+ ).go
753
+ #+end_src
754
+
755
+ Each callable has access to the =CallbackEnvironment=, called =fatty= here and
756
+ the =String= label that the user selected.
757
+
758
+ Each callable can return a value that the =@on_accept= proc can process as it
759
+ pleases. In the example, they are printed to the status area.
760
+
761
+ If you want to set one of the choices as the initial choice, set
762
+ =initial_choice_idx:= to the Integer index of one of the choices.
763
+
764
+ If you want a value associated with the user's cancellation of the chooser
765
+ with C-c or C-g, set =cancel_value:= to that value.
766
+
767
+
768
+ *** =environment=
769
+ This returns a =Hash= of runtime conditions detected by =Fatty=:
770
+
771
+ | Key | Description |
772
+ |-----------------------+------------------------------------------------------|
773
+ | =:arch= | System CPU architecture, e.g., =x86_64= |
774
+ | =:os= | Operating system detected |
775
+ | =:ruby_platform= | Ruby platform, e.g., =x86_64-linux= |
776
+ | =:screen= | Whether the terminal is running under =screen= |
777
+ | =:ssh= | Whether the terminal is running under SSH |
778
+ | =:tmux= | Whether the terminal is running under =tmux= |
779
+ | =:terminal= | Detected underlying terminal, e.g., =kitty= |
780
+ | =:terminal_version= | Version of the terminal, if known |
781
+ | =:term= | The =TERM= terminal type |
782
+ | =:truecolor_detected= | Whether truecolor capability appears to be available |
783
+ | =:truecolor_enabled= | Whether Fatty is actually using truecolor rendering |
784
+ | =:curses= | Runtime curses capabilities and parameters |
785
+
786
+ The environment report also includes a nested =:curses= hash:
787
+
788
+ | Key | Description |
789
+ |---------------------+--------------------------------------------------------|
790
+ | =:started= | Whether curses has been initialized |
791
+ | =:truecolor= | Whether the active curses context is using truecolor |
792
+ | =:key_min= | Lowest keycode with a curses name |
793
+ | =:key_max= | Highest keycode with a curses name |
794
+ | =:lines= | Number of terminal rows available |
795
+ | =:cols= | Number of terminal columns available |
796
+ | =:has_colors= | Whether curses reports color support |
797
+ | =:colors= | Number of colors available to curses |
798
+ | =:color_pairs= | Number of color pairs available to curses |
799
+ | =:can_change_color= | Whether curses can redefine color values |
800
+
801
+ * Default Interaction
802
+ ** Parts of the Screen
803
+ *** Input Field
804
+ Just above the bottom of the screen where all the action takes place: it is a
805
+ line for editing the input. It displays a prompt followed by an area in which
806
+ you build the command line using =fatty's= editing facilities.
807
+
808
+ *** Output Pane
809
+ Most of the top part of the screen is reserved for displaying whatever output
810
+ is sent to it with the =on_accept= callback to the =Terminal=. It can render
811
+ colored ANSI-encoded strings and will page long output so you can view it a
812
+ page at a time and even search the output.
813
+
814
+ *** Status Area
815
+ The one to four lines just above the Input Field that displays output to the
816
+ user that is out of band for the Output Pane. Brief messages of confirmation,
817
+ warning, or error can be displayed there so as to get the user's immediate
818
+ attention. Progress bars also render there where their visibility is made
819
+ prominent.
820
+
821
+ *** Alert Area
822
+ Alerts are short-lived, non-scrolling messages shown below the input field.
823
+ They are intended for user-visible conditions that require attention. =Fatty=
824
+ uses this area to warn the user of unrecognized key codes and of unbound key
825
+ presses.
826
+
827
+ ** Command-line Editing
828
+ =fatty= aims to have a full-featured command-line editor for editing a single
829
+ line of text that is sent to the =on_accept= proc for the =Terminal=. The
830
+ default keybindings are based on Emacs keybindings, but they are fully
831
+ customizable. Implementing =vim=-like keybindings is not currently possible
832
+ because =fatty= has no notion of editing "modes" as would be required for any
833
+ kind of a modal editor---at least not yet. Maybe some day.
834
+
835
+ *** Input Context
72
836
  When editing the input line or text input for widgets like the `prompt`,
73
837
  `fatty` provides emacs-like editing keybindings by default. Many of these
74
838
  commands can take a count prefix argument to repeat the command count times.
75
839
  For example, `M-8 M-0 #` will insert 80 '#' characters at the cursor.
76
840
 
841
+ **** Cursor Motion
842
+ =Fatty= implements all the expected actions for moving the cursor around the
843
+ input buffer. You can move by characters or words or to the beginning or end
844
+ of the buffer.
845
+
77
846
  | Key | Description |
78
847
  |------------+-------------------------------------------------------|
79
848
  | C-a | move to the beginning of the line |
@@ -90,77 +859,1270 @@ For example, `M-8 M-0 #` will insert 80 '#' characters at the cursor.
90
859
  | M-b | move cursor left one word |
91
860
  | M-:left | move cursor left one word |
92
861
  | C-:left | move cursor left one word |
93
- | C-t | transpose characters |
94
- | M-t | transpose words |
95
- | | |
862
+
863
+
864
+ **** Clearing
865
+ This is one action that does not affect the input buffer but the output pane.
866
+ It clears it.
867
+
868
+ | Key | Description |
869
+ |------------+-------------------------------------------------------|
870
+ | C-l | clear the output pane |
871
+
872
+ **** Undo and Redo
873
+ =Fatty= keeps a stack of buffer-modifying actions, recording the buffer
874
+ contents before any change is made. The undo-redo facility allows you to
875
+ traverse that stack, =undo= travels backward to an earlier buffer state, and,
876
+ once at an earlier state, =redo= travels forward to a more recent buffer
877
+ state.
878
+
879
+ | Key | Description |
880
+ |------------+-------------------------------------------------------|
881
+ | C-/ | undo |
882
+ | C-_ | undo |
883
+ | C-M-/ | redo |
884
+ | M-/ | redo |
885
+
886
+ **** Deletion and Killing
887
+ =Fatty= implements the emacs-esque notion of "killing" text by deleting it but
888
+ also adding it to the "kill ring", a circular structure of all prior kills.
889
+ It is circular in that successive "yanks", or pastes, traverse the kill ring
890
+ and return to the beginning after reaching the end. By contrast a "deletion"
891
+ merely removes text from the buffer without adding it to the kill ring.
892
+
893
+ When the region is active, destructive editing commands kill the region rather
894
+ than deleting characters or words.
895
+
896
+ | Key | Description |
897
+ |------------+-------------------------------------------------------|
96
898
  | C-d | delete character at cursor |
97
899
  | :delete | delete character at cursor |
98
900
  | :backspace | delete character before cursor |
99
901
  | M-d | kill word at cursor |
100
902
  | C-w | kill word before cursor |
101
903
  | C-k | kill to end of line |
102
- | | |
103
- | C-/ | undo |
104
- | C-_ | undo |
105
- | C-M-/ | redo |
106
- | M-/ | redo |
107
- | | |
108
- | C-:space | set the mark at the current cursor position |
109
- | C-@ | set the mark at the current cursor position |
110
- | C-g | clear the mark |
111
- | C-w | kill the region |
112
- | M-w | copy the region |
113
- | | |
114
- | C-y | yank last kill at cursor |
115
- | M-y | replace last yank with next in kill ring |
116
- | | |
117
- | C-u | universal count argument (time 4 each press) |
118
- | M-<digit> | accumulate count argument |
119
- | | |
904
+ | C-t | transpose characters |
905
+ | M-t | transpose words |
906
+
907
+ **** Yanking, Mark, and Region
908
+ Text that has been killed can be "yanked" into the buffer at the cursor
909
+ position with the "yank" action. That text can be replaced by successive
910
+ entries in the kill ring with the yank_pop action.
911
+
912
+ In addition, =fatty= allows you to define a "region" by setting a mark and
913
+ moving the cursor. The text between the mark and the cursor is the region.
914
+ The region can be killed or copied, that is deleted and added to the kill ring or
915
+ just added to the kill ring.
916
+
917
+ Notice that commands that normally delete characters or words, kill the region
918
+ if the region is active.
919
+
920
+ | Key | Description |
921
+ |------------+---------------------------------------------------|
922
+ | C-y | yank last kill at the cursor |
923
+ | M-y | replace last yank with next item in the kill ring |
924
+ | | |
925
+ | C-d | when region active, kill the region |
926
+ | :delete | when region active, kill the region |
927
+ | :backspace | when region active, kill the region |
928
+ | M-d | when region active, kill the region |
929
+ | C-w | when region active, kill the region |
930
+ | | |
931
+ | C-:space | set the mark at the current cursor position |
932
+ | C-@ | set the mark at the current cursor position |
933
+ | C-g | clear the mark |
934
+ | C-w | kill the region |
935
+ | M-w | copy the region |
936
+
937
+ **** Count Prefix
938
+ By default each action acts on one item, so the default count is 1. But for
939
+ certain commands that operate on characters or words, you can modify the count
940
+ by typing =M-<digit>= keys before the command. For example, if you want to
941
+ insert exactly 80 '#' characters into the buffer, =M-8 M-0 #= will do the
942
+ trick.
943
+
944
+ There is one command, however, for which the count is not used as a repeat
945
+ count: for =page_top= (bound in paging to =g=) the count is used as a line
946
+ number to navigate to.
947
+
948
+ Fatty follows the emacs convention of making =C-u= multiply the count prefix
949
+ by 4 with each press. So you can insert 64 '#' characters with =C-u C-u C-u
950
+ #=.
951
+
952
+ | Key | Description |
953
+ |-----------+----------------------------------------------------|
954
+ | C-u | universal count argument (times 4 with each press) |
955
+ | M-<digit> | accumulate a count argument |
956
+
957
+
958
+ **** History Navigation
959
+ =Fatty= sports a rather sophisticated history system. It not only records
960
+ each completed item of input but can also be made to note the kind of items
961
+ and the "context" in which it was entered.
962
+
963
+ History is essentially partitioned into separate "kinds" of history so that
964
+ the command line can occupy one part of history and search strings a different
965
+ part of history. That way, the history is made relevant to the current input
966
+ buffer that you are working on.
967
+
968
+ In addition, each history item can note its "context". This is less strict
969
+ than "kinds" of history but allows the history mechanism to favor history
970
+ items from the same "context" as the current context. For example, in the
971
+ =fatty= demo, any line that is not a builtin command is treated as a shell
972
+
973
+ if you traverse history items, you will be shown command executed from the
974
+ directory you are in before other history items. It still will give you items
975
+ from other contexts, it just pushes items from the current context to the
976
+ front of the list.
977
+
978
+ You can navigate history items with the =up= and =down= keys as you would
979
+ expect. But you can also have the history items presented in a popup that
980
+ shows all history items then allows you to narrow the items displayed by
981
+ typing text into its narrowing input field. Bonus: the narrowing input field
982
+ itself has a history that you can access with =M-p= and =M-n=. History on
983
+ history.
984
+
985
+ | Key | Description |
986
+ |------------+-------------------------------------------------------|
120
987
  | C-p | replace the line with the prior history item |
121
988
  | :up | replace the line with the prior history item |
122
989
  | C-n | replace the line with the next history item |
123
- | :down | replace the line with the prior history item |
990
+ | :down | replace the line with the next history item |
124
991
  | C-r | search the history in a popup |
125
- | | |
126
- | :enter | feed the line to the on_accept proc and page output |
127
- | M-:enter | feed the line to the on_accept proc and scroll output |
128
- | C-c | quit `fatty` |
129
- | C-d | quit `fatty` only if the input line is empty |
130
- | C-l | clear the output pane |
131
- | | |
132
992
 
993
+ **** Completion
994
+ =Fatty= also provides a completion facility that makes entering input easier.
995
+ ***** Input Completion
996
+ As you type a command-line, =fatty= will auto-suggest ways to complete the
997
+ line with dimmed text to the right of the cursor:
998
+
999
+ #+CAPTION: Input editing showing region and predictive completion.
1000
+ #+ATTR_HTML: :alt Fatty command line screenshot :width 900
1001
+
1002
+ [[file:images/auto_suggest.png]]
1003
+
1004
+ After typing 'ec' =fatty= suggests a command-line pulled from history using
1005
+ the dimmed text to the right of the cursor. The suggestion can be accepted by
1006
+ moving the cursor into the suggestion, and any text then to the left of the
1007
+ cursor becomes part of the input buffer.
1008
+
1009
+ =fatty= may have several completions suggested by the text-so-far and you can
1010
+ cycle through them by hitting the =tab= key. And if you want, you can see all
1011
+ the suggestions in a popup with =M-tab=. That allows narrowing by the text of
1012
+ the completions and the narrowing filter has its own history.
1013
+
1014
+ #+CAPTION: Completion popup with narrowing.
1015
+ #+ATTR_HTML: :alt Fatty command line screenshot :width 900
1016
+
1017
+ [[file:images/completion_popup.png]]
1018
+
1019
+ ***** Sources for Input Completion
1020
+ For input completion, =fatty= uses two sources:
1021
+
1022
+ 1. The input history.
1023
+ 2. The candidates returned by the =Terminal#completion_proc=.
1024
+
1025
+ History completion is always available when the terminal has history enabled.
1026
+ It lets the user complete from lines they have previously accepted. This is
1027
+ especially useful for command shells, REPL-style interfaces, and applications
1028
+ where users often repeat or slightly modify earlier input.
1029
+
1030
+ Application completion is supplied by the optional =completion_proc= passed to
1031
+ =Fatty::Terminal.new=. The proc is called with the input buffer's text-so-far
1032
+ and returns candidate strings. This is where an application can offer
1033
+ completions for commands, file names, account names, option names, object
1034
+ identifiers, or anything else meaningful to the application.
1035
+
1036
+ The two sources are combined before they are presented to the user. Fatty
1037
+ deduplicates candidates and then offers the result through the normal completion
1038
+ interface. =TAB= cycles through completions inline. =M-TAB= opens a completion
1039
+ popup, where the user can narrow the candidate list by typing in the
1040
+ =Complete:= field.
1041
+
1042
+ Application candidates are offered first. History candidates fill in behind
1043
+ them. This gives the host application priority for current, domain-specific
1044
+ completions while still letting users recover earlier accepted input.
1045
+
1046
+ The completion popup is still a popup filter. Its narrowing text has its own
1047
+ history, separate from command input history and from other popup filters. Use
1048
+ =M-p= and =M-n= inside the completion popup to recall previous completion
1049
+ filters.
1050
+
1051
+ ***** File Name Completion
1052
+ Apart from command-line completion, =fatty= offers completion of file names if
1053
+ the cursor is at the end of a string that looks like a file name: that is, it
1054
+ starts with './', '../', '~/' or '/'.
1055
+
1056
+ 1. If the string-so-far matches only one directory or file name, it inserts
1057
+ that into the input buffer and continues completion from there.
1058
+ 2. If the string-so-far matches multiple files or directories it auto-suggests
1059
+ the first and =TAB= cycles through each of the candidates which you can
1060
+ accept by moving the cursor past the suggestion. Likewise =S-TAB= cycles
1061
+ backwards through the list of candidates.
1062
+ 3. If the string-so-far matches no files, completion does nothing.
1063
+
1064
+ Just as with command line completion, you can invoke a popup of completions
1065
+ with =M-TAB= and a popup of possible file names will appear within which you
1066
+ may filter the candidates down by narrowing. Unlike other forms of
1067
+ completion, the file name completion popup allows you to fill out the path by
1068
+ navigating directories within the popup by hitting =TAB= or =right= to descend
1069
+ into a sub-directory or =S-TAB= or =left= to ascend to the parent directory.
1070
+ The popup title shows the path so far.
1071
+
1072
+ **** Accept or Cancel
1073
+ Finally, when the command-line is to your satisfaction, you can submit the
1074
+ line to the =on_accept= proc which determines how your app responds to it.
1075
+
1076
+ You can end a =fatty= session with =C-c= at the command line, or (only if the
1077
+ buffer is empty) with =C-d=.
1078
+
1079
+ | Key | Description |
1080
+ |----------+-------------------------------------------------------|
1081
+ | :enter | feed the line to the on_accept proc and page output |
1082
+ | M-:enter | feed the line to the on_accept proc and scroll output |
1083
+ | C-c | quit `fatty` |
1084
+ | C-d | quit `fatty` only if the input line is empty |
133
1085
 
134
1086
  *** Paging Context
1087
+ Once the command-line is accepted, `fatty` sends output to the large output
1088
+ pane, and if the output is more than one screen long presents a paging
1089
+ environment for viewing and searching the output. From there, =fatty= offers
1090
+ the expected navigation aids for examining the output:
135
1091
 
136
- By default, `fatty` sends output to the large output pane, and if the output
137
- is more than one screen long presents a paging environment for viewing and
138
- searching the environment.
1092
+ | Key | Count? | Description |
1093
+ |-------------+--------+-----------------------------------------------------|
1094
+ | =up= | Yes | move output one line up |
1095
+ | =k= | Yes | move output one line up |
1096
+ | =down= | Yes | move output one line down |
1097
+ | =j= | Yes | move output one line down |
1098
+ | =page_up= | Yes | move back one page |
1099
+ | =b= | Yes | move back one page |
1100
+ | =u= | Yes | move back one page |
1101
+ | =M-v= | Yes | move back one page |
1102
+ | =backspace= | Yes | move back one page |
1103
+ | =page_down= | Yes | move forward one page |
1104
+ | =f= | Yes | move forward one page |
1105
+ | =d= | Yes | move forward one page |
1106
+ | =space= | Yes | move forward one page |
1107
+ | =C-v= | Yes | move forward one page |
1108
+ | =home= | No | move to the top of output |
1109
+ | =M-<= | No | move to the top of output |
1110
+ | =g= | Yes | move to the top, or to the counted original line* |
1111
+ | =end= | No | move to the end of output |
1112
+ | =M->= | No | move to the end of output |
1113
+ | =G= | No | move to the end of output |
1114
+ | ~=~ | No | toggle the display of line numbers |
1115
+ | =M-s= | No | toggle between paging and scrolling |
1116
+ | =/= | No | initiate a fixed-string search forward |
1117
+ | =?= | No | initiate a fixed-string search backward |
1118
+ | =C-s= | No | initiate an Isearch session forward |
1119
+ | =C-r= | No | initiate an Isearch session backward |
1120
+ | =C-M-s= | No | initiate an Regexp session forward |
1121
+ | =C-M-r= | No | initiate an Regexp session backward |
1122
+ | =C-/= | No | initiate an Regexp session forward |
1123
+ | =<= | No | narrow output to lines containing all entered terms |
1124
+ | =>= | No | widen output by clearing the narrowing |
1125
+ | =C-c= | No | quit paging |
1126
+ | =q= | No | quit paging |
1127
+ | =C-g= | No | quit paging |
1128
+ | =escape= | No | quit paging |
1129
+ | =n= | Yes | move to the next instance of the current search |
1130
+ | =N= | Yes | move to the prior instance of the current search |
139
1131
 
140
- | Key | Description |
141
- |-------+---------------------------|
142
- | :up | move output one line up |
143
- | k | move output one line up |
144
- | :down | move output one line down |
145
- | j | move output one line down |
146
- | | |
1132
+ =*= Without a count, =g= moves to the beginning of output. With a numeric
1133
+ prefix, it moves to that original output line; for example, =M-1 M-0 M-0 M-0
1134
+ g= moves to line 1000. It uses the original line numbers even when output is
1135
+ narrowed.
147
1136
 
1137
+ As you can see, =fatty= tries to accommodate a number of finger habits besides
1138
+ =emacs=, including =vim= and =Emacs Info=, by offering several alternative
1139
+ ways to invoke navigation actions.
148
1140
 
149
- * Alerts
150
- Alerts are short-lived, non-scrolling messages shown below the input line.
151
- They are intended for user-visible conditions that require attention.
1141
+ One unusual action is invoked by =M-s= which toggles the output between
1142
+ scrolling mode without pause and paging mode. It can be invoked while doing
1143
+ either to enter the other mode.
152
1144
 
153
- * Markdown
154
- ** Forced line breaks
155
- Use ~<br>~ to force a line break.
1145
+ You can add line numbers to the display while paging by using the ~=~ key.
156
1146
 
157
- #+begin_example
158
- This line has a forced line break <br> and this should appear on the next line.
159
- #+end_example
1147
+ You can also "narrow" the output with =<=, which will prompt you for one or
1148
+ more terms to match. The output will then show only lines that match all the
1149
+ terms typed. To return to the full output, "widen" with =>=.
160
1150
 
161
- will render as
1151
+ *** Searching Output
1152
+ While paging, you can initiate a search of the output as indicated in the
1153
+ above key bindings for paging. Once a search session is initiated, =fatty=
1154
+ presents an input line for entering search terms.
162
1155
 
163
- #+begin_example
164
- This line has a forced line break
165
- and this should appear on the next line.
166
- #+end_example
1156
+ After a search session has ended, =fatty= returns to paging but remembers the
1157
+ last search term and allows navigation among them with =n= and =N=: =n=
1158
+ searches in the direction of the last search and =N= searches in the opposite
1159
+ direction. Notice that the current search term and direction are added to the
1160
+ paging bar after the information on your location within the output.
1161
+
1162
+ **** String and Regex Search
1163
+ =/= and =?= prompt for a fixed string and search forward or backward. =C-M-s=
1164
+ (or =C-/=) and =C-M-r= (or =C-?=) perform the corresponding regular-expression
1165
+ searches. Accepting the search leaves the selected match active for later
1166
+ navigation with =n= and =N=.
1167
+
1168
+ **** Incremental Search
1169
+ =C-s= and =C-r= begin forward or backward incremental search. The output
1170
+ moves to matching text as the search term is edited. Accepting commits the
1171
+ match; canceling restores the output position from before the search began.
1172
+
1173
+
1174
+ * Configuration
1175
+ When a Fatty application starts, Fatty installs default configuration files
1176
+ under =~/.config/fatty= if they do not already exist. Fatty does not overwrite
1177
+ files that are already there, so you can edit these files directly and keep
1178
+ them under version control.
1179
+
1180
+ The config files are:
1181
+
1182
+ - =config.yml= :: general configuration;
1183
+ - =keydefs.yml= :: terminal-specific keycode naming;
1184
+ - =keybindings.yml= :: user-defined keybindings;
1185
+ - =themes/= :: YAML theme files.
1186
+
1187
+ The default user-specific configuration directory follows =XDG_CONFIG_HOME= if
1188
+ it is set. Otherwise it is =~/.config/fatty=.
1189
+
1190
+ In addition to the user-specific configuration, fatty will read app-specific
1191
+ configuration files from =~/.config/fatty/apps/<app_name>= if =Terminal= was
1192
+ given an =app_name= parameter. It will use a different directory of your
1193
+ choosing if =Terminal= was given an =app_config_dir= directory name.
1194
+
1195
+ ** General Configuration =config.yml=
1196
+
1197
+ =config.yml= controls general behavior such as word movement, ESC handling,
1198
+ logging, history, the initial theme, and truecolor rendering.
1199
+
1200
+ A small configuration might look like this:
1201
+
1202
+ #+begin_src yaml :tangle no
1203
+ word_char_re: "[[:alnum:]_.:-]"
1204
+
1205
+ esc_delay: 0
1206
+
1207
+ history:
1208
+ file: "~/.fatty_history"
1209
+ max: 10000
1210
+
1211
+ theme: wordperfect
1212
+
1213
+ truecolor: auto
1214
+ #+end_src
1215
+
1216
+ *** =word_char_re=
1217
+ This setting is a Ruby regular expression fragment used to decide what counts
1218
+ as a word in the input buffer. It must be a =String= representing a =Regexp=
1219
+ that matches a single character that is meant to be a part of a "word" for
1220
+ purposes of cursor movement. The default setting treats letters, digits, and
1221
+ underscore as word characters:
1222
+
1223
+ #+begin_src yaml :tangle no
1224
+ word_char_re: "[[:alnum:]_]"
1225
+ #+end_src
1226
+
1227
+ For command-line applications, you may want dashes to be part of a word:
1228
+
1229
+ #+begin_src yaml :tangle no
1230
+ word_char_re: "[[:alnum:]_-]"
1231
+ #+end_src
1232
+
1233
+ For Ruby constants, hostnames, or command-like tokens, you may want a broader
1234
+ definition:
1235
+
1236
+ #+begin_src yaml :tangle no
1237
+ word_char_re: "[[:alnum:]_.:-]"
1238
+ #+end_src
1239
+
1240
+ *** =esc_delay=
1241
+ =esc_delay= controls how long curses waits after receiving =ESC= before
1242
+ deciding whether another key is part of the same Meta-key sequence. The value
1243
+ is in milliseconds.
1244
+
1245
+ #+begin_src yaml :tangle no
1246
+ esc_delay: 0
1247
+ #+end_src
1248
+
1249
+
1250
+ *** =history=
1251
+ **** =file=
1252
+ The path to the history file to use, by default =~/.fatty_history=
1253
+
1254
+ **** =max=
1255
+ The maximum number of lines of history retained, by default 10,000.
1256
+
1257
+ *** =theme=
1258
+ The =theme= setting names the theme Fatty applies at startup:
1259
+
1260
+ #+begin_src yaml :tangle no
1261
+ theme: nordic
1262
+ #+end_src
1263
+
1264
+ *** =truecolor=
1265
+ The =truecolor= setting controls whether Fatty uses truecolor escape sequences
1266
+ for rendering colors. The usual setting is =auto=, meaning that =truecolor=
1267
+ is enabled if available. It can be set to =true=, =false=, =on=, =off= or
1268
+ =auto=.
1269
+
1270
+ #+begin_src yaml :tangle no
1271
+ truecolor: auto
1272
+ #+end_src
1273
+
1274
+ *** =log=
1275
+ Logging is configured under =log=:
1276
+
1277
+ #+begin_src yaml :tangle no
1278
+ log:
1279
+ file: "~/log/fatty.log"
1280
+ level: debug
1281
+ tags:
1282
+ - keycode
1283
+ - keyevent
1284
+ - keybinding
1285
+ - action
1286
+ - command
1287
+ - session
1288
+ - render
1289
+ - perf
1290
+ #+end_src
1291
+
1292
+ **** =file=
1293
+ The path to the log file to use.
1294
+
1295
+ **** =level=
1296
+ Can be one of the following
1297
+ - =info= :: only informational messages
1298
+ - =warn= :: only non-fatal warning messages and =info= messages
1299
+ - =error= :: only fatal error messages and =warn= and =info= messages
1300
+ - =debug= :: detailed messages to assist debugging =fatty= plus all of the
1301
+ above.
1302
+
1303
+ **** =tags=
1304
+
1305
+ =log.tags= controls which tagged log messages are written. Tags are ordinary
1306
+ symbolic labels used by Fatty's logging calls; they are not checked against a
1307
+ fixed registry. A tag is useful if Fatty logs messages with that tag.
1308
+
1309
+ The special tag =all= enables all tagged log messages except =perf=.
1310
+ Performance logging is intentionally noisy and must be enabled explicitly with
1311
+ =perf=.
1312
+
1313
+ #+begin_src yaml :tangle no
1314
+ log:
1315
+ tags:
1316
+ - keycode
1317
+ - keybinding
1318
+ - render
1319
+ - perf
1320
+ #+end_src
1321
+
1322
+ ** Key code definitions =keydefs.yml=
1323
+ Most keys are named correctly by curses. Some terminal emulators, multiplexers,
1324
+ or keyboard modes emit numeric keycodes that curses does not name usefully.
1325
+ The =keydefs.yml= file lets you assign names and modifiers to those numeric
1326
+ codes.
1327
+
1328
+ When Fatty runs inside =tmux=, key definitions are looked up under =tmux=, not
1329
+ under the outer terminal emulator. =tmux= translates the outer terminal's input
1330
+ before Fatty sees it, so keycodes observed inside =tmux= may differ from
1331
+ keycodes observed directly under GNOME Terminal, Kitty, Alacritty, or another
1332
+ terminal. Use =keytest= in the same environment where the application will
1333
+ normally run. The same comment applies to =screen=.
1334
+
1335
+ A key definition is terminal-specific. For example:
1336
+
1337
+ #+begin_src yaml :tangle no
1338
+ tmux:
1339
+ 555:
1340
+ key: left
1341
+ meta: true
1342
+ 570:
1343
+ key: right
1344
+ meta: true
1345
+ 557:
1346
+ key: left
1347
+ ctrl: true
1348
+ 572:
1349
+ key: right
1350
+ ctrl: true
1351
+ #+end_src
1352
+
1353
+ This says that, when Fatty detects that the terminal is =tmux=, curses keycode
1354
+ =555= should be treated as =M-left=, keycode =570= as =M-right=, and so on.
1355
+
1356
+ The demo executable has a =keytest= command that is useful when building this
1357
+ file. Run =fatty=, use the =keytest= command, press the key you want to
1358
+ identify, and add an entry for the numeric code if Fatty does not already know
1359
+ what to call it.
1360
+
1361
+ Here is what I found running =keytest= on a variety of popular terminals to
1362
+ determine how each reported =PageDown=, =C-PageDown=, =M-PageDown=, and
1363
+ =C-M-PageDown=:
1364
+
1365
+ | Terminal | Key Pressed | Ctrl? | Meta? | Code | Status |
1366
+ |----------------+-------------+-------+-------+--------+-------------------|
1367
+ | gnome-terminal | PageDown | no | no | 338 | Defined and bound |
1368
+ | gnome-terminal | PageDown | yes | no | 559 | Undefined |
1369
+ | gnome-terminal | PageDown | no | yes | 557 | Undefined |
1370
+ | gnome-terminal | PageDown | yes | yes | 561 | Undefined |
1371
+ |----------------+-------------+-------+-------+--------+-------------------|
1372
+ | alacritty | PageDown | no | no | 338 | Defined and bound |
1373
+ | alacritty | PageDown | yes | no | 559 | Undefined |
1374
+ | alacritty | PageDown | no | yes | 557 | Undefined |
1375
+ | alacritty | PageDown | yes | yes | 561 | Undefined |
1376
+ |----------------+-------------+-------+-------+--------+-------------------|
1377
+ | kitty | PageDown | no | no | 338 | Defined and bound |
1378
+ | kitty | PageDown | yes | no | 565 | Undefined |
1379
+ | kitty | PageDown | no | yes | 563 | Undefined |
1380
+ | kitty | PageDown | yes | yes | 567 | Undefined |
1381
+ |----------------+-------------+-------+-------+--------+-------------------|
1382
+ | terminator | PageDown | no | no | 338 | Defined and bound |
1383
+ | terminator | PageDown | yes | no | <none> | Undefinable |
1384
+ | terminator | PageDown | no | yes | 557 | Undefined |
1385
+ | terminator | PageDown | yes | yes | 561 | Undefined |
1386
+ |----------------+-------------+-------+-------+--------+-------------------|
1387
+ | ghostty | PageDown | no | no | 338 | Defined and bound |
1388
+ | ghostty | PageDown | yes | no | 564 | Undefined |
1389
+ | ghostty | PageDown | no | yes | 562 | Undefined |
1390
+ | ghostty | PageDown | yes | yes | 566 | Undefined |
1391
+ |----------------+-------------+-------+-------+--------+-------------------|
1392
+ | konsole | PageDown | no | no | 338 | Defined and bound |
1393
+ | konsole | PageDown | yes | no | 559 | Undefined |
1394
+ | konsole | PageDown | no | yes | 557 | Undefined |
1395
+ | konsole | PageDown | yes | yes | 561 | Undefined |
1396
+
1397
+ I could turn this into a =keydefs.yml= file that would allow me to define all
1398
+ those keys:
1399
+
1400
+ #+begin_src yaml :tangle no
1401
+ gnome-terminal:
1402
+ 559:
1403
+ key: page_down
1404
+ ctrl: true
1405
+ 557:
1406
+ key: page_down
1407
+ meta: true
1408
+ 561:
1409
+ key: page_down
1410
+ ctrl: true
1411
+ meta: true
1412
+
1413
+ alacritty:
1414
+ 559:
1415
+ key: page_down
1416
+ ctrl: true
1417
+ 557:
1418
+ key: page_down
1419
+ meta: true
1420
+ 561:
1421
+ key: page_down
1422
+ ctrl: true
1423
+ meta: true
1424
+
1425
+ kitty:
1426
+ 565:
1427
+ key: page_down
1428
+ ctrl: true
1429
+ 563:
1430
+ key: page_down
1431
+ meta: true
1432
+ 567:
1433
+ key: page_down
1434
+ ctrl: true
1435
+ meta: true
1436
+
1437
+ terminator:
1438
+ # 559:
1439
+ # key: page_down
1440
+ # ctrl: true
1441
+ 557:
1442
+ key: page_down
1443
+ meta: true
1444
+ 561:
1445
+ key: page_down
1446
+ ctrl: true
1447
+ meta: true
1448
+
1449
+ ghostty:
1450
+ 564:
1451
+ key: page_down
1452
+ ctrl: true
1453
+ 562:
1454
+ key: page_down
1455
+ meta: true
1456
+ 566:
1457
+ key: page_down
1458
+ ctrl: true
1459
+ meta: true
1460
+
1461
+ konsole:
1462
+ 559:
1463
+ key: page_down
1464
+ ctrl: true
1465
+ 557:
1466
+ key: page_down
1467
+ meta: true
1468
+ 561:
1469
+ key: page_down
1470
+ ctrl: true
1471
+ meta: true
1472
+ #+end_src
1473
+
1474
+ After installing that keydefs, I find that the PageDown variants are all
1475
+ recognized by name and modifiers (except for the strange failure of
1476
+ =terminator= to recognize =C-PageDown=!).
1477
+
1478
+ ** Key bindings =keybindings.yml=
1479
+ Fatty has built-in Emacs-style bindings for ordinary input editing, history,
1480
+ completion, popup navigation, and paging. The =keybindings.yml= file lets you
1481
+ add or override bindings without changing Ruby code.
1482
+
1483
+ A keybinding names a key chord, the context in which it applies, and the Fatty
1484
+ action to run:
1485
+
1486
+ #+begin_src yaml :tangle no
1487
+ - key: left
1488
+ meta: true
1489
+ action: move_word_left
1490
+
1491
+ - key: right
1492
+ meta: true
1493
+ action: move_word_right
1494
+
1495
+ - key: space
1496
+ context: paging
1497
+ action: page_down
1498
+
1499
+ - key: backspace
1500
+ context: paging
1501
+ action: page_up
1502
+ #+end_src
1503
+
1504
+ *** Key Names
1505
+ A keybinding uses the unmodified key name under the =key= field:
1506
+ #+begin_src yaml :tangle no
1507
+ - key: f
1508
+ meta: true
1509
+ context: text
1510
+ action: move_word_right
1511
+ #+end_src
1512
+
1513
+ **** Printable Keys
1514
+ Most printable keys can be written as the character itself. Quote keys that
1515
+ YAML might otherwise treat specially or make hard to read, such as =":"=,
1516
+ ="#"=, ="["=, ="]"=, ="{"=, ="}"=, ="\""=, and ="\\"=.
1517
+ #+begin_src yaml :tangle no
1518
+ - key: a
1519
+ - key: /
1520
+ - key: .
1521
+ - key: =
1522
+ - key: "\\"
1523
+ - key: "\""
1524
+ #+end_src
1525
+
1526
+ **** Curses and Fatty Named Keys
1527
+ =Fatty= translates codes returned by curses for special keys into names:
1528
+ | | | | | |
1529
+ |----------------+----------------+-----------------+-------------------+----------------|
1530
+ | =tab= | =insert= | =delete= | =home= | =end= |
1531
+ | =left= | =right= | =page_up= | =page_down= | =up= |
1532
+ | =down= | =f1= | =f2= | =f3= | =f4= |
1533
+ | =f8= | =f9= | =f10= | =f11= | =f12= |
1534
+ | =keypad_0= | =keypad_1= | =keypad_2= | =keypad_3= | =keypad_4= |
1535
+ | =keypad_8= | =keypad_9= | =keypad_divide= | =keypad_multiply= | =keypad_minus= |
1536
+ | =keypad_enter= | =keypad_comma= | | | |
1537
+
1538
+ Some terminals can report application-keypad sequences such as
1539
+ =keypad_divide=, =keypad_multiply=, or =keypad_enter=, but numeric keypad
1540
+ digits are often not distinguishable from ordinary digits when NumLock is on,
1541
+ and are often reported as navigation keys such as =end=, =down=, =page_down=,
1542
+ or =left= when NumLock is off. Use =keytest= to see what your terminal
1543
+ actually sends.
1544
+
1545
+ =Fatty= also provides convenient names to other common keys:
1546
+
1547
+ - =enter=,
1548
+ - =escape=, and
1549
+ - =space=
1550
+
1551
+ **** Possibly Invisible Keys
1552
+ Some physical keys may never reach Fatty at all. They may be handled by the
1553
+ keyboard firmware, the kernel, the desktop environment, the window manager, or
1554
+ the terminal emulator before curses can report them. Common examples include
1555
+ =Print Screen=, =Scroll Lock=, =Pause/Break=, =Caps Lock=, =Num Lock=, =Fn=,
1556
+ =Menu/Application=, =Super/Windows=, power/sleep/wake keys, brightness keys,
1557
+ volume/media keys, keyboard-backlight keys, airplane-mode keys, and touchpad
1558
+ toggle keys.
1559
+
1560
+ Use =keytest= to check. If Fatty sees a key as a raw numeric code, you can
1561
+ name it in =keydefs.yml= and bind it. If =keytest= shows nothing, the key is
1562
+ being intercepted before Fatty receives it.
1563
+
1564
+ **** Unnamed but Recognized Keys
1565
+ Some terminals report modified special keys as distinct numeric keycodes that
1566
+ =fatty= and =curses= do not name. Use =keytest= to discover those codes, then
1567
+ assign names to them in =keydefs.yml=. The name can be one of Fatty's usual
1568
+ names, or a new name of your own.
1569
+
1570
+ For example, if a keyboard has an extra thumb key and =keytest= reports it as
1571
+ an uncoded keycode =601=, you could name it:
1572
+
1573
+ #+begin_src yaml :tangle no
1574
+ gnome-terminal:
1575
+ 601:
1576
+ key: thumb_left
1577
+ #+end_src
1578
+
1579
+ Then bind it in =keybindings.yml=:
1580
+
1581
+ #+begin_src yaml :tangle no
1582
+ - key: thumb_left
1583
+ context: text
1584
+ action: move_word_left
1585
+ #+end_src
1586
+
1587
+ Fatty does not require custom key names to come from a fixed registry. A
1588
+ custom name only needs to be used consistently between =keydefs.yml= and
1589
+ =keybindings.yml=.
1590
+
1591
+ *** Mouse Events
1592
+ Mouse events can also be bound:
1593
+
1594
+ #+begin_src yaml :tangle no
1595
+ - button: left_clicked
1596
+ context: popup
1597
+ action: choose
1598
+ #+end_src
1599
+
1600
+ Recognized mouse button names include:
1601
+
1602
+ #+begin_src org :tangle no
1603
+ left_pressed
1604
+ left_released
1605
+ left_clicked
1606
+ left_double_clicked
1607
+ left_triple_clicked
1608
+ middle_pressed
1609
+ middle_released
1610
+ middle_clicked
1611
+ middle_double_clicked
1612
+ middle_triple_clicked
1613
+ right_pressed
1614
+ right_released
1615
+ right_clicked
1616
+ right_double_clicked
1617
+ right_triple_clicked
1618
+ scroll_up
1619
+ scroll_down
1620
+ #+end_src
1621
+
1622
+ *** Modifiers
1623
+ A binding may include these modifiers:
1624
+
1625
+ #+begin_src yaml :tangle no
1626
+ ctrl: true
1627
+ meta: true
1628
+ shift: true
1629
+ #+end_src
1630
+
1631
+ *** Contexts
1632
+ In =Fatty=, the key map is partitioned by "contexts" and in different
1633
+ environments the contexts are tried in order from highest priority to lowest
1634
+ and the first binding found wins. The following table lists the contexts
1635
+ associated with each interaction environment:
1636
+
1637
+ | Environment | Contexts High to Low |
1638
+ |--------------------+----------------------------------------|
1639
+ | Input Field | :input, :text, :terminal |
1640
+ | Output Paging | :paging, :terminal |
1641
+ | Output Scrolling | :terminal |
1642
+ | String Search | :search, :text, :terminal |
1643
+ | Incremental Search | :isearch, :text, :terminal |
1644
+ | Prompt Popup | :prompt, :text, :terminal |
1645
+ | Popup Choose | :popup, :text, :terminal |
1646
+ | Popup Multi-Choose | :popup_multi, :popup, :text, :terminal |
1647
+
1648
+ The available contexts are:
1649
+
1650
+ | Context | Meaning |
1651
+ |----------------+-------------------------------------------------------|
1652
+ | =:terminal= | global terminal/session commands |
1653
+ | =:text= | ordinary text-editing commands shared by input fields |
1654
+ | =:input= | the main command input field |
1655
+ | =:paging= | output pager navigation |
1656
+ | =:search= | non-incremental search field |
1657
+ | =:isearch= | incremental search field |
1658
+ | =:prompt= | prompt popup input |
1659
+ | =:popup= | popup selection/navigation |
1660
+ | =:popup_multi= | multi-select popup behavior |
1661
+
1662
+ The `:terminal` context is the lowest-priority fallback context for
1663
+ terminal-wide commands. It is where Fatty puts bindings that are not specific
1664
+ to text editing, paging, search, or popup selection, such as theme selection,
1665
+ key testing, and quitting. Because `:terminal` is searched last, any more
1666
+ specific context can override a terminal-wide binding.
1667
+
1668
+ *** Actions
1669
+ A keybinding connects a key event in a context to an "action." An action is a
1670
+ named Fatty command such as `move_left`, `delete_backward`, `accept_line`,
1671
+ `page_down`, `choose_theme`, or `quit`.
1672
+
1673
+ A binding entry names the action with the `action` field:
1674
+
1675
+ #+begin_src yaml :tangle no
1676
+ - key: left
1677
+ context: text
1678
+ action: move_left
1679
+
1680
+ - key: f
1681
+ meta: true
1682
+ context: text
1683
+ action: move_word_right
1684
+
1685
+ - key: page_down
1686
+ context: paging
1687
+ action: page_down
1688
+ #+end_src
1689
+
1690
+ Most movement, deletion, history, completion, paging, and popup-navigation
1691
+ actions honor Fatty's numeric count argument. The count is entered before the
1692
+ command, Emacs-style. For example, =M-5 C-f= moves forward five characters if
1693
+ =C-f= is bound to move_right, and =M-3 C-n= moves down three items if =C-n= is
1694
+ bound to the current environment's =next-item= action.
1695
+
1696
+ Use `keytest` to check what action is currently bound to a key. If a key is
1697
+ bound, `keytest` reports the matching context, action name, arguments, and the
1698
+ object that handles the action.
1699
+
1700
+ **** Input buffer actions
1701
+ Input buffer actions edit the contents of a text buffer. These are the ordinary
1702
+ line-editing operations shared by the main input field, search fields, prompt
1703
+ popups, and other text-like interactions. Many of these actions honor the
1704
+ numeric prefix count.
1705
+
1706
+ Terminology notes:
1707
+ - kill ring :: like emacs, =fatty= keeps a "clipboard" of items with the
1708
+ newest item in the current position and older items "before" it but in a
1709
+ "ring" that return to the beginning;
1710
+ - yank :: retrieve the most recent item from the kill ring
1711
+ - yank pop :: retrieve successively older items from the kill ring
1712
+ - delete :: removes text without copying it to the kill ring;
1713
+ - kill :: removes text and copies it to the kill ring for later yank;
1714
+ - word :: a sequence of contiguous characters that are each matched by the
1715
+ =word_char_re= from the config file or "[[:alnum]_]" by default;
1716
+
1717
+ | Action Name | Count? | Description |
1718
+ |----------------------+--------+--------------------------------------------|
1719
+ | bol | No | Move cursor to beginning of buffer |
1720
+ | clear | No | Clear the buffer |
1721
+ | clear_mark | No | Clear the region mark |
1722
+ | copy_region | No | Copy the text in the region to kill ring |
1723
+ | delete_char_backward | Yes | Delete char to the left of cursor |
1724
+ | delete_char_forward | Yes | Delete char to the right of cursor |
1725
+ | delete_region | No | Delete the text in the region |
1726
+ | eol | No | Move cursor to end of buffer |
1727
+ | kill_region | No | Kill the text in the region |
1728
+ | kill_to_bol | No | Kill the text from cursor to beginning |
1729
+ | kill_to_eol | No | Kill the text from cursor to end |
1730
+ | kill_word_backward | Yes | Kill the word to the left of cursor |
1731
+ | kill_word_forward | Yes | Kill the word to the right of cursor |
1732
+ | move_left | Yes | Move cursor one char left |
1733
+ | move_right | Yes | Move cursor one char right |
1734
+ | move_word_left | Yes | Move cursor one word left |
1735
+ | move_word_right | Yes | Move cursor one word right |
1736
+ | redo | No | Redo an undone action |
1737
+ | set_mark | No | Set the region mark at cursor |
1738
+ | transpose_chars | Yes | Switch position of char with prior char |
1739
+ | transpose_words | Yes | Switch position of word with prior char |
1740
+ | undo | No | Revert last edit action |
1741
+ | yank | No | Copy kill ring to buffer at cursor |
1742
+ | yank_pop | Yes | Copy next kill ring item and replace prior |
1743
+
1744
+ **** Input field actions
1745
+ Input field actions operate on an editable input field. An input field occurs
1746
+ at the main input prompt, but also in things like search-narrowing input,
1747
+ incremental-search, a prompt for text, etc. It is lower level than the shell
1748
+ session input described in the next section. In particular, it can have its
1749
+ own history attached to it depending on its purpose. They handle accepting
1750
+ the current line, moving through history, and receiving pasted text.
1751
+
1752
+ | Action Name | Count? | Description |
1753
+ |--------------+--------+-----------------------------------------------------------|
1754
+ | accept_line | No | Accept the current text as the input |
1755
+ | history_prev | No | Insert the prior relevant history item in the input field |
1756
+ | history_next | No | Insert the next relevant history item in the input field |
1757
+
1758
+ **** Shell session actions
1759
+ Shell session actions operate at the session level around the main command
1760
+ input and output area. They submit input, interrupt running work, clear output,
1761
+ start completion, open completion/history popups, and manage numeric prefix
1762
+ entry.
1763
+
1764
+ | Action Name | Count? | Description |
1765
+ |--------------------+--------+------------------------------------------------------------------|
1766
+ | submit_line | No | Pass the current text to the on_accept proc and page output |
1767
+ | submit_and_scroll | No | Pass the current text to the on_accept proc and scroll output |
1768
+ | interrupt | No | End the current session |
1769
+ | interrupt_if_empty | No | End the current session only if the current text is empty |
1770
+ | clear_output | No | Clear the output pane |
1771
+ | complete | No | Suggest the next possible completion as an autosuggestion |
1772
+ | complete_previous | No | Suggest the prior possible completion as an autosuggestion |
1773
+ | completion_popup | No | Display all possible completions in a popup for selection |
1774
+ | history_search | No | Display history item starting with the text-so-far for selection |
1775
+ | count_digit | No | Add a digit to the numeric prefix |
1776
+ | universal_argument | No | Set the numeric argument to 4 or multiply the current one by 4 |
1777
+
1778
+ **** Pager actions
1779
+ Pager actions navigate output when the output area is in paging mode. They move
1780
+ by line, page, top, bottom, or end of output, handle mouse-wheel scrolling, and
1781
+ switch between paging and scrolling behavior. Most movement actions honor the
1782
+ numeric prefix count.
1783
+
1784
+ | Action Name | Count? | Description |
1785
+ |---------------------+--------+--------------------------------------------------|
1786
+ | page_up | Yes | Move the output view one page up |
1787
+ | page_down | Yes | Move the output view one page down |
1788
+ | end_of_output | No | Move the output to the end of output and scroll |
1789
+ | line_up | Yes | Move the output view one line up |
1790
+ | line_down | Yes | Move the output view one line down |
1791
+ | scroll_up | Yes | Move the output view six lines up |
1792
+ | scroll_down | Yes | Move the output view six lines down |
1793
+ | page_top | Yes | Move to the beginning, or to the counted line |
1794
+ | page_bottom | No | Move the output to the end of output |
1795
+ | paging_to_scrolling | No | Quit paging and scroll output as it is produced |
1796
+ | toggle_paging | No | Toggle output mode between paging and scrolling |
1797
+ | quit_paging | No | Quit paging and return control to the input line |
1798
+
1799
+ **** Pager search actions
1800
+
1801
+ Pager search actions can /initiate/ a search session within the pager: a
1802
+ string search, a regular expression search, or an incremental-search. Once a
1803
+ search session is entered, other actions navigate matches in the output.
1804
+ There are also actions to end a search session.
1805
+
1806
+ While the search session is active, accepting commits the current match and
1807
+ canceling restores the pager to the position it had when the search began.
1808
+ After the search is accepted, =n= and =N= repeat the committed search in the
1809
+ pager; at that point the search session is no longer active.
1810
+
1811
+ | Action Name | Count? | Description |
1812
+ |-----------------------------+--------+---------------------------------------------------------------------|
1813
+ | pager_search_forward | No | Initiate a string search forward from current output point |
1814
+ | pager_search_backward | No | Initiate a string search backward from current output point |
1815
+ | pager_regex_search_forward | No | Initiate a regex search forward from current output point |
1816
+ | pager_regex_search_backward | No | Initiate a regex search backward from current output point |
1817
+ | pager_isearch_forward | No | Initiate an incremental search forward from current output point |
1818
+ | pager_isearch_backward | No | Initiate an incremental search backward from current output point |
1819
+ | pager_search_next | No | Move output to the next search match |
1820
+ | pager_search_prev | No | Move output to the prior search match |
1821
+ | search_toggle_regex | No | Toggle between regex and string search |
1822
+ | search_step_forward | No | Preview the next match using the current search text |
1823
+ | search_step_backward | No | Preview the prior match using the current search text |
1824
+ | isearch_next | No | Move output to the next isearch match |
1825
+ | isearch_prev | No | Move output to the prior isearch match |
1826
+ | search_accept | No | Commit the current search and close the search session |
1827
+ | search_cancel | No | Cancel the active search session and restore its starting point |
1828
+ | isearch_accept | No | Commit the current incremental search and close the search session |
1829
+ | isearch_cancel | No | Cancel the active incremental search and restore its starting point |
1830
+
1831
+ **** Popup actions
1832
+ Popup actions control selection and narrowing popups such as choose, menu,
1833
+ completion, and history popups. You can narrow the selections presented by
1834
+ typing in the input field presented. You can move through the list, accept or
1835
+ cancel the selection, page through choices, jump to the top or bottom,
1836
+ recenter the selection, and toggle selection in multi-select popups.
1837
+
1838
+ | Action Name | Count? | Description |
1839
+ |-----------------------+--------+-----------------------------------------------------------|
1840
+ | popup_cancel | No | Close the popup and return its cancel_value |
1841
+ | popup_accept | No | Close the popup and return the selection |
1842
+ | popup_next | No | Move the selection cursor to the next item |
1843
+ | popup_prev | No | Move the selection cursor to the prior item |
1844
+ | popup_page_down | No | Move the selection list down one page |
1845
+ | popup_page_up | No | Move the selection list up one page |
1846
+ | popup_top | No | Move to the top of the selection list |
1847
+ | popup_bottom | No | Move to the bottom of the selection list |
1848
+ | popup_recenter | No | Recenter the selection list on the current item |
1849
+ | popup_toggle_selected | No | In multi-select, toggle the selection of the current item |
1850
+
1851
+ **** Prompt popup actions
1852
+ Prompt popup actions control prompt dialogs that collect a string from the
1853
+ user. They accept the prompt, cancel it, or cancel only when the prompt is
1854
+ empty.
1855
+
1856
+ | Action Name | Count? | Description |
1857
+ |------------------------+--------+------------------------------------------------------|
1858
+ | prompt_accept | No | Close the prompt popup and return the current string |
1859
+ | prompt_cancel | No | Close the prompt popup and return its cancel_value |
1860
+ | prompt_cancel_if_empty | No | Cancel only if the input buffer is empty |
1861
+
1862
+ **** Utility Actions
1863
+ Some actions exist to support =Fatty='s own input machinery and are not
1864
+ normally useful as ordinary user bindings. For example, =insert=, =replace=,
1865
+ and =paste= need text supplied by code: =paste= is used for bracketed paste
1866
+ events from the terminal and normalizes pasted text for a one-line input
1867
+ field; =self_insert= is selected automatically for printable characters. They
1868
+ are listed here for completeness.
1869
+
1870
+ | Action Name | Args | Count? | Description |
1871
+ |-------------+------+--------+------------------------------------|
1872
+ | insert | str | Yes | Insert the string at point |
1873
+ | self_insert | | Yes | Insert the bound key at point |
1874
+ | replace | str | No | Replace the buffer with the string |
1875
+ | set | str | No | Alias for =replace= |
1876
+ | paste | str | No | Insert pasted text at point |
1877
+
1878
+ ** Themes =themes/=
1879
+ *** Distributed Themes
1880
+ As distributed, =fatty= comes with the following themes, most of which are
1881
+ based on popular themes used in many text editors and IDEs.
1882
+
1883
+ Within a =fatty= terminal, themes can be changed interactively. The
1884
+ default Emacs-style bindings include:
1885
+
1886
+ | Key | Action |
1887
+ |---------+-----------------------------------------|
1888
+ | ~C-M-t~ | cycle to the next loaded theme |
1889
+ | ~M-=~ | preview and choose a theme from a popup |
1890
+
1891
+ Here are the builtin themes:
1892
+
1893
+ | Name | Description |
1894
+ |------------------+----------------------------------------------------|
1895
+ | capuchin_monk | A tribute to St. Francis of Assisi |
1896
+ | catppuccin_latte | Pastel theme of the latte flavor |
1897
+ | catppuccin_mocha | Pastel theme of the mocha flavor |
1898
+ | cyberpunk | Brash colorful theme |
1899
+ | dracula | Dark theme inspired by the Count |
1900
+ | everforest_dark | Dark green-esque theme |
1901
+ | gruvbox_dark | Engineered for focus, built for developers |
1902
+ | gruvbox_light | Engineered for focus, built for developers |
1903
+ | mono | Minimal black-and-white theme |
1904
+ | monokai | Based on https://monokai.pro/history |
1905
+ | nordic | Shiver me timbers, it's northern |
1906
+ | onedark | Shameless imitation of Atom editor theme |
1907
+ | solarized_dark | Ethan Schoonover's famous terminal theme |
1908
+ | solarized_light | And its light variant |
1909
+ | terminal | Use the terminal's color scheme |
1910
+ | tokyo_night | Celebrates the neon lights and dark vibes of Tokyo |
1911
+ | wordperfect | Nostalgic nod to the good old word processor |
1912
+
1913
+ *** Custom Themes
1914
+ **** Theme Name
1915
+ A Fatty theme is a YAML file in the user-specific config directory (normally
1916
+ =~/.config/fatty/themes=) or the app-specific config directory (by default
1917
+ =~/.config/fatty/apps/<app_name>/themes=). The filename does not determine
1918
+ the theme name: the theme file must have a =name= field to set the name:
1919
+
1920
+ #+begin_src yaml :tangle no
1921
+ name: nordic
1922
+ #+end_src
1923
+
1924
+ **** Inheritance from Another Theme
1925
+ Themes can inherit from other themes with the =inherit:= tag:
1926
+
1927
+ #+begin_src yaml :tangle no
1928
+ name: my_nordic
1929
+ inherit: nordic
1930
+
1931
+ output:
1932
+ fg: "#eeeeee"
1933
+ #+end_src
1934
+
1935
+ This means that all the theme setting from the =nordic= theme apply except to
1936
+ the extent changed by the rest of the =my_nordic= theme file.
1937
+
1938
+ **** Inheritance within a Theme
1939
+ Not only may one theme inherit from another theme, some roles within a theme
1940
+ inherit from other roles. For example, the =input= role inherits from the
1941
+ =output= role.
1942
+
1943
+ Thus, a minimal theme can define only =output= and inherit everything else
1944
+ through Fatty's role defaults:
1945
+
1946
+ #+begin_src yaml :tangle no
1947
+ name: simple_dark
1948
+ inherit: null
1949
+
1950
+ output:
1951
+ fg: white
1952
+ bg: black
1953
+ #+end_src
1954
+
1955
+ This means that the input field will have the same coloring and attributes as
1956
+ the output pane.
1957
+
1958
+ **** Theme Roles
1959
+ Here is a listing of what roles act as the parent for each of the roles, their
1960
+ purpose, and the role, if any, from which they inherit.
1961
+
1962
+ | Role name | Used for | Parent Role |
1963
+ |-------------------------+-------------------------------------------+-----------------------|
1964
+ | =output= | Main output area text. | <none> |
1965
+ | =input= | Main command/input field. | =output= |
1966
+ | =input_suggestion= | Autosuggestion or completion suffix text. | =input= |
1967
+ | =cursor= | Cursor cell styling where applicable. | =input= |
1968
+ | =region= | Selected text / active region. | =output= |
1969
+ | =status= | Status area base role. | =output= |
1970
+ | =info= | Informational messages. | =output= |
1971
+ | =good= | Success messages. | =info= |
1972
+ | =warn= | Warning messages. | =info= |
1973
+ | =error= | Error messages. | =warn= |
1974
+ | =alert= | Alert panel base role. | =output= |
1975
+ | =pager_status= | Pager status / prompt row. | =status= |
1976
+ | =line_number= | Line numbers when toggled on | =output= |
1977
+ | =search_input= | Search input field. | =popup= |
1978
+ | =match_current= | Current search match. | =region= |
1979
+ | =match_other= | Other search matches. | =region= |
1980
+ | =popup= | Popup body. | =output= |
1981
+ | =popup_frame= | Popup border/frame. | =popup= |
1982
+ | =popup_input= | Popup filter/input field. | =input= |
1983
+ | =popup_selection= | Selected popup item row. | =region= |
1984
+ | =popup_counts= | Popup counts/status row. | =popup= |
1985
+ | =markdown_h1= | Markdown level-1 headings. | =output= |
1986
+ | =markdown_h2= | Markdown level-2 headings. | =markdown_h1= |
1987
+ | =markdown_h3= | Markdown level-3 headings. | =markdown_h2= |
1988
+ | =markdown_code= | Inline markdown code. | =output= |
1989
+ | =markdown_code_block= | Markdown fenced-code block base role. | =markdown_code= |
1990
+ | =markdown_code_gutter= | Code block gutter/prefix. | =markdown_code_block= |
1991
+ | =markdown_strong= | Markdown strong/bold text. | =output= |
1992
+ | =markdown_emphasis= | Markdown emphasized text. | =output= |
1993
+ | =markdown_link= | Markdown link text. | =output= |
1994
+ | =markdown_url= | Markdown displayed URL text. | =markdown_link= |
1995
+ | =markdown_quote_gutter= | Markdown block quote gutter. | =output= |
1996
+ | =markdown_highlight= | Markdown highlight / marked text. | =output= |
1997
+ | =markdown_table_header= | Markdown table header cells. | =markdown_strong= |
1998
+ | =markdown_table_cell= | Markdown table body cells. | =output= |
1999
+ | =markdown_underline= | Markdown underline text. | =output= |
2000
+ | =markdown_hrule= | Markdown horizontal rules. | =output= |
2001
+
2002
+ **** Role Colors and Attributes and Frame Style
2003
+ Each role can have assigned to it a foreground color (=fg:=), a background
2004
+ color (=bg:=), and one or more "attributes" (=attrs:=).
2005
+
2006
+ ***** Colors
2007
+ The colors can be a hex string of the form "#RRGGBB", that is a mixture of
2008
+ red, green, and blue at level given by three valid hexadecimal numbers RR, GG,
2009
+ and BB. For example "#FF0000" is pure red, "#330044" is a middling mixture of
2010
+ red and blue, a shade of purple. In addition, =fatty= recognizes color names
2011
+ defined as 16 ANSI colors, 256 xterm colors, and X11 colors, all of which you
2012
+ can see by invoking =fatty='s CLI command =colors=.
2013
+
2014
+ The special color =default= means to inherit colors from the underlying terminal
2015
+ program.
2016
+
2017
+ ***** Attributes
2018
+ Each role can also have non-color attributes applied by specifying an =attrs=
2019
+ list. Supported attributes are =bold=, =dim=, =italic=, =underline=, and
2020
+ =reverse=. Italic is emitted by the truecolor renderer and by markdown ANSI
2021
+ output, but curses terminals may display it inconsistently or not at all.
2022
+
2023
+ ***** Frame Style
2024
+ In addition, one role, =popup_frame:=, in addition to these have two
2025
+ attributes for styling the "frame" drawn around popup:
2026
+
2027
+ | =border:= | single, double, or ascii |
2028
+ | =corners:= | rounded or square |
2029
+
2030
+ ***** An Example Theme
2031
+
2032
+ #+begin_src yaml :tangle no
2033
+ name: example
2034
+ inherit: null
2035
+
2036
+ output:
2037
+ fg: "#d8dee9"
2038
+ bg: "#2e3440"
2039
+
2040
+ input:
2041
+ fg: "#eceff4"
2042
+ bg: "#3b4252"
2043
+ attrs: [bold]
2044
+
2045
+ input_suggestion:
2046
+ fg: "#81a1c1"
2047
+ bg: "#3b4252"
2048
+
2049
+ cursor:
2050
+ fg: "#2e3440"
2051
+ bg: "#88c0d0"
2052
+
2053
+ region:
2054
+ fg: "#2e3440"
2055
+ bg: "#88c0d0"
2056
+
2057
+ info:
2058
+ fg: "#d8dee9"
2059
+ bg: "#2e3440"
2060
+
2061
+ good:
2062
+ fg: green
2063
+ bg: "#2e3440"
2064
+
2065
+ warn:
2066
+ fg: "#2e3440"
2067
+ bg: "#ebcb8b"
2068
+
2069
+ error:
2070
+ fg: "#eceff4"
2071
+ bg: "#bf616a"
2072
+
2073
+ pager_status:
2074
+ fg: black
2075
+ bg: lightgreen
2076
+
2077
+ search_input:
2078
+ fg: black
2079
+ bg: cyan
2080
+
2081
+ match_current:
2082
+ fg: black
2083
+ bg: yellow
2084
+
2085
+ match_other:
2086
+ fg: black
2087
+ bg: lightgray
2088
+
2089
+ popup:
2090
+ fg: "#d8dee9"
2091
+ bg: "#3b4252"
2092
+
2093
+ popup_input:
2094
+ fg: "#eceff4"
2095
+ bg: "#434c5e"
2096
+
2097
+ popup_selection:
2098
+ fg: "#2e3440"
2099
+ bg: "#88c0d0"
2100
+
2101
+ popup_counts:
2102
+ fg: "#2e3440"
2103
+ bg: white
2104
+
2105
+ popup_frame:
2106
+ fg: "#81a1c1"
2107
+ bg: "#3b4252"
2108
+ border: single
2109
+ corners: rounded
2110
+ #+end_src
2111
+
2112
+
2113
+ Or you can group them under =markdown= without the =markdown_= prefix:
2114
+
2115
+ #+begin_src yaml :tangle no
2116
+ markdown:
2117
+ h1:
2118
+ fg: yellow
2119
+ bg: navy
2120
+ attrs: [bold]
2121
+ code:
2122
+ fg: lightgreen
2123
+ bg: black
2124
+ link:
2125
+ fg: cyan
2126
+ bg: navy
2127
+ attrs: [underline]
2128
+ #+end_src