howzit 2.0.9 → 2.0.12
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/.travis.yml +1 -1
- data/CHANGELOG.md +25 -0
- data/fish/completions/howzit.fish +34 -2
- data/lib/howzit/buildnote.rb +156 -30
- data/lib/howzit/colors.rb +3 -0
- data/lib/howzit/console_logger.rb +1 -0
- data/lib/howzit/hash.rb +25 -4
- data/lib/howzit/prompt.rb +37 -2
- data/lib/howzit/stringutils.rb +130 -3
- data/lib/howzit/task.rb +10 -6
- data/lib/howzit/topic.rb +59 -8
- data/lib/howzit/util.rb +25 -0
- data/lib/howzit/version.rb +1 -1
- data/lib/howzit.rb +11 -0
- data/spec/task_spec.rb +6 -1
- metadata +1 -4
- data/lib/howzit/buildnotes.rb +0 -1252
- data/spec/buildnotes.md.bak +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 700c444e11477c62c6de0f06b81b3029b21f3363f507c5a6cab927f2365d2be5
|
4
|
+
data.tar.gz: be862c2c31427abae2428a024009aea0f73885a5dc9652c5d2b5f308c181ca3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz: '
|
6
|
+
metadata.gz: d07c309680eef72f5edb3b41d40354aa9cefa1b6efebd1c0dfbaa59f7cbfc95c6211433f081b6f21afa069cd5ae30f79dc42f1a4a08c96680b533b16519ca25b
|
7
|
+
data.tar.gz: '08ca9d51ec3793c1750d70f500bde1e68c1f327cd41d0b92839c317d6a421f96cbb8ef2f03204ab6cfe1ddb1d83a497084aa9590233d6ceb860921fdb473267e'
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
### 2.0.12
|
2
|
+
|
3
|
+
2022-08-05 11:27
|
4
|
+
|
5
|
+
#### IMPROVED
|
6
|
+
|
7
|
+
- Show how many tasks will be included when requesting confirmation for an include? directive
|
8
|
+
|
9
|
+
### 2.0.11
|
10
|
+
|
11
|
+
2022-08-05 11:21
|
12
|
+
|
13
|
+
#### IMPROVED
|
14
|
+
|
15
|
+
- Update fish completions with all current command line options
|
16
|
+
|
17
|
+
### 2.0.10
|
18
|
+
|
19
|
+
2022-08-05 08:17
|
20
|
+
|
21
|
+
#### IMPROVED
|
22
|
+
|
23
|
+
- Provide more helpful feedback if no content is found in build note
|
24
|
+
- Confirm whether the user wants to create a new note when one isn't found
|
25
|
+
|
1
26
|
### 2.0.9
|
2
27
|
|
3
28
|
2022-08-05 07:29
|
@@ -1,3 +1,35 @@
|
|
1
1
|
complete -xc howzit -a "(howzit -L)"
|
2
|
-
complete -
|
3
|
-
|
2
|
+
complete -c howzit -l default -d "Answer all prompts with default response"
|
3
|
+
complete -c howzit -x -s m -l matching -a "partial exact fuzzy beginswith" -d "Topics matching type"
|
4
|
+
complete -c howzit -x -l multiple -a "first best all choose" -d "Multiple result handling"
|
5
|
+
complete -c howzit -s u -l no-upstream -d "Don't traverse up parent directories for additional build notes"
|
6
|
+
complete -c howzit -s u -l upstream -d "Traverse up parent directories for additional build notes"
|
7
|
+
complete -c howzit -s L -l list-completions -d "List topics for completion"
|
8
|
+
complete -c howzit -s l -l list -d "List available topics"
|
9
|
+
complete -c howzit -s R -l list-runnable -d "List topics containing @ directives (verbose)"
|
10
|
+
complete -c howzit -s T -l task-list -d "List topics containing @ directives (completion-compatible)"
|
11
|
+
complete -c howzit -l templates -d "List available templates"
|
12
|
+
complete -c howzit -l title-only -d "Output title only"
|
13
|
+
complete -c howzit -s c -l create -d "Create a skeleton build note in the current working directory"
|
14
|
+
complete -c howzit -f -l config-get -d "Display the configuration settings or setting for a specific key"
|
15
|
+
complete -c howzit -f -l config-set -d "Set a config value (must be a valid key)"
|
16
|
+
complete -c howzit -l edit-config -d "Edit configuration file using editor (subl)"
|
17
|
+
complete -c howzit -s e -l edit -d "Edit buildnotes file in current working directory using editor (subl)"
|
18
|
+
complete -c howzit -x -l grep -d "Display sections matching a search pattern"
|
19
|
+
complete -c howzit -s r -l run -a "(howzit -T)" -d "Execute @run, @open, and/or @copy commands for given topic"
|
20
|
+
complete -c howzit -s s -l select -d "Select topic from menu"
|
21
|
+
complete -c howzit -l color -d "Colorize output (default on)"
|
22
|
+
complete -c howzit -l no-color -d "Don't colorize output (default on)"
|
23
|
+
complete -c howzit -x -l header-format -d "Formatting style for topic titles (border, block)"
|
24
|
+
complete -c howzit -l md-highlight -d "Highlight Markdown syntax (default on), requires mdless or mdcat"
|
25
|
+
complete -c howzit -l no-md-highlight -d "Don't highlight Markdown syntax (default on), requires mdless or mdcat"
|
26
|
+
complete -c howzit -l pager -d "Paginate output (default on)"
|
27
|
+
complete -c howzit -l no-pager -d "Don't paginate output (default on)"
|
28
|
+
complete -c howzit -l show-code -d "Display the content of fenced run blocks"
|
29
|
+
complete -c howzit -s t -l title -d "Output title with build notes"
|
30
|
+
complete -c howzit -x -s w -l wrap -d "Wrap to specified width (default 80, 0 to disable)"
|
31
|
+
complete -c howzit -s d -l debug -d "Show debug messages (and all messages)"
|
32
|
+
complete -c howzit -s q -l quiet -d "Silence info message"
|
33
|
+
complete -c howzit -l verbose -d "Show all messages"
|
34
|
+
complete -c howzit -s h -l help -d "Display this screen"
|
35
|
+
complete -c howzit -s v -l version -d "Display version number"
|
data/lib/howzit/buildnote.rb
CHANGED
@@ -7,10 +7,27 @@ module Howzit
|
|
7
7
|
|
8
8
|
attr_reader :metadata, :title
|
9
9
|
|
10
|
+
##
|
11
|
+
## Initialize a build note
|
12
|
+
##
|
13
|
+
## @param file [String] The path to the build note file
|
14
|
+
## @param args [Array] additional args
|
15
|
+
##
|
10
16
|
def initialize(file: nil, args: [])
|
11
17
|
@topics = []
|
12
|
-
|
13
|
-
|
18
|
+
if note_file.nil?
|
19
|
+
res = Prompt.yn('No build notes file found, create one?', default: true)
|
20
|
+
|
21
|
+
create_note if res
|
22
|
+
Process.exit 0
|
23
|
+
end
|
24
|
+
content = Util.read_file(note_file)
|
25
|
+
if content.nil? || content.empty?
|
26
|
+
Howzit.console.error("{br}No content found in build note (#{note_file}){x}".c)
|
27
|
+
Process.exit 1
|
28
|
+
else
|
29
|
+
@metadata = content.split(/^#/)[0].strip.get_metadata
|
30
|
+
end
|
14
31
|
|
15
32
|
read_help(file)
|
16
33
|
end
|
@@ -19,14 +36,25 @@ module Howzit
|
|
19
36
|
puts "#<Howzit::BuildNote @topics=[#{@topics.count}]>"
|
20
37
|
end
|
21
38
|
|
39
|
+
##
|
40
|
+
## Public method to begin processing the build note based on command line options
|
41
|
+
##
|
22
42
|
def run
|
23
43
|
process
|
24
44
|
end
|
25
45
|
|
46
|
+
##
|
47
|
+
## Public method to open build note in editor
|
48
|
+
##
|
26
49
|
def edit
|
27
50
|
edit_note
|
28
51
|
end
|
29
52
|
|
53
|
+
##
|
54
|
+
## Find a topic based on a fuzzy match
|
55
|
+
##
|
56
|
+
## @param term [String] The search term
|
57
|
+
##
|
30
58
|
def find_topic(term)
|
31
59
|
@topics.filter do |topic|
|
32
60
|
rx = term.to_rx
|
@@ -34,11 +62,19 @@ module Howzit
|
|
34
62
|
end
|
35
63
|
end
|
36
64
|
|
65
|
+
##
|
66
|
+
## Call grep on all topics, filtering out those that don't match
|
67
|
+
##
|
68
|
+
## @param term [String] The search pattern
|
69
|
+
##
|
37
70
|
def grep(term)
|
38
71
|
@topics.filter { |topic| topic.grep(term) }
|
39
72
|
end
|
40
73
|
|
41
74
|
# Output a list of topic titles
|
75
|
+
#
|
76
|
+
# @return [String] formatted list of topics in build note
|
77
|
+
#
|
42
78
|
def list
|
43
79
|
output = []
|
44
80
|
output.push("{bg}Topics:{x}\n".c)
|
@@ -48,14 +84,32 @@ module Howzit
|
|
48
84
|
output.join("\n")
|
49
85
|
end
|
50
86
|
|
87
|
+
|
88
|
+
##
|
89
|
+
## Return an array of topic titles
|
90
|
+
##
|
91
|
+
## @return [Array] array of topic titles
|
92
|
+
##
|
51
93
|
def list_topics
|
52
94
|
@topics.map { |topic| topic.title }
|
53
95
|
end
|
54
96
|
|
97
|
+
##
|
98
|
+
## Return a list of topic titles suitable for shell completion
|
99
|
+
##
|
100
|
+
## @return [String] newline-separated list of topic titles
|
101
|
+
##
|
55
102
|
def list_completions
|
56
103
|
list_topics.join("\n")
|
57
104
|
end
|
58
105
|
|
106
|
+
##
|
107
|
+
## Return a list of topics containing @directives,
|
108
|
+
## suitable for shell completion
|
109
|
+
##
|
110
|
+
## @return [String] newline-separated list of topic
|
111
|
+
## titles
|
112
|
+
##
|
59
113
|
def list_runnable_completions
|
60
114
|
output = []
|
61
115
|
@topics.each do |topic|
|
@@ -64,6 +118,12 @@ module Howzit
|
|
64
118
|
output.join("\n")
|
65
119
|
end
|
66
120
|
|
121
|
+
##
|
122
|
+
## Return a formatted list of topics containing
|
123
|
+
## directives suitable for console output
|
124
|
+
##
|
125
|
+
## @return [String] formatted list
|
126
|
+
##
|
67
127
|
def list_runnable
|
68
128
|
output = []
|
69
129
|
output.push(%({bg}"Runnable" Topics:{x}\n).c)
|
@@ -82,6 +142,11 @@ module Howzit
|
|
82
142
|
output.join("\n")
|
83
143
|
end
|
84
144
|
|
145
|
+
##
|
146
|
+
## Read the help file contents
|
147
|
+
##
|
148
|
+
## @param file [String] The filepath
|
149
|
+
##
|
85
150
|
def read_file(file)
|
86
151
|
read_help_file(file)
|
87
152
|
end
|
@@ -210,28 +275,24 @@ module Howzit
|
|
210
275
|
buildnotes.reverse
|
211
276
|
end
|
212
277
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
278
|
+
##
|
279
|
+
## Glob current directory for valid build note filenames
|
280
|
+
## (must start with "build" or "howzit" and have
|
281
|
+
## extension of "txt", "md", or "markdown")
|
282
|
+
##
|
283
|
+
## @return [String] file path
|
284
|
+
##
|
221
285
|
def glob_note
|
222
|
-
|
223
|
-
# Check for a build note file in the current folder. Filename must start
|
224
|
-
# with "build" and have an extension of txt, md, or markdown.
|
225
|
-
|
226
|
-
Dir.glob('*.{txt,md,markdown}').each do |f|
|
227
|
-
if build_note?(f)
|
228
|
-
filename = f
|
229
|
-
break
|
230
|
-
end
|
231
|
-
end
|
232
|
-
filename
|
286
|
+
Dir.glob('*.{txt,md,markdown}').select(&:build_note?)[0]
|
233
287
|
end
|
234
288
|
|
289
|
+
##
|
290
|
+
## Search for a valid build note, checking current
|
291
|
+
## directory, git top level directory, and parent
|
292
|
+
## directories
|
293
|
+
##
|
294
|
+
## @return [String] filepath
|
295
|
+
##
|
235
296
|
def find_note_file
|
236
297
|
filename = glob_note
|
237
298
|
|
@@ -253,6 +314,11 @@ module Howzit
|
|
253
314
|
File.expand_path(filename)
|
254
315
|
end
|
255
316
|
|
317
|
+
##
|
318
|
+
## Search upstream directories for build notes
|
319
|
+
##
|
320
|
+
## @return [Array] array of build note paths
|
321
|
+
##
|
256
322
|
def read_upstream
|
257
323
|
buildnotes = glob_upstream
|
258
324
|
|
@@ -263,6 +329,13 @@ module Howzit
|
|
263
329
|
topics_dict
|
264
330
|
end
|
265
331
|
|
332
|
+
##
|
333
|
+
## Test to ensure that any `required` metadata in a
|
334
|
+
## template is fulfilled by the build note
|
335
|
+
##
|
336
|
+
## @param template [String] The template to read
|
337
|
+
## from
|
338
|
+
##
|
266
339
|
def ensure_requirements(template)
|
267
340
|
t_leader = Util.read_file(template).split(/^#/)[0].strip
|
268
341
|
if t_leader.length > 0
|
@@ -271,8 +344,8 @@ module Howzit
|
|
271
344
|
required = t_meta['required'].strip.split(/\s*,\s*/)
|
272
345
|
required.each do |req|
|
273
346
|
unless @metadata.keys.include?(req.downcase)
|
274
|
-
Howzit.console.error %({
|
275
|
-
Howzit.console.error %({
|
347
|
+
Howzit.console.error %({bRw}ERROR:{xbr} Missing required metadata key from template '{bw}#{File.basename(template, '.md')}{xr}'{x}).c
|
348
|
+
Howzit.console.error %({br}Please define {by}#{req.downcase}{xr} in build notes{x}).c
|
276
349
|
Process.exit 1
|
277
350
|
end
|
278
351
|
end
|
@@ -280,6 +353,11 @@ module Howzit
|
|
280
353
|
end
|
281
354
|
end
|
282
355
|
|
356
|
+
##
|
357
|
+
## Read a list of topics from an included template
|
358
|
+
##
|
359
|
+
## @param content [String] The template contents
|
360
|
+
##
|
283
361
|
def get_template_topics(content)
|
284
362
|
leader = content.split(/^#/)[0].strip
|
285
363
|
|
@@ -327,10 +405,16 @@ module Howzit
|
|
327
405
|
template_topics
|
328
406
|
end
|
329
407
|
|
330
|
-
|
331
|
-
|
408
|
+
##
|
409
|
+
## Import the contents of a filename as new topics
|
410
|
+
##
|
411
|
+
## @param mtch [MatchData] the filename match from
|
412
|
+
## the include directive
|
413
|
+
##
|
414
|
+
def include_file(mtch)
|
415
|
+
file = File.expand_path(mtch[1])
|
332
416
|
|
333
|
-
return
|
417
|
+
return mtch[0] unless File.exist?(file)
|
334
418
|
|
335
419
|
content = Util.read_file(file)
|
336
420
|
home = ENV['HOME']
|
@@ -345,6 +429,11 @@ module Howzit
|
|
345
429
|
end
|
346
430
|
end
|
347
431
|
|
432
|
+
##
|
433
|
+
## Get the title of the build note (top level header)
|
434
|
+
##
|
435
|
+
## @param truncate [Integer] Truncate to width
|
436
|
+
##
|
348
437
|
def note_title(truncate = 0)
|
349
438
|
help = Util.read_file(note_file)
|
350
439
|
title = help.match(/(?:^(\S.*?)(?=\n==)|^# ?(.*?)$)/)
|
@@ -357,7 +446,13 @@ module Howzit
|
|
357
446
|
title && truncate.positive? ? title.trunc(truncate) : title
|
358
447
|
end
|
359
448
|
|
360
|
-
# Read in the build notes file and output a hash of
|
449
|
+
# Read in the build notes file and output a hash of
|
450
|
+
# "Title" => contents
|
451
|
+
#
|
452
|
+
# @param path [String] The build note path
|
453
|
+
#
|
454
|
+
# @return [Array] array of Topics
|
455
|
+
#
|
361
456
|
def read_help_file(path = nil)
|
362
457
|
topics = []
|
363
458
|
|
@@ -365,6 +460,11 @@ module Howzit
|
|
365
460
|
|
366
461
|
help = Util.read_file(filename)
|
367
462
|
|
463
|
+
if help.nil? || help.empty?
|
464
|
+
Howzit.console.error("{br}No content found in #{filename}{x}".c)
|
465
|
+
Process.exit 1
|
466
|
+
end
|
467
|
+
|
368
468
|
@title = note_title
|
369
469
|
|
370
470
|
help.gsub!(/@include\((.*?)\)/) do
|
@@ -403,6 +503,11 @@ module Howzit
|
|
403
503
|
topics
|
404
504
|
end
|
405
505
|
|
506
|
+
##
|
507
|
+
## Read build note and include upstream topics
|
508
|
+
##
|
509
|
+
## @param path [String] The build note path
|
510
|
+
##
|
406
511
|
def read_help(path = nil)
|
407
512
|
@topics = read_help_file(path)
|
408
513
|
return unless path.nil? && Howzit.options[:include_upstream]
|
@@ -412,8 +517,17 @@ module Howzit
|
|
412
517
|
upstream_topics.each do |topic|
|
413
518
|
@topics.push(topic) unless find_topic(title.sub(/^.+:/, '')).count.positive?
|
414
519
|
end
|
520
|
+
|
521
|
+
if note_file && @topics.empty?
|
522
|
+
Howzit.console.error("{br}Note file found but no topics detected in #{note_file}{x}".c)
|
523
|
+
Process.exit 1
|
524
|
+
end
|
525
|
+
|
415
526
|
end
|
416
527
|
|
528
|
+
##
|
529
|
+
## Open build note in editor
|
530
|
+
##
|
417
531
|
def edit_note
|
418
532
|
editor = Howzit.options.fetch(:editor, ENV['EDITOR'])
|
419
533
|
|
@@ -431,7 +545,16 @@ module Howzit
|
|
431
545
|
end
|
432
546
|
end
|
433
547
|
|
434
|
-
|
548
|
+
##
|
549
|
+
## Run or print a topic
|
550
|
+
##
|
551
|
+
## @param topic [Topic] The topic
|
552
|
+
## @param run [Boolean] execute directives if
|
553
|
+
## true
|
554
|
+
## @param single [Boolean] is being output as a
|
555
|
+
## single topic
|
556
|
+
##
|
557
|
+
def process_topic(topic, run, single: false)
|
435
558
|
new_topic = topic.dup
|
436
559
|
|
437
560
|
# Handle variable replacement
|
@@ -445,6 +568,9 @@ module Howzit
|
|
445
568
|
output.nil? ? '' : output.join("\n")
|
446
569
|
end
|
447
570
|
|
571
|
+
##
|
572
|
+
## Search and process the build note
|
573
|
+
##
|
448
574
|
def process
|
449
575
|
output = []
|
450
576
|
|
@@ -537,10 +663,10 @@ module Howzit
|
|
537
663
|
|
538
664
|
if !topic_matches.empty?
|
539
665
|
# If we found a match
|
540
|
-
topic_matches.each { |topic_match| output.push(process_topic(topic_match, Howzit.options[:run], true)) }
|
666
|
+
topic_matches.each { |topic_match| output.push(process_topic(topic_match, Howzit.options[:run], single: true)) }
|
541
667
|
else
|
542
668
|
# If there's no argument or no match found, output all
|
543
|
-
topics.each { |k| output.push(process_topic(k, false, false)) }
|
669
|
+
topics.each { |k| output.push(process_topic(k, false, single: false)) }
|
544
670
|
end
|
545
671
|
Howzit.options[:paginate] = false if Howzit.options[:run]
|
546
672
|
Util.show(output.join("\n").strip, Howzit.options)
|
data/lib/howzit/colors.rb
CHANGED
@@ -4,7 +4,9 @@
|
|
4
4
|
module Howzit
|
5
5
|
# Terminal output color functions.
|
6
6
|
module Color
|
7
|
+
# Regexp to match excape sequences
|
7
8
|
ESCAPE_REGEX = /(?<=\[)(?:(?:(?:[349]|10)[0-9]|[0-9])?;?)+(?=m)/.freeze
|
9
|
+
|
8
10
|
# All available color names. Available as methods and string extensions.
|
9
11
|
#
|
10
12
|
# @example Use a color as a method. Color reset will be added to end of string.
|
@@ -79,6 +81,7 @@ module Howzit
|
|
79
81
|
[:default, '0;39']
|
80
82
|
].map(&:freeze).freeze
|
81
83
|
|
84
|
+
# Array of attribute keys only
|
82
85
|
ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first
|
83
86
|
|
84
87
|
# Returns true if Howzit::Color supports the +feature+.
|
data/lib/howzit/hash.rb
CHANGED
@@ -5,7 +5,7 @@ class ::Hash
|
|
5
5
|
##
|
6
6
|
## Freeze all values in a hash
|
7
7
|
##
|
8
|
-
## @return Hash with all values frozen
|
8
|
+
## @return [Hash] Hash with all values frozen
|
9
9
|
##
|
10
10
|
def deep_freeze
|
11
11
|
chilled = {}
|
@@ -16,10 +16,18 @@ class ::Hash
|
|
16
16
|
chilled.freeze
|
17
17
|
end
|
18
18
|
|
19
|
+
##
|
20
|
+
## Deep freeze a hash in place (destructive)
|
21
|
+
##
|
19
22
|
def deep_freeze!
|
20
23
|
replace deep_thaw.deep_freeze
|
21
24
|
end
|
22
25
|
|
26
|
+
##
|
27
|
+
## Unfreeze nested hash values
|
28
|
+
##
|
29
|
+
## @return [Hash] Hash with all values unfrozen
|
30
|
+
##
|
23
31
|
def deep_thaw
|
24
32
|
chilled = {}
|
25
33
|
each do |k, v|
|
@@ -29,27 +37,40 @@ class ::Hash
|
|
29
37
|
chilled.dup
|
30
38
|
end
|
31
39
|
|
40
|
+
##
|
41
|
+
## Unfreeze nested hash values in place (destructive)
|
42
|
+
##
|
32
43
|
def deep_thaw!
|
33
44
|
replace deep_thaw
|
34
45
|
end
|
35
46
|
|
36
47
|
# Turn all keys into string
|
37
48
|
#
|
38
|
-
#
|
49
|
+
# @return [Hash] hash with all keys as strings
|
50
|
+
#
|
39
51
|
def stringify_keys
|
40
52
|
each_with_object({}) { |(k, v), hsh| hsh[k.to_s] = v.is_a?(Hash) ? v.stringify_keys : v }
|
41
53
|
end
|
42
54
|
|
55
|
+
##
|
56
|
+
## Turn all keys into strings in place (destructive)
|
57
|
+
##
|
43
58
|
def stringify_keys!
|
44
|
-
|
59
|
+
replace stringify_keys
|
45
60
|
end
|
46
61
|
|
47
62
|
# Turn all keys into symbols
|
63
|
+
#
|
64
|
+
# @return [Hash] hash with all keys as symbols
|
65
|
+
#
|
48
66
|
def symbolize_keys
|
49
67
|
each_with_object({}) { |(k, v), hsh| hsh[k.to_sym] = v.is_a?(Hash) ? v.symbolize_keys : v }
|
50
68
|
end
|
51
69
|
|
70
|
+
##
|
71
|
+
## Turn all keys into symbols in place (destructive)
|
72
|
+
##
|
52
73
|
def symbolize_keys!
|
53
|
-
|
74
|
+
replace symbolize_keys
|
54
75
|
end
|
55
76
|
end
|
data/lib/howzit/prompt.rb
CHANGED
@@ -4,10 +4,22 @@ module Howzit
|
|
4
4
|
# Command line prompt utils
|
5
5
|
module Prompt
|
6
6
|
class << self
|
7
|
-
|
7
|
+
|
8
|
+
##
|
9
|
+
## Display and read a Yes/No prompt
|
10
|
+
##
|
11
|
+
## @param prompt [String] The prompt string
|
12
|
+
## @param default [Boolean] default value if
|
13
|
+
## return is pressed or prompt is
|
14
|
+
## skipped
|
15
|
+
##
|
16
|
+
## @return [Boolean] result
|
17
|
+
##
|
18
|
+
def yn(prompt, default: true)
|
8
19
|
return default unless $stdout.isatty
|
9
20
|
|
10
21
|
return default if Howzit.options[:default]
|
22
|
+
|
11
23
|
tty_state = `stty -g`
|
12
24
|
system 'stty raw -echo cbreak isig'
|
13
25
|
yn = color_single_options(default ? %w[Y n] : %w[y N])
|
@@ -20,6 +32,14 @@ module Howzit
|
|
20
32
|
res.empty? ? default : res =~ /y/i
|
21
33
|
end
|
22
34
|
|
35
|
+
##
|
36
|
+
## Helper function to colorize the Y/N prompt
|
37
|
+
##
|
38
|
+
## @param choices [Array] The choices with
|
39
|
+
## default capitalized
|
40
|
+
##
|
41
|
+
## @return [String] colorized string
|
42
|
+
##
|
23
43
|
def color_single_options(choices = %w[y n])
|
24
44
|
out = []
|
25
45
|
choices.each do |choice|
|
@@ -33,6 +53,12 @@ module Howzit
|
|
33
53
|
Color.template("{xg}[#{out.join('/')}{xg}]{x}")
|
34
54
|
end
|
35
55
|
|
56
|
+
##
|
57
|
+
## Create a numbered list of options. Outputs directly
|
58
|
+
## to console, returns nothing
|
59
|
+
##
|
60
|
+
## @param matches [Array] The list items
|
61
|
+
##
|
36
62
|
def options_list(matches)
|
37
63
|
counter = 1
|
38
64
|
puts
|
@@ -43,6 +69,15 @@ module Howzit
|
|
43
69
|
puts
|
44
70
|
end
|
45
71
|
|
72
|
+
##
|
73
|
+
## Choose from a list of items. If fzf is available,
|
74
|
+
## uses that, otherwise generates its own list of
|
75
|
+
## options and accepts a numeric response
|
76
|
+
##
|
77
|
+
## @param matches [Array] The options list
|
78
|
+
##
|
79
|
+
## @return [Array] the selected results
|
80
|
+
##
|
46
81
|
def choose(matches)
|
47
82
|
if Util.command_exist?('fzf')
|
48
83
|
settings = [
|
@@ -80,7 +115,7 @@ module Howzit
|
|
80
115
|
end
|
81
116
|
line = line == '' ? 1 : line.to_i
|
82
117
|
|
83
|
-
return matches[line - 1] if line.positive? && line <= matches.length
|
118
|
+
return [matches[line - 1]] if line.positive? && line <= matches.length
|
84
119
|
|
85
120
|
puts 'Out of range'
|
86
121
|
options_list(matches)
|