howzit 1.2.11 → 1.2.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/.gitignore +1 -0
- data/CHANGELOG.md +9 -0
- data/Rakefile +11 -0
- data/bin/howzit +1 -2
- data/lib/howzit/buildnotes.rb +85 -79
- data/lib/howzit/colors.rb +323 -0
- data/lib/howzit/version.rb +1 -1
- data/lib/howzit.rb +1 -0
- data/spec/ruby_gem_spec.rb +33 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 875d039552876588bf298e4297ffec3955a6a73da6c2f89f608f7d11ed9d4e93
|
4
|
+
data.tar.gz: ecd7f0dd8f67999de4e223745f79096cc06ebd84e867400bc729f2323e8f8360
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfcad0fc1a4fbf0140dc91ae5cf03e52c3d42aed4a404c91fab642db05d9849b92c0c73d4f87d79ca0b58d1583a6a0ab5ef0ef9936812913567aeb3f24508a93
|
7
|
+
data.tar.gz: db44b03b2f2c25c84187692a9d46a328a22d4a7dce0adc20304ab31fe58e91b6ca4bef6848ab69efcce5e0c60e9c8955501f4232d5643f664d35cb81da9bcbdf
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
### 1.2.12
|
2
|
+
|
3
|
+
2022-08-01 16:23
|
4
|
+
|
5
|
+
#### IMPROVED
|
6
|
+
|
7
|
+
- Replace ANSI escape codes with color template system
|
8
|
+
- When @including an external file, if the file doesn't contain any level 2+ headers, import it as plain text.
|
9
|
+
|
1
10
|
### 1.2.11
|
2
11
|
|
3
12
|
2022-08-01 08:23
|
data/Rakefile
CHANGED
@@ -18,3 +18,14 @@ RuboCop::RakeTask.new do |t|
|
|
18
18
|
end
|
19
19
|
|
20
20
|
YARD::Rake::YardocTask.new
|
21
|
+
|
22
|
+
desc 'Development version check'
|
23
|
+
task :ver do
|
24
|
+
gver = `git ver`
|
25
|
+
cver = IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
|
26
|
+
res = `grep VERSION lib/howzit/version.rb`
|
27
|
+
version = res.match(/VERSION *= *['"](\d+\.\d+\.\d+(\w+)?)/)[1]
|
28
|
+
puts "git tag: #{gver}"
|
29
|
+
puts "version.rb: #{version}"
|
30
|
+
puts "changelog: #{cver}"
|
31
|
+
end
|
data/bin/howzit
CHANGED
data/lib/howzit/buildnotes.rb
CHANGED
@@ -2,8 +2,9 @@ module Howzit
|
|
2
2
|
# Primary Class for this module
|
3
3
|
class BuildNotes
|
4
4
|
include Prompt
|
5
|
+
include Color
|
5
6
|
|
6
|
-
attr_accessor :cli_args, :arguments, :metadata
|
7
|
+
attr_accessor :cli_args, :options, :arguments, :metadata
|
7
8
|
|
8
9
|
def topics
|
9
10
|
@topics ||= read_help
|
@@ -40,8 +41,8 @@ module Howzit
|
|
40
41
|
# favoring environment settings
|
41
42
|
def which_pager
|
42
43
|
if @options[:pager] =~ /auto/i
|
43
|
-
pagers = [ENV['
|
44
|
-
'bat', 'less', 'more', '
|
44
|
+
pagers = [ENV['PAGER'], ENV['GIT_PAGER'],
|
45
|
+
'bat', 'less', 'more', 'pager']
|
45
46
|
pagers.delete_if(&:nil?).select!(&:available?)
|
46
47
|
return nil if pagers.empty?
|
47
48
|
|
@@ -145,12 +146,12 @@ module Howzit
|
|
145
146
|
choices.each do |choice|
|
146
147
|
case choice
|
147
148
|
when /[A-Z]/
|
148
|
-
out.push("
|
149
|
+
out.push(Color.template("{bg}#{choice}{xg}"))
|
149
150
|
else
|
150
|
-
out.push(choice)
|
151
|
+
out.push(Color.template("{w}#{choice}"))
|
151
152
|
end
|
152
153
|
end
|
153
|
-
"
|
154
|
+
Color.template("{g}[#{out.join('/')}{g}]{x}")
|
154
155
|
end
|
155
156
|
|
156
157
|
# Create a buildnotes skeleton
|
@@ -161,7 +162,7 @@ module Howzit
|
|
161
162
|
end
|
162
163
|
# First make sure there isn't already a buildnotes file
|
163
164
|
if note_file
|
164
|
-
fname = "
|
165
|
+
fname = Color.template("{by}#{note_file}{bw}")
|
165
166
|
res = yn("#{fname} exists and appears to be a build note, continue anyway?", false)
|
166
167
|
unless res
|
167
168
|
puts 'Canceled'
|
@@ -170,19 +171,19 @@ module Howzit
|
|
170
171
|
end
|
171
172
|
|
172
173
|
title = File.basename(Dir.pwd)
|
173
|
-
printf "
|
174
|
-
input =
|
174
|
+
printf Color.template("{bw}Project name {xg}[#{title}]{bw}: {x}")
|
175
|
+
input = $stdin.gets.chomp
|
175
176
|
title = input unless input.empty?
|
176
177
|
|
177
178
|
summary = ''
|
178
|
-
printf
|
179
|
-
input =
|
179
|
+
printf Color.template('{bw}Project summary: {x}')
|
180
|
+
input = $stdin.gets.chomp
|
180
181
|
summary = input unless input.empty?
|
181
182
|
|
182
|
-
|
183
|
-
printf "
|
184
|
-
input =
|
185
|
-
|
183
|
+
fname = 'buildnotes.md'
|
184
|
+
printf Color.template("{bw}Build notes filename (must begin with 'howzit' or 'build')\n{xg}[#{fname}]{bw}: {x}")
|
185
|
+
input = $stdin.gets.chomp
|
186
|
+
fname = input unless input.empty?
|
186
187
|
|
187
188
|
note = <<~EOBUILDNOTES
|
188
189
|
# #{title}
|
@@ -209,8 +210,8 @@ module Howzit
|
|
209
210
|
|
210
211
|
EOBUILDNOTES
|
211
212
|
|
212
|
-
if File.exist?(
|
213
|
-
file = "
|
213
|
+
if File.exist?(fname)
|
214
|
+
file = Color.template("{by}#{fname}")
|
214
215
|
res = yn("Are you absolutely sure you want to overwrite #{file}", false)
|
215
216
|
|
216
217
|
unless res
|
@@ -219,9 +220,9 @@ module Howzit
|
|
219
220
|
end
|
220
221
|
end
|
221
222
|
|
222
|
-
File.open(
|
223
|
+
File.open(fname, 'w') do |f|
|
223
224
|
f.puts note
|
224
|
-
puts "Build notes for #{title} written to #{
|
225
|
+
puts Color.template("{by}Build notes for #{title} written to #{fname}")
|
225
226
|
end
|
226
227
|
end
|
227
228
|
|
@@ -229,8 +230,8 @@ module Howzit
|
|
229
230
|
def format_header(title, opts = {})
|
230
231
|
options = {
|
231
232
|
hr: "\u{254C}",
|
232
|
-
color: '
|
233
|
-
border: '
|
233
|
+
color: '{bg}',
|
234
|
+
border: '{x}',
|
234
235
|
mark: false
|
235
236
|
}
|
236
237
|
|
@@ -239,29 +240,29 @@ module Howzit
|
|
239
240
|
cols = TTY::Screen.columns
|
240
241
|
|
241
242
|
cols = @options[:wrap] if (@options[:wrap]).positive? && cols > @options[:wrap]
|
242
|
-
title = "
|
243
|
+
title = Color.template("#{options[:border]}#{options[:hr] * 2}( #{options[:color]}#{title}#{options[:border]} )")
|
243
244
|
|
244
245
|
tail = if should_mark_iterm?
|
245
246
|
"#{options[:hr] * (cols - title.uncolor.length - 15)}#{options[:mark] ? iterm_marker : ''}"
|
246
247
|
else
|
247
248
|
options[:hr] * (cols - title.uncolor.length)
|
248
249
|
end
|
249
|
-
"#{title}#{tail}
|
250
|
+
Color.template("#{title}#{tail}{x}")
|
250
251
|
end
|
251
252
|
|
252
253
|
def os_open(command)
|
253
254
|
os = RbConfig::CONFIG['target_os']
|
254
|
-
out = "
|
255
|
+
out = Color.template("{bg}Opening {bw}#{command}")
|
255
256
|
case os
|
256
257
|
when /darwin.*/i
|
257
|
-
warn "#{out} (macOS)
|
258
|
+
warn Color.template("#{out} (macOS){x}") if @options[:log_level] < 2
|
258
259
|
`open #{Shellwords.escape(command)}`
|
259
260
|
when /mingw|mswin/i
|
260
|
-
warn "#{out} (Windows)
|
261
|
+
warn Color.template("#{out} (Windows){x}") if @options[:log_level] < 2
|
261
262
|
`start #{Shellwords.escape(command)}`
|
262
263
|
else
|
263
264
|
if 'xdg-open'.available?
|
264
|
-
warn "#{out} (Linux)
|
265
|
+
warn Color.template("#{out} (Linux){x}") if @options[:log_level] < 2
|
265
266
|
`xdg-open #{Shellwords.escape(command)}`
|
266
267
|
else
|
267
268
|
warn out if @options[:log_level] < 2
|
@@ -300,7 +301,7 @@ module Howzit
|
|
300
301
|
directives.each do |c|
|
301
302
|
if c[0].nil?
|
302
303
|
title = c[3] ? c[3].strip : ''
|
303
|
-
warn "
|
304
|
+
warn Color.template("{bg}Running block {bw}#{title}{x}") if @options[:log_level] < 2
|
304
305
|
block = c[4].strip
|
305
306
|
script = Tempfile.new('howzit_script')
|
306
307
|
begin
|
@@ -322,18 +323,18 @@ module Howzit
|
|
322
323
|
warn "No topic match for @include(#{search})"
|
323
324
|
else
|
324
325
|
if @included.include?(matches[0])
|
325
|
-
warn "
|
326
|
+
warn Color.template("{by}Tasks from {bw}#{matches[0]} already included, skipping{x}") if @options[:log_level] < 2
|
326
327
|
else
|
327
|
-
warn "
|
328
|
+
warn Color.template("{by}Including tasks from {bw}#{matches[0]}{x}") if @options[:log_level] < 2
|
328
329
|
process_topic(matches[0], true)
|
329
|
-
warn "
|
330
|
+
warn Color.template("{by}End include {bw}#{matches[0]}{x}") if @options[:log_level] < 2
|
330
331
|
end
|
331
332
|
end
|
332
333
|
when /run/i
|
333
|
-
warn "
|
334
|
+
warn Color.template("{bg}Running {bw}#{obj}{x}") if @options[:log_level] < 2
|
334
335
|
system(obj)
|
335
336
|
when /copy/i
|
336
|
-
warn "
|
337
|
+
warn Color.template("{bg}Copied {bw}#{obj}{bg} to clipboard{x}") if @options[:log_level] < 2
|
337
338
|
`echo #{Shellwords.escape(obj)}'\\c'|pbcopy`
|
338
339
|
when /open|url/i
|
339
340
|
os_open(obj)
|
@@ -341,7 +342,7 @@ module Howzit
|
|
341
342
|
end
|
342
343
|
end
|
343
344
|
else
|
344
|
-
warn "
|
345
|
+
warn Color.template("{r}--run: No {br}@directive{xr} found in {bw}#{key}{x}")
|
345
346
|
end
|
346
347
|
output.push("Ran #{tasks} #{tasks == 1 ? 'task' : 'tasks'}") if @options[:log_level] < 2
|
347
348
|
|
@@ -371,12 +372,12 @@ module Howzit
|
|
371
372
|
unless matches.empty?
|
372
373
|
if opt[:single]
|
373
374
|
title = "From #{matches[0]}:"
|
374
|
-
color = '
|
375
|
-
rule = '
|
375
|
+
color = '{yK}'
|
376
|
+
rule = '{kK}'
|
376
377
|
else
|
377
378
|
title = "Include #{matches[0]}"
|
378
|
-
color = '
|
379
|
-
rule = '
|
379
|
+
color = '{yK}'
|
380
|
+
rule = '{x}'
|
380
381
|
end
|
381
382
|
output.push(format_header("#{'> ' * @nest_level}#{title}", { color: color, hr: '.', border: rule })) unless @included.include?(matches[0])
|
382
383
|
|
@@ -405,15 +406,15 @@ module Howzit
|
|
405
406
|
when /open|url/
|
406
407
|
"\u{279A}"
|
407
408
|
end
|
408
|
-
output.push("
|
409
|
+
output.push(Color.template("{bmK}#{icon} {bwK}#{obj}{x}"))
|
409
410
|
when /(`{3,})run *(.*?)$/i
|
410
411
|
m = Regexp.last_match
|
411
412
|
desc = m[2].length.positive? ? "Block: #{m[2]}" : 'Code Block'
|
412
|
-
output.push("\
|
413
|
+
output.push(Color.template("{bmK}\u{25B6} {bwK}#{desc}{x}\n```"))
|
413
414
|
when /@@@run *(.*?)$/i
|
414
415
|
m = Regexp.last_match
|
415
416
|
desc = m[1].length.positive? ? "Block: #{m[1]}" : 'Code Block'
|
416
|
-
output.push("\
|
417
|
+
output.push(Color.template("{bmK}\u{25B6} {bwK}#{desc}{x}"))
|
417
418
|
else
|
418
419
|
l.wrap!(@options[:wrap]) if (@options[:wrap]).positive?
|
419
420
|
output.push(l)
|
@@ -444,9 +445,9 @@ module Howzit
|
|
444
445
|
# Output a list of topic titles
|
445
446
|
def list_topics
|
446
447
|
output = []
|
447
|
-
output.push("\
|
448
|
+
output.push(Color.template("{bg}Topics:{x}\n"))
|
448
449
|
topics.each_key do |title|
|
449
|
-
output.push("-
|
450
|
+
output.push(Color.template("- {bw}#{title}{x}"))
|
450
451
|
end
|
451
452
|
output.join("\n")
|
452
453
|
end
|
@@ -456,14 +457,14 @@ module Howzit
|
|
456
457
|
topics.keys.join("\n")
|
457
458
|
end
|
458
459
|
|
459
|
-
def get_note_title(
|
460
|
+
def get_note_title(truncate = 0)
|
460
461
|
title = nil
|
461
|
-
help = IO.read(
|
462
|
+
help = IO.read(note_file).strip
|
462
463
|
title = help.match(/(?:^(\S.*?)(?=\n==)|^# ?(.*?)$)/)
|
463
464
|
title = if title
|
464
465
|
title[1].nil? ? title[2] : title[1]
|
465
466
|
else
|
466
|
-
|
467
|
+
note_file.sub(/(\.\w+)?$/, '')
|
467
468
|
end
|
468
469
|
|
469
470
|
title && truncate.positive? ? title.trunc(truncate) : title
|
@@ -486,7 +487,7 @@ module Howzit
|
|
486
487
|
|
487
488
|
def list_runnable
|
488
489
|
output = []
|
489
|
-
output.push(%(
|
490
|
+
output.push(Color.template(%({bg}"Runnable" Topics:{x}\n)))
|
490
491
|
topics.each do |title, sect|
|
491
492
|
s_out = []
|
492
493
|
lines = sect.split(/\n/)
|
@@ -507,7 +508,7 @@ module Howzit
|
|
507
508
|
end
|
508
509
|
end
|
509
510
|
unless s_out.empty?
|
510
|
-
output.push("-
|
511
|
+
output.push(Color.template("- {bw}#{title}{x}"))
|
511
512
|
output.push(s_out.join("\n"))
|
512
513
|
end
|
513
514
|
end
|
@@ -531,8 +532,8 @@ module Howzit
|
|
531
532
|
required = t_meta['required'].strip.split(/\s*,\s*/)
|
532
533
|
required.each do |req|
|
533
534
|
unless @metadata.keys.include?(req.downcase)
|
534
|
-
warn %(
|
535
|
-
warn %(
|
535
|
+
warn Color.template(%({xr}ERROR: Missing required metadata key from template '{bw}#{File.basename(template, '.md')}{xr}'{x}))
|
536
|
+
warn Color.template(%({xr}Please define {by}#{req.downcase}{xr} in build notes{x}))
|
536
537
|
Process.exit 1
|
537
538
|
end
|
538
539
|
end
|
@@ -581,6 +582,24 @@ module Howzit
|
|
581
582
|
template_topics
|
582
583
|
end
|
583
584
|
|
585
|
+
def include_file(m)
|
586
|
+
file = File.expand_path(m[1])
|
587
|
+
|
588
|
+
return m[0] unless File.exist?(file)
|
589
|
+
|
590
|
+
content = IO.read(file)
|
591
|
+
home = ENV['HOME']
|
592
|
+
short_path = File.dirname(file.sub(/^#{home}/, '~'))
|
593
|
+
prefix = "#{short_path}/#{File.basename(file)}:"
|
594
|
+
parts = content.split(/^##+/)
|
595
|
+
parts.shift
|
596
|
+
if parts.empty?
|
597
|
+
content
|
598
|
+
else
|
599
|
+
"## #{parts.join('## ')}".gsub(/^(##+ *)(?=\S)/, "\\1#{prefix}")
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
584
603
|
# Read in the build notes file and output a hash of "Title" => contents
|
585
604
|
def read_help_file(path = nil)
|
586
605
|
filename = path.nil? ? note_file : path
|
@@ -588,21 +607,7 @@ module Howzit
|
|
588
607
|
help = IO.read(filename)
|
589
608
|
|
590
609
|
help.gsub!(/@include\((.*?)\)/) do
|
591
|
-
|
592
|
-
file = File.expand_path(m[1])
|
593
|
-
if File.exist?(file)
|
594
|
-
content = IO.read(file)
|
595
|
-
home = ENV['HOME']
|
596
|
-
short_path = File.dirname(file.sub(/^#{home}/, '~'))
|
597
|
-
prefix = "#{short_path}:"
|
598
|
-
parts = content.split(/^##+/)
|
599
|
-
parts.shift
|
600
|
-
content = '## ' + parts.join('## ')
|
601
|
-
content.gsub!(/^(##+ *)(?=\S)/, "\\1#{prefix}")
|
602
|
-
content
|
603
|
-
else
|
604
|
-
m[0]
|
605
|
-
end
|
610
|
+
include_file(Regexp.last_match)
|
606
611
|
end
|
607
612
|
|
608
613
|
template_topics = get_template_topics(help)
|
@@ -674,6 +679,7 @@ module Howzit
|
|
674
679
|
end
|
675
680
|
|
676
681
|
def initialize(args = [])
|
682
|
+
Color.coloring = $stdout.isatty
|
677
683
|
flags = {
|
678
684
|
run: false,
|
679
685
|
list_topics: false,
|
@@ -779,8 +785,8 @@ module Howzit
|
|
779
785
|
@options[:log_level] = 0
|
780
786
|
end
|
781
787
|
|
782
|
-
opts.on('-u', '--upstream', 'Traverse up parent directories for additional build notes') do
|
783
|
-
@options[:include_upstream] =
|
788
|
+
opts.on('-u', '--[no-]upstream', 'Traverse up parent directories for additional build notes') do |p|
|
789
|
+
@options[:include_upstream] = p
|
784
790
|
end
|
785
791
|
|
786
792
|
opts.on('--show-code', 'Display the content of fenced run blocks') do
|
@@ -805,21 +811,21 @@ module Howzit
|
|
805
811
|
Dir.chdir(template_folder)
|
806
812
|
Dir.glob('*.md').each do |file|
|
807
813
|
template = File.basename(file, '.md')
|
808
|
-
puts "
|
809
|
-
puts "
|
814
|
+
puts Color.template("{Mk}template:{Yk}#{template}{x}")
|
815
|
+
puts Color.template("{bk}[{bl}tasks{bk}]──────────────────────────────────────┐{x}")
|
810
816
|
metadata = file.extract_metadata
|
811
817
|
topics = read_help_file(file)
|
812
818
|
topics.each_key do |topic|
|
813
|
-
puts "
|
819
|
+
puts Color.template(" {bk}│{bw}-{x} {bcK}#{template}:#{topic.sub(/^.*?:/, '')}{x}")
|
814
820
|
end
|
815
821
|
if metadata.size > 0
|
816
822
|
meta = []
|
817
|
-
meta << metadata['required'].split(/\s*,\s*/).map {|m| "
|
823
|
+
meta << metadata['required'].split(/\s*,\s*/).map {|m| "*{bw}#{m}{xw}" } if metadata.key?('required')
|
818
824
|
meta << metadata['optional'].split(/\s*,\s*/).map {|m| "#{m}" } if metadata.key?('optional')
|
819
|
-
puts "
|
820
|
-
puts "
|
825
|
+
puts Color.template("{bk}[{bl}meta{bk}]───────────────────────────────────────┤{x}")
|
826
|
+
puts Color.template(" {bk}│ {xw}#{meta.join(", ")}{x}")
|
821
827
|
end
|
822
|
-
puts "
|
828
|
+
puts Color.template(" {bk}└───────────────────────────────────────────┘{x}")
|
823
829
|
end
|
824
830
|
Process.exit 0
|
825
831
|
end
|
@@ -1083,13 +1089,13 @@ module Howzit
|
|
1083
1089
|
end
|
1084
1090
|
|
1085
1091
|
if @options[:title_only]
|
1086
|
-
out = get_note_title(
|
1092
|
+
out = get_note_title(20)
|
1087
1093
|
$stdout.print(out.strip)
|
1088
1094
|
Process.exit(0)
|
1089
1095
|
elsif @options[:output_title]
|
1090
|
-
title = get_note_title
|
1096
|
+
title = get_note_title
|
1091
1097
|
if title && !title.empty?
|
1092
|
-
header = format_header(title, { hr: "\u{2550}", color: '
|
1098
|
+
header = format_header(title, { hr: "\u{2550}", color: '{bwK}' })
|
1093
1099
|
output.push("#{header}\n")
|
1094
1100
|
end
|
1095
1101
|
end
|
@@ -1127,8 +1133,8 @@ module Howzit
|
|
1127
1133
|
matches = match_topic(search)
|
1128
1134
|
|
1129
1135
|
if matches.empty?
|
1130
|
-
output.push(%(
|
1131
|
-
|
1136
|
+
output.push(Color.template(%({bR}ERROR:{xr} No topic match found for {bw}#{search}{x}\n)))
|
1137
|
+
unless @options[:show_all_on_error]
|
1132
1138
|
show(output.join("\n"), { color: true, highlight: false, paginate: false, wrap: 0 })
|
1133
1139
|
Process.exit 1
|
1134
1140
|
end
|
@@ -0,0 +1,323 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Cribbed from <https://github.com/flori/term-ansicolor>
|
4
|
+
module Howzit
|
5
|
+
# Terminal output color functions.
|
6
|
+
module Color
|
7
|
+
ESCAPE_REGEX = /(?<=\[)(?:(?:(?:[349]|10)[0-9]|[0-9])?;?)+(?=m)/.freeze
|
8
|
+
# All available color names. Available as methods and string extensions.
|
9
|
+
#
|
10
|
+
# @example Use a color as a method. Color reset will be added to end of string.
|
11
|
+
# Color.yellow('This text is yellow') => "\e[33mThis text is yellow\e[0m"
|
12
|
+
#
|
13
|
+
# @example Use a color as a string extension. Color reset added automatically.
|
14
|
+
# 'This text is green'.green => "\e[1;32mThis text is green\e[0m"
|
15
|
+
#
|
16
|
+
# @example Send a text string as a color
|
17
|
+
# Color.send('red') => "\e[31m"
|
18
|
+
ATTRIBUTES = [
|
19
|
+
[:clear, 0], # String#clear is already used to empty string in Ruby 1.9
|
20
|
+
[:reset, 0], # synonym for :clear
|
21
|
+
[:bold, 1],
|
22
|
+
[:dark, 2],
|
23
|
+
[:italic, 3], # not widely implemented
|
24
|
+
[:underline, 4],
|
25
|
+
[:underscore, 4], # synonym for :underline
|
26
|
+
[:blink, 5],
|
27
|
+
[:rapid_blink, 6], # not widely implemented
|
28
|
+
[:negative, 7], # no reverse because of String#reverse
|
29
|
+
[:concealed, 8],
|
30
|
+
[:strikethrough, 9], # not widely implemented
|
31
|
+
[:strike, 9], # not widely implemented
|
32
|
+
[:black, 30],
|
33
|
+
[:red, 31],
|
34
|
+
[:green, 32],
|
35
|
+
[:yellow, 33],
|
36
|
+
[:blue, 34],
|
37
|
+
[:magenta, 35],
|
38
|
+
[:purple, 35],
|
39
|
+
[:cyan, 36],
|
40
|
+
[:white, 37],
|
41
|
+
[:bgblack, 40],
|
42
|
+
[:bgred, 41],
|
43
|
+
[:bggreen, 42],
|
44
|
+
[:bgyellow, 43],
|
45
|
+
[:bgblue, 44],
|
46
|
+
[:bgmagenta, 45],
|
47
|
+
[:bgpurple, 45],
|
48
|
+
[:bgcyan, 46],
|
49
|
+
[:bgwhite, 47],
|
50
|
+
[:boldblack, 90],
|
51
|
+
[:boldred, 91],
|
52
|
+
[:boldgreen, 92],
|
53
|
+
[:boldyellow, 93],
|
54
|
+
[:boldblue, 94],
|
55
|
+
[:boldmagenta, 95],
|
56
|
+
[:boldpurple, 95],
|
57
|
+
[:boldcyan, 96],
|
58
|
+
[:boldwhite, 97],
|
59
|
+
[:boldbgblack, 100],
|
60
|
+
[:boldbgred, 101],
|
61
|
+
[:boldbggreen, 102],
|
62
|
+
[:boldbgyellow, 103],
|
63
|
+
[:boldbgblue, 104],
|
64
|
+
[:boldbgmagenta, 105],
|
65
|
+
[:boldbgpurple, 105],
|
66
|
+
[:boldbgcyan, 106],
|
67
|
+
[:boldbgwhite, 107],
|
68
|
+
[:softpurple, '0;35;40'],
|
69
|
+
[:hotpants, '7;34;40'],
|
70
|
+
[:knightrider, '7;30;40'],
|
71
|
+
[:flamingo, '7;31;47'],
|
72
|
+
[:yeller, '1;37;43'],
|
73
|
+
[:whiteboard, '1;30;47'],
|
74
|
+
[:chalkboard, '1;37;40'],
|
75
|
+
[:led, '0;32;40'],
|
76
|
+
[:redacted, '0;30;40'],
|
77
|
+
[:alert, '1;31;43'],
|
78
|
+
[:error, '1;37;41'],
|
79
|
+
[:default, '0;39']
|
80
|
+
].map(&:freeze).freeze
|
81
|
+
|
82
|
+
ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first
|
83
|
+
|
84
|
+
# Returns true if Howzit::Color supports the +feature+.
|
85
|
+
#
|
86
|
+
# The feature :clear, that is mixing the clear color attribute into String,
|
87
|
+
# is only supported on ruby implementations, that do *not* already
|
88
|
+
# implement the String#clear method. It's better to use the reset color
|
89
|
+
# attribute instead.
|
90
|
+
def support?(feature)
|
91
|
+
case feature
|
92
|
+
when :clear
|
93
|
+
!String.instance_methods(false).map(&:to_sym).include?(:clear)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Template coloring
|
98
|
+
class ::String
|
99
|
+
##
|
100
|
+
## Extract the longest valid %color name from a string.
|
101
|
+
##
|
102
|
+
## Allows %colors to bleed into other text and still
|
103
|
+
## be recognized, e.g. %greensomething still finds
|
104
|
+
## %green.
|
105
|
+
##
|
106
|
+
## @return [String] a valid color name
|
107
|
+
##
|
108
|
+
def validate_color
|
109
|
+
valid_color = nil
|
110
|
+
compiled = ''
|
111
|
+
normalize_color.split('').each do |char|
|
112
|
+
compiled += char
|
113
|
+
valid_color = compiled if Color.attributes.include?(compiled.to_sym) || compiled =~ /^([fb]g?)?#([a-f0-9]{6})$/i
|
114
|
+
end
|
115
|
+
|
116
|
+
valid_color
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
## Normalize a color name, removing underscores,
|
121
|
+
## replacing "bright" with "bold", and converting
|
122
|
+
## bgbold to boldbg
|
123
|
+
##
|
124
|
+
## @return [String] Normalized color name
|
125
|
+
##
|
126
|
+
def normalize_color
|
127
|
+
gsub(/_/, '').sub(/bright/i, 'bold').sub(/bgbold/, 'boldbg')
|
128
|
+
end
|
129
|
+
|
130
|
+
# Get the calculated ANSI color at the end of the
|
131
|
+
# string
|
132
|
+
#
|
133
|
+
# @return ANSI escape sequence to match color
|
134
|
+
#
|
135
|
+
def last_color_code
|
136
|
+
m = scan(ESCAPE_REGEX)
|
137
|
+
|
138
|
+
em = ['0']
|
139
|
+
fg = nil
|
140
|
+
bg = nil
|
141
|
+
rgbf = nil
|
142
|
+
rgbb = nil
|
143
|
+
|
144
|
+
m.each do |c|
|
145
|
+
case c
|
146
|
+
when '0'
|
147
|
+
em = ['0']
|
148
|
+
fg, bg, rgbf, rgbb = nil
|
149
|
+
when /^[34]8/
|
150
|
+
case c
|
151
|
+
when /^3/
|
152
|
+
fg = nil
|
153
|
+
rgbf = c
|
154
|
+
when /^4/
|
155
|
+
bg = nil
|
156
|
+
rgbb = c
|
157
|
+
end
|
158
|
+
else
|
159
|
+
c.split(/;/).each do |i|
|
160
|
+
x = i.to_i
|
161
|
+
if x <= 9
|
162
|
+
em << x
|
163
|
+
elsif x >= 30 && x <= 39
|
164
|
+
rgbf = nil
|
165
|
+
fg = x
|
166
|
+
elsif x >= 40 && x <= 49
|
167
|
+
rgbb = nil
|
168
|
+
bg = x
|
169
|
+
elsif x >= 90 && x <= 97
|
170
|
+
rgbf = nil
|
171
|
+
fg = x
|
172
|
+
elsif x >= 100 && x <= 107
|
173
|
+
rgbb = nil
|
174
|
+
bg = x
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
escape = "\e[#{em.join(';')}m"
|
181
|
+
escape += "\e[#{rgbb}m" if rgbb
|
182
|
+
escape += "\e[#{rgbf}m" if rgbf
|
183
|
+
escape + "\e[#{[fg, bg].delete_if(&:nil?).join(';')}m"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
class << self
|
188
|
+
# Returns true if the coloring function of this module
|
189
|
+
# is switched on, false otherwise.
|
190
|
+
def coloring?
|
191
|
+
@coloring
|
192
|
+
end
|
193
|
+
|
194
|
+
attr_writer :coloring
|
195
|
+
|
196
|
+
##
|
197
|
+
## Enables colored output
|
198
|
+
##
|
199
|
+
## @example Turn color on or off based on TTY
|
200
|
+
## Howzit::Color.coloring = STDOUT.isatty
|
201
|
+
def coloring
|
202
|
+
@coloring ||= true
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
## Convert a template string to a colored string.
|
207
|
+
## Colors are specified with single letters inside
|
208
|
+
## curly braces. Uppercase changes background color.
|
209
|
+
##
|
210
|
+
## w: white, k: black, g: green, l: blue, y: yellow, c: cyan,
|
211
|
+
## m: magenta, r: red, b: bold, u: underline, i: italic,
|
212
|
+
## x: reset (remove background, color, emphasis)
|
213
|
+
##
|
214
|
+
## @example Convert a templated string
|
215
|
+
## Color.template('{Rwb}Warning:{x} {w}you look a little {g}ill{x}')
|
216
|
+
##
|
217
|
+
## @param input [String, Array] The template
|
218
|
+
## string. If this is an array, the
|
219
|
+
## elements will be joined with a
|
220
|
+
## space.
|
221
|
+
##
|
222
|
+
## @return [String] Colorized string
|
223
|
+
##
|
224
|
+
def template(input)
|
225
|
+
input = input.join(' ') if input.is_a? Array
|
226
|
+
input.gsub!(/%/, '%%')
|
227
|
+
fmt = input.gsub(/\{(\w+)\}/) do
|
228
|
+
Regexp.last_match(1).split('').map { |c| "%<#{c}>s" }.join('')
|
229
|
+
end
|
230
|
+
|
231
|
+
colors = { w: white, k: black, g: green, l: blue,
|
232
|
+
y: yellow, c: cyan, m: magenta, r: red,
|
233
|
+
W: bgwhite, K: bgblack, G: bggreen, L: bgblue,
|
234
|
+
Y: bgyellow, C: bgcyan, M: bgmagenta, R: bgred,
|
235
|
+
b: bold, u: underline, i: italic, x: reset }
|
236
|
+
|
237
|
+
format(fmt, colors)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
ATTRIBUTES.each do |c, v|
|
242
|
+
new_method = <<-EOSCRIPT
|
243
|
+
def #{c}(string = nil)
|
244
|
+
result = ''
|
245
|
+
result << "\e[#{v}m" if Howzit::Color.coloring?
|
246
|
+
if block_given?
|
247
|
+
result << yield
|
248
|
+
elsif string.respond_to?(:to_str)
|
249
|
+
result << string.to_str
|
250
|
+
elsif respond_to?(:to_str)
|
251
|
+
result << to_str
|
252
|
+
else
|
253
|
+
return result #only switch on
|
254
|
+
end
|
255
|
+
result << "\e[0m" if Howzit::Color.coloring?
|
256
|
+
result
|
257
|
+
end
|
258
|
+
EOSCRIPT
|
259
|
+
|
260
|
+
module_eval(new_method)
|
261
|
+
|
262
|
+
next unless c =~ /bold/
|
263
|
+
|
264
|
+
# Accept brightwhite in addition to boldwhite
|
265
|
+
new_method = <<-EOSCRIPT
|
266
|
+
def #{c.to_s.sub(/bold/, 'bright')}(string = nil)
|
267
|
+
result = ''
|
268
|
+
result << "\e[#{v}m" if Howzit::Color.coloring?
|
269
|
+
if block_given?
|
270
|
+
result << yield
|
271
|
+
elsif string.respond_to?(:to_str)
|
272
|
+
result << string.to_str
|
273
|
+
elsif respond_to?(:to_str)
|
274
|
+
result << to_str
|
275
|
+
else
|
276
|
+
return result #only switch on
|
277
|
+
end
|
278
|
+
result << "\e[0m" if Howzit::Color.coloring?
|
279
|
+
result
|
280
|
+
end
|
281
|
+
EOSCRIPT
|
282
|
+
|
283
|
+
module_eval(new_method)
|
284
|
+
end
|
285
|
+
|
286
|
+
def rgb(hex)
|
287
|
+
is_bg = hex.match(/^bg?#/) ? true : false
|
288
|
+
hex_string = hex.sub(/^([fb]g?)?#/, '')
|
289
|
+
|
290
|
+
parts = hex_string.match(/(?<r>..)(?<g>..)(?<b>..)/)
|
291
|
+
t = []
|
292
|
+
%w[r g b].each do |e|
|
293
|
+
t << parts[e].hex
|
294
|
+
end
|
295
|
+
color =
|
296
|
+
"\e[#{is_bg ? '48' : '38'};2;#{t.join(';')}m"
|
297
|
+
end
|
298
|
+
|
299
|
+
# Regular expression that is used to scan for ANSI-sequences while
|
300
|
+
# uncoloring strings.
|
301
|
+
COLORED_REGEXP = /\e\[(?:(?:[349]|10)[0-7]|[0-9])?m/.freeze
|
302
|
+
|
303
|
+
# Returns an uncolored version of the string, that is all
|
304
|
+
# ANSI-sequences are stripped from the string.
|
305
|
+
def uncolor(string = nil) # :yields:
|
306
|
+
if block_given?
|
307
|
+
yield.to_str.gsub(COLORED_REGEXP, '')
|
308
|
+
elsif string.respond_to?(:to_str)
|
309
|
+
string.to_str.gsub(COLORED_REGEXP, '')
|
310
|
+
elsif respond_to?(:to_str)
|
311
|
+
to_str.gsub(COLORED_REGEXP, '')
|
312
|
+
else
|
313
|
+
''
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# Returns an array of all Howzit::Color attributes as symbols.
|
318
|
+
def attributes
|
319
|
+
ATTRIBUTE_NAMES
|
320
|
+
end
|
321
|
+
extend self
|
322
|
+
end
|
323
|
+
end
|
data/lib/howzit/version.rb
CHANGED
data/lib/howzit.rb
CHANGED
data/spec/ruby_gem_spec.rb
CHANGED
@@ -9,3 +9,36 @@ describe Howzit::BuildNotes do
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
13
|
+
describe Howzit::BuildNotes do
|
14
|
+
Dir.chdir('spec')
|
15
|
+
subject { Howzit::BuildNotes.new(['--no-upstream']) }
|
16
|
+
|
17
|
+
describe ".note_file" do
|
18
|
+
it "locates a build note file" do
|
19
|
+
expect(subject.note_file).not_to be_empty
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe ".get_note_title" do
|
24
|
+
it "is named howzit test" do
|
25
|
+
expect(subject.get_note_title).to match(/howzit test/)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe ".grep_topics" do
|
30
|
+
it "found editable" do
|
31
|
+
expect(subject.grep_topics('editable')).to include('File Structure')
|
32
|
+
expect(subject.grep_topics('editable')).not_to include('Build')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe ".list_topic_titles" do
|
37
|
+
it "found 4 topics" do
|
38
|
+
expect(subject.topics.keys.count).to eq 4
|
39
|
+
end
|
40
|
+
it "outputs a newline-separated string" do
|
41
|
+
expect(subject.list_topic_titles.scan(/\n/).count).to eq 3
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: howzit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
@@ -269,6 +269,7 @@ files:
|
|
269
269
|
- lib/.rubocop.yml
|
270
270
|
- lib/howzit.rb
|
271
271
|
- lib/howzit/buildnotes.rb
|
272
|
+
- lib/howzit/colors.rb
|
272
273
|
- lib/howzit/prompt.rb
|
273
274
|
- lib/howzit/stringutils.rb
|
274
275
|
- lib/howzit/version.rb
|