Module: Howzit::StringUtils

Included in:
String
Defined in:
lib/howzit/stringutils.rb

Overview

String Extensions

Instance Method Summary collapse

Instance Method Details

#available?Boolean

Test if an executable is available on the system

Returns:

  • (Boolean)

    executable is available



289
290
291
# File 'lib/howzit/stringutils.rb', line 289

def available?
  Util.valid_command?(self)
end

#build_note?Boolean

Test if the filename matches the conditions to be a build note

Returns:

  • (Boolean)

    true if filename passes test



91
92
93
94
95
96
97
# File 'lib/howzit/stringutils.rb', line 91

def build_note?
  return false if downcase !~ /^(howzit[^.]*|build[^.]+)/

  return false if Howzit.config.should_ignore(self)

  true
end

#cString

Shortcut for calling Color.template

Returns:

  • (String)

    colorized string



162
163
164
# File 'lib/howzit/stringutils.rb', line 162

def c
  Color.template(self)
end

#comp_distance(term) ⇒ Float

Compare strings and return a distance

Parameters:

  • other (String)

    The string to compare

  • term (String)

    The search term

Returns:

  • (Float)

    distance



12
13
14
15
# File 'lib/howzit/stringutils.rb', line 12

def comp_distance(term)
  chars = term.split(//)
  contains_count(chars) + distance(chars)
end

#contains_count(chars) ⇒ Object

Number of matching characters the string contains

Parameters:

  • chars (String|Array)

    The characters



22
23
24
25
26
27
# File 'lib/howzit/stringutils.rb', line 22

def contains_count(chars)
  chars = chars.split(//) if chars.is_a?(String)
  count = 0
  chars.each { |char| count += 1 if self =~ /#{char}/i }
  count
end

#distance(chars) ⇒ Number

Determine the minimum distance between characters that they all still fall within

Parameters:

  • chars (Array)

    The characters

Returns:

  • (Number)

    distance



73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/howzit/stringutils.rb', line 73

def distance(chars)
  distance = 0
  max = self.length - chars.length
  return max unless in_order(chars) == chars.length

  while distance < max
    return distance if in_distance?(chars, distance)

    distance += 1
  end
  distance
end

#extract_metadataHash

Split the content at the first top-level header and assume everything before it is metadata. Passes to

metadata for processing

Returns:

  • (Hash)

    key/value pairs



357
358
359
360
361
362
363
364
# File 'lib/howzit/stringutils.rb', line 357

def 
  if File.exist?(self)
    leader = Util.read_file(self).split(/^#/)[0].strip
    leader.length.positive? ? leader. : {}
  else
    {}
  end
end

#format_header(opts = {}) ⇒ String

Make a fancy title line for the topic

Parameters:

  • opts (Hash) (defaults to: {})

    options

Returns:

  • (String)

    formatted string



431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/howzit/stringutils.rb', line 431

def format_header(opts = {})
  title = dup
  options = {
    hr: "\u{254C}",
    color: '{bg}',
    border: '{x}',
    mark: should_mark_iterm?
  }

  options.merge!(opts)

  case Howzit.options[:header_format]
  when :block
    Color.template("\n\n#{options[:color]}\u{258C}#{title}#{should_mark_iterm? && options[:mark] ? iterm_marker : ''}{x}")
  else
    cols = TTY::Screen.columns

    cols = Howzit.options[:wrap] if Howzit.options[:wrap].positive? && cols > Howzit.options[:wrap]
    title = Color.template("#{options[:border]}#{options[:hr] * 2}( #{options[:color]}#{title}#{options[:border]} )")

    tail = if should_mark_iterm?
             "#{options[:hr] * (cols - title.uncolor.length - 15)}#{options[:mark] ? iterm_marker : ''}"
           else
             options[:hr] * (cols - title.uncolor.length)
           end
    Color.template("\n\n#{title}#{tail}{x}\n\n")
  end
end

#in_distance?(chars, distance) ⇒ Boolean

Determine if a series of characters are all within a given distance of each other in the String

Parameters:

  • chars (String|Array)

    The characters

  • distance (Number)

    The distance

Returns:

  • (Boolean)

    true if within distance



59
60
61
62
63
# File 'lib/howzit/stringutils.rb', line 59

def in_distance?(chars, distance)
  chars = chars.split(//) if chars.is_a?(String)
  rx = Regexp.new(chars.join(".{,#{distance}}"), 'i')
  self =~ rx ? true : false
end

#in_order(chars) ⇒ Boolean

Determine if characters are in order

Parameters:

  • chars (String|Array)

    The characters

Returns:

  • (Boolean)

    characters are in order



36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/howzit/stringutils.rb', line 36

def in_order(chars)
  chars = chars.split(//) if chars.is_a?(String)
  position = 0
  in_order = 0
  chars.each do |char|
    new_pos = self[position..-1] =~ /#{char}/i
    if new_pos
      position += new_pos
      in_order += 1
    end
  end
  in_order
end

#iterm_markerString

Output an iTerm marker

Returns:

  • (String)

    ANSI escape sequence for iTerm marker



421
422
423
# File 'lib/howzit/stringutils.rb', line 421

def iterm_marker
  "\e]1337;SetMark\a" if should_mark_iterm?
end

#metadataHash

Examine text for multimarkdown-style metadata and return key/value pairs

Returns:

  • (Hash)

    The metadata as key/value pairs



371
372
373
374
375
376
377
378
379
380
# File 'lib/howzit/stringutils.rb', line 371

def 
  data = {}
  scan(/(?mi)^(\S[\s\S]+?): ([\s\S]*?)(?=\n\S[\s\S]*?:|\Z)/).each do |m|
    data[m[0].strip.downcase] = m[1]
  end
  out = (data)
  Howzit.named_arguments ||= {}
  Howzit.named_arguments = out.merge(Howzit.named_arguments)
  out
end

#normalize_metadata(meta) ⇒ Hash

Autocorrect some keys

Parameters:

  • meta (Hash)

    The metadata

Returns:

  • (Hash)

    corrected metadata



389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/howzit/stringutils.rb', line 389

def (meta)
  data = {}
  meta.each do |k, v|
    case k
    when /^te?m?pl(ate)?s?$/
      data['template'] = v
    when /^req\w*$/
      data['required'] = v
    else
      data[k] = v
    end
  end
  data
end

#note_title(file, truncate = 0) ⇒ Object

Get the title of the build note (top level header)

Parameters:

  • truncate (Integer) (defaults to: 0)

    Truncate to width



104
105
106
107
108
109
110
111
112
113
# File 'lib/howzit/stringutils.rb', line 104

def note_title(file, truncate = 0)
  title = match(/(?:^(\S.*?)(?=\n==)|^# ?(.*?)$)/)
  title = if title
            title[1].nil? ? title[2] : title[1]
          else
            file.sub(/(\.\w+)?$/, '')
          end

  title && truncate.positive? ? title.trunc(truncate) : title
end

#preserve_escapesString

Replace slash escaped characters in a string with a zero-width space that will prevent a shell from interpreting them when output to console

Returns:



122
123
124
# File 'lib/howzit/stringutils.rb', line 122

def preserve_escapes
  gsub(/\\([a-z])/, '\​\1')
end

#render_argumentsString

Render $X placeholders based on positional arguments

Returns:

  • (String)

    rendered string



327
328
329
330
331
332
# File 'lib/howzit/stringutils.rb', line 327

def render_arguments
  str = dup
  str.render_named_placeholders
  str.render_numeric_placeholders
  Howzit.arguments.nil? ? str : str.gsub(/\$[@*]/, Shellwords.join(Howzit.arguments))
end

#render_named_placeholdersObject



334
335
336
337
338
339
340
# File 'lib/howzit/stringutils.rb', line 334

def render_named_placeholders
  gsub!(/\$\{(?<name>[A-Z0-9_]+(?::.*?)?)\}/i) do
    m = Regexp.last_match
    arg, default = m['name'].split(/:/).map(&:strip)
    Howzit.named_arguments.key?(arg) && !Howzit.named_arguments[arg].nil? ? Howzit.named_arguments[arg] : default
  end
end

#render_numeric_placeholdersObject



342
343
344
345
346
347
348
# File 'lib/howzit/stringutils.rb', line 342

def render_numeric_placeholders
  gsub!(/\$\{?(\d+)\}?/) do
    arg, default = Regexp.last_match(1).split(/:/)
    idx = arg.to_i - 1
    Howzit.arguments.length > idx ? Howzit.arguments[idx] : default || Regexp.last_match(0)
  end
end

#render_template(vars) ⇒ String

Render [%variable] placeholders in a templated string

Parameters:

  • vars (Hash)

    Key/value pairs of variable values

Returns:

  • (String)

    Rendered string



301
302
303
304
305
306
307
308
309
310
311
# File 'lib/howzit/stringutils.rb', line 301

def render_template(vars)
  vars.each do |k, v|
    gsub!(/\[%#{k}(:.*?)?\]/, v)
  end

  # Replace empty variables with default
  gsub!(/\[%([^\]]+?):(.*?)\]/, '\2')

  # Remove remaining empty variables
  gsub(/\[%.*?\]/, '')
end

#render_template!(vars) ⇒ Object

Render [%variable] placeholders in place

Parameters:

  • vars (Hash)

    Key/value pairs of variable values



318
319
320
# File 'lib/howzit/stringutils.rb', line 318

def render_template!(vars)
  replace render_template(vars)
end

#should_mark_iterm?Boolean

Test if iTerm markers should be output. Requires that the $TERM_PROGRAM be iTerm and howzit is not running directives or paginating output

Returns:

  • (Boolean)

    should mark?



411
412
413
# File 'lib/howzit/stringutils.rb', line 411

def should_mark_iterm?
  ENV['TERM_PROGRAM'] =~ /^iTerm/ && !Howzit.options[:run] && !Howzit.options[:paginate]
end

#split_line(width, indent = '') ⇒ Object

Splits a line at nearest word break

Parameters:

  • width (Integer)

    The width of the first segment

  • indent (String) (defaults to: '')

    The indent string



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/howzit/stringutils.rb', line 267

def split_line(width, indent = '')
  line = dup
  at = line.index(/\s/)
  last_at = at

  while !at.nil? && at < width
    last_at = at
    at = line.index(/\s/, last_at + 1)
  end

  if last_at.nil?
    [indent + line[0, width], line[width, line.length]]
  else
    [indent + line[0, last_at], line[last_at + 1, line.length]]
  end
end

#to_config_value(orig_value = nil) ⇒ Object

Convert a string to a valid YAML value

Parameters:

  • orig_value (defaults to: nil)

    The original value from which type will be determined

Returns:

  • coerced value



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/howzit/stringutils.rb', line 133

def to_config_value(orig_value = nil)
  if orig_value
    case orig_value.class.to_s
    when /Integer/
      to_i
    when /(True|False)Class/
      self =~ /^(t(rue)?|y(es)?|1)$/i ? true : false
    else
      self
    end
  else
    case self
    when /^[0-9]+$/
      to_i
    when /^(t(rue)?|y(es)?)$/i
      true
    when /^(f(alse)?|n(o)?)$/i
      false
    else
      self
    end
  end
end

#to_rxRegexp

Convert a string to a regex object based on matching settings

Returns:

  • (Regexp)

    Receive regex representation of the object.



171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/howzit/stringutils.rb', line 171

def to_rx
  case Howzit.options[:matching]
  when 'exact'
    /^#{self}$/i
  when 'beginswith'
    /^#{self}/i
  when 'fuzzy'
    /#{split(//).join('.{0,3}?')}/i
  else
    /#{self}/i
  end
end

#trunc(len) ⇒ Object

Truncate string to nearest word

Parameters:

  • len (Integer)

    max length of string



244
245
246
247
248
249
250
# File 'lib/howzit/stringutils.rb', line 244

def trunc(len)
  split(/ /).each_with_object([]) do |x, ob|
    break ob unless ob.join(' ').length + ' '.length + x.length <= len

    ob.push(x)
  end.join(' ').strip
end

#trunc!(len) ⇒ Object

Truncate string in place (destructive)

Parameters:

  • len (Integer)

    The length to truncate at



257
258
259
# File 'lib/howzit/stringutils.rb', line 257

def trunc!(len)
  replace trunc(len)
end

#uncolorObject

Just strip out color codes when requested



185
186
187
# File 'lib/howzit/stringutils.rb', line 185

def uncolor
  gsub(/\e\[[\d;]+m/, '').gsub(/\e\]1337;SetMark/, '')
end

#wrap(width) ⇒ String

Wrap text at a specified width.

Adapted from https://github.com/pazdera/word_wrap/, copyright (c) 2014, 2015 Radek Pazdera Distributed under the MIT License

Parameters:

  • width (Integer)

    The width at which to wrap lines

Returns:



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/howzit/stringutils.rb', line 200

def wrap(width)
  width ||= 80
  output = []
  indent = ''

  text = gsub(/\t/, '  ')

  text.lines do |line|
    line.chomp! "\n"
    if line.length > width
      indent = if line.uncolor =~ /^(\s*(?:[+\-*]|\d+\.) )/
                 ' ' * Regexp.last_match[1].length
               else
                 ''
               end
      new_lines = line.split_line(width)

      while new_lines.length > 1 && new_lines[1].length + indent.length > width
        output.push new_lines[0]

        new_lines = new_lines[1].split_line(width, indent)
      end
      output += [new_lines[0], indent + new_lines[1]]
    else
      output.push line
    end
  end
  output.map!(&:rstrip)
  output.join("\n")
end

#wrap!(width) ⇒ Object

Wrap string in place (destructive)

Parameters:

  • width (Integer)

    The width at which to wrap



236
237
238
# File 'lib/howzit/stringutils.rb', line 236

def wrap!(width)
  replace(wrap(width))
end