howzit 1.2.9 → 1.2.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 311a04719d7aa7de00e54224eda363bc249dd6f8e3fdd31ad5d2a625eaa8bff8
4
- data.tar.gz: 87f632ce5ac4855880b5d7ddade20983c42b252fa51bbf7fd0b654cf389970ff
3
+ metadata.gz: 875d039552876588bf298e4297ffec3955a6a73da6c2f89f608f7d11ed9d4e93
4
+ data.tar.gz: ecd7f0dd8f67999de4e223745f79096cc06ebd84e867400bc729f2323e8f8360
5
5
  SHA512:
6
- metadata.gz: 3ef0f06a0d408c652ae516e92d78dbb7d6bd6bf48260cb21dd66e63f4ecf27c38eed30eae42b79c53f87a824e8622b93416aef7e29feae25d2003ed00827332d
7
- data.tar.gz: 16025164eb7fb5550058e361fd478b7b37e4e560cbb1b06965c79a9f091df2862d0387cd908cdfcc11456a7b358f3f507ee876036f95190971acdaa610aa979a
6
+ metadata.gz: bfcad0fc1a4fbf0140dc91ae5cf03e52c3d42aed4a404c91fab642db05d9849b92c0c73d4f87d79ca0b58d1583a6a0ab5ef0ef9936812913567aeb3f24508a93
7
+ data.tar.gz: db44b03b2f2c25c84187692a9d46a328a22d4a7dce0adc20304ab31fe58e91b6ca4bef6848ab69efcce5e0c60e9c8955501f4232d5643f664d35cb81da9bcbdf
data/.gitignore CHANGED
@@ -41,3 +41,4 @@ Gemfile.lock
41
41
 
42
42
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
43
43
  .rvmrc
44
+ results.log
data/CHANGELOG.md CHANGED
@@ -1,6 +1,27 @@
1
- ### 1.2.9
1
+ ### 1.2.12
2
2
 
3
- 2022-08-01 07:09
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
+
10
+ ### 1.2.11
11
+
12
+ 2022-08-01 08:23
13
+
14
+ #### IMPROVED
15
+
16
+ - Code cleanup and refactoring
17
+
18
+ ### 1.2.10
19
+
20
+ 2022-08-01 07:45
21
+
22
+ #### FIXED
23
+
24
+ - Headline formatting when iTerm markers are inserted
4
25
 
5
26
  ### 1.2.8
6
27
 
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in howzit.gemspec.
data/Guardfile CHANGED
@@ -1,4 +1,6 @@
1
- scope groups: [:doc, :lint, :unit]
1
+ # frozen_string_literal: true
2
+
3
+ scope groups: %i[doc lint unit]
2
4
 
3
5
  group :doc do
4
6
  guard :yard do
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Howzit
2
2
 
3
3
  [![Gem](https://img.shields.io/gem/v/howzit.svg)](https://rubygems.org/gems/howzit)
4
+ [![Travis](https://img.shields.io/travis/makenew/ruby-gem.svg)](https://travis-ci.org/makenew/ruby-gem)
4
5
  [![GitHub license](https://img.shields.io/github/license/ttscoff/howzit.svg)](./LICENSE.txt)
5
6
 
6
7
  A command-line reference tool for tracking project build systems
data/Rakefile CHANGED
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bump/tasks'
2
4
  require 'bundler/gem_tasks'
3
5
  require 'rspec/core/rake_task'
4
6
  require 'rubocop/rake_task'
5
7
  require 'yard'
6
8
 
7
- task default: [:test, :yard]
9
+ task default: %i[test yard]
8
10
 
9
11
  desc 'Run test suite'
10
- task test: [:rubocop, :spec]
12
+ task test: %i[rubocop spec]
11
13
 
12
14
  RSpec::Core::RakeTask.new
13
15
 
@@ -16,3 +18,14 @@ RuboCop::RakeTask.new do |t|
16
18
  end
17
19
 
18
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
@@ -1,6 +1,8 @@
1
- #!/usr/bin/env ruby
1
+ #!/usr/bin/env ruby -W1
2
2
  # frozen_string_literal: true
3
+
3
4
  $LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
4
5
  require 'howzit'
5
6
 
6
- Howzit::BuildNotes.new(ARGV)
7
+ how = Howzit::BuildNotes.new(ARGV)
8
+ how.process
data/howzit.gemspec CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency 'rubocop', '~> 0.28'
32
32
  spec.add_development_dependency 'rspec', '~> 3.1'
33
33
  spec.add_development_dependency 'simplecov', '~> 0.9'
34
- spec.add_development_dependency 'codecov', '~> 0.1'
34
+ # spec.add_development_dependency 'codecov', '~> 0.1'
35
35
  spec.add_development_dependency 'fuubar', '~> 2.0'
36
36
 
37
37
  spec.add_development_dependency 'yard', '~> 0.9.5'
@@ -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 :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['GIT_PAGER'], ENV['PAGER'],
44
- 'bat', 'less', 'more', 'cat', 'pager']
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
 
@@ -132,8 +133,12 @@ module Howzit
132
133
  end
133
134
  end
134
135
 
136
+ def should_mark_iterm?
137
+ ENV['TERM_PROGRAM'] =~ /^iTerm/ && !@options[:run] && !@options[:paginate]
138
+ end
139
+
135
140
  def iterm_marker
136
- "\e]1337;SetMark\a\n" if ENV['TERM_PROGRAM'] =~ /^iTerm/ && !@options[:run] && !@options[:paginate]
141
+ "\e]1337;SetMark\a" if should_mark_iterm?
137
142
  end
138
143
 
139
144
  def color_single_options(choices = %w[y n])
@@ -141,12 +146,12 @@ module Howzit
141
146
  choices.each do |choice|
142
147
  case choice
143
148
  when /[A-Z]/
144
- out.push("\e[1;32m#{choice}\e[0;32m")
149
+ out.push(Color.template("{bg}#{choice}{xg}"))
145
150
  else
146
- out.push(choice)
151
+ out.push(Color.template("{w}#{choice}"))
147
152
  end
148
153
  end
149
- "\e[0;32m[#{out.join('/')}]\e[0m"
154
+ Color.template("{g}[#{out.join('/')}{g}]{x}")
150
155
  end
151
156
 
152
157
  # Create a buildnotes skeleton
@@ -157,7 +162,7 @@ module Howzit
157
162
  end
158
163
  # First make sure there isn't already a buildnotes file
159
164
  if note_file
160
- fname = "\e[1;33m#{note_file}\e[1;37m"
165
+ fname = Color.template("{by}#{note_file}{bw}")
161
166
  res = yn("#{fname} exists and appears to be a build note, continue anyway?", false)
162
167
  unless res
163
168
  puts 'Canceled'
@@ -166,19 +171,19 @@ module Howzit
166
171
  end
167
172
 
168
173
  title = File.basename(Dir.pwd)
169
- printf "\e[1;37mProject name \e[0;32m[#{title}]\e[1;37m: \e[0m"
170
- input = STDIN.gets.chomp
174
+ printf Color.template("{bw}Project name {xg}[#{title}]{bw}: {x}")
175
+ input = $stdin.gets.chomp
171
176
  title = input unless input.empty?
172
177
 
173
178
  summary = ''
174
- printf "\e[1;37mProject summary: \e[0m"
175
- input = STDIN.gets.chomp
179
+ printf Color.template('{bw}Project summary: {x}')
180
+ input = $stdin.gets.chomp
176
181
  summary = input unless input.empty?
177
182
 
178
- filename = 'buildnotes.md'
179
- printf "\e[1;37mBuild notes filename (must begin with 'howzit' or 'build')\n\e[0;32m[#{filename}]\e[1;37m: \e[0m"
180
- input = STDIN.gets.chomp
181
- filename = input unless input.empty?
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?
182
187
 
183
188
  note = <<~EOBUILDNOTES
184
189
  # #{title}
@@ -205,8 +210,8 @@ module Howzit
205
210
 
206
211
  EOBUILDNOTES
207
212
 
208
- if File.exist?(filename)
209
- file = "\e[1;33m#{filename}"
213
+ if File.exist?(fname)
214
+ file = Color.template("{by}#{fname}")
210
215
  res = yn("Are you absolutely sure you want to overwrite #{file}", false)
211
216
 
212
217
  unless res
@@ -215,9 +220,9 @@ module Howzit
215
220
  end
216
221
  end
217
222
 
218
- File.open(filename, 'w') do |f|
223
+ File.open(fname, 'w') do |f|
219
224
  f.puts note
220
- puts "Build notes for #{title} written to #{filename}"
225
+ puts Color.template("{by}Build notes for #{title} written to #{fname}")
221
226
  end
222
227
  end
223
228
 
@@ -225,32 +230,39 @@ module Howzit
225
230
  def format_header(title, opts = {})
226
231
  options = {
227
232
  hr: "\u{254C}",
228
- color: '1;32',
229
- border: '0'
233
+ color: '{bg}',
234
+ border: '{x}',
235
+ mark: false
230
236
  }
231
237
 
232
238
  options.merge!(opts)
233
239
 
234
240
  cols = TTY::Screen.columns
241
+
235
242
  cols = @options[:wrap] if (@options[:wrap]).positive? && cols > @options[:wrap]
236
- title = "\e[#{options[:border]}m#{options[:hr]}#{options[:hr]}( \e[#{options[:color]}m#{title}\e[#{options[:border]}m )"
237
- tail = options[:hr] * (cols - title.uncolor.length)
238
- options[:hr] =~ /╌/ ? "#{iterm_marker}#{title}#{tail}\e[0m" : "#{title}#{tail}\e[0m"
243
+ title = Color.template("#{options[:border]}#{options[:hr] * 2}( #{options[:color]}#{title}#{options[:border]} )")
244
+
245
+ tail = if should_mark_iterm?
246
+ "#{options[:hr] * (cols - title.uncolor.length - 15)}#{options[:mark] ? iterm_marker : ''}"
247
+ else
248
+ options[:hr] * (cols - title.uncolor.length)
249
+ end
250
+ Color.template("#{title}#{tail}{x}")
239
251
  end
240
252
 
241
253
  def os_open(command)
242
254
  os = RbConfig::CONFIG['target_os']
243
- out = "\e[1;32mOpening \e[3;37m#{command}"
255
+ out = Color.template("{bg}Opening {bw}#{command}")
244
256
  case os
245
257
  when /darwin.*/i
246
- warn "#{out} (macOS)\e[0m" if @options[:log_level] < 2
258
+ warn Color.template("#{out} (macOS){x}") if @options[:log_level] < 2
247
259
  `open #{Shellwords.escape(command)}`
248
260
  when /mingw|mswin/i
249
- warn "#{out} (Windows)\e[0m" if @options[:log_level] < 2
261
+ warn Color.template("#{out} (Windows){x}") if @options[:log_level] < 2
250
262
  `start #{Shellwords.escape(command)}`
251
263
  else
252
264
  if 'xdg-open'.available?
253
- warn "#{out} (Linux)\e[0m" if @options[:log_level] < 2
265
+ warn Color.template("#{out} (Linux){x}") if @options[:log_level] < 2
254
266
  `xdg-open #{Shellwords.escape(command)}`
255
267
  else
256
268
  warn out if @options[:log_level] < 2
@@ -289,7 +301,7 @@ module Howzit
289
301
  directives.each do |c|
290
302
  if c[0].nil?
291
303
  title = c[3] ? c[3].strip : ''
292
- warn "\e[1;32mRunning block \e[3;37m#{title}\e[0m" if @options[:log_level] < 2
304
+ warn Color.template("{bg}Running block {bw}#{title}{x}") if @options[:log_level] < 2
293
305
  block = c[4].strip
294
306
  script = Tempfile.new('howzit_script')
295
307
  begin
@@ -311,18 +323,18 @@ module Howzit
311
323
  warn "No topic match for @include(#{search})"
312
324
  else
313
325
  if @included.include?(matches[0])
314
- warn "\e[1;33mTasks from \e[3;37m#{matches[0]} already included, skipping\e[0m" if @options[:log_level] < 2
326
+ warn Color.template("{by}Tasks from {bw}#{matches[0]} already included, skipping{x}") if @options[:log_level] < 2
315
327
  else
316
- warn "\e[1;33mIncluding tasks from \e[3;37m#{matches[0]}\e[0m" if @options[:log_level] < 2
328
+ warn Color.template("{by}Including tasks from {bw}#{matches[0]}{x}") if @options[:log_level] < 2
317
329
  process_topic(matches[0], true)
318
- warn "\e[1;33mEnd include \e[3;37m#{matches[0]}\e[0m" if @options[:log_level] < 2
330
+ warn Color.template("{by}End include {bw}#{matches[0]}{x}") if @options[:log_level] < 2
319
331
  end
320
332
  end
321
333
  when /run/i
322
- warn "\e[1;32mRunning \e[3;37m#{obj}\e[0m" if @options[:log_level] < 2
334
+ warn Color.template("{bg}Running {bw}#{obj}{x}") if @options[:log_level] < 2
323
335
  system(obj)
324
336
  when /copy/i
325
- warn "\e[1;32mCopied \e[3;37m#{obj}\e[1;32m to clipboard\e[0m" if @options[:log_level] < 2
337
+ warn Color.template("{bg}Copied {bw}#{obj}{bg} to clipboard{x}") if @options[:log_level] < 2
326
338
  `echo #{Shellwords.escape(obj)}'\\c'|pbcopy`
327
339
  when /open|url/i
328
340
  os_open(obj)
@@ -330,7 +342,7 @@ module Howzit
330
342
  end
331
343
  end
332
344
  else
333
- warn "\e[0;31m--run: No \e[1;31m@directive\e[0;31;40m found in \e[1;37m#{key}\e[0m"
345
+ warn Color.template("{r}--run: No {br}@directive{xr} found in {bw}#{key}{x}")
334
346
  end
335
347
  output.push("Ran #{tasks} #{tasks == 1 ? 'task' : 'tasks'}") if @options[:log_level] < 2
336
348
 
@@ -344,7 +356,7 @@ module Howzit
344
356
 
345
357
  output = []
346
358
  if opt[:header]
347
- output.push(format_header(key))
359
+ output.push(format_header(key, { mark: should_mark_iterm? }))
348
360
  output.push('')
349
361
  end
350
362
  topic = topics[key].strip
@@ -360,12 +372,12 @@ module Howzit
360
372
  unless matches.empty?
361
373
  if opt[:single]
362
374
  title = "From #{matches[0]}:"
363
- color = '33;40'
364
- rule = '30;40'
375
+ color = '{yK}'
376
+ rule = '{kK}'
365
377
  else
366
378
  title = "Include #{matches[0]}"
367
- color = '33;40'
368
- rule = '0'
379
+ color = '{yK}'
380
+ rule = '{x}'
369
381
  end
370
382
  output.push(format_header("#{'> ' * @nest_level}#{title}", { color: color, hr: '.', border: rule })) unless @included.include?(matches[0])
371
383
 
@@ -394,15 +406,15 @@ module Howzit
394
406
  when /open|url/
395
407
  "\u{279A}"
396
408
  end
397
- output.push("\e[1;35;40m#{icon} \e[3;37;40m#{obj}\e[0m")
409
+ output.push(Color.template("{bmK}#{icon} {bwK}#{obj}{x}"))
398
410
  when /(`{3,})run *(.*?)$/i
399
411
  m = Regexp.last_match
400
412
  desc = m[2].length.positive? ? "Block: #{m[2]}" : 'Code Block'
401
- output.push("\e[1;35;40m\u{25B6} \e[3;37;40m#{desc}\e[0m\n```")
413
+ output.push(Color.template("{bmK}\u{25B6} {bwK}#{desc}{x}\n```"))
402
414
  when /@@@run *(.*?)$/i
403
415
  m = Regexp.last_match
404
416
  desc = m[1].length.positive? ? "Block: #{m[1]}" : 'Code Block'
405
- output.push("\e[1;35;40m\u{25B6} \e[3;37;40m#{desc}\e[0m")
417
+ output.push(Color.template("{bmK}\u{25B6} {bwK}#{desc}{x}"))
406
418
  else
407
419
  l.wrap!(@options[:wrap]) if (@options[:wrap]).positive?
408
420
  output.push(l)
@@ -433,9 +445,9 @@ module Howzit
433
445
  # Output a list of topic titles
434
446
  def list_topics
435
447
  output = []
436
- output.push("\e[1;32mTopics:\e[0m\n")
448
+ output.push(Color.template("{bg}Topics:{x}\n"))
437
449
  topics.each_key do |title|
438
- output.push("- \e[1;37m#{title}\e[0m")
450
+ output.push(Color.template("- {bw}#{title}{x}"))
439
451
  end
440
452
  output.join("\n")
441
453
  end
@@ -445,14 +457,14 @@ module Howzit
445
457
  topics.keys.join("\n")
446
458
  end
447
459
 
448
- def get_note_title(filename, truncate = 0)
460
+ def get_note_title(truncate = 0)
449
461
  title = nil
450
- help = IO.read(filename).strip
462
+ help = IO.read(note_file).strip
451
463
  title = help.match(/(?:^(\S.*?)(?=\n==)|^# ?(.*?)$)/)
452
464
  title = if title
453
465
  title[1].nil? ? title[2] : title[1]
454
466
  else
455
- filename.sub(/(\.\w+)?$/, '')
467
+ note_file.sub(/(\.\w+)?$/, '')
456
468
  end
457
469
 
458
470
  title && truncate.positive? ? title.trunc(truncate) : title
@@ -475,7 +487,7 @@ module Howzit
475
487
 
476
488
  def list_runnable
477
489
  output = []
478
- output.push(%(\e[1;32m"Runnable" Topics:\e[0m\n))
490
+ output.push(Color.template(%({bg}"Runnable" Topics:{x}\n)))
479
491
  topics.each do |title, sect|
480
492
  s_out = []
481
493
  lines = sect.split(/\n/)
@@ -496,7 +508,7 @@ module Howzit
496
508
  end
497
509
  end
498
510
  unless s_out.empty?
499
- output.push("- \e[1;37m#{title}\e[0m")
511
+ output.push(Color.template("- {bw}#{title}{x}"))
500
512
  output.push(s_out.join("\n"))
501
513
  end
502
514
  end
@@ -520,8 +532,8 @@ module Howzit
520
532
  required = t_meta['required'].strip.split(/\s*,\s*/)
521
533
  required.each do |req|
522
534
  unless @metadata.keys.include?(req.downcase)
523
- warn %(\e[0;31mERROR: Missing required metadata key from template '\e[1;37m#{File.basename(template, '.md')}\e[0;31m'\e[0m)
524
- warn %(\e[0;31mPlease define \e[1;33m#{req.downcase}\e[0;31m in build notes\e[0m)
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}))
525
537
  Process.exit 1
526
538
  end
527
539
  end
@@ -570,6 +582,24 @@ module Howzit
570
582
  template_topics
571
583
  end
572
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
+
573
603
  # Read in the build notes file and output a hash of "Title" => contents
574
604
  def read_help_file(path = nil)
575
605
  filename = path.nil? ? note_file : path
@@ -577,21 +607,7 @@ module Howzit
577
607
  help = IO.read(filename)
578
608
 
579
609
  help.gsub!(/@include\((.*?)\)/) do
580
- m = Regexp.last_match
581
- file = File.expand_path(m[1])
582
- if File.exist?(file)
583
- content = IO.read(file)
584
- home = ENV['HOME']
585
- short_path = File.dirname(file.sub(/^#{home}/, '~'))
586
- prefix = "#{short_path}:"
587
- parts = content.split(/^##+/)
588
- parts.shift
589
- content = '## ' + parts.join('## ')
590
- content.gsub!(/^(##+ *)(?=\S)/, "\\1#{prefix}")
591
- content
592
- else
593
- m[0]
594
- end
610
+ include_file(Regexp.last_match)
595
611
  end
596
612
 
597
613
  template_topics = get_template_topics(help)
@@ -662,7 +678,8 @@ module Howzit
662
678
  matches
663
679
  end
664
680
 
665
- def initialize(args)
681
+ def initialize(args = [])
682
+ Color.coloring = $stdout.isatty
666
683
  flags = {
667
684
  run: false,
668
685
  list_topics: false,
@@ -705,7 +722,8 @@ module Howzit
705
722
  OptionParser.new do |opts|
706
723
  opts.banner = "Usage: #{__FILE__} [OPTIONS] [TOPIC]"
707
724
  opts.separator ''
708
- opts.separator 'Show build notes for the current project (buildnotes.md). Include a topic name to see just that topic, or no argument to display all.'
725
+ opts.separator 'Show build notes for the current project (buildnotes.md).
726
+ Include a topic name to see just that topic, or no argument to display all.'
709
727
  opts.separator ''
710
728
  opts.separator 'Options:'
711
729
 
@@ -714,7 +732,8 @@ module Howzit
714
732
  Process.exit 0
715
733
  end
716
734
 
717
- opts.on('-e', '--edit', "Edit buildnotes file in current working directory using #{File.basename(ENV['EDITOR'])}") do
735
+ opts.on('-e', '--edit', "Edit buildnotes file in current working directory
736
+ using #{File.basename(ENV['EDITOR'])}") do
718
737
  edit_note
719
738
  Process.exit 0
720
739
  end
@@ -732,7 +751,8 @@ module Howzit
732
751
  @options[:list_topics] = true
733
752
  end
734
753
 
735
- opts.on('-m', '--matching TYPE', MATCHING_OPTIONS, 'Topics matching type', "(#{MATCHING_OPTIONS.join(', ')})") do |c|
754
+ opts.on('-m', '--matching TYPE', MATCHING_OPTIONS,
755
+ 'Topics matching type', "(#{MATCHING_OPTIONS.join(', ')})") do |c|
736
756
  @options[:matching] = c
737
757
  end
738
758
 
@@ -765,8 +785,8 @@ module Howzit
765
785
  @options[:log_level] = 0
766
786
  end
767
787
 
768
- opts.on('-u', '--upstream', 'Traverse up parent directories for additional build notes') do
769
- @options[:include_upstream] = true
788
+ opts.on('-u', '--[no-]upstream', 'Traverse up parent directories for additional build notes') do |p|
789
+ @options[:include_upstream] = p
770
790
  end
771
791
 
772
792
  opts.on('--show-code', 'Display the content of fenced run blocks') do
@@ -791,21 +811,21 @@ module Howzit
791
811
  Dir.chdir(template_folder)
792
812
  Dir.glob('*.md').each do |file|
793
813
  template = File.basename(file, '.md')
794
- puts "\e[7;30;45mtemplate: \e[7;33;40m#{template}\e[0m"
795
- puts "\e[1;30m[\e[1;37mtasks\e[1;30m]──────────────────────────────────────┐\e[0m"
814
+ puts Color.template("{Mk}template:{Yk}#{template}{x}")
815
+ puts Color.template("{bk}[{bl}tasks{bk}]──────────────────────────────────────┐{x}")
796
816
  metadata = file.extract_metadata
797
817
  topics = read_help_file(file)
798
- topics.keys.each do |topic|
799
- puts " \e[1;30m│\e[1;37m-\e[0m \e[1;36;40m#{template}:#{topic.sub(/^.*?:/, '')}\e[0m"
818
+ topics.each_key do |topic|
819
+ puts Color.template(" {bk}│{bw}-{x} {bcK}#{template}:#{topic.sub(/^.*?:/, '')}{x}")
800
820
  end
801
821
  if metadata.size > 0
802
822
  meta = []
803
- meta << metadata['required'].split(/\s*,\s*/).map {|m| "*\e[1;37m#{m}\e[0;37m" } if metadata.key?('required')
823
+ meta << metadata['required'].split(/\s*,\s*/).map {|m| "*{bw}#{m}{xw}" } if metadata.key?('required')
804
824
  meta << metadata['optional'].split(/\s*,\s*/).map {|m| "#{m}" } if metadata.key?('optional')
805
- puts "\e[1;30m[\e[1;34mmeta\e[1;30m]───────────────────────────────────────┤\e[0m"
806
- puts " \e[1;30m│\e[1;37m \e[0;37m#{meta.join(", ")}\e[0m"
825
+ puts Color.template("{bk}[{bl}meta{bk}]───────────────────────────────────────┤{x}")
826
+ puts Color.template(" {bk}│ {xw}#{meta.join(", ")}{x}")
807
827
  end
808
- puts " \e[1;30m└───────────────────────────────────────────┘\e[0m"
828
+ puts Color.template(" {bk}└───────────────────────────────────────────┘{x}")
809
829
  end
810
830
  Process.exit 0
811
831
  end
@@ -834,7 +854,7 @@ module Howzit
834
854
  end
835
855
  end.parse!(args)
836
856
 
837
- process(args)
857
+ @cli_args = args
838
858
  end
839
859
 
840
860
  def edit_note
@@ -851,7 +871,7 @@ module Howzit
851
871
  end
852
872
 
853
873
  ##
854
- ## @brief Traverse up directory tree looking for build notes
874
+ ## Traverse up directory tree looking for build notes
855
875
  ##
856
876
  ## @return topics dictionary
857
877
  ##
@@ -1053,7 +1073,7 @@ module Howzit
1053
1073
  `#{ENV['EDITOR']} "#{config_file}"`
1054
1074
  end
1055
1075
 
1056
- def process(args)
1076
+ def process
1057
1077
  output = []
1058
1078
 
1059
1079
  unless note_file
@@ -1069,13 +1089,13 @@ module Howzit
1069
1089
  end
1070
1090
 
1071
1091
  if @options[:title_only]
1072
- out = get_note_title(note_file, 20)
1092
+ out = get_note_title(20)
1073
1093
  $stdout.print(out.strip)
1074
1094
  Process.exit(0)
1075
1095
  elsif @options[:output_title]
1076
- title = get_note_title(note_file)
1096
+ title = get_note_title
1077
1097
  if title && !title.empty?
1078
- header = format_header(title, { hr: "\u{2550}", color: '1;37;40' })
1098
+ header = format_header(title, { hr: "\u{2550}", color: '{bwK}' })
1079
1099
  output.push("#{header}\n")
1080
1100
  end
1081
1101
  end
@@ -1107,14 +1127,14 @@ module Howzit
1107
1127
  elsif @options[:choose]
1108
1128
  topic_match = choose(topics.keys)
1109
1129
  # If there are arguments use those to search for a matching topic
1110
- elsif !args.empty?
1130
+ elsif !@cli_args.empty?
1111
1131
 
1112
- search = args.join(' ').strip.downcase
1132
+ search = @cli_args.join(' ').strip.downcase
1113
1133
  matches = match_topic(search)
1114
1134
 
1115
1135
  if matches.empty?
1116
- output.push(%(\e[0;31mERROR: No topic match found for \e[1;33m#{search}\e[0m\n))
1117
- if !@options[:show_all_on_error]
1136
+ output.push(Color.template(%({bR}ERROR:{xr} No topic match found for {bw}#{search}{x}\n)))
1137
+ unless @options[:show_all_on_error]
1118
1138
  show(output.join("\n"), { color: true, highlight: false, paginate: false, wrap: 0 })
1119
1139
  Process.exit 1
1120
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
@@ -5,7 +5,7 @@ module Howzit
5
5
  module StringUtils
6
6
  # Just strip out color codes when requested
7
7
  def uncolor
8
- gsub(/\e\[[\d;]+m/, '').gsub(/\e]1337;SetMark/,'')
8
+ gsub(/\e\[[\d;]+m/, '').gsub(/\e\]1337;SetMark/,'')
9
9
  end
10
10
 
11
11
  # Adapted from https://github.com/pazdera/word_wrap/,
@@ -2,5 +2,5 @@
2
2
  # Primary module for this gem.
3
3
  module Howzit
4
4
  # Current Howzit version.
5
- VERSION = '1.2.9'.freeze
5
+ VERSION = '1.2.12'.freeze
6
6
  end
data/lib/howzit.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'howzit/version'
2
2
  require 'howzit/prompt'
3
+ require 'howzit/colors'
3
4
  require 'howzit/buildnotes'
4
5
  require 'howzit/stringutils'
5
6
  require 'optparse'
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Howzit::BuildNotes do
4
- subject(:ruby_gem) { Howzit::BuildNotes.new }
4
+ subject(:ruby_gem) { Howzit::BuildNotes.new([]) }
5
5
 
6
6
  describe ".new" do
7
7
  it "makes a new instance" do
@@ -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
data/spec/spec_helper.rb CHANGED
@@ -1,13 +1,13 @@
1
- require 'simplecov'
1
+ # require 'simplecov'
2
2
 
3
- SimpleCov.start
3
+ # SimpleCov.start
4
4
 
5
- if ENV['CI'] == 'true'
6
- require 'codecov'
7
- SimpleCov.formatter = SimpleCov::Formatter::Codecov
8
- else
9
- SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
10
- end
5
+ # if ENV['CI'] == 'true'
6
+ # require 'codecov'
7
+ # SimpleCov.formatter = SimpleCov::Formatter::Codecov
8
+ # else
9
+ # SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
10
+ # end
11
11
 
12
12
  require 'howzit'
13
13
 
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.9
4
+ version: 1.2.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
@@ -150,20 +150,6 @@ dependencies:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0.9'
153
- - !ruby/object:Gem::Dependency
154
- name: codecov
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - "~>"
158
- - !ruby/object:Gem::Version
159
- version: '0.1'
160
- type: :development
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - "~>"
165
- - !ruby/object:Gem::Version
166
- version: '0.1'
167
153
  - !ruby/object:Gem::Dependency
168
154
  name: fuubar
169
155
  requirement: !ruby/object:Gem::Requirement
@@ -283,6 +269,7 @@ files:
283
269
  - lib/.rubocop.yml
284
270
  - lib/howzit.rb
285
271
  - lib/howzit/buildnotes.rb
272
+ - lib/howzit/colors.rb
286
273
  - lib/howzit/prompt.rb
287
274
  - lib/howzit/stringutils.rb
288
275
  - lib/howzit/version.rb