howzit 1.2.10 → 1.2.13

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: fba37c5c428aeab92ed337697a52323d5b98d5f9398776187e000043e3a5173b
4
- data.tar.gz: 6f3a3ebfe50f50476837c2d702cd1c9854d49ff4960c51a06641e79d88abb410
3
+ metadata.gz: 36a27d1566deff8857d39035c0e40f696a29d78b7ae24274a73ceacc54541321
4
+ data.tar.gz: 7b13d6644bf397e10f629689b159102a33dd5aa2c3610f368819de3dc8736007
5
5
  SHA512:
6
- metadata.gz: 015bc504dca39d5b7a32b6d81a4df037abf61467f58b392e88389557f38a2abfb078fd3dc5ced7411b5d1654479d5a79b94da843f8384d4cf77216e9efa1c24c
7
- data.tar.gz: c0b2003e8c1b24efa1e193520a66e01813fab6e9d8e21318a7d0c82718540fa8ef1c99b075d40dc07254c678015ca3fe687b64a2be6f8ce541c22e786ac63c9a
6
+ metadata.gz: 9da1ea212df950e75a34006a44b0ade09c02fd4fe7c02ea0fddfffcf2936e6ca5db0d7f0c9e6cc44aac3d65eb4544aa9d4c03c156487f9311f39f08ee12199d4
7
+ data.tar.gz: 99f9be58ff9184a2f96e7db6fed83e3fc19f0945e31c9d58daa32db022b175a8c99a329a9c023528af52100f46583ac892091eb04ed5d9e9e791087fe8f8c82d
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,3 +1,24 @@
1
+ ### 1.2.13
2
+
3
+ 2022-08-01 20:50
4
+
5
+ ### 1.2.12
6
+
7
+ 2022-08-01 16:23
8
+
9
+ #### IMPROVED
10
+
11
+ - Replace ANSI escape codes with color template system
12
+ - When @including an external file, if the file doesn't contain any level 2+ headers, import it as plain text.
13
+
14
+ ### 1.2.11
15
+
16
+ 2022-08-01 08:23
17
+
18
+ #### IMPROVED
19
+
20
+ - Code cleanup and refactoring
21
+
1
22
  ### 1.2.10
2
23
 
3
24
  2022-08-01 07:45
@@ -6,10 +27,6 @@
6
27
 
7
28
  - Headline formatting when iTerm markers are inserted
8
29
 
9
- ### 1.2.9
10
-
11
- 2022-08-01 07:09
12
-
13
30
  ### 1.2.8
14
31
 
15
32
  2022-08-01 07:01
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,19 @@ 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
32
+
33
+ desc 'Changelog version check'
34
+ task :cver do
35
+ puts IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
36
+ 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
 
@@ -145,12 +146,12 @@ module Howzit
145
146
  choices.each do |choice|
146
147
  case choice
147
148
  when /[A-Z]/
148
- out.push("\e[1;32m#{choice}\e[0;32m")
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
- "\e[0;32m[#{out.join('/')}]\e[0m"
154
+ Color.template("{g}[#{out.join('/')}{g}]{x}")
154
155
  end
155
156
 
156
157
  # Create a buildnotes skeleton
@@ -159,30 +160,40 @@ module Howzit
159
160
  warn "\nCanceled"
160
161
  exit!
161
162
  end
163
+ default = !$stdout.isatty || @options[:default]
162
164
  # First make sure there isn't already a buildnotes file
163
165
  if note_file
164
- fname = "\e[1;33m#{note_file}\e[1;37m"
165
- res = yn("#{fname} exists and appears to be a build note, continue anyway?", false)
166
- unless res
167
- puts 'Canceled'
168
- Process.exit 0
166
+ fname = Color.template("{by}#{note_file}{bw}")
167
+ unless default
168
+ res = yn("#{fname} exists and appears to be a build note, continue anyway?", false)
169
+ unless res
170
+ puts 'Canceled'
171
+ Process.exit 0
172
+ end
169
173
  end
170
174
  end
171
175
 
172
176
  title = File.basename(Dir.pwd)
173
- printf "\e[1;37mProject name \e[0;32m[#{title}]\e[1;37m: \e[0m"
174
- input = STDIN.gets.chomp
175
- title = input unless input.empty?
176
-
177
+ if default
178
+ input = title
179
+ else
180
+ printf Color.template("{bw}Project name {xg}[#{title}]{bw}: {x}")
181
+ input = $stdin.gets.chomp
182
+ title = input unless input.empty?
183
+ end
177
184
  summary = ''
178
- printf "\e[1;37mProject summary: \e[0m"
179
- input = STDIN.gets.chomp
180
- summary = input unless input.empty?
185
+ unless default
186
+ printf Color.template('{bw}Project summary: {x}')
187
+ input = $stdin.gets.chomp
188
+ summary = input unless input.empty?
189
+ end
181
190
 
182
- filename = 'buildnotes.md'
183
- printf "\e[1;37mBuild notes filename (must begin with 'howzit' or 'build')\n\e[0;32m[#{filename}]\e[1;37m: \e[0m"
184
- input = STDIN.gets.chomp
185
- filename = input unless input.empty?
191
+ fname = 'buildnotes.md'
192
+ unless default
193
+ printf Color.template("{bw}Build notes filename (must begin with 'howzit' or 'build')\n{xg}[#{fname}]{bw}: {x}")
194
+ input = $stdin.gets.chomp
195
+ fname = input unless input.empty?
196
+ end
186
197
 
187
198
  note = <<~EOBUILDNOTES
188
199
  # #{title}
@@ -209,8 +220,8 @@ module Howzit
209
220
 
210
221
  EOBUILDNOTES
211
222
 
212
- if File.exist?(filename)
213
- file = "\e[1;33m#{filename}"
223
+ if File.exist?(fname) && !default
224
+ file = Color.template("{by}#{fname}")
214
225
  res = yn("Are you absolutely sure you want to overwrite #{file}", false)
215
226
 
216
227
  unless res
@@ -219,9 +230,9 @@ module Howzit
219
230
  end
220
231
  end
221
232
 
222
- File.open(filename, 'w') do |f|
233
+ File.open(fname, 'w') do |f|
223
234
  f.puts note
224
- puts "Build notes for #{title} written to #{filename}"
235
+ puts Color.template("{by}Build notes for #{title} written to #{fname}")
225
236
  end
226
237
  end
227
238
 
@@ -229,8 +240,8 @@ module Howzit
229
240
  def format_header(title, opts = {})
230
241
  options = {
231
242
  hr: "\u{254C}",
232
- color: '1;32',
233
- border: '0',
243
+ color: '{bg}',
244
+ border: '{x}',
234
245
  mark: false
235
246
  }
236
247
 
@@ -239,29 +250,29 @@ module Howzit
239
250
  cols = TTY::Screen.columns
240
251
 
241
252
  cols = @options[:wrap] if (@options[:wrap]).positive? && cols > @options[:wrap]
242
- title = "\e[#{options[:border]}m#{options[:hr] * 2}( \e[#{options[:color]}m#{title}\e[#{options[:border]}m )"
253
+ title = Color.template("#{options[:border]}#{options[:hr] * 2}( #{options[:color]}#{title}#{options[:border]} )")
243
254
 
244
255
  tail = if should_mark_iterm?
245
256
  "#{options[:hr] * (cols - title.uncolor.length - 15)}#{options[:mark] ? iterm_marker : ''}"
246
257
  else
247
258
  options[:hr] * (cols - title.uncolor.length)
248
259
  end
249
- "#{title}#{tail}\e[0m"
260
+ Color.template("#{title}#{tail}{x}")
250
261
  end
251
262
 
252
263
  def os_open(command)
253
264
  os = RbConfig::CONFIG['target_os']
254
- out = "\e[1;32mOpening \e[3;37m#{command}"
265
+ out = Color.template("{bg}Opening {bw}#{command}")
255
266
  case os
256
267
  when /darwin.*/i
257
- warn "#{out} (macOS)\e[0m" if @options[:log_level] < 2
268
+ warn Color.template("#{out} (macOS){x}") if @options[:log_level] < 2
258
269
  `open #{Shellwords.escape(command)}`
259
270
  when /mingw|mswin/i
260
- warn "#{out} (Windows)\e[0m" if @options[:log_level] < 2
271
+ warn Color.template("#{out} (Windows){x}") if @options[:log_level] < 2
261
272
  `start #{Shellwords.escape(command)}`
262
273
  else
263
274
  if 'xdg-open'.available?
264
- warn "#{out} (Linux)\e[0m" if @options[:log_level] < 2
275
+ warn Color.template("#{out} (Linux){x}") if @options[:log_level] < 2
265
276
  `xdg-open #{Shellwords.escape(command)}`
266
277
  else
267
278
  warn out if @options[:log_level] < 2
@@ -300,7 +311,7 @@ module Howzit
300
311
  directives.each do |c|
301
312
  if c[0].nil?
302
313
  title = c[3] ? c[3].strip : ''
303
- warn "\e[1;32mRunning block \e[3;37m#{title}\e[0m" if @options[:log_level] < 2
314
+ warn Color.template("{bg}Running block {bw}#{title}{x}") if @options[:log_level] < 2
304
315
  block = c[4].strip
305
316
  script = Tempfile.new('howzit_script')
306
317
  begin
@@ -322,18 +333,18 @@ module Howzit
322
333
  warn "No topic match for @include(#{search})"
323
334
  else
324
335
  if @included.include?(matches[0])
325
- warn "\e[1;33mTasks from \e[3;37m#{matches[0]} already included, skipping\e[0m" if @options[:log_level] < 2
336
+ warn Color.template("{by}Tasks from {bw}#{matches[0]} already included, skipping{x}") if @options[:log_level] < 2
326
337
  else
327
- warn "\e[1;33mIncluding tasks from \e[3;37m#{matches[0]}\e[0m" if @options[:log_level] < 2
338
+ warn Color.template("{by}Including tasks from {bw}#{matches[0]}{x}") if @options[:log_level] < 2
328
339
  process_topic(matches[0], true)
329
- warn "\e[1;33mEnd include \e[3;37m#{matches[0]}\e[0m" if @options[:log_level] < 2
340
+ warn Color.template("{by}End include {bw}#{matches[0]}{x}") if @options[:log_level] < 2
330
341
  end
331
342
  end
332
343
  when /run/i
333
- warn "\e[1;32mRunning \e[3;37m#{obj}\e[0m" if @options[:log_level] < 2
344
+ warn Color.template("{bg}Running {bw}#{obj}{x}") if @options[:log_level] < 2
334
345
  system(obj)
335
346
  when /copy/i
336
- warn "\e[1;32mCopied \e[3;37m#{obj}\e[1;32m to clipboard\e[0m" if @options[:log_level] < 2
347
+ warn Color.template("{bg}Copied {bw}#{obj}{bg} to clipboard{x}") if @options[:log_level] < 2
337
348
  `echo #{Shellwords.escape(obj)}'\\c'|pbcopy`
338
349
  when /open|url/i
339
350
  os_open(obj)
@@ -341,7 +352,7 @@ module Howzit
341
352
  end
342
353
  end
343
354
  else
344
- warn "\e[0;31m--run: No \e[1;31m@directive\e[0;31;40m found in \e[1;37m#{key}\e[0m"
355
+ warn Color.template("{r}--run: No {br}@directive{xr} found in {bw}#{key}{x}")
345
356
  end
346
357
  output.push("Ran #{tasks} #{tasks == 1 ? 'task' : 'tasks'}") if @options[:log_level] < 2
347
358
 
@@ -371,12 +382,12 @@ module Howzit
371
382
  unless matches.empty?
372
383
  if opt[:single]
373
384
  title = "From #{matches[0]}:"
374
- color = '33;40'
375
- rule = '30;40'
385
+ color = '{yK}'
386
+ rule = '{kK}'
376
387
  else
377
388
  title = "Include #{matches[0]}"
378
- color = '33;40'
379
- rule = '0'
389
+ color = '{yK}'
390
+ rule = '{x}'
380
391
  end
381
392
  output.push(format_header("#{'> ' * @nest_level}#{title}", { color: color, hr: '.', border: rule })) unless @included.include?(matches[0])
382
393
 
@@ -405,15 +416,15 @@ module Howzit
405
416
  when /open|url/
406
417
  "\u{279A}"
407
418
  end
408
- output.push("\e[1;35;40m#{icon} \e[3;37;40m#{obj}\e[0m")
419
+ output.push(Color.template("{bmK}#{icon} {bwK}#{obj}{x}"))
409
420
  when /(`{3,})run *(.*?)$/i
410
421
  m = Regexp.last_match
411
422
  desc = m[2].length.positive? ? "Block: #{m[2]}" : 'Code Block'
412
- output.push("\e[1;35;40m\u{25B6} \e[3;37;40m#{desc}\e[0m\n```")
423
+ output.push(Color.template("{bmK}\u{25B6} {bwK}#{desc}{x}\n```"))
413
424
  when /@@@run *(.*?)$/i
414
425
  m = Regexp.last_match
415
426
  desc = m[1].length.positive? ? "Block: #{m[1]}" : 'Code Block'
416
- output.push("\e[1;35;40m\u{25B6} \e[3;37;40m#{desc}\e[0m")
427
+ output.push(Color.template("{bmK}\u{25B6} {bwK}#{desc}{x}"))
417
428
  else
418
429
  l.wrap!(@options[:wrap]) if (@options[:wrap]).positive?
419
430
  output.push(l)
@@ -444,9 +455,9 @@ module Howzit
444
455
  # Output a list of topic titles
445
456
  def list_topics
446
457
  output = []
447
- output.push("\e[1;32mTopics:\e[0m\n")
458
+ output.push(Color.template("{bg}Topics:{x}\n"))
448
459
  topics.each_key do |title|
449
- output.push("- \e[1;37m#{title}\e[0m")
460
+ output.push(Color.template("- {bw}#{title}{x}"))
450
461
  end
451
462
  output.join("\n")
452
463
  end
@@ -456,14 +467,14 @@ module Howzit
456
467
  topics.keys.join("\n")
457
468
  end
458
469
 
459
- def get_note_title(filename, truncate = 0)
470
+ def get_note_title(truncate = 0)
460
471
  title = nil
461
- help = IO.read(filename).strip
472
+ help = IO.read(note_file).strip
462
473
  title = help.match(/(?:^(\S.*?)(?=\n==)|^# ?(.*?)$)/)
463
474
  title = if title
464
475
  title[1].nil? ? title[2] : title[1]
465
476
  else
466
- filename.sub(/(\.\w+)?$/, '')
477
+ note_file.sub(/(\.\w+)?$/, '')
467
478
  end
468
479
 
469
480
  title && truncate.positive? ? title.trunc(truncate) : title
@@ -486,7 +497,7 @@ module Howzit
486
497
 
487
498
  def list_runnable
488
499
  output = []
489
- output.push(%(\e[1;32m"Runnable" Topics:\e[0m\n))
500
+ output.push(Color.template(%({bg}"Runnable" Topics:{x}\n)))
490
501
  topics.each do |title, sect|
491
502
  s_out = []
492
503
  lines = sect.split(/\n/)
@@ -507,7 +518,7 @@ module Howzit
507
518
  end
508
519
  end
509
520
  unless s_out.empty?
510
- output.push("- \e[1;37m#{title}\e[0m")
521
+ output.push(Color.template("- {bw}#{title}{x}"))
511
522
  output.push(s_out.join("\n"))
512
523
  end
513
524
  end
@@ -531,8 +542,8 @@ module Howzit
531
542
  required = t_meta['required'].strip.split(/\s*,\s*/)
532
543
  required.each do |req|
533
544
  unless @metadata.keys.include?(req.downcase)
534
- warn %(\e[0;31mERROR: Missing required metadata key from template '\e[1;37m#{File.basename(template, '.md')}\e[0;31m'\e[0m)
535
- warn %(\e[0;31mPlease define \e[1;33m#{req.downcase}\e[0;31m in build notes\e[0m)
545
+ warn Color.template(%({xr}ERROR: Missing required metadata key from template '{bw}#{File.basename(template, '.md')}{xr}'{x}))
546
+ warn Color.template(%({xr}Please define {by}#{req.downcase}{xr} in build notes{x}))
536
547
  Process.exit 1
537
548
  end
538
549
  end
@@ -581,6 +592,24 @@ module Howzit
581
592
  template_topics
582
593
  end
583
594
 
595
+ def include_file(m)
596
+ file = File.expand_path(m[1])
597
+
598
+ return m[0] unless File.exist?(file)
599
+
600
+ content = IO.read(file)
601
+ home = ENV['HOME']
602
+ short_path = File.dirname(file.sub(/^#{home}/, '~'))
603
+ prefix = "#{short_path}/#{File.basename(file)}:"
604
+ parts = content.split(/^##+/)
605
+ parts.shift
606
+ if parts.empty?
607
+ content
608
+ else
609
+ "## #{parts.join('## ')}".gsub(/^(##+ *)(?=\S)/, "\\1#{prefix}")
610
+ end
611
+ end
612
+
584
613
  # Read in the build notes file and output a hash of "Title" => contents
585
614
  def read_help_file(path = nil)
586
615
  filename = path.nil? ? note_file : path
@@ -588,21 +617,7 @@ module Howzit
588
617
  help = IO.read(filename)
589
618
 
590
619
  help.gsub!(/@include\((.*?)\)/) do
591
- m = Regexp.last_match
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
620
+ include_file(Regexp.last_match)
606
621
  end
607
622
 
608
623
  template_topics = get_template_topics(help)
@@ -673,7 +688,8 @@ module Howzit
673
688
  matches
674
689
  end
675
690
 
676
- def initialize(args)
691
+ def initialize(args = [])
692
+ Color.coloring = $stdout.isatty
677
693
  flags = {
678
694
  run: false,
679
695
  list_topics: false,
@@ -683,7 +699,8 @@ module Howzit
683
699
  title_only: false,
684
700
  choose: false,
685
701
  quiet: false,
686
- verbose: false
702
+ verbose: false,
703
+ default: false
687
704
  }
688
705
 
689
706
  defaults = {
@@ -716,7 +733,8 @@ module Howzit
716
733
  OptionParser.new do |opts|
717
734
  opts.banner = "Usage: #{__FILE__} [OPTIONS] [TOPIC]"
718
735
  opts.separator ''
719
- 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.'
736
+ opts.separator 'Show build notes for the current project (buildnotes.md).
737
+ Include a topic name to see just that topic, or no argument to display all.'
720
738
  opts.separator ''
721
739
  opts.separator 'Options:'
722
740
 
@@ -725,7 +743,8 @@ module Howzit
725
743
  Process.exit 0
726
744
  end
727
745
 
728
- opts.on('-e', '--edit', "Edit buildnotes file in current working directory using #{File.basename(ENV['EDITOR'])}") do
746
+ opts.on('-e', '--edit', "Edit buildnotes file in current working directory
747
+ using $EDITOR") do
729
748
  edit_note
730
749
  Process.exit 0
731
750
  end
@@ -743,7 +762,8 @@ module Howzit
743
762
  @options[:list_topics] = true
744
763
  end
745
764
 
746
- opts.on('-m', '--matching TYPE', MATCHING_OPTIONS, 'Topics matching type', "(#{MATCHING_OPTIONS.join(', ')})") do |c|
765
+ opts.on('-m', '--matching TYPE', MATCHING_OPTIONS,
766
+ 'Topics matching type', "(#{MATCHING_OPTIONS.join(', ')})") do |c|
747
767
  @options[:matching] = c
748
768
  end
749
769
 
@@ -776,8 +796,8 @@ module Howzit
776
796
  @options[:log_level] = 0
777
797
  end
778
798
 
779
- opts.on('-u', '--upstream', 'Traverse up parent directories for additional build notes') do
780
- @options[:include_upstream] = true
799
+ opts.on('-u', '--[no-]upstream', 'Traverse up parent directories for additional build notes') do |p|
800
+ @options[:include_upstream] = p
781
801
  end
782
802
 
783
803
  opts.on('--show-code', 'Display the content of fenced run blocks') do
@@ -788,7 +808,7 @@ module Howzit
788
808
  @options[:wrap] = w.to_i
789
809
  end
790
810
 
791
- opts.on('--edit-config', "Edit configuration file using #{File.basename(ENV['EDITOR'])}") do
811
+ opts.on('--edit-config', "Edit configuration file using default $EDITOR") do
792
812
  edit_config(defaults)
793
813
  Process.exit 0
794
814
  end
@@ -802,21 +822,21 @@ module Howzit
802
822
  Dir.chdir(template_folder)
803
823
  Dir.glob('*.md').each do |file|
804
824
  template = File.basename(file, '.md')
805
- puts "\e[7;30;45mtemplate: \e[7;33;40m#{template}\e[0m"
806
- puts "\e[1;30m[\e[1;37mtasks\e[1;30m]──────────────────────────────────────┐\e[0m"
825
+ puts Color.template("{Mk}template:{Yk}#{template}{x}")
826
+ puts Color.template("{bk}[{bl}tasks{bk}]──────────────────────────────────────┐{x}")
807
827
  metadata = file.extract_metadata
808
828
  topics = read_help_file(file)
809
- topics.keys.each do |topic|
810
- puts " \e[1;30m│\e[1;37m-\e[0m \e[1;36;40m#{template}:#{topic.sub(/^.*?:/, '')}\e[0m"
829
+ topics.each_key do |topic|
830
+ puts Color.template(" {bk}│{bw}-{x} {bcK}#{template}:#{topic.sub(/^.*?:/, '')}{x}")
811
831
  end
812
832
  if metadata.size > 0
813
833
  meta = []
814
- meta << metadata['required'].split(/\s*,\s*/).map {|m| "*\e[1;37m#{m}\e[0;37m" } if metadata.key?('required')
834
+ meta << metadata['required'].split(/\s*,\s*/).map {|m| "*{bw}#{m}{xw}" } if metadata.key?('required')
815
835
  meta << metadata['optional'].split(/\s*,\s*/).map {|m| "#{m}" } if metadata.key?('optional')
816
- puts "\e[1;30m[\e[1;34mmeta\e[1;30m]───────────────────────────────────────┤\e[0m"
817
- puts " \e[1;30m│\e[1;37m \e[0;37m#{meta.join(", ")}\e[0m"
836
+ puts Color.template("{bk}[{bl}meta{bk}]───────────────────────────────────────┤{x}")
837
+ puts Color.template(" {bk}│ {xw}#{meta.join(", ")}{x}")
818
838
  end
819
- puts " \e[1;30m└───────────────────────────────────────────┘\e[0m"
839
+ puts Color.template(" {bk}└───────────────────────────────────────────┘{x}")
820
840
  end
821
841
  Process.exit 0
822
842
  end
@@ -843,9 +863,13 @@ module Howzit
843
863
  puts "Howzit v#{VERSION}"
844
864
  Process.exit 0
845
865
  end
866
+
867
+ opts.on('--default', 'Answer all prompts with default response') do
868
+ @options[:default] = true
869
+ end
846
870
  end.parse!(args)
847
871
 
848
- process(args)
872
+ @cli_args = args
849
873
  end
850
874
 
851
875
  def edit_note
@@ -862,7 +886,7 @@ module Howzit
862
886
  end
863
887
 
864
888
  ##
865
- ## @brief Traverse up directory tree looking for build notes
889
+ ## Traverse up directory tree looking for build notes
866
890
  ##
867
891
  ## @return topics dictionary
868
892
  ##
@@ -1031,7 +1055,7 @@ module Howzit
1031
1055
  File.join(config_dir, 'templates')
1032
1056
  end
1033
1057
 
1034
- def create_config
1058
+ def create_config(defaults)
1035
1059
  dir, file = [config_dir, config_file]
1036
1060
  unless File.directory?(dir)
1037
1061
  warn "Creating config directory at #{dir}"
@@ -1046,7 +1070,7 @@ module Howzit
1046
1070
  end
1047
1071
 
1048
1072
  def load_config(defaults)
1049
- file = create_config
1073
+ file = create_config(defaults)
1050
1074
  config = YAML.load(IO.read(file))
1051
1075
  newconfig = config ? defaults.merge(config) : defaults
1052
1076
  write_config(newconfig)
@@ -1064,7 +1088,7 @@ module Howzit
1064
1088
  `#{ENV['EDITOR']} "#{config_file}"`
1065
1089
  end
1066
1090
 
1067
- def process(args)
1091
+ def process
1068
1092
  output = []
1069
1093
 
1070
1094
  unless note_file
@@ -1080,13 +1104,13 @@ module Howzit
1080
1104
  end
1081
1105
 
1082
1106
  if @options[:title_only]
1083
- out = get_note_title(note_file, 20)
1107
+ out = get_note_title(20)
1084
1108
  $stdout.print(out.strip)
1085
1109
  Process.exit(0)
1086
1110
  elsif @options[:output_title]
1087
- title = get_note_title(note_file)
1111
+ title = get_note_title
1088
1112
  if title && !title.empty?
1089
- header = format_header(title, { hr: "\u{2550}", color: '1;37;40' })
1113
+ header = format_header(title, { hr: "\u{2550}", color: '{bwK}' })
1090
1114
  output.push("#{header}\n")
1091
1115
  end
1092
1116
  end
@@ -1118,14 +1142,14 @@ module Howzit
1118
1142
  elsif @options[:choose]
1119
1143
  topic_match = choose(topics.keys)
1120
1144
  # If there are arguments use those to search for a matching topic
1121
- elsif !args.empty?
1145
+ elsif !@cli_args.empty?
1122
1146
 
1123
- search = args.join(' ').strip.downcase
1147
+ search = @cli_args.join(' ').strip.downcase
1124
1148
  matches = match_topic(search)
1125
1149
 
1126
1150
  if matches.empty?
1127
- output.push(%(\e[0;31mERROR: No topic match found for \e[1;33m#{search}\e[0m\n))
1128
- if !@options[:show_all_on_error]
1151
+ output.push(Color.template(%({bR}ERROR:{xr} No topic match found for {bw}#{search}{x}\n)))
1152
+ unless @options[:show_all_on_error]
1129
1153
  show(output.join("\n"), { color: true, highlight: false, paginate: false, wrap: 0 })
1130
1154
  Process.exit 1
1131
1155
  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/prompt.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Howzit
4
+ # Command line prompt utils
4
5
  module Prompt
5
6
  def yn(prompt, default = true)
7
+ return default if !$stdout.isatty
8
+
6
9
  system 'stty cbreak'
7
10
  yn = color_single_options(default ? %w[Y n] : %w[y N])
8
11
  $stdout.syswrite "\e[1;37m#{prompt} #{yn}\e[1;37m? \e[0m"
@@ -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.10'.freeze
5
+ VERSION = '1.2.13'.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,32 @@ describe Howzit::BuildNotes do
9
9
  end
10
10
  end
11
11
  end
12
+
13
+ describe Howzit::BuildNotes do
14
+ Dir.chdir('spec')
15
+ how = Howzit::BuildNotes.new(['--no-upstream', '--default'])
16
+ how.create_note
17
+ subject { how }
18
+
19
+ describe ".note_file" do
20
+ it "locates a build note file" do
21
+ expect(subject.note_file).not_to be_empty
22
+ end
23
+ end
24
+
25
+ describe ".grep_topics" do
26
+ it "found editable" do
27
+ expect(subject.grep_topics('editable')).to include('File Structure')
28
+ expect(subject.grep_topics('editable')).not_to include('Build')
29
+ end
30
+ end
31
+
32
+ describe ".list_topic_titles" do
33
+ it "found 4 topics" do
34
+ expect(subject.topics.keys.count).to eq 4
35
+ end
36
+ it "outputs a newline-separated string" do
37
+ expect(subject.list_topic_titles.scan(/\n/).count).to eq 3
38
+ end
39
+ end
40
+ 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,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: howzit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.10
4
+ version: 1.2.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-01 00:00:00.000000000 Z
11
+ date: 2022-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -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