howzit 2.0.14 → 2.0.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +33 -0
- data/bin/howzit +8 -2
- data/lib/howzit/buildnote.rb +272 -155
- data/lib/howzit/colors.rb +9 -0
- data/lib/howzit/config.rb +1 -0
- data/lib/howzit/prompt.rb +20 -4
- data/lib/howzit/stringutils.rb +18 -2
- data/lib/howzit/task.rb +161 -5
- data/lib/howzit/topic.rb +6 -96
- data/lib/howzit/util.rb +8 -3
- data/lib/howzit/version.rb +1 -1
- data/lib/howzit.rb +12 -0
- data/spec/prompt_spec.rb +37 -0
- data/spec/spec_helper.rb +7 -9
- data/spec/task_spec.rb +7 -2
- data/spec/topic_spec.rb +45 -15
- data/spec/util_spec.rb +52 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa788ff480288582a4473e2bb2fa9388afc98165c9c78b2c90a1bb9b964d7919
|
4
|
+
data.tar.gz: e3012f8ed229832f7cefa29b1ff9a4d971858886c8c9778ec7fe6a3b74faa181
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 893cc81a4db7d7297f8a3d195f6a02bec4a0e419c56ca6a76e97bfbbe49636ef6e6162457f83f914422bb42d5906df8f38a375309d9d5a1b6844d57d488c2e55
|
7
|
+
data.tar.gz: e286bd61f5d4d4dd9632691c3486a9daa51701aac8c4facd3f5a62d01e871ec0cd06d7ff4ac1f19c0f9ff7d24d6ef1d3b9f812d68aa6d62877c99b5bfed7b261
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,36 @@
|
|
1
|
+
### 2.0.17
|
2
|
+
|
3
|
+
2022-08-07 06:09
|
4
|
+
|
5
|
+
### 2.0.16
|
6
|
+
|
7
|
+
2022-08-07 06:05
|
8
|
+
|
9
|
+
#### NEW
|
10
|
+
|
11
|
+
- --edit-template NAME will open a template in your editor, offering to create it if missing
|
12
|
+
|
13
|
+
#### FIXED
|
14
|
+
|
15
|
+
- If --show-code is given (or :show_all_code: is set to true), show content of @directives instead of title
|
16
|
+
|
17
|
+
### 2.0.15
|
18
|
+
|
19
|
+
2022-08-05 17:16
|
20
|
+
|
21
|
+
#### IMPROVED
|
22
|
+
|
23
|
+
- -R can take an argument to filter results
|
24
|
+
- Show a topic preview when using fzf to select from available topics
|
25
|
+
- Paginate help output
|
26
|
+
- Refactor Topic.run
|
27
|
+
|
28
|
+
#### FIXED
|
29
|
+
|
30
|
+
- Invalid options for more pager
|
31
|
+
- Error running grep command
|
32
|
+
- Show method not accepting paginate option
|
33
|
+
|
1
34
|
### 2.0.14
|
2
35
|
|
3
36
|
2022-08-05 13:24
|
data/bin/howzit
CHANGED
@@ -49,7 +49,8 @@ OptionParser.new do |opts|
|
|
49
49
|
Howzit.options[:list_topics] = true
|
50
50
|
end
|
51
51
|
|
52
|
-
opts.on('-R', '--list-runnable', 'List topics containing @ directives (verbose)') do
|
52
|
+
opts.on('-R', '--list-runnable [PATTERN]', 'List topics containing @ directives (verbose)') do |pat|
|
53
|
+
Howzit.options[:for_topic] = pat
|
53
54
|
Howzit.options[:list_runnable] = true
|
54
55
|
end
|
55
56
|
|
@@ -93,6 +94,11 @@ OptionParser.new do |opts|
|
|
93
94
|
Process.exit 0
|
94
95
|
end
|
95
96
|
|
97
|
+
opts.on('--edit-template NAME', 'Create or edit a template') do |template|
|
98
|
+
Howzit.buildnote.edit_template(template)
|
99
|
+
Process.exit 0
|
100
|
+
end
|
101
|
+
|
96
102
|
opts.on('--config-get [KEY]', 'Display the configuration settings or setting for a specific key') do |k|
|
97
103
|
if k.nil?
|
98
104
|
Howzit::Config::DEFAULTS.sort_by { |key, _| key }.each do |key, _|
|
@@ -201,7 +207,7 @@ OptionParser.new do |opts|
|
|
201
207
|
opts.separator("\n Misc:\n\n") #=================================================================== MISC
|
202
208
|
|
203
209
|
opts.on('-h', '--help', 'Display this screen') do
|
204
|
-
|
210
|
+
Howzit::Util.page opts.to_s
|
205
211
|
Process.exit 0
|
206
212
|
end
|
207
213
|
|
data/lib/howzit/buildnote.rb
CHANGED
@@ -11,27 +11,24 @@ module Howzit
|
|
11
11
|
## Initialize a build note
|
12
12
|
##
|
13
13
|
## @param file [String] The path to the build note file
|
14
|
-
## @param args [Array] additional args
|
15
14
|
##
|
16
|
-
def initialize(file: nil
|
15
|
+
def initialize(file: nil)
|
17
16
|
@topics = []
|
18
|
-
if note_file.nil?
|
19
|
-
res = Prompt.yn('No build notes file found, create one?', default: true)
|
17
|
+
create_note(prompt: true) if note_file.nil?
|
20
18
|
|
21
|
-
create_note if res
|
22
|
-
Process.exit 0
|
23
|
-
end
|
24
19
|
content = Util.read_file(note_file)
|
25
|
-
if content.nil? || content.empty?
|
26
|
-
|
27
|
-
|
28
|
-
else
|
29
|
-
@metadata = content.split(/^#/)[0].strip.get_metadata
|
30
|
-
end
|
20
|
+
raise "{br}No content found in build note (#{note_file}){x}".c if content.nil? || content.empty?
|
21
|
+
|
22
|
+
@metadata = content.split(/^#/)[0].strip.get_metadata
|
31
23
|
|
32
24
|
read_help(file)
|
33
25
|
end
|
34
26
|
|
27
|
+
##
|
28
|
+
## Inspect
|
29
|
+
##
|
30
|
+
## @return description
|
31
|
+
##
|
35
32
|
def inspect
|
36
33
|
puts "#<Howzit::BuildNote @topics=[#{@topics.count}]>"
|
37
34
|
end
|
@@ -50,12 +47,25 @@ module Howzit
|
|
50
47
|
edit_note
|
51
48
|
end
|
52
49
|
|
50
|
+
##
|
51
|
+
## Public method to open a template in the editor
|
52
|
+
##
|
53
|
+
## @param template [String] The template title
|
54
|
+
##
|
55
|
+
def edit_template(template)
|
56
|
+
file = template.sub(/(\.md)?$/i, '.md')
|
57
|
+
file = File.join(Howzit.config.template_folder, file)
|
58
|
+
edit_template_file(file)
|
59
|
+
end
|
60
|
+
|
53
61
|
##
|
54
62
|
## Find a topic based on a fuzzy match
|
55
63
|
##
|
56
64
|
## @param term [String] The search term
|
57
65
|
##
|
58
|
-
def find_topic(term)
|
66
|
+
def find_topic(term = nil)
|
67
|
+
return @topics if term.nil?
|
68
|
+
|
59
69
|
@topics.filter do |topic|
|
60
70
|
rx = term.to_rx
|
61
71
|
topic.title.downcase =~ rx
|
@@ -127,18 +137,18 @@ module Howzit
|
|
127
137
|
def list_runnable
|
128
138
|
output = []
|
129
139
|
output.push(%({bg}"Runnable" Topics:{x}\n).c)
|
130
|
-
|
140
|
+
|
141
|
+
find_topic(Howzit.options[:for_topic]).each do |topic|
|
131
142
|
s_out = []
|
132
143
|
|
133
|
-
topic.tasks.each
|
134
|
-
s_out.push(task.to_list)
|
135
|
-
end
|
144
|
+
topic.tasks.each { |task| s_out.push(task.to_list) }
|
136
145
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
146
|
+
next if s_out.empty?
|
147
|
+
|
148
|
+
output.push("- {bw}#{topic.title}{x}".c)
|
149
|
+
output.push(s_out.join("\n"))
|
141
150
|
end
|
151
|
+
|
142
152
|
output.join("\n")
|
143
153
|
end
|
144
154
|
|
@@ -151,22 +161,75 @@ module Howzit
|
|
151
161
|
read_help_file(file)
|
152
162
|
end
|
153
163
|
|
164
|
+
##
|
165
|
+
## Create a template file
|
166
|
+
##
|
167
|
+
## @param file [String] file path
|
168
|
+
## @param prompt [Boolean] confirm file creation?
|
169
|
+
##
|
170
|
+
def create_template_file(file, prompt: false)
|
171
|
+
trap('SIGINT') do
|
172
|
+
Howzit.console.info "\nCancelled"
|
173
|
+
exit!
|
174
|
+
end
|
175
|
+
|
176
|
+
default = !$stdout.isatty || Howzit.options[:default]
|
177
|
+
|
178
|
+
if prompt && !default && !File.exist?(file)
|
179
|
+
res = Prompt.yn("{bg}Template {bw}#{File.basename(file)}{bg} not found, create it?{x}".c, default: true)
|
180
|
+
Process.exit 0 unless res
|
181
|
+
end
|
182
|
+
|
183
|
+
title = File.basename(file, '.md')
|
184
|
+
|
185
|
+
note = <<~EOBUILDNOTES
|
186
|
+
# #{title}
|
187
|
+
|
188
|
+
## Template Topic
|
189
|
+
|
190
|
+
EOBUILDNOTES
|
191
|
+
|
192
|
+
if File.exist?(file) && !default
|
193
|
+
file = "{by}#{file}".c
|
194
|
+
unless Prompt.yn("Are you sure you want to overwrite #{file}", default: false)
|
195
|
+
puts 'Cancelled'
|
196
|
+
Process.exit 0
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
File.open(file, 'w') do |f|
|
201
|
+
f.puts note
|
202
|
+
puts "{by}Template {bw}#{title}{by} written to {bw}#{file}{x}".c
|
203
|
+
end
|
204
|
+
|
205
|
+
if File.exist?(file) && !default && Prompt.yn("{bg}Do you want to open {bw}#{file} {bg}for editing?{x}".c,
|
206
|
+
default: false)
|
207
|
+
edit_template_file(file)
|
208
|
+
end
|
209
|
+
|
210
|
+
Process.exit 0
|
211
|
+
end
|
212
|
+
|
154
213
|
# Create a buildnotes skeleton
|
155
|
-
def create_note
|
214
|
+
def create_note(prompt: false)
|
156
215
|
trap('SIGINT') do
|
157
216
|
Howzit.console.info "\nCancelled"
|
158
217
|
exit!
|
159
218
|
end
|
219
|
+
|
160
220
|
default = !$stdout.isatty || Howzit.options[:default]
|
221
|
+
|
222
|
+
if prompt && !default
|
223
|
+
res = Prompt.yn('No build notes file found, create one?', default: true)
|
224
|
+
Process.exit 0 unless res
|
225
|
+
end
|
226
|
+
|
161
227
|
# First make sure there isn't already a buildnotes file
|
162
228
|
if note_file
|
163
229
|
fname = "{by}#{note_file}{bw}".c
|
164
230
|
unless default
|
165
231
|
res = Prompt.yn("#{fname} exists and appears to be a build note, continue anyway?", default: false)
|
166
|
-
unless res
|
167
|
-
puts 'Canceled'
|
168
|
-
Process.exit 0
|
169
|
-
end
|
232
|
+
Process.exit 0 unless res
|
170
233
|
end
|
171
234
|
end
|
172
235
|
|
@@ -221,9 +284,7 @@ module Howzit
|
|
221
284
|
|
222
285
|
if File.exist?(fname) && !default
|
223
286
|
file = "{by}#{fname}".c
|
224
|
-
|
225
|
-
|
226
|
-
unless res
|
287
|
+
unless Prompt.yn("Are you absolutely sure you want to overwrite #{file}", default: false)
|
227
288
|
puts 'Canceled'
|
228
289
|
Process.exit 0
|
229
290
|
end
|
@@ -231,24 +292,161 @@ module Howzit
|
|
231
292
|
|
232
293
|
File.open(fname, 'w') do |f|
|
233
294
|
f.puts note
|
234
|
-
puts "{by}Build notes for #{title} written to #{fname}".c
|
295
|
+
puts "{by}Build notes for {bw}#{title}{by} written to {bw}#{fname}{x}".c
|
235
296
|
end
|
236
297
|
|
237
|
-
if File.exist?(fname) && !default
|
238
|
-
|
239
|
-
|
240
|
-
edit_note if res
|
298
|
+
if File.exist?(fname) && !default && Prompt.yn("{bg}Do you want to open {bw}#{fname} {bg}for editing?{x}".c,
|
299
|
+
default: false)
|
300
|
+
edit_note
|
241
301
|
end
|
242
302
|
|
243
303
|
Process.exit 0
|
244
304
|
end
|
245
305
|
|
306
|
+
##
|
307
|
+
## Accessor method for note_file (path to located build note)
|
308
|
+
##
|
309
|
+
## @return [String] path
|
310
|
+
##
|
246
311
|
def note_file
|
247
312
|
@note_file ||= find_note_file
|
248
313
|
end
|
249
314
|
|
250
315
|
private
|
251
316
|
|
317
|
+
##
|
318
|
+
## Import the contents of a filename as new topics
|
319
|
+
##
|
320
|
+
## @param mtch [MatchData] the filename match from
|
321
|
+
## the include directive
|
322
|
+
##
|
323
|
+
def include_file(mtch)
|
324
|
+
file = File.expand_path(mtch[1])
|
325
|
+
|
326
|
+
return mtch[0] unless File.exist?(file)
|
327
|
+
|
328
|
+
content = Util.read_file(file)
|
329
|
+
home = ENV['HOME']
|
330
|
+
short_path = File.dirname(file.sub(/^#{home}/, '~'))
|
331
|
+
prefix = "#{short_path}/#{File.basename(file)}:"
|
332
|
+
parts = content.split(/^##+/)
|
333
|
+
parts.shift
|
334
|
+
if parts.empty?
|
335
|
+
content
|
336
|
+
else
|
337
|
+
"## #{parts.join('## ')}".gsub(/^(##+ *)(?=\S)/, "\\1#{prefix}")
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
##
|
342
|
+
## Test to ensure that any `required` metadata in a
|
343
|
+
## template is fulfilled by the build note
|
344
|
+
##
|
345
|
+
## @param template [String] The template to read
|
346
|
+
## from
|
347
|
+
##
|
348
|
+
def ensure_requirements(template)
|
349
|
+
t_leader = Util.read_file(template).split(/^#/)[0].strip
|
350
|
+
if t_leader.length > 0
|
351
|
+
t_meta = t_leader.get_metadata
|
352
|
+
if t_meta.key?('required')
|
353
|
+
required = t_meta['required'].strip.split(/\s*,\s*/)
|
354
|
+
required.each do |req|
|
355
|
+
unless @metadata.keys.include?(req.downcase)
|
356
|
+
Howzit.console.error %({bRw}ERROR:{xbr} Missing required metadata key from template '{bw}#{File.basename(template, '.md')}{xr}'{x}).c
|
357
|
+
Howzit.console.error %({br}Please define {by}#{req.downcase}{xr} in build notes{x}).c
|
358
|
+
Process.exit 1
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
##
|
366
|
+
## Test a template string for bracketed subtopics
|
367
|
+
##
|
368
|
+
## @param template [String] The template name
|
369
|
+
##
|
370
|
+
## @return [Array] [[String] updated template name, [Array]
|
371
|
+
## subtopic titles]
|
372
|
+
##
|
373
|
+
def detect_subtopics(template)
|
374
|
+
subtopics = nil
|
375
|
+
|
376
|
+
if template =~ /\[(.*?)\]$/
|
377
|
+
subtopics = Regexp.last_match[1].split(/\s*\|\s*/).map { |t| t.gsub(/\*/, '.*?')}
|
378
|
+
template.sub!(/\[.*?\]$/, '').strip
|
379
|
+
end
|
380
|
+
|
381
|
+
[template, subtopics]
|
382
|
+
end
|
383
|
+
|
384
|
+
##
|
385
|
+
## Enumerate templates and read their associated files
|
386
|
+
## into topics
|
387
|
+
##
|
388
|
+
## @param templates [Array] The templates to read
|
389
|
+
##
|
390
|
+
## @return [Array] template topics
|
391
|
+
##
|
392
|
+
def gather_templates(templates)
|
393
|
+
template_topics = []
|
394
|
+
|
395
|
+
templates.each do |template|
|
396
|
+
template, subtopics = detect_subtopics(template)
|
397
|
+
|
398
|
+
file = template.sub(/(\.md)?$/i, '.md')
|
399
|
+
file = File.join(Howzit.config.template_folder, file)
|
400
|
+
|
401
|
+
next unless File.exist?(file)
|
402
|
+
|
403
|
+
ensure_requirements(file)
|
404
|
+
|
405
|
+
template_topics.concat(read_template(template, file, subtopics))
|
406
|
+
end
|
407
|
+
|
408
|
+
template_topics
|
409
|
+
end
|
410
|
+
|
411
|
+
##
|
412
|
+
## Filter topics based on subtopic titles
|
413
|
+
##
|
414
|
+
## @param note [BuildNote] The note
|
415
|
+
## @param subtopics [Array] The subtopics to
|
416
|
+
## extract
|
417
|
+
##
|
418
|
+
## @return [Array] extracted subtopics
|
419
|
+
##
|
420
|
+
def extract_subtopics(note, subtopics)
|
421
|
+
template_topics = []
|
422
|
+
|
423
|
+
subtopics.each do |subtopic|
|
424
|
+
note.topics.each { |topic| template_topics.push(topic) if topic.title =~ /^(.*?:)?#{subtopic}$/i }
|
425
|
+
end
|
426
|
+
|
427
|
+
template_topics
|
428
|
+
end
|
429
|
+
|
430
|
+
##
|
431
|
+
## Read a template file
|
432
|
+
##
|
433
|
+
## @param template [String] The template title
|
434
|
+
## @param file [String] The file path
|
435
|
+
## @param subtopics [Array] The subtopics to
|
436
|
+
## extract, nil to return all
|
437
|
+
##
|
438
|
+
## @return [Array] extracted topics
|
439
|
+
##
|
440
|
+
def read_template(template, file, subtopics = nil)
|
441
|
+
note = BuildNote.new(file: file)
|
442
|
+
|
443
|
+
template_topics = subtopics.nil? ? note.topics : extract_subtopics(note, subtopics)
|
444
|
+
template_topics.map do |topic|
|
445
|
+
topic.parent = template
|
446
|
+
topic
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
252
450
|
##
|
253
451
|
## Traverse up directory tree looking for build notes
|
254
452
|
##
|
@@ -329,30 +527,6 @@ module Howzit
|
|
329
527
|
topics_dict
|
330
528
|
end
|
331
529
|
|
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
|
-
##
|
339
|
-
def ensure_requirements(template)
|
340
|
-
t_leader = Util.read_file(template).split(/^#/)[0].strip
|
341
|
-
if t_leader.length > 0
|
342
|
-
t_meta = t_leader.get_metadata
|
343
|
-
if t_meta.key?('required')
|
344
|
-
required = t_meta['required'].strip.split(/\s*,\s*/)
|
345
|
-
required.each do |req|
|
346
|
-
unless @metadata.keys.include?(req.downcase)
|
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
|
349
|
-
Process.exit 1
|
350
|
-
end
|
351
|
-
end
|
352
|
-
end
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
530
|
##
|
357
531
|
## Read a list of topics from an included template
|
358
532
|
##
|
@@ -363,87 +537,17 @@ module Howzit
|
|
363
537
|
|
364
538
|
template_topics = []
|
365
539
|
|
366
|
-
if leader.
|
367
|
-
data = leader.get_metadata
|
540
|
+
return template_topics if leader.empty?
|
368
541
|
|
369
|
-
|
370
|
-
templates = data['template'].strip.split(/\s*,\s*/)
|
371
|
-
templates.each do |t|
|
372
|
-
tasks = nil
|
373
|
-
if t =~ /\[(.*?)\]$/
|
374
|
-
tasks = Regexp.last_match[1].split(/\s*,\s*/).map {|t| t.gsub(/\*/, '.*?')}
|
375
|
-
t = t.sub(/\[.*?\]$/, '').strip
|
376
|
-
end
|
542
|
+
data = leader.get_metadata
|
377
543
|
|
378
|
-
|
379
|
-
|
380
|
-
if File.exist?(template)
|
381
|
-
ensure_requirements(template)
|
382
|
-
|
383
|
-
t_topics = BuildNote.new(file: template)
|
384
|
-
if tasks
|
385
|
-
tasks.each do |task|
|
386
|
-
t_topics.topics.each do |topic|
|
387
|
-
if topic.title =~ /^(.*?:)?#{task}$/i
|
388
|
-
topic.parent = t
|
389
|
-
template_topics.push(topic)
|
390
|
-
end
|
391
|
-
end
|
392
|
-
end
|
393
|
-
else
|
394
|
-
t_topics.topics.map! do |topic|
|
395
|
-
topic.parent = t
|
396
|
-
topic
|
397
|
-
end
|
398
|
-
|
399
|
-
template_topics.concat(t_topics.topics)
|
400
|
-
end
|
401
|
-
end
|
402
|
-
end
|
403
|
-
end
|
404
|
-
end
|
405
|
-
template_topics
|
406
|
-
end
|
544
|
+
if data.key?('template')
|
545
|
+
templates = data['template'].strip.split(/\s*,\s*/)
|
407
546
|
|
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])
|
416
|
-
|
417
|
-
return mtch[0] unless File.exist?(file)
|
418
|
-
|
419
|
-
content = Util.read_file(file)
|
420
|
-
home = ENV['HOME']
|
421
|
-
short_path = File.dirname(file.sub(/^#{home}/, '~'))
|
422
|
-
prefix = "#{short_path}/#{File.basename(file)}:"
|
423
|
-
parts = content.split(/^##+/)
|
424
|
-
parts.shift
|
425
|
-
if parts.empty?
|
426
|
-
content
|
427
|
-
else
|
428
|
-
"## #{parts.join('## ')}".gsub(/^(##+ *)(?=\S)/, "\\1#{prefix}")
|
547
|
+
template_topics.concat(gather_templates(templates))
|
429
548
|
end
|
430
|
-
end
|
431
|
-
|
432
|
-
##
|
433
|
-
## Get the title of the build note (top level header)
|
434
|
-
##
|
435
|
-
## @param truncate [Integer] Truncate to width
|
436
|
-
##
|
437
|
-
def note_title(truncate = 0)
|
438
|
-
help = Util.read_file(note_file)
|
439
|
-
title = help.match(/(?:^(\S.*?)(?=\n==)|^# ?(.*?)$)/)
|
440
|
-
title = if title
|
441
|
-
title[1].nil? ? title[2] : title[1]
|
442
|
-
else
|
443
|
-
note_file.sub(/(\.\w+)?$/, '')
|
444
|
-
end
|
445
549
|
|
446
|
-
|
550
|
+
template_topics
|
447
551
|
end
|
448
552
|
|
449
553
|
# Read in the build notes file and output a hash of
|
@@ -465,7 +569,7 @@ module Howzit
|
|
465
569
|
Process.exit 1
|
466
570
|
end
|
467
571
|
|
468
|
-
@title = note_title
|
572
|
+
@title = help.note_title(filename)
|
469
573
|
|
470
574
|
help.gsub!(/@include\((.*?)\)/) do
|
471
575
|
include_file(Regexp.last_match)
|
@@ -535,14 +639,33 @@ module Howzit
|
|
535
639
|
|
536
640
|
raise "Invalid editor (#{editor})" unless Util.valid_command?(editor)
|
537
641
|
|
538
|
-
if note_file.nil?
|
539
|
-
|
642
|
+
create_note(prompt: true) if note_file.nil?
|
643
|
+
`#{editor} "#{note_file}"`
|
644
|
+
end
|
540
645
|
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
646
|
+
##
|
647
|
+
## Public method to create a new template
|
648
|
+
##
|
649
|
+
## @param template [String] The template name
|
650
|
+
##
|
651
|
+
def create_template(template)
|
652
|
+
file = template.sub(/(\.md)?$/i, '.md')
|
653
|
+
file = File.join(Howzit.config.template_folder, file)
|
654
|
+
create_template_file(file, prompt: false)
|
655
|
+
end
|
656
|
+
|
657
|
+
##
|
658
|
+
## Open template in editor
|
659
|
+
##
|
660
|
+
def edit_template_file(file)
|
661
|
+
editor = Howzit.options.fetch(:editor, ENV['EDITOR'])
|
662
|
+
|
663
|
+
raise 'No editor defined' if editor.nil?
|
664
|
+
|
665
|
+
raise "Invalid editor (#{editor})" unless Util.valid_command?(editor)
|
666
|
+
|
667
|
+
create_template_file(file, prompt: true) unless File.exist?(file)
|
668
|
+
`#{editor} "#{file}"`
|
546
669
|
end
|
547
670
|
|
548
671
|
##
|
@@ -577,17 +700,11 @@ module Howzit
|
|
577
700
|
unless note_file
|
578
701
|
Process.exit 0 if Howzit.options[:list_runnable_titles] || Howzit.options[:list_topic_titles]
|
579
702
|
|
580
|
-
|
581
|
-
ARGV.length.times do
|
582
|
-
ARGV.shift
|
583
|
-
end
|
584
|
-
res = yn("No build notes file found, create one?", true)
|
585
|
-
create_note if res
|
586
|
-
Process.exit 1
|
703
|
+
create_note(prompt: true)
|
587
704
|
end
|
588
705
|
|
589
706
|
if Howzit.options[:title_only]
|
590
|
-
out = note_title(20)
|
707
|
+
out = Util.read_file(note_file).note_title(note_file, 20)
|
591
708
|
$stdout.print(out.strip)
|
592
709
|
Process.exit(0)
|
593
710
|
elsif Howzit.options[:output_title] && !Howzit.options[:run]
|
@@ -620,15 +737,15 @@ module Howzit
|
|
620
737
|
|
621
738
|
topic_matches = []
|
622
739
|
if Howzit.options[:grep]
|
623
|
-
matches =
|
740
|
+
matches = grep(Howzit.options[:grep])
|
624
741
|
case Howzit.options[:multiple_matches]
|
625
742
|
when :all
|
626
|
-
topic_matches.concat(matches.
|
743
|
+
topic_matches.concat(matches.sort_by(&:title))
|
627
744
|
else
|
628
|
-
topic_matches.concat(Prompt.choose(matches))
|
745
|
+
topic_matches.concat(Prompt.choose(matches.map(&:title), height: :max))
|
629
746
|
end
|
630
747
|
elsif Howzit.options[:choose]
|
631
|
-
titles = Prompt.choose(list_topics)
|
748
|
+
titles = Prompt.choose(list_topics, height: :max)
|
632
749
|
titles.each { |title| topic_matches.push(find_topic(title)[0]) }
|
633
750
|
# If there are arguments use those to search for a matching topic
|
634
751
|
elsif !Howzit.cli_args.empty?
|
data/lib/howzit/colors.rb
CHANGED
@@ -243,6 +243,7 @@ module Howzit
|
|
243
243
|
|
244
244
|
ATTRIBUTES.each do |c, v|
|
245
245
|
new_method = <<-EOSCRIPT
|
246
|
+
# Color string as #{c}
|
246
247
|
def #{c}(string = nil)
|
247
248
|
result = ''
|
248
249
|
result << "\e[#{v}m" if Howzit::Color.coloring?
|
@@ -266,6 +267,7 @@ module Howzit
|
|
266
267
|
|
267
268
|
# Accept brightwhite in addition to boldwhite
|
268
269
|
new_method = <<-EOSCRIPT
|
270
|
+
# color string as #{c}
|
269
271
|
def #{c.to_s.sub(/bold/, 'bright')}(string = nil)
|
270
272
|
result = ''
|
271
273
|
result << "\e[#{v}m" if Howzit::Color.coloring?
|
@@ -286,6 +288,13 @@ module Howzit
|
|
286
288
|
module_eval(new_method)
|
287
289
|
end
|
288
290
|
|
291
|
+
##
|
292
|
+
## Generate escape codes for hex colors
|
293
|
+
##
|
294
|
+
## @param hex [String] The hexadecimal color code
|
295
|
+
##
|
296
|
+
## @return [String] ANSI escape string
|
297
|
+
##
|
289
298
|
def rgb(hex)
|
290
299
|
is_bg = hex.match(/^bg?#/) ? true : false
|
291
300
|
hex_string = hex.sub(/^([fb]g?)?#/, '')
|