howzit 2.0.14 → 2.0.17
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 +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?)?#/, '')
|