howzit 1.2.9 → 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 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