howzit 2.1.27 → 2.1.29

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.
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Howzit
4
+ # Conditional Content processor
5
+ # Handles @if/@unless/@end blocks in topic content
6
+ module ConditionalContent
7
+ class << self
8
+ ##
9
+ ## Process conditional blocks in content
10
+ ##
11
+ ## @param content [String] The content to process
12
+ ## @param context [Hash] Context for condition evaluation
13
+ ##
14
+ ## @return [String] Content with conditional blocks processed
15
+ ##
16
+ def process(content, context = {})
17
+ lines = content.split(/\n/)
18
+ output = []
19
+ condition_stack = []
20
+ # Track if any condition in the current chain has been true
21
+ # This is used for @elsif and @else to know if a previous branch matched
22
+ chain_matched_stack = []
23
+
24
+ lines.each do |line|
25
+ # Check for @if or @unless
26
+ if line =~ /^@(if|unless)\s+(.+)$/i
27
+ directive = Regexp.last_match(1).downcase
28
+ condition = Regexp.last_match(2).strip
29
+
30
+ # Evaluate condition
31
+ result = ConditionEvaluator.evaluate(condition, context)
32
+ # For @unless, negate the result
33
+ result = !result if directive == 'unless'
34
+
35
+ condition_stack << result
36
+ chain_matched_stack << result
37
+
38
+ # Don't include the @if/@unless line itself
39
+ next
40
+ end
41
+
42
+ # Check for @elsif
43
+ if line =~ /^@elsif\s+(.+)$/i
44
+ condition = Regexp.last_match(1).strip
45
+
46
+ # If previous condition in chain was true, this branch is false
47
+ # Otherwise, evaluate the condition
48
+ if !condition_stack.empty? && chain_matched_stack.last
49
+ # Previous branch matched, so this one is false
50
+ condition_stack[-1] = false
51
+ else
52
+ # Previous branch didn't match, evaluate this condition
53
+ result = ConditionEvaluator.evaluate(condition, context)
54
+ condition_stack[-1] = result
55
+ chain_matched_stack[-1] = result if result
56
+ end
57
+
58
+ # Don't include the @elsif line itself
59
+ next
60
+ end
61
+
62
+ # Check for @else
63
+ if line =~ /^@else\s*$/i
64
+ # If any previous condition in chain was true, this branch is false
65
+ # Otherwise, this branch is true
66
+ if !condition_stack.empty? && chain_matched_stack.last
67
+ # Previous branch matched, so else is false
68
+ condition_stack[-1] = false
69
+ else
70
+ # No previous branch matched, so else is true
71
+ condition_stack[-1] = true
72
+ chain_matched_stack[-1] = true
73
+ end
74
+
75
+ # Don't include the @else line itself
76
+ next
77
+ end
78
+
79
+ # Check for @end - only skip if it's closing an @if/@unless/@elsif/@else block
80
+ if (line =~ /^@end\s*$/) && !condition_stack.empty?
81
+ # This @end closes a conditional block, so skip it
82
+ condition_stack.pop
83
+ chain_matched_stack.pop
84
+ next
85
+ end
86
+ # Otherwise, this @end is for @before/@after, so include it
87
+
88
+ # Include the line only if all conditions in stack are true
89
+ output << line if condition_stack.all? { |cond| cond }
90
+ end
91
+
92
+ output.join("\n")
93
+ end
94
+ end
95
+ end
96
+ end
data/lib/howzit/config.rb CHANGED
@@ -129,14 +129,26 @@ module Howzit
129
129
 
130
130
  ## Update editor config
131
131
  def update_editor
132
- puts 'No $EDITOR defined, no value in config'
132
+ begin
133
+ puts 'No $EDITOR defined, no value in config'
134
+ rescue Errno::EPIPE
135
+ # Pipe closed, ignore
136
+ end
133
137
  editor = Prompt.read_editor
134
138
  if editor.nil?
135
- puts 'Cancelled, no editor stored.'
139
+ begin
140
+ puts 'Cancelled, no editor stored.'
141
+ rescue Errno::EPIPE
142
+ # Pipe closed, ignore
143
+ end
136
144
  Process.exit 1
137
145
  end
138
146
  update_config_option({ config_editor: editor, editor: editor })
139
- puts "Default editor set to #{editor}, modify in config file"
147
+ begin
148
+ puts "Default editor set to #{editor}, modify in config file"
149
+ rescue Errno::EPIPE
150
+ # Pipe closed, ignore
151
+ end
140
152
  editor
141
153
  end
142
154
 
@@ -39,7 +39,13 @@ module Howzit
39
39
  ## @param level [Symbol] The level
40
40
  ##
41
41
  def write(msg, level = :info)
42
- $stderr.puts msg if LOG_LEVELS[level] >= @log_level
42
+ return unless LOG_LEVELS[level] >= @log_level
43
+
44
+ begin
45
+ $stderr.puts msg
46
+ rescue Errno::EPIPE
47
+ # Pipe closed, ignore
48
+ end
43
49
  end
44
50
 
45
51
  ##
@@ -66,7 +72,13 @@ module Howzit
66
72
  ## @param msg The message
67
73
  ##
68
74
  def warn(msg)
69
- write msg, :warn
75
+ return unless LOG_LEVELS[:warn] >= @log_level
76
+
77
+ begin
78
+ $stderr.puts msg
79
+ rescue Errno::EPIPE
80
+ # Pipe closed, ignore
81
+ end
70
82
  end
71
83
 
72
84
  ##
data/lib/howzit/prompt.rb CHANGED
@@ -100,9 +100,7 @@ module Howzit
100
100
  return fzf_result(res)
101
101
  end
102
102
 
103
- if Util.command_exist?('gum')
104
- return gum_choose(matches, query: query, multi: true)
105
- end
103
+ return gum_choose(matches, query: query, multi: true) if Util.command_exist?('gum')
106
104
 
107
105
  tty_menu(matches, query: query)
108
106
  end
@@ -150,7 +148,11 @@ module Howzit
150
148
  end
151
149
 
152
150
  if query
153
- puts "\nSelect a topic for `#{query}`:"
151
+ begin
152
+ puts "\nSelect a topic for `#{query}`:"
153
+ rescue Errno::EPIPE
154
+ # Pipe closed, ignore
155
+ end
154
156
  end
155
157
  options_list(matches)
156
158
  read_selection(matches)
@@ -168,7 +170,11 @@ module Howzit
168
170
 
169
171
  return [matches[line - 1]] if line.positive? && line <= matches.length
170
172
 
171
- puts 'Out of range'
173
+ begin
174
+ puts 'Out of range'
175
+ rescue Errno::EPIPE
176
+ # Pipe closed, ignore
177
+ end
172
178
  read_selection(matches)
173
179
  end
174
180
  ensure
@@ -229,9 +235,7 @@ module Howzit
229
235
  return res.empty? ? [] : res.split(/\n/)
230
236
  end
231
237
 
232
- if Util.command_exist?('gum')
233
- return gum_choose(matches, prompt: prompt_text, multi: true, required: false)
234
- end
238
+ return gum_choose(matches, prompt: prompt_text, multi: true, required: false) if Util.command_exist?('gum')
235
239
 
236
240
  text_template_input(matches)
237
241
  end
@@ -265,7 +269,11 @@ module Howzit
265
269
  exit
266
270
  end
267
271
 
268
- puts "\n{bw}Available templates:{x} #{available.join(', ')}".c
272
+ begin
273
+ puts "\n{bw}Available templates:{x} #{available.join(', ')}".c
274
+ rescue Errno::EPIPE
275
+ # Pipe closed, ignore
276
+ end
269
277
  printf '{bw}Enter templates to include, comma-separated (return to skip):{x} '.c
270
278
  input = Readline.readline('', true).strip
271
279
 
@@ -321,7 +329,7 @@ module Howzit
321
329
  ## @return [String] the entered value
322
330
  ##
323
331
  def get_line(prompt_text, default: nil)
324
- return (default || '') unless $stdout.isatty
332
+ return default || '' unless $stdout.isatty
325
333
 
326
334
  if Util.command_exist?('gum')
327
335
  result = gum_input(prompt_text, placeholder: default || '')
@@ -346,7 +354,7 @@ module Howzit
346
354
  ##
347
355
  def gum_choose(matches, prompt: nil, multi: false, required: true, query: nil)
348
356
  prompt_text = prompt || (query ? "Select for '#{query}'" : 'Select')
349
- args = ['gum', 'choose']
357
+ args = %w[gum choose]
350
358
  args << '--no-limit' if multi
351
359
  args << "--header=#{Shellwords.escape(prompt_text)}"
352
360
  args << '--cursor.foreground=6'
@@ -376,7 +384,7 @@ module Howzit
376
384
  ## @return [String] The entered value
377
385
  ##
378
386
  def gum_input(prompt_text, placeholder: '')
379
- args = ['gum', 'input']
387
+ args = %w[gum input]
380
388
  args << "--header=#{Shellwords.escape(prompt_text)}"
381
389
  args << "--placeholder=#{Shellwords.escape(placeholder)}" unless placeholder.empty?
382
390
  args << '--cursor.foreground=6'
@@ -57,7 +57,7 @@ module Howzit
57
57
 
58
58
  # Build the table with emoji header - center emoji in 6-char column
59
59
  header = "| 🚥 | #{'Task'.ljust(task_width)} |"
60
- separator = "| :--: | #{':' + '-' * (task_width - 1)} |"
60
+ separator = "| :--: | #{":#{'-' * (task_width - 1)}"} |"
61
61
 
62
62
  table_lines = [header, separator]
63
63
  rows.each do |row|
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Howzit
4
+ # Script Communication module
5
+ # Handles communication from scripts to Howzit via a communication file
6
+ module ScriptComm
7
+ class << self
8
+ ##
9
+ ## Create a communication file for scripts to write to
10
+ ##
11
+ ## @return [String] Path to the communication file
12
+ ##
13
+ def create_comm_file
14
+ file = Tempfile.new('howzit_comm')
15
+ file.close
16
+ file.path
17
+ end
18
+
19
+ ##
20
+ ## Set up the communication file and environment variable
21
+ ##
22
+ ## @return [String] Path to the communication file
23
+ ##
24
+ def setup
25
+ comm_file = create_comm_file
26
+ ENV['HOWZIT_COMM_FILE'] = comm_file
27
+ comm_file
28
+ end
29
+
30
+ ##
31
+ ## Read and process the communication file after script execution
32
+ ##
33
+ ## @param comm_file [String] Path to the communication file
34
+ ##
35
+ ## @return [Hash] Hash with :logs and :vars keys
36
+ ##
37
+ def process(comm_file)
38
+ return { logs: [], vars: {} } unless File.exist?(comm_file)
39
+
40
+ logs = []
41
+ vars = {}
42
+
43
+ begin
44
+ content = File.read(comm_file)
45
+ content.each_line do |line|
46
+ line = line.strip
47
+ next if line.empty?
48
+
49
+ case line
50
+ when /^LOG:(info|warn|error|debug):(.+)$/i
51
+ level = Regexp.last_match(1).downcase.to_sym
52
+ message = Regexp.last_match(2)
53
+ logs << { level: level, message: message }
54
+ when /^VAR:([A-Z0-9_]+)=(.*)$/i
55
+ key = Regexp.last_match(1)
56
+ value = Regexp.last_match(2)
57
+ vars[key] = value
58
+ end
59
+ end
60
+ rescue StandardError => e
61
+ Howzit.console&.warn("Error reading communication file: #{e.message}")
62
+ ensure
63
+ # Clean up the file
64
+ File.unlink(comm_file) if File.exist?(comm_file)
65
+ end
66
+
67
+ { logs: logs, vars: vars }
68
+ end
69
+
70
+ ##
71
+ ## Process communication and apply logs/variables
72
+ ##
73
+ ## @param comm_file [String] Path to the communication file
74
+ ##
75
+ def apply(comm_file)
76
+ result = process(comm_file)
77
+ return if result[:logs].empty? && result[:vars].empty?
78
+
79
+ # Apply log messages
80
+ result[:logs].each do |log_entry|
81
+ level = log_entry[:level]
82
+ message = log_entry[:message]
83
+ next unless Howzit.console
84
+
85
+ case level
86
+ when :info
87
+ Howzit.console.info(message)
88
+ when :warn
89
+ Howzit.console.warn(message)
90
+ when :error
91
+ Howzit.console.error(message)
92
+ when :debug
93
+ Howzit.console.debug(message)
94
+ end
95
+ end
96
+
97
+ # Apply variables to named_arguments
98
+ return if result[:vars].empty?
99
+
100
+ Howzit.named_arguments ||= {}
101
+ Howzit.named_arguments.merge!(result[:vars])
102
+ end
103
+ end
104
+ end
105
+ end
@@ -38,7 +38,7 @@ module Howzit
38
38
  position = 0
39
39
  in_order = 0
40
40
  chars.each do |char|
41
- new_pos = self[position..-1] =~ /#{char}/i
41
+ new_pos = self[position..] =~ /#{char}/i
42
42
  if new_pos
43
43
  position += new_pos
44
44
  in_order += 1
@@ -72,7 +72,7 @@ module Howzit
72
72
  ##
73
73
  def distance(chars)
74
74
  distance = 0
75
- max = self.length - chars.length
75
+ max = length - chars.length
76
76
  return max unless in_order(chars) == chars.length
77
77
 
78
78
  while distance < max
@@ -175,9 +175,43 @@ module Howzit
175
175
  when 'beginswith'
176
176
  /^#{self}/i
177
177
  when 'fuzzy'
178
- /#{split(//).join('.{0,3}?')}/i
178
+ # For fuzzy matching, use token-based matching where each word gets fuzzy character matching
179
+ # This allows "lst tst" to match "List Available Tests" by applying fuzzy matching to each token
180
+ words = split(/\s+/).reject(&:empty?)
181
+ if words.length > 1
182
+ # Multiple words: apply character-by-character fuzzy matching to each word token
183
+ # Then allow flexible matching between words
184
+ pattern = words.map do |w|
185
+ # Apply fuzzy matching to each word (character-by-character with up to 3 chars between)
186
+ w.split(//).map { |c| Regexp.escape(c) }.join('.{0,3}?')
187
+ end.join('.*')
188
+ /#{pattern}/i
189
+ else
190
+ # Single word: character-by-character fuzzy matching for flexibility
191
+ /#{split(//).join('.{0,3}?')}/i
192
+ end
193
+ when 'token'
194
+ # Token-based matching: match words in order with any text between them
195
+ # "list tests" matches "list available tests", "list of tests", etc.
196
+ words = split(/\s+/).reject(&:empty?)
197
+ if words.length > 1
198
+ pattern = words.map { |w| Regexp.escape(w) }.join('.*')
199
+ /#{pattern}/i
200
+ else
201
+ /#{Regexp.escape(self)}/i
202
+ end
179
203
  else
180
- /#{self}/i
204
+ # Default 'partial' mode: token-based matching for multi-word searches
205
+ # This allows "list tests" to match "list available tests"
206
+ words = split(/\s+/).reject(&:empty?)
207
+ if words.length > 1
208
+ # Token-based: match words in order with any text between
209
+ pattern = words.map { |w| Regexp.escape(w) }.join('.*')
210
+ /#{pattern}/i
211
+ else
212
+ # Single word: simple substring match
213
+ /#{Regexp.escape(self)}/i
214
+ end
181
215
  end
182
216
  end
183
217
 
@@ -185,7 +219,7 @@ module Howzit
185
219
  def uncolor
186
220
  # force UTF-8 and remove invalid characters, then remove color codes
187
221
  # and iTerm markers
188
- gsub(Howzit::Color::COLORED_REGEXP, "").gsub(/\e\]1337;SetMark/, "")
222
+ gsub(Howzit::Color::COLORED_REGEXP, '').gsub(/\e\]1337;SetMark/, '')
189
223
  end
190
224
 
191
225
  # Wrap text at a specified width.
@@ -337,7 +371,7 @@ module Howzit
337
371
  gsub!(/\$\{(?<name>[A-Z0-9_]+(?::.*?)?)\}/i) do
338
372
  m = Regexp.last_match
339
373
  arg, default = m['name'].split(/:/).map(&:strip)
340
- if Howzit.named_arguments && Howzit.named_arguments.key?(arg) && !Howzit.named_arguments[arg].nil?
374
+ if Howzit.named_arguments&.key?(arg) && !Howzit.named_arguments[arg].nil?
341
375
  Howzit.named_arguments[arg]
342
376
  elsif default
343
377
  default
data/lib/howzit/task.rb CHANGED
@@ -26,7 +26,7 @@ module Howzit
26
26
  @arguments = attributes[:arguments] || []
27
27
 
28
28
  @type = attributes[:type] || :run
29
- @title = attributes[:title].nil? ? nil : attributes[:title].to_s
29
+ @title = attributes[:title]&.to_s
30
30
  @parent = attributes[:parent] || nil
31
31
 
32
32
  @action = attributes[:action].render_arguments || nil
@@ -61,6 +61,7 @@ module Howzit
61
61
  Howzit.console.info "#{@prefix}{bg}Running block {bw}#{@title}{x}".c if Howzit.options[:log_level] < 2
62
62
  block = @action
63
63
  script = Tempfile.new('howzit_script')
64
+ comm_file = ScriptComm.setup
64
65
  begin
65
66
  script.write(block)
66
67
  script.close
@@ -69,6 +70,8 @@ module Howzit
69
70
  ensure
70
71
  script.close
71
72
  script.unlink
73
+ # Process script communication
74
+ ScriptComm.apply(comm_file) if comm_file
72
75
  end
73
76
 
74
77
  update_last_status(res ? 0 : 1)
@@ -112,7 +115,13 @@ module Howzit
112
115
  end
113
116
  Howzit.console.info("#{@prefix}{bg}Running {bw}#{display_title}{x}".c)
114
117
  ENV['HOWZIT_SCRIPTS'] = File.expand_path('~/.config/howzit/scripts')
115
- res = system(@action)
118
+ comm_file = ScriptComm.setup
119
+ begin
120
+ res = system(@action)
121
+ ensure
122
+ # Process script communication
123
+ ScriptComm.apply(comm_file) if comm_file
124
+ end
116
125
  update_last_status(res ? 0 : 1)
117
126
  res
118
127
  end
data/lib/howzit/topic.rb CHANGED
@@ -14,13 +14,15 @@ module Howzit
14
14
  ##
15
15
  ## @param title [String] The topic title
16
16
  ## @param content [String] The raw topic content
17
+ ## @param metadata [Hash] Optional metadata hash
17
18
  ##
18
- def initialize(title, content)
19
+ def initialize(title, content, metadata = nil)
19
20
  @title = title
20
21
  @content = content
21
22
  @parent = nil
22
23
  @nest_level = 0
23
24
  @named_args = {}
25
+ @metadata = metadata
24
26
  arguments
25
27
 
26
28
  @tasks = gather_tasks
@@ -37,7 +39,7 @@ module Howzit
37
39
  args.each_with_index do |arg, idx|
38
40
  arg_name, default = arg.split(/:/).map(&:strip)
39
41
 
40
- @named_args[arg_name] = if Howzit.arguments.count >= idx + 1
42
+ @named_args[arg_name] = if Howzit.arguments && Howzit.arguments.count >= idx + 1
41
43
  Howzit.arguments[idx]
42
44
  else
43
45
  default
@@ -81,7 +83,11 @@ module Howzit
81
83
 
82
84
  if @tasks.count.positive?
83
85
  unless @prereqs.empty?
84
- puts TTY::Box.frame("{by}#{@prereqs.join("\n\n").wrap(cols - 4)}{x}".c, width: cols)
86
+ begin
87
+ puts TTY::Box.frame("{by}#{@prereqs.join("\n\n").wrap(cols - 4)}{x}".c, width: cols)
88
+ rescue Errno::EPIPE
89
+ # Pipe closed, ignore
90
+ end
85
91
  res = Prompt.yn('Have the above prerequisites been met?', default: true)
86
92
  Process.exit 1 unless res
87
93
 
@@ -123,7 +129,13 @@ module Howzit
123
129
 
124
130
  output.push(@results[:message]) if Howzit.options[:log_level] < 2 && !nested && !Howzit.options[:run]
125
131
 
126
- puts TTY::Box.frame("{bw}#{@postreqs.join("\n\n").wrap(cols - 4)}{x}".c, width: cols) unless @postreqs.empty?
132
+ unless @postreqs.empty?
133
+ begin
134
+ puts TTY::Box.frame("{bw}#{@postreqs.join("\n\n").wrap(cols - 4)}{x}".c, width: cols)
135
+ rescue Errno::EPIPE
136
+ # Pipe closed, ignore
137
+ end
138
+ end
127
139
 
128
140
  output
129
141
  end
@@ -175,6 +187,7 @@ module Howzit
175
187
  return [] if matches.empty?
176
188
 
177
189
  topic = matches[0]
190
+ return [] if topic.nil?
178
191
 
179
192
  rule = '{kKd}'
180
193
  color = '{Kyd}'
@@ -253,14 +266,16 @@ module Howzit
253
266
  output.push(@title.format_header)
254
267
  output.push('')
255
268
  end
256
- topic = @content.dup
269
+ # Process conditional blocks first
270
+ metadata = @metadata || Howzit.buildnote&.metadata
271
+ topic = ConditionalContent.process(@content.dup, { metadata: metadata })
257
272
  unless Howzit.options[:show_all_code]
258
273
  topic.gsub!(/(?mix)^(`{3,})run([?!]*)\s*
259
274
  ([^\n]*)[\s\S]*?\n\1\s*$/, '@@@run\2 \3')
260
275
  end
261
276
  topic.split(/\n/).each do |l|
262
277
  case l
263
- when /@(before|after|prereq|end)/
278
+ when /@(before|after|prereq|end|if|unless)/
264
279
  next
265
280
  when /@include(?<optional>[!?]{1,2})?\((?<action>[^)]+)\)/
266
281
  output.concat(process_include(Regexp.last_match.named_captures.symbolize_keys, opt))
@@ -299,7 +314,7 @@ module Howzit
299
314
  # Store the actual title (not overridden by show_all_code - that's only for display)
300
315
  task_args = { type: :include,
301
316
  arguments: nil,
302
- title: title.dup, # Make a copy to avoid reference issues
317
+ title: title.dup, # Make a copy to avoid reference issues
303
318
  action: obj,
304
319
  parent: self }
305
320
  # Set named_arguments before processing titles for variable substitution
@@ -362,15 +377,21 @@ module Howzit
362
377
 
363
378
  def gather_tasks
364
379
  runnable = []
365
- @prereqs = @content.scan(/(?<=@before\n).*?(?=\n@end)/im).map(&:strip)
366
- @postreqs = @content.scan(/(?<=@after\n).*?(?=\n@end)/im).map(&:strip)
380
+ # Process conditional blocks first
381
+ # Set named_arguments before processing so conditions can access them
382
+ Howzit.named_arguments = @named_args
383
+ metadata = @metadata || Howzit.buildnote&.metadata
384
+ processed_content = ConditionalContent.process(@content, { metadata: metadata })
385
+
386
+ @prereqs = processed_content.scan(/(?<=@before\n).*?(?=\n@end)/im).map(&:strip)
387
+ @postreqs = processed_content.scan(/(?<=@after\n).*?(?=\n@end)/im).map(&:strip)
367
388
 
368
389
  rx = /(?mix)(?:
369
390
  @(?<cmd>include|run|copy|open|url)(?<optional>[!?]{1,2})?\((?<action>[^)]*?)\)(?<title>[^\n]+)?
370
391
  |(?<fence>`{3,})run(?<optional2>[!?]{1,2})?(?<title2>[^\n]+)?(?<block>.*?)\k<fence>
371
392
  )/
372
393
  matches = []
373
- @content.scan(rx) { matches << Regexp.last_match }
394
+ processed_content.scan(rx) { matches << Regexp.last_match }
374
395
  matches.each do |m|
375
396
  c = m.named_captures.symbolize_keys
376
397
  Howzit.named_arguments = @named_args
data/lib/howzit/util.rb CHANGED
@@ -118,7 +118,11 @@ module Howzit
118
118
  # Paginate the output
119
119
  def page(text)
120
120
  unless $stdout.isatty
121
- puts text
121
+ begin
122
+ puts text
123
+ rescue Errno::EPIPE
124
+ # Pipe closed, ignore
125
+ end
122
126
  return
123
127
  end
124
128
 
@@ -181,7 +185,11 @@ module Howzit
181
185
  if options[:paginate] && Howzit.options[:paginate]
182
186
  page(output)
183
187
  else
184
- puts output
188
+ begin
189
+ puts output
190
+ rescue Errno::EPIPE
191
+ # Pipe closed, ignore
192
+ end
185
193
  end
186
194
  end
187
195
 
@@ -219,7 +227,7 @@ module Howzit
219
227
  ##
220
228
  def os_paste
221
229
  os = RbConfig::CONFIG['target_os']
222
- out = "{bg}Pasting from clipboard".c
230
+ out = '{bg}Pasting from clipboard'.c
223
231
  case os
224
232
  when /darwin.*/i
225
233
  Howzit.console.debug("#{out} (macOS){x}".c)
@@ -3,5 +3,5 @@
3
3
  # Primary module for this gem.
4
4
  module Howzit
5
5
  # Current Howzit version.
6
- VERSION = '2.1.27'
6
+ VERSION = '2.1.29'
7
7
  end
data/lib/howzit.rb CHANGED
@@ -13,7 +13,7 @@ COLOR_FILE = 'theme.yaml'
13
13
  IGNORE_FILE = 'ignore.yaml'
14
14
 
15
15
  # Available options for matching method
16
- MATCHING_OPTIONS = %w[partial exact fuzzy beginswith].freeze
16
+ MATCHING_OPTIONS = %w[partial exact fuzzy beginswith token].freeze
17
17
 
18
18
  # Available options for multiple_matches method
19
19
  MULTIPLE_OPTIONS = %w[first best all choose].freeze
@@ -38,6 +38,9 @@ require_relative 'howzit/stringutils'
38
38
 
39
39
  require_relative 'howzit/console_logger'
40
40
  require_relative 'howzit/config'
41
+ require_relative 'howzit/script_comm'
42
+ require_relative 'howzit/condition_evaluator'
43
+ require_relative 'howzit/conditional_content'
41
44
  require_relative 'howzit/task'
42
45
  require_relative 'howzit/topic'
43
46
  require_relative 'howzit/buildnote'