howzit 2.1.10 → 2.1.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67377505c45973e38c5cc2430f2d9428ace8a5680a2bce0cfc26af369a1a98b2
4
- data.tar.gz: bd6e00ccaf0484f486c965b0136fbd7a0cbb4af6cd7cf21c777b029c316ff13b
3
+ metadata.gz: 31789699efb3b8c0029260e92583ba0a2f085a5df7658c1aea4ccb48e0c6d88c
4
+ data.tar.gz: 6b6e77397fa316bd2c31abc833ab659d2b569f5c65ef161a0ab60e162ec907b5
5
5
  SHA512:
6
- metadata.gz: 9fc93201a2080344c7ea080697ccd6a38a0c6a4a7473d98c9bbe2b18f4685ada0799cc9013358d01b7e29696e3a63d074c0e6421dbbf9db55fe69e0586efeae8
7
- data.tar.gz: 1a627938a04104298a13ee667876f7c7559707e08a1fdecc136837af26e1c9e0130ab34fd4be1f5b1a3b79172c3d7844cec4c43d147aa1308ce8d21c2c4251ad
6
+ metadata.gz: 494458cae0e118f9ea697b79da44c8a0e684a60a68a03a15d26d8df646a09d7381cc78f32ea02f606298603e2eefd51763781b96e308a6515fe885e566efff4b
7
+ data.tar.gz: 0400d801a5e0ae49d9156bec1957c4c2f5060b6c48ee2aa05d9f504e18d86e7c03a2dc8a8bf98913d0da0015ca0d59742a73268bead4a71919653b7a0ccfade7
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,11 @@
1
+ ### 2.1.12
2
+
3
+ 2024-08-05 12:01
4
+
5
+ ### 2.1.11
6
+
7
+ 2024-08-05 12:01
8
+
1
9
  ### 2.1.10
2
10
 
3
11
  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.12'
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.12
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