fatty 0.99.0 → 0.99.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.org +11 -29
- data/README.md +2207 -18
- data/README.org +2066 -104
- data/exe/fatty +236 -93
- data/help/help.md +233 -0
- data/lib/fatty/action.rb +3 -7
- data/lib/fatty/actionable.rb +2 -0
- data/lib/fatty/alert.rb +65 -39
- data/lib/fatty/ansi/renderer.rb +26 -6
- data/lib/fatty/ansi.rb +1 -0
- data/lib/fatty/api/alert.rb +15 -0
- data/lib/fatty/api/choose.rb +145 -0
- data/lib/fatty/api/environment.rb +12 -0
- data/lib/fatty/api/keytest.rb +11 -0
- data/lib/fatty/api/menu.rb +104 -0
- data/lib/fatty/api/output.rb +55 -0
- data/lib/fatty/api/progress.rb +24 -0
- data/lib/fatty/api/prompt.rb +77 -0
- data/lib/fatty/api/status.rb +55 -0
- data/lib/fatty/api.rb +38 -0
- data/lib/fatty/callback_environment.rb +74 -0
- data/lib/fatty/colors/color.rb +366 -232
- data/lib/fatty/colors/pairs.rb +2 -0
- data/lib/fatty/command.rb +78 -0
- data/lib/fatty/config.rb +227 -39
- data/lib/fatty/config_files/config.yml +4 -12
- data/lib/fatty/config_files/keydefs.yml +13 -23
- data/lib/fatty/config_files/themes/capuchin_monk.yml +140 -0
- data/lib/fatty/config_files/themes/catppuccin_latte.yml +140 -0
- data/lib/fatty/config_files/themes/catppuccin_mocha.yml +140 -0
- data/lib/fatty/config_files/themes/cyberpunk.yml +141 -0
- data/lib/fatty/config_files/themes/dracula.yml +140 -0
- data/lib/fatty/config_files/themes/everforest_dark.yml +140 -0
- data/lib/fatty/config_files/themes/gruvbox_dark.yml +140 -0
- data/lib/fatty/config_files/themes/gruvbox_light.yml +140 -0
- data/lib/fatty/config_files/themes/mono.yml +119 -47
- data/lib/fatty/config_files/themes/monokai.yml +140 -0
- data/lib/fatty/config_files/themes/nordic.yml +1 -0
- data/lib/fatty/config_files/themes/onedark.yml +140 -0
- data/lib/fatty/config_files/themes/solarized_dark.yml +1 -0
- data/lib/fatty/config_files/themes/solarized_light.yml +140 -0
- data/lib/fatty/config_files/themes/terminal.yml +67 -0
- data/lib/fatty/config_files/themes/tokyo_night.yml +140 -0
- data/lib/fatty/config_files/themes/wordperfect.yml +80 -9
- data/lib/fatty/core_ext/hash.rb +19 -0
- data/lib/fatty/core_ext/string.rb +19 -12
- data/lib/fatty/core_ext.rb +1 -0
- data/lib/fatty/curses/context.rb +86 -84
- data/lib/fatty/curses/event_source.rb +128 -40
- data/lib/fatty/curses/key_decoder.rb +77 -11
- data/lib/fatty/env.rb +21 -20
- data/lib/fatty/help.rb +1 -29
- data/lib/fatty/history/entry.rb +2 -0
- data/lib/fatty/history.rb +17 -14
- data/lib/fatty/input_buffer.rb +71 -47
- data/lib/fatty/input_field.rb +216 -149
- data/lib/fatty/key_event.rb +67 -66
- data/lib/fatty/key_map.rb +54 -16
- data/lib/fatty/keymaps/emacs.rb +40 -6
- data/lib/fatty/log_formats/json.rb +2 -0
- data/lib/fatty/log_formats/text.rb +2 -0
- data/lib/fatty/logger.rb +7 -2
- data/lib/fatty/markdown/ansi_renderer.rb +75 -6
- data/lib/fatty/markdown/render.rb +2 -3
- data/lib/fatty/mouse_event.rb +142 -2
- data/lib/fatty/output_buffer.rb +2 -0
- data/lib/fatty/pager.rb +158 -47
- data/lib/fatty/progress.rb +417 -0
- data/lib/fatty/renderer/curses.rb +340 -184
- data/lib/fatty/renderer/truecolor.rb +266 -163
- data/lib/fatty/renderer.rb +115 -97
- data/lib/fatty/screen.rb +2 -0
- data/lib/fatty/session/alert_session.rb +39 -21
- data/lib/fatty/session/isearch_session.rb +92 -38
- data/lib/fatty/session/keytest_session.rb +105 -85
- data/lib/fatty/session/modal_session.rb +38 -1
- data/lib/fatty/session/output_session.rb +472 -21
- data/lib/fatty/session/popup_session.rb +251 -134
- data/lib/fatty/session/prompt_session.rb +68 -26
- data/lib/fatty/session/search_session.rb +79 -31
- data/lib/fatty/session/shell_session.rb +383 -368
- data/lib/fatty/session/status_session.rb +167 -0
- data/lib/fatty/session.rb +95 -95
- data/lib/fatty/terminal/popup_owner.rb +14 -11
- data/lib/fatty/terminal.rb +515 -813
- data/lib/fatty/themes/loader.rb +4 -2
- data/lib/fatty/themes/manager.rb +4 -0
- data/lib/fatty/themes/registry.rb +2 -0
- data/lib/fatty/themes/resolver.rb +175 -162
- data/lib/fatty/version.rb +1 -1
- data/lib/fatty/viewport.rb +8 -1
- data/lib/fatty.rb +35 -7
- metadata +42 -40
- data/.envrc +0 -2
- data/.simplecov +0 -23
- data/.yardopts +0 -4
- data/CHANGELOG.md +0 -34
- data/Rakefile +0 -15
- data/TODO.org +0 -163
- data/examples/markdown/native-markdown.md +0 -370
- data/examples/markdown/ox-gfm-markdown.md +0 -373
- data/examples/markdown/ox-gfm-markdown.org +0 -376
- data/fatty.gemspec +0 -42
- data/lib/fatty/accept_env.rb +0 -32
- data/lib/fatty/config_files/help.md +0 -120
- data/lib/fatty/config_files/help.org +0 -124
- data/lib/fatty/config_files/themes/wordperfect_light.yml +0 -77
- data/lib/fatty/menu_env.rb +0 -22
- data/lib/fatty/session/input_session.rb +0 -99
- data/lib/fatty/sessions.rb +0 -14
- data/lib/fatty/terminal/progress.rb +0 -374
- data/lib/fatty/view/alert_view.rb +0 -14
- data/lib/fatty/view/cursor_view.rb +0 -18
- data/lib/fatty/view/input_view.rb +0 -9
- data/lib/fatty/view/output_view.rb +0 -9
- data/lib/fatty/view/status_view.rb +0 -14
- data/lib/fatty/view.rb +0 -33
- data/lib/fatty/views.rb +0 -9
- data/sig/fatty.rbs +0 -4
data/help/help.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
- [Fatty Help](#org245a92e)
|
|
2
|
+
- [The `fatty` demo command](#orgffe615f)
|
|
3
|
+
- [Builtin commands](#orgd27e40e)
|
|
4
|
+
- [Keybindings](#orgab9d7b6)
|
|
5
|
+
- [Input Context](#org68708fa)
|
|
6
|
+
- [Paging Context](#org7729d50)
|
|
7
|
+
- [Popup Context](#org684d15c)
|
|
8
|
+
- [Prompt Context](#org741e059)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
<a id="org245a92e"></a>
|
|
12
|
+
|
|
13
|
+
# Fatty Help
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
<a id="orgffe615f"></a>
|
|
17
|
+
|
|
18
|
+
## The `fatty` demo command
|
|
19
|
+
|
|
20
|
+
When you run `fatty`, it operates as a simple demo of the `fatty` gem by presenting an editable command-line using emacs key bindings. It has several builtin commands to demonstrate `fatty` features, and any command it does not recognize is handed off to the shell.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
<a id="orgd27e40e"></a>
|
|
24
|
+
|
|
25
|
+
## Builtin commands
|
|
26
|
+
|
|
27
|
+
Here are the commands builtin to `fatty`
|
|
28
|
+
|
|
29
|
+
| Command | Description |
|
|
30
|
+
|---------------------------- |-------------------------------------------------------------------------- |
|
|
31
|
+
| cd | Change the current directory used by the shell |
|
|
32
|
+
| colors | Display all available colors, names and rgb codes |
|
|
33
|
+
| choose | Present a series of choices in a popup window |
|
|
34
|
+
| choose\_multi | Present choices with a "checkbox" for selecting multiple values |
|
|
35
|
+
| choosevals | Also present choices in a popup window but return an associated value |
|
|
36
|
+
| choosevals\_multi | Also present a checkbox but return associated values |
|
|
37
|
+
| environment | Print out the environment detected by Fatty |
|
|
38
|
+
| good | Display a "good" message colored to indicate success |
|
|
39
|
+
| help | Display this file on the output pane |
|
|
40
|
+
| info | Display an "info" message on the status line |
|
|
41
|
+
| keytest | Enter key diagnostic mode report keycodes, key names, and bindings |
|
|
42
|
+
| markdown <file.md> | Render the markdown file to the output pane; defaults to a demo file |
|
|
43
|
+
| markdown\_org | Render the markdown file exported from org mode with ofx-gfm package |
|
|
44
|
+
| menu | Present a menu of labeled routines to run |
|
|
45
|
+
| oops | Display an "oops" message colored to indicate failure |
|
|
46
|
+
| progress bar | Show progress by a filling bar using ASCII characters |
|
|
47
|
+
| progress braille\_bar | Same, but using braille characters |
|
|
48
|
+
| progress count <N> | Display an animated progress indicator counting up to 40 or the given N |
|
|
49
|
+
| progress percent <N> | Same but also show the percent complete |
|
|
50
|
+
| progress simple\_percent <N> | Same but show only the percent complete |
|
|
51
|
+
| progress spinner | Animate a "spinner" showing a busy state |
|
|
52
|
+
| progress trail | Show progress by displaying an "indicator" character for each step |
|
|
53
|
+
| progress unicode\_bar | Same, but using unicode characters |
|
|
54
|
+
| prompt | Popup a text box for entering a value in response to a prompt |
|
|
55
|
+
| <shell\_command> | Anything else is treated as a shell command, run, and its output displayed |
|
|
56
|
+
| warn | Display a "warn" message colored to indicate caution |
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
<a id="orgab9d7b6"></a>
|
|
60
|
+
|
|
61
|
+
## Keybindings
|
|
62
|
+
|
|
63
|
+
The following tables explain the keybindings available in \`fatty\` in different contexts. Named keys are indicated by \`:name\` and key categories, such as \`<digits>\` are indicated with brackets.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
<a id="org68708fa"></a>
|
|
67
|
+
|
|
68
|
+
### Input Context
|
|
69
|
+
|
|
70
|
+
When editing the input line or text input for widgets like the \`prompt\`, \`fatty\` provides emacs-like editing keybindings by default. Many of these commands can take a count prefix argument to repeat the command count times. For example, \`M-8 M-0 #\` will insert 80 '#' characters at the cursor.
|
|
71
|
+
|
|
72
|
+
| Key | Description |
|
|
73
|
+
|---------- |------------------------------------------------------ |
|
|
74
|
+
| C-a | move to the beginning of the line |
|
|
75
|
+
| :home | move to the beginning of the line |
|
|
76
|
+
| C-e | move to the end of the line |
|
|
77
|
+
| :end | move to the end of the line |
|
|
78
|
+
| C-f | move cursor right one character |
|
|
79
|
+
| :right | move cursor right one character |
|
|
80
|
+
| C-b | move cursor left one character |
|
|
81
|
+
| :left | move cursor left one character |
|
|
82
|
+
| M-f | move cursor right one word |
|
|
83
|
+
| M-:right | move cursor right one word |
|
|
84
|
+
| C-:right | move cursor right one word |
|
|
85
|
+
| M-b | move cursor left one word |
|
|
86
|
+
| M-:left | move cursor left one word |
|
|
87
|
+
| C-:left | move cursor left one word |
|
|
88
|
+
| C-t | transpose characters |
|
|
89
|
+
| M-t | transpose words |
|
|
90
|
+
| | |
|
|
91
|
+
| C-d | delete character at cursor |
|
|
92
|
+
| :delete | delete character at cursor |
|
|
93
|
+
| :backspace | delete character before cursor |
|
|
94
|
+
| M-d | kill word at cursor |
|
|
95
|
+
| C-w | kill word before cursor |
|
|
96
|
+
| C-k | kill to end of line |
|
|
97
|
+
| | |
|
|
98
|
+
| C-/ | undo |
|
|
99
|
+
| C-\_ | undo |
|
|
100
|
+
| C-M-/ | redo |
|
|
101
|
+
| M-/ | redo |
|
|
102
|
+
| | |
|
|
103
|
+
| C-:space | set the mark at the current cursor position |
|
|
104
|
+
| C-@ | set the mark at the current cursor position |
|
|
105
|
+
| C-g | clear the mark |
|
|
106
|
+
| C-w | kill the region |
|
|
107
|
+
| M-w | copy the region |
|
|
108
|
+
| | |
|
|
109
|
+
| C-y | yank last kill at cursor |
|
|
110
|
+
| M-y | replace last yank with next in kill ring |
|
|
111
|
+
| | |
|
|
112
|
+
| C-u | universal count argument (time 4 each press) |
|
|
113
|
+
| M-<digit> | accumulate count argument |
|
|
114
|
+
| | |
|
|
115
|
+
| C-p | replace the line with the prior history item |
|
|
116
|
+
| :up | replace the line with the prior history item |
|
|
117
|
+
| C-n | replace the line with the next history item |
|
|
118
|
+
| :down | replace the line with the prior history item |
|
|
119
|
+
| C-r | search the history in a popup |
|
|
120
|
+
| | |
|
|
121
|
+
| :enter | feed the line to the on\_accept proc and page output |
|
|
122
|
+
| M-:enter | feed the line to the on\_accept proc and scroll output |
|
|
123
|
+
| C-c | quit \`fatty\` |
|
|
124
|
+
| C-d | quit \`fatty\` only if the input line is empty |
|
|
125
|
+
| C-l | clear the output pane |
|
|
126
|
+
| | |
|
|
127
|
+
| :tab | Suggest the next completion |
|
|
128
|
+
| S-:tab | Suggest the prior completion |
|
|
129
|
+
| M-:tab | Popup all completions based on input-so-far |
|
|
130
|
+
| | |
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
<a id="org7729d50"></a>
|
|
134
|
+
|
|
135
|
+
### Paging Context
|
|
136
|
+
|
|
137
|
+
By default, \`fatty\` sends output to the large output pane, and if the output is more than one screen long presents a paging environment for viewing and searching the environment.
|
|
138
|
+
|
|
139
|
+
| Key | Description |
|
|
140
|
+
|----------- |-------------------------------------- |
|
|
141
|
+
| :up | move output one line up |
|
|
142
|
+
| k | move output one line up |
|
|
143
|
+
| :down | move output one line down |
|
|
144
|
+
| j | move output one line down |
|
|
145
|
+
| | |
|
|
146
|
+
| :space | move to next page |
|
|
147
|
+
| :page\_down | move to next page |
|
|
148
|
+
| C-v | move to next page |
|
|
149
|
+
| d | move to next page |
|
|
150
|
+
| :backspace | move to prior page |
|
|
151
|
+
| :page\_up | move to prior page |
|
|
152
|
+
| M-v | move to prior page |
|
|
153
|
+
| u | move to prior page |
|
|
154
|
+
| | |
|
|
155
|
+
| `/` | initiate a forward string search |
|
|
156
|
+
| ? | initiate a backward string search |
|
|
157
|
+
| | |
|
|
158
|
+
| C-/ | initiate a forward regex search |
|
|
159
|
+
| C-M-S | initiate a forward regex search |
|
|
160
|
+
| | |
|
|
161
|
+
| C-s | initiate a forward incremental search |
|
|
162
|
+
| C-r | initiate a backward incremental search |
|
|
163
|
+
| | |
|
|
164
|
+
| C-c | cancel the search |
|
|
165
|
+
| C-g | cancel the search |
|
|
166
|
+
| | |
|
|
167
|
+
| :return | use the typed string for the search |
|
|
168
|
+
| :enter | use the typed string for the search |
|
|
169
|
+
| | |
|
|
170
|
+
| n | find the next match |
|
|
171
|
+
| N | find the prior match |
|
|
172
|
+
| | |
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
<a id="org684d15c"></a>
|
|
176
|
+
|
|
177
|
+
### Popup Context
|
|
178
|
+
|
|
179
|
+
Certain keys cause a popup, such as C-r to bring up history items and M-:tab to bring up completions.
|
|
180
|
+
|
|
181
|
+
| Key | Description |
|
|
182
|
+
|-------------- |--------------------------------------------------------------------- |
|
|
183
|
+
| <search terms> | typing text narrows to those items matching all space-separated words |
|
|
184
|
+
| C-c | cancel the popup with no return |
|
|
185
|
+
| C-g | cancel the popup with no return |
|
|
186
|
+
| :escape | cancel the popup with no return |
|
|
187
|
+
| | |
|
|
188
|
+
| :enter | select the current item |
|
|
189
|
+
| :return | select the current item |
|
|
190
|
+
| | |
|
|
191
|
+
| :up | select the prior item |
|
|
192
|
+
| C-p | select the prior item |
|
|
193
|
+
| :down | select the next item |
|
|
194
|
+
| C-n | select the next item |
|
|
195
|
+
| | |
|
|
196
|
+
| :page\_up | display prior page of items |
|
|
197
|
+
| M-v | display prior page of items |
|
|
198
|
+
| :page\_down | display next page of items |
|
|
199
|
+
| C-v | display next page of items |
|
|
200
|
+
| | |
|
|
201
|
+
| :home | display first page of items |
|
|
202
|
+
| M-< | display first page of items |
|
|
203
|
+
| :end | display last page of items |
|
|
204
|
+
| M-> | display last page of items |
|
|
205
|
+
| | |
|
|
206
|
+
| C-l | re-center display on current item |
|
|
207
|
+
| | |
|
|
208
|
+
| :space | if popup allows multiple selections, add current item to selections |
|
|
209
|
+
| | |
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
<a id="org741e059"></a>
|
|
213
|
+
|
|
214
|
+
### Prompt Context
|
|
215
|
+
|
|
216
|
+
Fatty can popup a prompt for getting text from the user by presenting a prompt together with an input field. The input field has all the line-editing capabilities of the primary input context.
|
|
217
|
+
|
|
218
|
+
| Key | Description |
|
|
219
|
+
|------- |--------------------------------- |
|
|
220
|
+
| C-c | cancel with no input |
|
|
221
|
+
| C-g | cancel with no input |
|
|
222
|
+
| :escape | cancel with no input |
|
|
223
|
+
| | |
|
|
224
|
+
| :return | return with current input |
|
|
225
|
+
| :enter | return with current input |
|
|
226
|
+
| C-j | return with current input |
|
|
227
|
+
| | |
|
|
228
|
+
| C-p | display prior prompt history item |
|
|
229
|
+
| :up | display prior prompt history item |
|
|
230
|
+
| C-n | display next prompt history item |
|
|
231
|
+
| :down | display next prompt history item |
|
|
232
|
+
| | |
|
|
233
|
+
| | |
|
data/lib/fatty/action.rb
CHANGED
|
@@ -71,7 +71,7 @@ module Fatty
|
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def self.call(name, env, *args, **kwargs)
|
|
74
|
-
arg_str = "name: #{name},
|
|
74
|
+
arg_str = "name: #{name}, session: #{env.session.id}, #{env}, args: #{args}, kwargs: #{kwargs}"
|
|
75
75
|
Fatty.debug("Action.call(#{arg_str})", tag: :action)
|
|
76
76
|
key = name.to_sym
|
|
77
77
|
defn = @defs[key] or raise ActionError, "Unknown action: #{key}"
|
|
@@ -79,7 +79,6 @@ module Fatty
|
|
|
79
79
|
target = env.public_send(defn[:on])
|
|
80
80
|
raise ActionError, "env.#{defn[:on]} is nil for action #{key}" unless target
|
|
81
81
|
|
|
82
|
-
# Inject count: from env.counter when the target accepts it.
|
|
83
82
|
if env.counter&.active? && !kwargs.key?(:count)
|
|
84
83
|
meth = target.method(defn[:method])
|
|
85
84
|
params = meth.parameters
|
|
@@ -88,12 +87,9 @@ module Fatty
|
|
|
88
87
|
params.any? { |(kind, pname)| kind == :key && pname == :count } ||
|
|
89
88
|
params.any? { |(kind, _pname)| kind == :keyrest }
|
|
90
89
|
|
|
91
|
-
if accepts_count
|
|
92
|
-
kwargs = kwargs.merge(count: env.counter.consume(default: 1))
|
|
93
|
-
end
|
|
90
|
+
kwargs = kwargs.merge(count: env.counter.consume(default: 1)) if accepts_count
|
|
94
91
|
end
|
|
95
|
-
|
|
96
|
-
target.public_send(defn[:method], *args, **kwargs)
|
|
92
|
+
target.__send__(defn[:method], *args, **kwargs)
|
|
97
93
|
end
|
|
98
94
|
|
|
99
95
|
def self.registered?(name)
|
data/lib/fatty/actionable.rb
CHANGED
data/lib/fatty/alert.rb
CHANGED
|
@@ -2,84 +2,99 @@
|
|
|
2
2
|
|
|
3
3
|
module Fatty
|
|
4
4
|
class Alert
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
attr_reader :level, :message, :details
|
|
5
|
+
attr_reader :role, :text, :details
|
|
8
6
|
|
|
9
7
|
# Return a new Alert object
|
|
10
8
|
#
|
|
11
9
|
# @param message [String]
|
|
12
|
-
# @param
|
|
10
|
+
# @param role [:good, :info, :warn, :error]
|
|
13
11
|
# @param details [Hash|String]
|
|
14
12
|
# @param sticky [Boolean]
|
|
15
13
|
# @return [Alert]
|
|
16
|
-
def initialize(
|
|
17
|
-
@
|
|
18
|
-
@
|
|
14
|
+
def initialize(text:, role: :info, details: nil, sticky: false)
|
|
15
|
+
@text = text
|
|
16
|
+
@role = role.to_sym
|
|
19
17
|
@details = details
|
|
20
18
|
@sticky = !!sticky
|
|
21
19
|
end
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
def inspect
|
|
22
|
+
txt = +"Alert: "
|
|
23
|
+
txt << "text: #{@text}; " if @text
|
|
24
|
+
txt << "role: #{@role}; " if @role
|
|
25
|
+
txt << "details: #{@details}; " if @details
|
|
26
|
+
txt << "sticky: #{@sticky}."
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Return a new Alert object at role good
|
|
30
|
+
#
|
|
31
|
+
# @param msg [String]
|
|
32
|
+
# @return [Alert] with role :good
|
|
33
|
+
def self.good(msg)
|
|
34
|
+
new(text: msg, role: :good)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Return a new Alert object at role :info
|
|
24
38
|
#
|
|
25
39
|
# @param msg [String]
|
|
26
|
-
# @return [Alert] with
|
|
40
|
+
# @return [Alert] with role :info
|
|
27
41
|
def self.info(msg)
|
|
28
|
-
new(
|
|
42
|
+
new(text: msg, role: :info)
|
|
29
43
|
end
|
|
30
44
|
|
|
31
|
-
# Return a new Alert object at
|
|
45
|
+
# Return a new Alert object at role :warn
|
|
32
46
|
#
|
|
33
47
|
# @param msg [String]
|
|
34
|
-
# @return [Alert] with
|
|
48
|
+
# @return [Alert] with role :warn
|
|
35
49
|
def self.warn(msg)
|
|
36
|
-
new(
|
|
50
|
+
new(text: msg, role: :warn)
|
|
37
51
|
end
|
|
38
52
|
|
|
39
|
-
# Return a new Alert object at
|
|
53
|
+
# Return a new Alert object at role error
|
|
40
54
|
#
|
|
41
55
|
# @param msg [String]
|
|
42
|
-
# @return [Alert] with
|
|
56
|
+
# @return [Alert] with role :error
|
|
43
57
|
def self.error(msg)
|
|
44
|
-
new(
|
|
58
|
+
new(text: msg, role: :error)
|
|
45
59
|
end
|
|
46
60
|
|
|
47
61
|
# Translate the "level" to a "role" used by the renderers. The returned
|
|
48
62
|
# roles are "composite" roles in the resolver in that they take the
|
|
49
|
-
# background
|
|
63
|
+
# background of the alert panel and apply a foreground color based on
|
|
50
64
|
# severity.
|
|
51
65
|
# @param level [:info, :warn, :error]
|
|
52
66
|
# @return [Symbol] composite or semantic role
|
|
53
67
|
# used by renderer (e.g., alert_good, :alert_info, :alert_warn, :alert_error)
|
|
54
|
-
def role
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
end
|
|
68
|
+
# def role
|
|
69
|
+
# case level
|
|
70
|
+
# when :good then :alert_good
|
|
71
|
+
# when :warn then :alert_warn
|
|
72
|
+
# when :error then :alert_error
|
|
73
|
+
# else :alert_info
|
|
74
|
+
# end
|
|
75
|
+
# end
|
|
62
76
|
|
|
63
77
|
# Build a string version of the Alert suitable for display to the user.
|
|
64
78
|
def format
|
|
65
79
|
icon =
|
|
66
|
-
case
|
|
67
|
-
when :
|
|
68
|
-
when :
|
|
69
|
-
|
|
80
|
+
case role
|
|
81
|
+
when :good then " ✔ "
|
|
82
|
+
when :info then " ℹ "
|
|
83
|
+
when :warn then " ⚠ "
|
|
84
|
+
when :error then " ✖ "
|
|
85
|
+
else " "
|
|
70
86
|
end
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
details_str = if details.is_a?(Hash)
|
|
75
|
-
" (" +
|
|
76
|
-
DETAIL_ORDER.filter_map { |k| "#{k}=#{details[k]}" if details.key?(k) }.join(" ") +
|
|
77
|
-
")"
|
|
87
|
+
details_str =
|
|
88
|
+
if details.nil? || details.empty?
|
|
89
|
+
""
|
|
78
90
|
else
|
|
79
|
-
|
|
91
|
+
key_strs = []
|
|
92
|
+
detail_key_order.each do |k|
|
|
93
|
+
key_strs << "#{k}=#{details[k]}"
|
|
94
|
+
end
|
|
95
|
+
" (#{key_strs.join(' ')})"
|
|
80
96
|
end
|
|
81
|
-
|
|
82
|
-
"#{icon} #{msg}#{details_str}"
|
|
97
|
+
"#{icon} #{text}#{details_str}"
|
|
83
98
|
end
|
|
84
99
|
|
|
85
100
|
# Return whether this Alert is sticky, meaning that it should not be
|
|
@@ -89,5 +104,16 @@ module Fatty
|
|
|
89
104
|
def sticky?
|
|
90
105
|
@sticky
|
|
91
106
|
end
|
|
107
|
+
|
|
108
|
+
def detail_key_order
|
|
109
|
+
keys = []
|
|
110
|
+
keys << :terminal if details.key?(:terminal)
|
|
111
|
+
keys << :key if details.key?(:key)
|
|
112
|
+
keys << :shift if details.key?(:shift)
|
|
113
|
+
keys << :ctrl if details.key?(:ctrl)
|
|
114
|
+
keys << :meta if details.key?(:meta)
|
|
115
|
+
other_keys = details.keys - [:terminal, :key, :shift, :ctrl, :meta]
|
|
116
|
+
keys + other_keys.sort
|
|
117
|
+
end
|
|
92
118
|
end
|
|
93
119
|
end
|
data/lib/fatty/ansi/renderer.rb
CHANGED
|
@@ -14,12 +14,23 @@ module Fatty
|
|
|
14
14
|
return unless spec
|
|
15
15
|
|
|
16
16
|
msg = text.to_s.tr("\r\n", " ")
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
|
|
18
|
+
segments = Fatty::Ansi.segment(msg).map do |segment_text, style|
|
|
19
|
+
{
|
|
20
|
+
text: segment_text,
|
|
21
|
+
role: role,
|
|
22
|
+
style: style,
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
render_segments_line(
|
|
27
|
+
row: row,
|
|
28
|
+
col: col,
|
|
29
|
+
width: width,
|
|
30
|
+
segments: segments,
|
|
31
|
+
palette: palette,
|
|
32
|
+
fill_role: role,
|
|
33
|
+
)
|
|
23
34
|
end
|
|
24
35
|
|
|
25
36
|
def render_segments_line(row:, col:, width:, segments:, palette:, fill_role: :output)
|
|
@@ -73,6 +84,15 @@ module Fatty
|
|
|
73
84
|
@io || $stdout
|
|
74
85
|
end
|
|
75
86
|
|
|
87
|
+
def show_cursor
|
|
88
|
+
"#{CSI}?25h"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def hide_cursor
|
|
92
|
+
"#{CSI}?25l"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# simplecov:disable
|
|
76
96
|
private
|
|
77
97
|
|
|
78
98
|
def save_cursor
|
data/lib/fatty/ansi.rb
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fatty
|
|
4
|
+
###########################################################################################
|
|
5
|
+
# Public API convenience methods for use by the library user
|
|
6
|
+
###########################################################################################
|
|
7
|
+
module AlertApi
|
|
8
|
+
# Display text in the status area with the given "role", which determines
|
|
9
|
+
# the coloring of the displayed text via themeing.
|
|
10
|
+
def alert(text, role: :info)
|
|
11
|
+
terminal.apply_command(Command.session(:alert, :show, text: text, role: role))
|
|
12
|
+
nil
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fatty
|
|
4
|
+
module ChooseApi
|
|
5
|
+
# The consumer can call #choose to cause an interactive popup session to
|
|
6
|
+
# present the user with a series of choices to select from.
|
|
7
|
+
def choose(prompt, choices:, initial_choice_idx: 0, cancel_value: nil)
|
|
8
|
+
items = normalize_choices(choices)
|
|
9
|
+
raise ArgumentError, "choices must not be empty" if items.empty?
|
|
10
|
+
|
|
11
|
+
labels = items.map(&:first)
|
|
12
|
+
popup = PopUpSession.new(
|
|
13
|
+
source: labels,
|
|
14
|
+
kind: :terminal_choose,
|
|
15
|
+
title: "Choose",
|
|
16
|
+
message: prompt,
|
|
17
|
+
prompt: "Narrow: ",
|
|
18
|
+
current: :top,
|
|
19
|
+
show_counts: false,
|
|
20
|
+
validate_unique_labels: true,
|
|
21
|
+
history: terminal.history,
|
|
22
|
+
history_kind: :popup_filter,
|
|
23
|
+
history_ctx: { kind: :popup_filter, popup: :choose, prompt: prompt.to_s },
|
|
24
|
+
)
|
|
25
|
+
popup.instance_variable_set(:@current, initial_choice_idx.to_i.clamp(0, labels.length - 1))
|
|
26
|
+
|
|
27
|
+
done = false
|
|
28
|
+
result = nil
|
|
29
|
+
|
|
30
|
+
acc_proc = ->(payload) do
|
|
31
|
+
item = payload[:item]
|
|
32
|
+
idx = labels.index(item)
|
|
33
|
+
result = idx ? items[idx][1] : cancel_value
|
|
34
|
+
done = true
|
|
35
|
+
end
|
|
36
|
+
cancel_proc = -> do
|
|
37
|
+
result = cancel_value
|
|
38
|
+
done = true
|
|
39
|
+
end
|
|
40
|
+
owner = Terminal::PopupOwner.new(on_result: acc_proc, on_cancel: cancel_proc)
|
|
41
|
+
|
|
42
|
+
begin
|
|
43
|
+
terminal.apply_command(Fatty::Command.terminal(:push_modal, session: popup, owner: owner))
|
|
44
|
+
terminal.render_frame
|
|
45
|
+
|
|
46
|
+
while !done && terminal.running?
|
|
47
|
+
dirty = false
|
|
48
|
+
command = terminal.event_source.next_event
|
|
49
|
+
|
|
50
|
+
if command
|
|
51
|
+
terminal.apply_command(command)
|
|
52
|
+
dirty = true
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
begin
|
|
56
|
+
dirty ||= !!terminal.tick_active_session
|
|
57
|
+
rescue StandardError => e
|
|
58
|
+
Fatty.error("PromptApi#choose tick failed: #{e.class}: #{e.message}", tag: :terminal)
|
|
59
|
+
dirty = true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
terminal.render_frame if dirty
|
|
63
|
+
end
|
|
64
|
+
ensure
|
|
65
|
+
terminal.render_frame
|
|
66
|
+
end
|
|
67
|
+
result
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# A simple Yes/No chooser.
|
|
71
|
+
def confirm(prompt, yes_label: "Yes", no_label: "No", cancel_value: false)
|
|
72
|
+
choose(
|
|
73
|
+
prompt,
|
|
74
|
+
choices: { yes_label => true, no_label => false },
|
|
75
|
+
cancel_value: cancel_value,
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# The consumer can call #choose_multi to cause an interactive popup session to
|
|
80
|
+
# present the user with a series of choices to select from.
|
|
81
|
+
def choose_multi(prompt, choices:, cancel_value: nil)
|
|
82
|
+
items = normalize_choices(choices)
|
|
83
|
+
raise ArgumentError, "choices must not be empty" if items.empty?
|
|
84
|
+
|
|
85
|
+
labels = items.map(&:first)
|
|
86
|
+
popup = PopUpSession.new(
|
|
87
|
+
source: labels,
|
|
88
|
+
kind: :terminal_choose_multi,
|
|
89
|
+
title: "Choose Many",
|
|
90
|
+
message: prompt,
|
|
91
|
+
prompt: "> ",
|
|
92
|
+
current: :top,
|
|
93
|
+
show_counts: true,
|
|
94
|
+
selection_mode: :multiple,
|
|
95
|
+
validate_unique_labels: true,
|
|
96
|
+
history: terminal.history,
|
|
97
|
+
history_kind: :popup_filter,
|
|
98
|
+
history_ctx: { kind: :popup_filter, popup: :choose_multi, prompt: prompt.to_s },
|
|
99
|
+
)
|
|
100
|
+
done = false
|
|
101
|
+
result = nil
|
|
102
|
+
|
|
103
|
+
label_to_value = items.to_h
|
|
104
|
+
acc_proc = ->(payload) do
|
|
105
|
+
selected = payload[:items] || {}
|
|
106
|
+
result =
|
|
107
|
+
selected.each_with_object({}) do |(label, _), h|
|
|
108
|
+
h[label] = label_to_value.fetch(label, cancel_value)
|
|
109
|
+
end
|
|
110
|
+
done = true
|
|
111
|
+
end
|
|
112
|
+
cancel_proc = -> do
|
|
113
|
+
result = cancel_value
|
|
114
|
+
done = true
|
|
115
|
+
end
|
|
116
|
+
owner = Terminal::PopupOwner.new(on_result: acc_proc, on_cancel: cancel_proc)
|
|
117
|
+
|
|
118
|
+
begin
|
|
119
|
+
terminal.apply_command(Fatty::Command.terminal(:push_modal, session: popup, owner: owner))
|
|
120
|
+
terminal.render_frame
|
|
121
|
+
|
|
122
|
+
while !done && terminal.running?
|
|
123
|
+
dirty = false
|
|
124
|
+
command = terminal.event_source.next_event
|
|
125
|
+
if command
|
|
126
|
+
terminal.apply_command(command)
|
|
127
|
+
dirty = true
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
begin
|
|
131
|
+
dirty ||= !!terminal.tick_active_session
|
|
132
|
+
rescue StandardError => e
|
|
133
|
+
Fatty.error("Terminal#choose_multi tick failed: #{e.class}: #{e.message}", tag: :terminal)
|
|
134
|
+
dirty = true
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
terminal.render_frame if dirty
|
|
138
|
+
end
|
|
139
|
+
ensure
|
|
140
|
+
terminal.render_frame
|
|
141
|
+
end
|
|
142
|
+
result
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|