howzit 2.0.9 → 2.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,37 @@
3
3
  module Howzit
4
4
  # String Extensions
5
5
  module StringUtils
6
+ ##
7
+ ## Test if the filename matches the conditions to be a build note
8
+ ##
9
+ ## @return [Boolean] true if filename passes test
10
+ ##
11
+ def build_note?
12
+ return false if downcase !~ /^(howzit[^.]*|build[^.]+)/
13
+
14
+ return false if Howzit.config.should_ignore(self)
15
+
16
+ true
17
+ end
18
+
19
+ ##
20
+ ## Replace slash escaped characters in a string with a
21
+ ## zero-width space that will prevent a shell from
22
+ ## interpreting them when output to console
23
+ ##
24
+ ## @return [String] new string
25
+ ##
26
+ def preserve_escapes
27
+ gsub(/\\([a-z])/, '\​\1')
28
+ end
29
+
6
30
  # Convert a string to a valid YAML value
31
+ #
32
+ # @param orig_value The original value from which
33
+ # type will be determined
34
+ #
35
+ # @return coerced value
36
+ #
7
37
  def to_config_value(orig_value = nil)
8
38
  if orig_value
9
39
  case orig_value.class.to_s
@@ -28,10 +58,21 @@ module Howzit
28
58
  end
29
59
  end
30
60
 
61
+ ##
62
+ ## Shortcut for calling Color.template
63
+ ##
64
+ ## @return [String] colorized string
65
+ ##
31
66
  def c
32
67
  Color.template(self)
33
68
  end
34
69
 
70
+
71
+ ##
72
+ ## Convert a string to a regex object based on matching settings
73
+ ##
74
+ ## @return [Regexp] Receive regex representation of the object.
75
+ ##
35
76
  def to_rx
36
77
  case Howzit.options[:matching]
37
78
  when 'exact'
@@ -50,9 +91,17 @@ module Howzit
50
91
  gsub(/\e\[[\d;]+m/, '').gsub(/\e\]1337;SetMark/,'')
51
92
  end
52
93
 
94
+ # Wrap text at a specified width.
95
+ #
53
96
  # Adapted from https://github.com/pazdera/word_wrap/,
54
- # copyright (c) 2014, 2015 Radek Pazdera
55
- # Distributed under the MIT License
97
+ # copyright (c) 2014, 2015 Radek Pazdera Distributed
98
+ # under the MIT License
99
+ #
100
+ # @param width [Integer] The width at which to
101
+ # wrap lines
102
+ #
103
+ # @return [String] wrapped string
104
+ #
56
105
  def wrap(width)
57
106
  width ||= 80
58
107
  output = []
@@ -84,12 +133,19 @@ module Howzit
84
133
  output.join("\n")
85
134
  end
86
135
 
136
+ ##
137
+ ## Wrap string in place (destructive)
138
+ ##
139
+ ## @param width [Integer] The width at which to wrap
140
+ ##
87
141
  def wrap!(width)
88
142
  replace(wrap(width))
89
143
  end
90
144
 
91
145
  # Truncate string to nearest word
92
- # @param len <number> max length of string
146
+ #
147
+ # @param len [Integer] max length of string
148
+ #
93
149
  def trunc(len)
94
150
  split(/ /).each_with_object([]) do |x, ob|
95
151
  break ob unless ob.join(' ').length + ' '.length + x.length <= len
@@ -98,10 +154,21 @@ module Howzit
98
154
  end.join(' ').strip
99
155
  end
100
156
 
157
+ ##
158
+ ## Truncate string in place (destructive)
159
+ ##
160
+ ## @param len [Integer] The length to truncate at
161
+ ##
101
162
  def trunc!(len)
102
163
  replace trunc(len)
103
164
  end
104
165
 
166
+ ##
167
+ ## Splits a line at nearest word break
168
+ ##
169
+ ## @param width [Integer] The width of the first segment
170
+ ## @param indent [String] The indent string
171
+ ##
105
172
  def split_line(width, indent = '')
106
173
  line = dup
107
174
  at = line.index(/\s/)
@@ -119,10 +186,23 @@ module Howzit
119
186
  end
120
187
  end
121
188
 
189
+ ##
190
+ ## Test if an executable is available on the system
191
+ ##
192
+ ## @return [Boolean] executable is available
193
+ ##
122
194
  def available?
123
195
  Util.valid_command?(self)
124
196
  end
125
197
 
198
+ ##
199
+ ## Render [%variable] placeholders in a templated string
200
+ ##
201
+ ## @param vars [Hash] Key/value pairs of variable
202
+ ## values
203
+ ##
204
+ ## @return [String] Rendered string
205
+ ##
126
206
  def render_template(vars)
127
207
  vars.each do |k, v|
128
208
  gsub!(/\[%#{k}(:.*?)?\]/, v)
@@ -131,10 +211,20 @@ module Howzit
131
211
  gsub(/\[%(.*?):(.*?)\]/, '\2')
132
212
  end
133
213
 
214
+ ##
215
+ ## Render [%variable] placeholders in place
216
+ ##
217
+ ## @param vars [Hash] Key/value pairs of variable values
218
+ ##
134
219
  def render_template!(vars)
135
220
  replace render_template(vars)
136
221
  end
137
222
 
223
+ ##
224
+ ## Render $X placeholders based on positional arguments
225
+ ##
226
+ ## @return [String] rendered string
227
+ ##
138
228
  def render_arguments
139
229
  return self if Howzit.arguments.nil? || Howzit.arguments.empty?
140
230
 
@@ -145,6 +235,13 @@ module Howzit
145
235
  gsub(/\$[@*]/, Shellwords.join(Howzit.arguments))
146
236
  end
147
237
 
238
+ ##
239
+ ## Split the content at the first top-level header and
240
+ ## assume everything before it is metadata. Passes to
241
+ ## #get_metadata for processing
242
+ ##
243
+ ## @return [Hash] key/value pairs
244
+ ##
148
245
  def extract_metadata
149
246
  if File.exist?(self)
150
247
  leader = Util.read_file(self).split(/^#/)[0].strip
@@ -154,6 +251,11 @@ module Howzit
154
251
  end
155
252
  end
156
253
 
254
+ ##
255
+ ## Examine text for multimarkdown-style metadata and return key/value pairs
256
+ ##
257
+ ## @return [Hash] The metadata as key/value pairs
258
+ ##
157
259
  def get_metadata
158
260
  data = {}
159
261
  scan(/(?mi)^(\S[\s\S]+?): ([\s\S]*?)(?=\n\S[\s\S]*?:|\Z)/).each do |m|
@@ -162,6 +264,13 @@ module Howzit
162
264
  normalize_metadata(data)
163
265
  end
164
266
 
267
+ ##
268
+ ## Autocorrect some keys
269
+ ##
270
+ ## @param meta [Hash] The metadata
271
+ ##
272
+ ## @return [Hash] corrected metadata
273
+ ##
165
274
  def normalize_metadata(meta)
166
275
  data = {}
167
276
  meta.each do |k, v|
@@ -177,15 +286,33 @@ module Howzit
177
286
  data
178
287
  end
179
288
 
289
+ ##
290
+ ## Test if iTerm markers should be output. Requires that
291
+ ## the $TERM_PROGRAM be iTerm and howzit is not running
292
+ ## directives or paginating output
293
+ ##
294
+ ## @return [Boolean] should mark?
295
+ ##
180
296
  def should_mark_iterm?
181
297
  ENV['TERM_PROGRAM'] =~ /^iTerm/ && !Howzit.options[:run] && !Howzit.options[:paginate]
182
298
  end
183
299
 
300
+ ##
301
+ ## Output an iTerm marker
302
+ ##
303
+ ## @return [String] ANSI escape sequence for iTerm
304
+ ## marker
305
+ ##
184
306
  def iterm_marker
185
307
  "\e]1337;SetMark\a" if should_mark_iterm?
186
308
  end
187
309
 
188
310
  # Make a fancy title line for the topic
311
+ #
312
+ # @param opts [Hash] options
313
+ #
314
+ # @return [String] formatted string
315
+ #
189
316
  def format_header(opts = {})
190
317
  title = dup
191
318
  options = {
data/lib/howzit/task.rb CHANGED
@@ -1,14 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Howzit
4
+ # Task object
4
5
  class Task
5
6
  attr_reader :type, :title, :action, :parent, :optional, :default
6
7
 
7
- def initialize(type, title, action, parent = nil, optional: false, default: true)
8
- @type = type
9
- @title = title
10
- @action = action.render_arguments
11
- @parent = parent
8
+ ##
9
+ ## Initialize a Task object
10
+ ##
11
+ def initialize(params, optional: false, default: true)
12
+ @type = params[:type]
13
+ @title = params[:title]
14
+ @action = params[:action].render_arguments
15
+ @parent = params[:parent] || nil
12
16
  @optional = optional
13
17
  @default = default
14
18
  end
@@ -22,7 +26,7 @@ module Howzit
22
26
  end
23
27
 
24
28
  def to_list
25
- " * #{@type}: #{@title.gsub(/\\n/, '\​n')}"
29
+ " * #{@type}: #{@title.preserve_escapes}"
26
30
  end
27
31
  end
28
32
  end
data/lib/howzit/topic.rb CHANGED
@@ -52,7 +52,13 @@ module Howzit
52
52
 
53
53
  @tasks.each do |task|
54
54
  if task.optional
55
- q = %({bg}#{task.type.to_s.capitalize} {xw}"{bw}#{task.title}{xw}"{x}).c
55
+ note = if task.type == :include
56
+ task_count = Howzit.buildnote.find_topic(task.action)[0].tasks.count
57
+ " (#{task_count} tasks)"
58
+ else
59
+ ""
60
+ end
61
+ q = %({bg}#{task.type.to_s.capitalize} {xw}"{bw}#{task.title}{xw}"#{note}{x}).c
56
62
  res = Prompt.yn(q, default: task.default)
57
63
  next unless res
58
64
 
@@ -81,7 +87,7 @@ module Howzit
81
87
 
82
88
  $stderr.puts "{by}Running tasks from {bw}#{matches[0].title}{x}".c if Howzit.options[:log_level] < 2
83
89
  output.push(matches[0].run(nested: true))
84
- $stderr.puts "{by}End include: #{matches[0].tasks.count} tasks".c if Howzit.options[:log_level] < 2
90
+ $stderr.puts "{by}End include: #{matches[0].tasks.count} tasks{x}".c if Howzit.options[:log_level] < 2
85
91
  tasks += matches[0].tasks.count
86
92
  when :run
87
93
  $stderr.puts "{bg}Running {bw}#{title}{x}".c if Howzit.options[:log_level] < 2
@@ -107,6 +113,11 @@ module Howzit
107
113
  output
108
114
  end
109
115
 
116
+ ##
117
+ ## Platform-agnostic copy-to-clipboard
118
+ ##
119
+ ## @param string [String] The string to copy
120
+ ##
110
121
  def os_copy(string)
111
122
  os = RbConfig::CONFIG['target_os']
112
123
  out = "{bg}Copying {bw}#{string}".c
@@ -131,6 +142,11 @@ module Howzit
131
142
  end
132
143
  end
133
144
 
145
+ ##
146
+ ## Platform-agnostic open command
147
+ ##
148
+ ## @param command [String] The command
149
+ ##
134
150
  def os_open(command)
135
151
  os = RbConfig::CONFIG['target_os']
136
152
  out = "{bg}Opening {bw}#{command}".c
@@ -153,6 +169,11 @@ module Howzit
153
169
  end
154
170
 
155
171
  # Output a topic with fancy title and bright white text.
172
+ #
173
+ # @param options [Hash] The options
174
+ #
175
+ # @return [Array] array of formatted lines
176
+ #
156
177
  def print_out(options = {})
157
178
  defaults = { single: false, header: true }
158
179
  opt = defaults.merge(options)
@@ -223,7 +244,7 @@ module Howzit
223
244
  "\u{279A}"
224
245
  end
225
246
 
226
- output.push("{bmK}#{icon} {bwK}#{title.gsub(/\\n/, '\​n')}{x}#{option}".c)
247
+ output.push("{bmK}#{icon} {bwK}#{title.preserve_escapes}{x}#{option}".c)
227
248
  when /(?<fence>`{3,})run(?<optional>[!?]{1,2})? *(?<title>.*?)$/i
228
249
  m = Regexp.last_match.named_captures.symbolize_keys
229
250
  optional = m[:optional] =~ /[?!]+/ ? true : false
@@ -256,6 +277,11 @@ module Howzit
256
277
 
257
278
  private
258
279
 
280
+ ##
281
+ ## Collect all directives in the topic content
282
+ ##
283
+ ## @return [Array] array of Task objects
284
+ ##
259
285
  def gather_tasks
260
286
  runnable = []
261
287
  @prereqs = @content.scan(/(?<=@before\n).*?(?=\n@end)/im).map(&:strip)
@@ -275,7 +301,12 @@ module Howzit
275
301
  default = c[:optional2] =~ /!/ ? false : true
276
302
  title = c[:title2].nil? ? '' : c[:title2].strip
277
303
  block = c[:block]&.strip
278
- runnable << Howzit::Task.new(:block, title, block, optional: optional, default: default)
304
+ runnable << Howzit::Task.new({ type: :block,
305
+ title: title,
306
+ action: block,
307
+ parent: nil },
308
+ optional: optional,
309
+ default: default)
279
310
  else
280
311
  cmd = c[:cmd]
281
312
  optional = c[:optional] =~ /[?!]{1,2}/ ? true : false
@@ -294,15 +325,35 @@ module Howzit
294
325
  # end
295
326
  # runnable.concat(tasks)
296
327
  # end
297
- runnable << Howzit::Task.new(:include, title, obj, optional: optional, default: default)
328
+ runnable << Howzit::Task.new({ type: :include,
329
+ title: title,
330
+ action: obj,
331
+ parent: nil },
332
+ optional: optional,
333
+ default: default)
298
334
  when /run/i
299
335
  # warn "{bg}Running {bw}#{obj}{x}".c if Howzit.options[:log_level] < 2
300
- runnable << Howzit::Task.new(:run, title, obj, optional: optional, default: default)
336
+ runnable << Howzit::Task.new({ type: :run,
337
+ title: title,
338
+ action: obj,
339
+ parent: nil },
340
+ optional: optional,
341
+ default: default)
301
342
  when /copy/i
302
343
  # warn "{bg}Copied {bw}#{obj}{bg} to clipboard{x}".c if Howzit.options[:log_level] < 2
303
- runnable << Howzit::Task.new(:copy, title, Shellwords.escape(obj), optional: optional, default: default)
344
+ runnable << Howzit::Task.new({ type: :copy,
345
+ title: title,
346
+ action: Shellwords.escape(obj),
347
+ parent: nil },
348
+ optional: optional,
349
+ default: default)
304
350
  when /open|url/i
305
- runnable << Howzit::Task.new(:open, title, obj, optional: optional, default: default)
351
+ runnable << Howzit::Task.new({ type: :open,
352
+ title: title,
353
+ action: obj,
354
+ parent: nil },
355
+ optional: optional,
356
+ default: default)
306
357
  end
307
358
  end
308
359
  end
data/lib/howzit/util.rb CHANGED
@@ -4,15 +4,40 @@ module Howzit
4
4
  # Util class
5
5
  module Util
6
6
  class << self
7
+
8
+ ##
9
+ ## Read a file with UTF-8 encoding and
10
+ ## leading/trailing whitespace removed
11
+ ##
12
+ ## @param path [String] The path to read
13
+ ##
14
+ ## @return [String] UTF-8 encoded string
15
+ ##
7
16
  def read_file(path)
8
17
  IO.read(path).force_encoding('utf-8').strip
9
18
  end
10
19
 
20
+ ##
21
+ ## Test if an external command exists and is
22
+ ## executable. Removes additional arguments and passes
23
+ ## just the executable to #command_exist?
24
+ ##
25
+ ## @param command [String] The command
26
+ ##
27
+ ## @return [Boolean] command is valid
28
+ ##
11
29
  def valid_command?(command)
12
30
  cmd = command.split(' ')[0]
13
31
  command_exist?(cmd)
14
32
  end
15
33
 
34
+ ##
35
+ ## Test if external command exists
36
+ ##
37
+ ## @param command [String] The command
38
+ ##
39
+ ## @return [Boolean] command exists
40
+ ##
16
41
  def command_exist?(command)
17
42
  exts = ENV.fetch('PATHEXT', '').split(::File::PATH_SEPARATOR)
18
43
  if Pathname.new(command).absolute?
@@ -3,5 +3,5 @@
3
3
  # Primary module for this gem.
4
4
  module Howzit
5
5
  # Current Howzit version.
6
- VERSION = '2.0.9'
6
+ VERSION = '2.0.12'
7
7
  end
data/lib/howzit.rb CHANGED
@@ -24,11 +24,22 @@ require 'tty/screen'
24
24
  require 'tty/box'
25
25
  # require 'tty/prompt'
26
26
 
27
+ # Main config dir
27
28
  CONFIG_DIR = '~/.config/howzit'
29
+
30
+ # Config file name
28
31
  CONFIG_FILE = 'howzit.yaml'
32
+
33
+ # Ignore file name
29
34
  IGNORE_FILE = 'ignore.yaml'
35
+
36
+ # Available options for matching method
30
37
  MATCHING_OPTIONS = %w[partial exact fuzzy beginswith].freeze
38
+
39
+ # Available options for multiple_matches method
31
40
  MULTIPLE_OPTIONS = %w[first best all choose].freeze
41
+
42
+ # Available options for header formatting
32
43
  HEADER_FORMAT_OPTIONS = %w[border block].freeze
33
44
 
34
45
  # Main module for howzit
data/spec/task_spec.rb CHANGED
@@ -3,7 +3,12 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Howzit::Task do
6
- subject(:task) { Howzit::Task.new(:run, 'List Directory', 'ls') }
6
+ subject(:task) do
7
+ Howzit::Task.new({ type: :run,
8
+ title: 'List Directory',
9
+ action: 'ls',
10
+ parent: nil })
11
+ end
7
12
 
8
13
  describe ".new" do
9
14
  it "makes a new task instance" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: howzit
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.9
4
+ version: 2.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
@@ -297,7 +297,6 @@ files:
297
297
  - lib/.rubocop.yml
298
298
  - lib/howzit.rb
299
299
  - lib/howzit/buildnote.rb
300
- - lib/howzit/buildnotes.rb
301
300
  - lib/howzit/colors.rb
302
301
  - lib/howzit/config.rb
303
302
  - lib/howzit/console_logger.rb
@@ -310,7 +309,6 @@ files:
310
309
  - lib/howzit/version.rb
311
310
  - spec/.rubocop.yml
312
311
  - spec/buildnote_spec.rb
313
- - spec/buildnotes.md.bak
314
312
  - spec/cli_spec.rb
315
313
  - spec/ruby_gem_spec.rb
316
314
  - spec/spec_helper.rb
@@ -344,7 +342,6 @@ summary: Provides a way to access Markdown project notes by topic with query cap
344
342
  test_files:
345
343
  - spec/.rubocop.yml
346
344
  - spec/buildnote_spec.rb
347
- - spec/buildnotes.md.bak
348
345
  - spec/cli_spec.rb
349
346
  - spec/ruby_gem_spec.rb
350
347
  - spec/spec_helper.rb