howzit 2.1.10 → 2.1.13

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: 67377505c45973e38c5cc2430f2d9428ace8a5680a2bce0cfc26af369a1a98b2
4
- data.tar.gz: bd6e00ccaf0484f486c965b0136fbd7a0cbb4af6cd7cf21c777b029c316ff13b
3
+ metadata.gz: f0fc931036b21c7bb13c805751a146432d14c40a9a910fd9999aa42fb34658cb
4
+ data.tar.gz: 31b927c85e4c7ba6938eff929e7670c1a997d79f21053ce230da3a1a5ca4b246
5
5
  SHA512:
6
- metadata.gz: 9fc93201a2080344c7ea080697ccd6a38a0c6a4a7473d98c9bbe2b18f4685ada0799cc9013358d01b7e29696e3a63d074c0e6421dbbf9db55fe69e0586efeae8
7
- data.tar.gz: 1a627938a04104298a13ee667876f7c7559707e08a1fdecc136837af26e1c9e0130ab34fd4be1f5b1a3b79172c3d7844cec4c43d147aa1308ce8d21c2c4251ad
6
+ metadata.gz: 6382fd78151da4a021d605276121fc48f0b1cb7692ee3a4657fd4dee820577f0f5357aa98be565dd120e440619906b1881da2269fe0e53c2321160a34dee26ac
7
+ data.tar.gz: d402a1dcf7715f04ed32fc282450c4132bf13cd3a43830eb464c298f79dc86fa4c47f30a6bd8d3698e588468f107b0b4eae6ad90c89195d878ef1f610846ab78
data/.rubocop.yml CHANGED
@@ -1,15 +1,51 @@
1
+ require:
2
+ - rubocop-rspec
3
+ - rubocop-rake
4
+
1
5
  AllCops:
2
6
  Include:
3
7
  - Gemfile
4
8
  - Guardfile
5
9
  - Rakefile
10
+ - bin/howzit
11
+ - lib/**/*.rb
12
+
13
+ Style/StringLiterals:
14
+ Enabled: true
15
+ EnforcedStyle: single_quotes
16
+
17
+ Style/StringLiteralsInInterpolation:
18
+ Enabled: true
19
+ EnforcedStyle: single_quotes
20
+
21
+ Layout/LineLength:
22
+ Max: 120
23
+
24
+ Metrics/MethodLength:
25
+ Max: 40
6
26
 
7
27
  Metrics/BlockLength:
28
+ Max: 40
8
29
  Exclude:
9
30
  - Rakefile
10
31
  - bin/howzit
11
32
  - lib/*.rb
12
33
 
34
+ Metrics/ClassLength:
35
+ Max: 300
36
+
37
+ Metrics/CyclomaticComplexity:
38
+ Max: 10
39
+
40
+ Metrics/PerceivedComplexity:
41
+ Max: 30
42
+
43
+ Metrics/AbcSize:
44
+ Max: 45
45
+
46
+ Metrics/CyclomaticComplexity:
47
+ Max: 15
48
+
13
49
  Style/RegexpLiteral:
14
50
  Exclude:
15
51
  - Guardfile
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ### 2.1.13
2
+
3
+ 2024-08-05 12:04
4
+
5
+ ### 2.1.12
6
+
7
+ 2024-08-05 12:01
8
+
9
+ ### 2.1.11
10
+
11
+ 2024-08-05 12:01
12
+
1
13
  ### 2.1.10
2
14
 
3
15
  2024-04-09 14:59
data/Gemfile CHANGED
@@ -2,5 +2,8 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
+ gem 'rubocop-rake', require: false
6
+ gem 'rubocop-rspec', require: false
7
+
5
8
  # Specify your gem's dependencies in howzit.gemspec.
6
9
  gemspec
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  # Howzit
2
3
 
3
4
  [![Gem](https://img.shields.io/gem/v/howzit.svg)](https://rubygems.org/gems/howzit)
@@ -8,8 +9,6 @@ A command-line reference tool for tracking project build systems
8
9
 
9
10
  Howzit is a tool that allows you to keep Markdown-formatted notes about a project's tools and procedures. It functions as an easy lookup for notes about a particular task, as well as a task runner to automatically execute appropriate commands.
10
11
 
11
- <!--README-->
12
-
13
12
  ## Features
14
13
 
15
14
  - Match topic titles with any portion of title
@@ -55,8 +54,6 @@ If you run into permission errors using the above command, you'll need to use `g
55
54
 
56
55
  This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details.
57
56
 
58
- <!--END README-->
59
-
60
57
  ## Warranty
61
58
 
62
59
  This software is provided "as is" and without any express or
@@ -74,6 +71,7 @@ purpose.
74
71
  [RubyDoc]: http://www.rubydoc.info/gems/howzit
75
72
  [Omniref]: https://www.omniref.com/ruby/gems/howzit
76
73
 
74
+
77
75
  ## Development and Testing
78
76
 
79
77
  ### Source Code
@@ -143,3 +141,4 @@ To submit a patch:
143
141
  5. Push to the branch (`git push origin my-new-feature`).
144
142
  6. Create a new Pull Request.
145
143
 
144
+
data/Rakefile CHANGED
@@ -76,4 +76,35 @@ task :dockertest, :version, :login do |_, args|
76
76
  # puts commit&.empty? ? "Error commiting Docker tag #{img}" : "Committed Docker tag #{img}"
77
77
  end
78
78
 
79
+ desc 'Alias for build'
79
80
  task package: :build
81
+
82
+ desc 'Bump incremental version number'
83
+ task :bump, :type do |_, args|
84
+ args.with_defaults(type: 'inc')
85
+ version_file = 'lib/howzit/version.rb'
86
+ content = IO.read(version_file)
87
+ content.sub!(/VERSION = '(?<major>\d+)\.(?<minor>\d+)\.(?<inc>\d+)(?<pre>\S+)?'/) do
88
+ m = Regexp.last_match
89
+ major = m['major'].to_i
90
+ minor = m['minor'].to_i
91
+ inc = m['inc'].to_i
92
+ pre = m['pre']
93
+
94
+ case args[:type]
95
+ when /^maj/
96
+ major += 1
97
+ minor = 0
98
+ inc = 0
99
+ when /^min/
100
+ minor += 1
101
+ inc = 0
102
+ else
103
+ inc += 1
104
+ end
105
+
106
+ $stdout.puts "At version #{major}.#{minor}.#{inc}#{pre}"
107
+ "VERSION = '#{major}.#{minor}.#{inc}#{pre}'"
108
+ end
109
+ File.open(version_file, 'w+') { |f| f.puts content }
110
+ end
@@ -69,6 +69,7 @@ module Howzit
69
69
  ##
70
70
  def find_topic(term = nil)
71
71
  return @topics if term.nil?
72
+
72
73
  @topics.filter do |topic|
73
74
  rx = term.to_rx
74
75
  topic.title.downcase.sub(/ *\(.*?\) *$/, '') =~ rx
@@ -114,7 +115,7 @@ module Howzit
114
115
  ## @return [Array] array of topic titles
115
116
  ##
116
117
  def list_topics
117
- @topics.map { |topic| topic.title }
118
+ @topics.map(&:title)
118
119
  end
119
120
 
120
121
  ##
@@ -360,19 +361,21 @@ module Howzit
360
361
  ##
361
362
  def ensure_requirements(template)
362
363
  t_leader = Util.read_file(template).split(/^#/)[0].strip
363
- if t_leader.length > 0
364
- t_meta = t_leader.get_metadata
365
-
366
- if t_meta.key?('required')
367
- required = t_meta['required'].strip.split(/\s*,\s*/)
368
- required.each do |req|
369
- unless @metadata.keys.include?(req.downcase)
370
- Howzit.console.error %({bRw}ERROR:{xbr} Missing required metadata key from template '{bw}#{File.basename(template, '.md')}{xr}'{x}).c
371
- Howzit.console.error %({br}Please define {by}#{req.downcase}{xr} in build notes{x}).c
372
- Process.exit 1
373
- end
374
- end
375
- end
364
+ return unless t_leader.length.positive?
365
+
366
+ t_meta = t_leader.get_metadata
367
+
368
+ return unless t_meta.key?('required')
369
+
370
+ required = t_meta['required'].strip.split(/\s*,\s*/)
371
+ required.each do |req|
372
+ next if @metadata.keys.include?(req.downcase)
373
+
374
+ Howzit.console.error %({bRw}ERROR:{xbr} Missing required metadata key from template '{bw}#{File.basename(
375
+ template, '.md'
376
+ )}{xr}'{x}).c
377
+ Howzit.console.error %({br}Please define {by}#{req.downcase}{xr} in build notes{x}).c
378
+ Process.exit 1
376
379
  end
377
380
  end
378
381
 
@@ -388,7 +391,7 @@ module Howzit
388
391
  subtopics = nil
389
392
 
390
393
  if template =~ /\[(.*?)\]$/
391
- subtopics = Regexp.last_match[1].split(/\s*\|\s*/).map { |t| t.gsub(/\*/, '.*?')}
394
+ subtopics = Regexp.last_match[1].split(/\s*\|\s*/).map { |t| t.gsub(/\*/, '.*?') }
392
395
  template.sub!(/\[.*?\]$/, '').strip
393
396
  end
394
397
 
@@ -640,11 +643,10 @@ module Howzit
640
643
  Howzit.has_read_upstream = true
641
644
  end
642
645
 
643
- if note_file && @topics.empty?
644
- Howzit.console.error("{br}Note file found but no topics detected in #{note_file}{x}".c)
645
- Process.exit 1
646
- end
646
+ return unless note_file && @topics.empty?
647
647
 
648
+ Howzit.console.error("{br}Note file found but no topics detected in #{note_file}{x}".c)
649
+ Process.exit 1
648
650
  end
649
651
 
650
652
  ##
@@ -704,7 +706,7 @@ module Howzit
704
706
  new_topic.print_out({ single: single })
705
707
  end
706
708
 
707
- output.nil? ? '' : output.join("\n")
709
+ output.nil? ? '' : output.join("\n\n")
708
710
  end
709
711
 
710
712
  ##
@@ -782,7 +784,7 @@ module Howzit
782
784
  when :all
783
785
  topic_matches.concat(matches)
784
786
  else
785
- titles = matches.map { |topic| topic.title }
787
+ titles = matches.map(&:title)
786
788
  res = Prompt.choose(titles)
787
789
  old_matching = Howzit.options[:matching]
788
790
  Howzit.options[:matching] = 'exact'
data/lib/howzit/colors.rb CHANGED
@@ -5,7 +5,7 @@ module Howzit
5
5
  # Terminal output color functions.
6
6
  module Color
7
7
  # Regexp to match excape sequences
8
- ESCAPE_REGEX = /(?<=\[)(?:(?:(?:[349]|10)[0-9]|[0-9])?;?)+(?=m)/.freeze
8
+ ESCAPE_REGEX = /(?<=\[)(?:(?:(?:[349]|10)[0-9]|[0-9])?;?)+(?=m)/
9
9
 
10
10
  # All available color names. Available as methods and string extensions.
11
11
  #
@@ -113,7 +113,9 @@ module Howzit
113
113
  compiled = ''
114
114
  normalize_color.split('').each do |char|
115
115
  compiled += char
116
- valid_color = compiled if Color.attributes.include?(compiled.to_sym) || compiled =~ /^([fb]g?)?#([a-f0-9]{6})$/i
116
+ if Color.attributes.include?(compiled.to_sym) || compiled =~ /^([fb]g?)?#([a-f0-9]{6})$/i
117
+ valid_color = compiled
118
+ end
117
119
  end
118
120
 
119
121
  valid_color
@@ -271,7 +273,12 @@ module Howzit
271
273
  input = input.join(' ') if input.is_a? Array
272
274
  fmt = input.gsub(/%/, '%%')
273
275
  fmt = fmt.gsub(/(?<!\\u|\$)\{(\w+)\}/i) do
274
- Regexp.last_match(1).split('').map { |c| "%<#{c}>s" }.join('')
276
+ m = Regexp.last_match(1)
277
+ if m =~ /^[wkglycmrWKGLYCMRdbuix]+$/
278
+ m.split('').map { |c| "%<#{c}>s" }.join('')
279
+ else
280
+ Regexp.last_match(0)
281
+ end
275
282
  end
276
283
 
277
284
  colors = { w: white, k: black, g: green, l: blue,
@@ -333,9 +340,28 @@ module Howzit
333
340
  module_eval(new_method)
334
341
  end
335
342
 
343
+ ##
344
+ ## Generate escape codes for hex colors
345
+ ##
346
+ ## @param hex [String] The hexadecimal color code
347
+ ##
348
+ ## @return [String] ANSI escape string
349
+ ##
350
+ def rgb(hex)
351
+ is_bg = hex.match(/^bg?#/) ? true : false
352
+ hex_string = hex.sub(/^([fb]g?)?#/, '')
353
+
354
+ parts = hex_string.match(/(?<r>..)(?<g>..)(?<b>..)/)
355
+ t = []
356
+ %w[r g b].each do |e|
357
+ t << parts[e].hex
358
+ end
359
+ "\e[#{is_bg ? '48' : '38'};2;#{t.join(';')}m"
360
+ end
361
+
336
362
  # Regular expression that is used to scan for ANSI-sequences while
337
363
  # uncoloring strings.
338
- COLORED_REGEXP = /\e\[(?:(?:[349]|10)[0-7]|[0-9])?m/.freeze
364
+ COLORED_REGEXP = /\e\[(?:(?:[349]|10)[0-7]|[0-9])?m/
339
365
 
340
366
  # Returns an uncolored version of the string, that is all
341
367
  # ANSI-sequences are stripped from the string.
data/lib/howzit/config.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Howzit
2
4
  # Config Class
3
5
  class Config
@@ -95,7 +97,7 @@ module Howzit
95
97
  def should_ignore(filename)
96
98
  return false unless File.exist?(ignore_file)
97
99
 
98
- @ignore_patterns ||= YAML.safe_load(Util.read_file(ignore_file))
100
+ @ignore_patterns ||= YAML.load(Util.read_file(ignore_file))
99
101
 
100
102
  ignore = false
101
103
 
data/lib/howzit/prompt.rb CHANGED
@@ -105,7 +105,7 @@ module Howzit
105
105
  Howzit.console.info 'Cancelled'
106
106
  Process.exit 0
107
107
  end
108
- return res.split(/\n/)
108
+ res.split(/\n/)
109
109
  end
110
110
 
111
111
  def fzf_options(height)
@@ -103,7 +103,7 @@ module Howzit
103
103
 
104
104
  # Just strip out color codes when requested
105
105
  def uncolor
106
- gsub(/\e\[[\d;]+m/, '').gsub(/\e\]1337;SetMark/,'')
106
+ gsub(/\e\[[\d;]+m/, '').gsub(/\e\]1337;SetMark/, '')
107
107
  end
108
108
 
109
109
  # Wrap text at a specified width.
@@ -277,7 +277,7 @@ module Howzit
277
277
  def extract_metadata
278
278
  if File.exist?(self)
279
279
  leader = Util.read_file(self).split(/^#/)[0].strip
280
- leader.length > 0 ? leader.get_metadata : {}
280
+ leader.length.positive? ? leader.get_metadata : {}
281
281
  else
282
282
  {}
283
283
  end
@@ -361,11 +361,11 @@ module Howzit
361
361
 
362
362
  case Howzit.options[:header_format]
363
363
  when :block
364
- Color.template("#{options[:color]}\u{258C}#{title}#{should_mark_iterm? && options[:mark] ? iterm_marker : ''}{x}")
364
+ Color.template("\n\n#{options[:color]}\u{258C}#{title}#{should_mark_iterm? && options[:mark] ? iterm_marker : ''}{x}")
365
365
  else
366
366
  cols = TTY::Screen.columns
367
367
 
368
- cols = Howzit.options[:wrap] if (Howzit.options[:wrap]).positive? && cols > Howzit.options[:wrap]
368
+ cols = Howzit.options[:wrap] if Howzit.options[:wrap].positive? && cols > Howzit.options[:wrap]
369
369
  title = Color.template("#{options[:border]}#{options[:hr] * 2}( #{options[:color]}#{title}#{options[:border]} )")
370
370
 
371
371
  tail = if should_mark_iterm?
@@ -373,7 +373,7 @@ module Howzit
373
373
  else
374
374
  options[:hr] * (cols - title.uncolor.length)
375
375
  end
376
- Color.template("#{title}#{tail}{x}")
376
+ Color.template("\n\n#{title}#{tail}{x}")
377
377
  end
378
378
  end
379
379
  end
data/lib/howzit/task.rb CHANGED
@@ -39,7 +39,7 @@ module Howzit
39
39
  ## @return [String] description
40
40
  ##
41
41
  def inspect
42
- %(<#Howzit::Task @type=:#{@type} @title="#{@title}" @arguments=#{@arguments} @block?=#{@action.split(/\n/).count > 1}>)
42
+ %(<#Howzit::Task @type=:#{@type} @title="#{@title}" @action="#{@action}" @arguments=#{@arguments} @block?=#{@action.split(/\n/).count > 1}>)
43
43
  end
44
44
 
45
45
  ##
@@ -95,7 +95,8 @@ module Howzit
95
95
  def run_run
96
96
  title = Howzit.options[:show_all_code] ? @action : @title
97
97
  Howzit.console.info("#{@prefix}{bg}Running {bw}#{title}{x}".c)
98
- return system(@action)
98
+ ENV['HOWZIT_SCRIPTS'] = File.expand_path('~/.config/howzit/scripts')
99
+ system(@action)
99
100
  end
100
101
 
101
102
  ##
@@ -105,7 +106,7 @@ module Howzit
105
106
  title = Howzit.options[:show_all_code] ? @action : @title
106
107
  Howzit.console.info("#{@prefix}{bg}Copied {bw}#{title}{bg} to clipboard{x}".c)
107
108
  Util.os_copy(@action)
108
- return true
109
+ true
109
110
  end
110
111
 
111
112
  ##
data/lib/howzit/topic.rb CHANGED
@@ -56,15 +56,28 @@ module Howzit
56
56
  @title =~ /#{term}/i || @content =~ /#{term}/i
57
57
  end
58
58
 
59
+ def ask_task(task)
60
+ note = if task.type == :include
61
+ task_count = Howzit.buildnote.find_topic(task.action)[0].tasks.count
62
+ " (#{task_count} tasks)"
63
+ else
64
+ ''
65
+ end
66
+ q = %({bg}#{task.type.to_s.capitalize} {xw}"{bw}#{task.title}{xw}"#{note}{x}).c
67
+ Prompt.yn(q, default: task.default)
68
+ end
69
+
70
+ def check_cols
71
+ TTY::Screen.columns > 60 ? 60 : TTY::Screen.columns
72
+ rescue StandardError
73
+ 60
74
+ end
75
+
59
76
  # Handle run command, execute directives in topic
60
77
  def run(nested: false)
61
78
  output = []
62
79
 
63
- cols = begin
64
- TTY::Screen.columns > 60 ? 60 : TTY::Screen.columns
65
- rescue StandardError
66
- 60
67
- end
80
+ cols = check_cols
68
81
 
69
82
  if @tasks.count.positive?
70
83
  unless @prereqs.empty?
@@ -75,18 +88,8 @@ module Howzit
75
88
  end
76
89
 
77
90
  @tasks.each do |task|
78
- if task.optional || Howzit.options[:ask]
79
- note = if task.type == :include
80
- task_count = Howzit.buildnote.find_topic(task.action)[0].tasks.count
81
- " (#{task_count} tasks)"
82
- else
83
- ''
84
- end
85
- q = %({bg}#{task.type.to_s.capitalize} {xw}"{bw}#{task.title}{xw}"#{note}{x}).c
86
- res = Prompt.yn(q, default: task.default)
87
- next unless res
91
+ next if (task.optional || Howzit.options[:ask]) && !ask_task(task)
88
92
 
89
- end
90
93
  run_output, total, success = task.run
91
94
 
92
95
  output.concat(run_output)
@@ -123,6 +126,116 @@ module Howzit
123
126
  output
124
127
  end
125
128
 
129
+ def title_option(color, topic, keys, opt)
130
+ option = colored_option(color, topic, keys)
131
+ "#{opt[:single] ? 'From' : 'Include'} #{topic.title}#{option}:"
132
+ end
133
+
134
+ def colored_option(color, topic, keys)
135
+ if topic.tasks.empty?
136
+ ''
137
+ else
138
+ optional = keys[:optional] =~ /[?!]+/ ? true : false
139
+ default = keys[:optional] =~ /!/ ? false : true
140
+ if optional
141
+ colored_yn(color, default)
142
+ else
143
+ ''
144
+ end
145
+ end
146
+ end
147
+
148
+ def colored_yn(color, default)
149
+ if default
150
+ " {xKk}[{gbK}Y{xKk}/{dbwK}n{xKk}]{x}#{color}".c
151
+ else
152
+ " {xKk}[{dbwK}y{xKk}/{bgK}N{xKk}]{x}#{color}".c
153
+ end
154
+ end
155
+
156
+ ##
157
+ ## Handle an include statement
158
+ ##
159
+ ## @param keys [Hash] The symbolized keys and values from the regex
160
+ ## that found the statement
161
+ ## @param opt [Hash] Options
162
+ ##
163
+ def process_include(keys, opt)
164
+ output = []
165
+
166
+ if keys[:action] =~ / *\[(.*?)\] *$/
167
+ Howzit.named_arguments = @named_args
168
+ Howzit.arguments = Regexp.last_match(1).split(/ *, */).map!(&:render_arguments)
169
+ end
170
+
171
+ matches = Howzit.buildnote.find_topic(keys[:action].sub(/ *\[.*?\] *$/, ''))
172
+
173
+ return [] if matches.empty?
174
+
175
+ topic = matches[0]
176
+
177
+ rule = '{kKd}'
178
+ color = '{Kyd}'
179
+ title = title_option(color, topic, keys, opt)
180
+ options = { color: color, hr: '.', border: rule }
181
+
182
+ output.push("#{'> ' * @nest_level}#{title}".format_header(options)) unless Howzit.inclusions.include?(topic)
183
+
184
+ if opt[:single] && Howzit.inclusions.include?(topic)
185
+ output.push("#{'> ' * @nest_level}#{title} included above".format_header(options))
186
+ elsif opt[:single]
187
+ @nest_level += 1
188
+
189
+ output.concat(topic.print_out({ single: true, header: false }))
190
+ output.push("#{'> ' * @nest_level}...".format_header(options))
191
+ @nest_level -= 1
192
+ end
193
+ Howzit.inclusions.push(topic)
194
+
195
+ output
196
+ end
197
+
198
+ def color_directive_yn(keys)
199
+ optional, default = define_optional(keys[:optional])
200
+ if optional
201
+ default ? ' {xk}[{g}Y{xk}/{dbw}n{xk}]{x}'.c : ' {xk}[{dbw}y{xk}/{g}N{xk}]{x}'.c
202
+ else
203
+ ''
204
+ end
205
+ end
206
+
207
+ def process_directive(keys)
208
+ cmd = keys[:cmd]
209
+ obj = keys[:action]
210
+ title = keys[:title].empty? ? obj : keys[:title].strip
211
+ title = Howzit.options[:show_all_code] ? obj : title
212
+ option = color_directive_yn(keys)
213
+ icon = case cmd
214
+ when 'run'
215
+ "\u{25B6}"
216
+ when 'copy'
217
+ "\u{271A}"
218
+ when /open|url/
219
+ "\u{279A}"
220
+ end
221
+
222
+ "{bmK}#{icon} {bwK}#{title.preserve_escapes}{x}#{option}".c
223
+ end
224
+
225
+ def define_optional(optional)
226
+ is_optional = optional =~ /[?!]+/ ? true : false
227
+ default = optional =~ /!/ ? false : true
228
+ [is_optional, default]
229
+ end
230
+
231
+ def title_code_block(keys)
232
+ if keys[:title].length.positive?
233
+ "Block: #{keys[:title]}#{color_directive_yn(keys)}"
234
+ else
235
+ "Code Block#{color_directive_yn(keys)}"
236
+ end
237
+ end
238
+
126
239
  # Output a topic with fancy title and bright white text.
127
240
  #
128
241
  # @param options [Hash] The options
@@ -139,99 +252,23 @@ module Howzit
139
252
  output.push('')
140
253
  end
141
254
  topic = @content.dup
142
- topic.gsub!(/(?mi)^(`{3,})run([?!]*) *([^\n]*)[\s\S]*?\n\1\s*$/, '@@@run\2 \3') unless Howzit.options[:show_all_code]
255
+ unless Howzit.options[:show_all_code]
256
+ topic.gsub!(/(?mix)^(`{3,})run([?!]*)\s*
257
+ ([^\n]*)[\s\S]*?\n\1\s*$/, '@@@run\2 \3')
258
+ end
143
259
  topic.split(/\n/).each do |l|
144
260
  case l
145
261
  when /@(before|after|prereq|end)/
146
262
  next
147
- when /@include(?<optional>[!?]{1,2})?\((?<action>[^\)]+)\)/
148
- m = Regexp.last_match.named_captures.symbolize_keys
149
-
150
- if m[:action] =~ / *\[(.*?)\] *$/
151
- Howzit.named_arguments = @named_args
152
- Howzit.arguments = Regexp.last_match(1).split(/ *, */).map!(&:render_arguments)
153
- end
154
-
155
- matches = Howzit.buildnote.find_topic(m[:action].sub(/ *\[.*?\] *$/, ''))
156
-
157
- unless matches.empty?
158
- i_topic = matches[0]
159
-
160
- rule = '{kKd}'
161
- color = '{Kyd}'
162
- option = if i_topic.tasks.empty?
163
- ''
164
- else
165
- optional = m[:optional] =~ /[?!]+/ ? true : false
166
- default = m[:optional] =~ /!/ ? false : true
167
- if optional
168
- default ? " {xKk}[{gbK}Y{xKk}/{dbwK}n{xKk}]{x}#{color}".c : " {xKk}[{dbwK}y{xKk}/{bgK}N{xKk}]{x}#{color}".c
169
- else
170
- ''
171
- end
172
- end
173
- title = "#{opt[:single] ? 'From' : 'Include'} #{i_topic.title}#{option}:"
174
- options = { color: color, hr: '.', border: rule }
175
- unless Howzit.inclusions.include?(i_topic)
176
- output.push("#{'> ' * @nest_level}#{title}".format_header(options))
177
- end
178
-
179
- if opt[:single] && Howzit.inclusions.include?(i_topic)
180
- output.push("#{'> ' * @nest_level}#{title} included above".format_header(options))
181
- elsif opt[:single]
182
- @nest_level += 1
183
-
184
- output.concat(i_topic.print_out({ single: true, header: false }))
185
- output.push("#{'> ' * @nest_level}...".format_header(options))
186
- @nest_level -= 1
187
- end
188
- Howzit.inclusions.push(i_topic)
189
- end
263
+ when /@include(?<optional>[!?]{1,2})?\((?<action>[^)]+)\)/
264
+ output.concat(process_include(Regexp.last_match.named_captures.symbolize_keys, opt))
190
265
  when /@(?<cmd>run|copy|open|url)(?<optional>[?!]{1,2})?\((?<action>.*?)\) *(?<title>.*?)$/
191
- m = Regexp.last_match.named_captures.symbolize_keys
192
- cmd = m[:cmd]
193
- obj = m[:action]
194
- title = m[:title].empty? ? obj : m[:title].strip
195
- title = Howzit.options[:show_all_code] ? obj : title
196
- optional = m[:optional] =~ /[?!]+/ ? true : false
197
- default = m[:optional] =~ /!/ ? false : true
198
- option = if optional
199
- default ? ' {xk}[{g}Y{xk}/{dbw}n{xk}]{x}'.c : ' {xk}[{dbw}y{xk}/{g}N{xk}]{x}'.c
200
- else
201
- ''
202
- end
203
- icon = case cmd
204
- when 'run'
205
- "\u{25B6}"
206
- when 'copy'
207
- "\u{271A}"
208
- when /open|url/
209
- "\u{279A}"
210
- end
211
-
212
- output.push("{bmK}#{icon} {bwK}#{title.preserve_escapes}{x}#{option}".c)
266
+ output.push(process_directive(Regexp.last_match.named_captures.symbolize_keys))
213
267
  when /(?<fence>`{3,})run(?<optional>[!?]{1,2})? *(?<title>.*?)$/i
214
- m = Regexp.last_match.named_captures.symbolize_keys
215
- optional = m[:optional] =~ /[?!]+/ ? true : false
216
- default = m[:optional] =~ /!/ ? false : true
217
- option = if optional
218
- default ? ' {xk}[{g}Y{xk}/{dbw}n{xk}]{x}'.c : ' {xk}[{dbw}y{xk}/{g}N{xk}]{x}'.c
219
- else
220
- ''
221
- end
222
- desc = m[:title].length.positive? ? "Block: #{m[:title]}#{option}" : "Code Block#{option}"
268
+ desc = title_code_block(Regexp.last_match.named_captures.symbolize_keys)
223
269
  output.push("{bmK}\u{25B6} {bwK}#{desc}{x}\n```".c)
224
270
  when /@@@run(?<optional>[!?]{1,2})? *(?<title>.*?)$/i
225
- m = Regexp.last_match.named_captures.symbolize_keys
226
- optional = m[:optional] =~ /[?!]+/ ? true : false
227
- default = m[:optional] =~ /!/ ? false : true
228
- option = if optional
229
- default ? ' {xk}[{g}Y{xk}/{dbw}n{xk}]{x}'.c : ' {xk}[{dbw}y{xk}/{g}N{xk}]{x}'.c
230
- else
231
- ''
232
- end
233
- desc = m[:title].length.positive? ? "Block: #{m[:title]}#{option}" : "Code Block#{option}"
234
- output.push("{bmK}\u{25B6} {bwK}#{desc}{x}".c)
271
+ output.push("{bmK}\u{25B6} {bwK}#{title_code_block(Regexp.last_match.named_captures.symbolize_keys)}{x}".c)
235
272
  else
236
273
  l.wrap!(Howzit.options[:wrap]) if Howzit.options[:wrap].positive?
237
274
  output.push(l)
@@ -241,6 +278,45 @@ module Howzit
241
278
  output.push('').map(&:render_arguments)
242
279
  end
243
280
 
281
+ include Comparable
282
+ def <=>(other)
283
+ @title <=> other.title
284
+ end
285
+
286
+ def define_task_args(keys)
287
+ cmd = keys[:cmd]
288
+ obj = keys[:action]
289
+ title = keys[:title].nil? ? obj : keys[:title].strip
290
+ title = Howzit.options[:show_all_code] ? obj : title
291
+ task_args = { type: :include,
292
+ arguments: nil,
293
+ title: title,
294
+ action: obj,
295
+ parent: self }
296
+ case cmd
297
+ when /include/i
298
+ if title =~ /\[(.*?)\] *$/
299
+ Howzit.named_arguments = @named_args
300
+ args = Regexp.last_match(1).split(/ *, */).map(&:render_arguments)
301
+ Howzit.arguments = args
302
+ arguments
303
+ title.sub!(/ *\[.*?\] *$/, '')
304
+ end
305
+
306
+ task_args[:type] = :include
307
+ task_args[:arguments] = Howzit.named_arguments
308
+ when /run/i
309
+ task_args[:type] = :run
310
+ when /copy/i
311
+ task_args[:type] = :copy
312
+ task_args[:action] = Shellwords.escape(obj)
313
+ when /open|url/i
314
+ task_args[:type] = :open
315
+ end
316
+
317
+ task_args
318
+ end
319
+
244
320
  private
245
321
 
246
322
  ##
@@ -264,8 +340,7 @@ module Howzit
264
340
  Howzit.named_arguments = @named_args
265
341
 
266
342
  if c[:cmd].nil?
267
- optional = c[:optional2] =~ /[?!]{1,2}/ ? true : false
268
- default = c[:optional2] =~ /!/ ? false : true
343
+ optional, default = define_optional(c[:optional2])
269
344
  title = c[:title2].nil? ? '' : c[:title2].strip
270
345
  block = c[:block]&.strip
271
346
  runnable << Howzit::Task.new({ type: :block,
@@ -275,63 +350,10 @@ module Howzit
275
350
  optional: optional,
276
351
  default: default)
277
352
  else
278
- cmd = c[:cmd]
279
- optional = c[:optional] =~ /[?!]{1,2}/ ? true : false
280
- default = c[:optional] =~ /!/ ? false : true
281
- obj = c[:action]
282
- title = c[:title].nil? ? obj : c[:title].strip
283
- title = Howzit.options[:show_all_code] ? obj : title
284
- case cmd
285
- when /include/i
286
- # matches = Howzit.buildnote.find_topic(obj)
287
- # unless matches.empty? || Howzit.inclusions.include?(matches[0].title)
288
- # tasks = matches[0].tasks.map do |inc|
289
- # Howzit.inclusions.push(matches[0].title)
290
- # inc.parent = matches[0]
291
- # inc
292
- # end
293
- # runnable.concat(tasks)
294
- # end
295
- args = []
296
- if title =~ /\[(.*?)\] *$/
297
- Howzit.named_arguments = @named_args
298
- args = Regexp.last_match(1).split(/ *, */).map(&:render_arguments)
299
- Howzit.arguments = args
300
- arguments
301
- title.sub!(/ *\[.*?\] *$/, '')
302
- end
303
-
304
- runnable << Howzit::Task.new({ type: :include,
305
- arguments: Howzit.named_arguments,
306
- title: title,
307
- action: obj,
308
- parent: self },
309
- optional: optional,
310
- default: default)
311
- when /run/i
312
- # warn "{bg}Running {bw}#{obj}{x}".c if Howzit.options[:log_level] < 2
313
- runnable << Howzit::Task.new({ type: :run,
314
- title: title,
315
- action: obj,
316
- parent: self },
317
- optional: optional,
318
- default: default)
319
- when /copy/i
320
- # warn "{bg}Copied {bw}#{obj}{bg} to clipboard{x}".c if Howzit.options[:log_level] < 2
321
- runnable << Howzit::Task.new({ type: :copy,
322
- title: title,
323
- action: Shellwords.escape(obj),
324
- parent: self },
325
- optional: optional,
326
- default: default)
327
- when /open|url/i
328
- runnable << Howzit::Task.new({ type: :open,
329
- title: title,
330
- action: obj,
331
- parent: self },
332
- optional: optional,
333
- default: default)
334
- end
353
+ optional, default = define_optional(c[:optional])
354
+ runnable << Howzit::Task.new(define_task_args(c),
355
+ optional: optional,
356
+ default: default)
335
357
  end
336
358
  end
337
359
 
data/lib/howzit/util.rb CHANGED
@@ -210,6 +210,33 @@ module Howzit
210
210
  end
211
211
  end
212
212
 
213
+ ##
214
+ ## Platform-agnostic paste-from-clipboard
215
+ ##
216
+ def os_paste
217
+ os = RbConfig::CONFIG['target_os']
218
+ out = "{bg}Pasting from clipboard".c
219
+ case os
220
+ when /darwin.*/i
221
+ Howzit.console.debug("#{out} (macOS){x}".c)
222
+ `pbpaste`
223
+ when /mingw|mswin/i
224
+ Howzit.console.debug("#{out} (Windows){x}".c)
225
+ `cat /dev/clipboard`
226
+ else
227
+ if 'xsel'.available?
228
+ Howzit.console.debug("#{out} (Linux, xsel){x}".c)
229
+ `xsel --clipboard --output`
230
+ elsif 'xclip'.available?
231
+ Howzit.console.debug("#{out} (Linux, xclip){x}".c)
232
+ `xclip -selection clipboard -o`
233
+ else
234
+ Howzit.console.debug(out)
235
+ Howzit.console.warn('Unable to determine executable for clipboard.')
236
+ end
237
+ end
238
+ end
239
+
213
240
  ##
214
241
  ## Platform-agnostic open command
215
242
  ##
@@ -3,5 +3,5 @@
3
3
  # Primary module for this gem.
4
4
  module Howzit
5
5
  # Current Howzit version.
6
- VERSION = '2.1.10'
6
+ VERSION = '2.1.13'
7
7
  end
data/lib/howzit.rb CHANGED
@@ -50,6 +50,7 @@ require 'tty/box'
50
50
  module Howzit
51
51
  class << self
52
52
  attr_accessor :arguments, :named_arguments, :cli_args
53
+
53
54
  ##
54
55
  ## Holds a Configuration object with methods and a @settings hash
55
56
  ##
@@ -91,8 +92,6 @@ module Howzit
91
92
  @has_read_upstream ||= false
92
93
  end
93
94
 
94
- def has_read_upstream=(has_read)
95
- @has_read_upstream = has_read
96
- end
95
+ attr_writer :has_read_upstream
97
96
  end
98
97
  end
data/spec/topic_spec.rb CHANGED
@@ -9,7 +9,7 @@ describe Howzit::Topic do
9
9
 
10
10
  describe '.new' do
11
11
  it 'makes a new topic instance' do
12
- expect(topic).to be_a Howzit::Topic
12
+ expect(topic).to be_a described_class
13
13
  end
14
14
  it 'has the correct title' do
15
15
  expect(topic.title).to eq title
@@ -51,9 +51,11 @@ describe Howzit::Topic do
51
51
  it 'returns true for matching pattern in content' do
52
52
  expect(topic.grep('prereq.*?ite')).to be_truthy
53
53
  end
54
+
54
55
  it 'returns true for matching pattern in title' do
55
56
  expect(topic.grep('bal.*?na')).to be_truthy
56
57
  end
58
+
57
59
  it 'fails on bad pattern' do
58
60
  expect(topic.grep('xxx+')).to_not be_truthy
59
61
  end
@@ -61,10 +63,12 @@ describe Howzit::Topic do
61
63
 
62
64
  describe '.run' do
63
65
  Howzit.options[:default] = true
66
+
64
67
  it 'shows prereq and postreq' do
65
68
  expect { topic.run }.to output(/prerequisite/).to_stdout
66
69
  expect { topic.run }.to output(/postrequisite/).to_stdout
67
70
  end
71
+
68
72
  it 'Copies to clipboard' do
69
73
  expect {
70
74
  ENV['RUBYOPT'] = '-W1'
@@ -77,15 +81,18 @@ describe Howzit::Topic do
77
81
  describe '.print_out' do
78
82
  Howzit.options[:header_format] = :block
79
83
  Howzit.options[:color] = false
84
+
80
85
  it 'prints the topic title' do
81
- expect(topic.print_out({single: true, header: true}).join("\n").uncolor).to match(/▌Topic Balogna/)
86
+ expect(topic.print_out({ single: true, header: true }).join("\n").uncolor).to match(/▌Topic Balogna/)
82
87
  end
88
+
83
89
  it 'prints a task title' do
84
- expect(topic.print_out({single: true, header: true}).join("\n").uncolor).to match(/▶ Null Output/)
90
+ expect(topic.print_out({ single: true, header: true }).join("\n").uncolor).to match(/▶ Null Output/)
85
91
  end
92
+
86
93
  it 'prints task action with --show-code' do
87
94
  Howzit.options[:show_all_code] = true
88
- expect(topic.print_out({single: true, header: true}).join("\n").uncolor).to match(/▶ ls -1/)
95
+ expect(topic.print_out({ single: true, header: true }).join("\n").uncolor).to match(/▶ ls -1/)
89
96
  end
90
97
  end
91
98
  end
data/src/_README.md ADDED
@@ -0,0 +1,145 @@
1
+ <!--README--><!--GITHUB-->
2
+ # Howzit
3
+
4
+ [![Gem](https://img.shields.io/gem/v/howzit.svg)](https://rubygems.org/gems/howzit)
5
+ [![Travis](https://api.travis-ci.com/ttscoff/howzit.svg?branch=main)](https://travis-ci.org/makenew/ruby-gem)
6
+ [![GitHub license](https://img.shields.io/github/license/ttscoff/howzit.svg)](./LICENSE.txt)
7
+ <!--END GITHUB-->
8
+ A command-line reference tool for tracking project build systems
9
+
10
+ Howzit is a tool that allows you to keep Markdown-formatted notes about a project's tools and procedures. It functions as an easy lookup for notes about a particular task, as well as a task runner to automatically execute appropriate commands.
11
+
12
+ ## Features
13
+
14
+ - Match topic titles with any portion of title
15
+ - Automatic pagination of output, with optional Markdown highlighting
16
+ - Use `@run()`, `@copy()`, and `@open()` to perform actions within a build notes file
17
+ - Use `@include()` to import another topic's tasks
18
+ - Use fenced code blocks to include/run embedded scripts
19
+ - Sets iTerm 2 marks on topic titles for navigation when paging is disabled
20
+ - Inside of git repositories, howzit will work from subdirectories, assuming build notes are in top level of repo
21
+ - Templates for easily including repeat tasks
22
+ - Grep topics for pattern and choose from matches
23
+ - Use positional and named variables when executing tasks
24
+
25
+ ## Getting Started
26
+
27
+ ### Prerequisites
28
+
29
+ - Ruby 2.4+ (It probably works on older Rubys, but is untested prior to 2.4.1.)
30
+ - Optional: if [`fzf`](https://github.com/junegunn/fzf) is available, it will be used for handling multiple choice selections
31
+ - Optional: if [`bat`](https://github.com/sharkdp/bat) is available it will page with that
32
+ - Optional: [`mdless`](https://github.com/ttscoff/mdless) or [`mdcat`](https://github.com/lunaryorn/mdcat) for formatting output
33
+
34
+ ### Installing
35
+
36
+ You can install `howzit` by running:
37
+
38
+ gem install howzit
39
+
40
+ If you run into permission errors using the above command, you'll need to use `gem install --user-install howzit`. If that fails, either use `sudo` (`sudo gem install howzit`) or if you're using Homebrew, you have the option to install via [brew-gem](https://github.com/sportngin/brew-gem):
41
+
42
+ brew install brew-gem
43
+ brew gem install howzit
44
+
45
+ ### Usage
46
+
47
+ [See the wiki](https://github.com/ttscoff/howzit/wiki) for documentation.
48
+
49
+ ## Author
50
+
51
+ **Brett Terpstra** - [brettterpstra.com](https://brettterpstra.com)
52
+
53
+ ## License
54
+
55
+ This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details.
56
+
57
+ ## Warranty
58
+
59
+ This software is provided "as is" and without any express or
60
+ implied warranties, including, without limitation, the implied
61
+ warranties of merchantibility and fitness for a particular
62
+ purpose.
63
+
64
+ ## Documentation
65
+
66
+ - [Howzit Wiki][Wiki].
67
+ - [YARD documentation][RubyDoc] is hosted by RubyDoc.info.
68
+ - [Interactive documentation][Omniref] is hosted by Omniref.
69
+
70
+ [Wiki]: https://github.com/ttscoff/howzit/wiki
71
+ [RubyDoc]: http://www.rubydoc.info/gems/howzit
72
+ [Omniref]: https://www.omniref.com/ruby/gems/howzit
73
+
74
+ <!--GITHUB-->
75
+ ## Development and Testing
76
+
77
+ ### Source Code
78
+
79
+ The [howzit source] is hosted on GitHub.
80
+ Clone the project with
81
+
82
+ ```
83
+ $ git clone https://github.com/ttscoff/howzit.git
84
+ ```
85
+
86
+ [howzit source]: https://github.com/ttscoff/howzit
87
+
88
+ ### Requirements
89
+
90
+ You will need [Ruby] with [Bundler].
91
+
92
+ Install the development dependencies with
93
+
94
+ ```
95
+ $ bundle
96
+ ```
97
+
98
+ [Bundler]: http://bundler.io/
99
+ [Ruby]: https://www.ruby-lang.org/
100
+
101
+ ### Rake
102
+
103
+ Run `$ rake -T` to see all Rake tasks.
104
+
105
+ ```
106
+ rake build # Build howzit-2.0.1.gem into the pkg directory
107
+ rake bump:current[tag] # Show current gem version
108
+ rake bump:major[tag] # Bump major part of gem version
109
+ rake bump:minor[tag] # Bump minor part of gem version
110
+ rake bump:patch[tag] # Bump patch part of gem version
111
+ rake bump:pre[tag] # Bump pre part of gem version
112
+ rake bump:set # Sets the version number using the VERSION environment variable
113
+ rake clean # Remove any temporary products
114
+ rake clobber # Remove any generated files
115
+ rake install # Build and install howzit-2.0.1.gem into system gems
116
+ rake install:local # Build and install howzit-2.0.1.gem into system gems without network access
117
+ rake release[remote] # Create tag v2.0.1 and build and push howzit-2.0.1.gem to Rubygems
118
+ rake rubocop # Run RuboCop
119
+ rake rubocop:auto_correct # Auto-correct RuboCop offenses
120
+ rake spec # Run RSpec code examples
121
+ rake test # Run test suite
122
+ rake yard # Generate YARD Documentation
123
+ ```
124
+
125
+ ### Guard
126
+
127
+ Guard tasks have been separated into the following groups:
128
+ `doc`, `lint`, and `unit`.
129
+ By default, `$ guard` will generate documentation, lint, and run unit tests.
130
+
131
+ ## Contributing
132
+
133
+ Please submit and comment on bug reports and feature requests.
134
+
135
+ To submit a patch:
136
+
137
+ 1. Fork it (https://github.com/ttscoff/howzit/fork).
138
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
139
+ 3. Make changes. Write and run tests.
140
+ 4. Commit your changes (`git commit -am 'Add some feature'`).
141
+ 5. Push to the branch (`git push origin my-new-feature`).
142
+ 6. Create a new Pull Request.
143
+
144
+ <!--END GITHUB-->
145
+ <!--END README-->
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: 2.1.10
4
+ version: 2.1.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: 2024-04-09 00:00:00.000000000 Z
11
+ date: 2024-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -339,6 +339,7 @@ files:
339
339
  - spec/task_spec.rb
340
340
  - spec/topic_spec.rb
341
341
  - spec/util_spec.rb
342
+ - src/_README.md
342
343
  - update_readmes.rb
343
344
  homepage: https://github.com/ttscoff/howzit
344
345
  licenses:
@@ -359,7 +360,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
359
360
  - !ruby/object:Gem::Version
360
361
  version: '0'
361
362
  requirements: []
362
- rubygems_version: 3.2.16
363
+ rubygems_version: 3.2.15
363
364
  signing_key:
364
365
  specification_version: 4
365
366
  summary: Provides a way to access Markdown project notes by topic with query capabilities