class SublimeDSL::Tools::RegexpWannabe

A string that would like to be a Regexp. May contain back-references to another RegexpWannabe, stored as '§1', '§2', etc.

Attributes

backref[R]
error[R]
regexp[R]
source[R]
warnings[R]

Public Class Methods

new(source, backref = nil) click to toggle source

FIXME: 3 possible sources: PList, JSON, Ruby Regexp

  • when from PList, the current way of doing things seems ok

  • when from JSON, t & n should be converted to \t and \n and back to t & n on output (they don't use (?x))

  • when from Ruby, source should not be altered on input, but the output depends on JSON vs PList

# File lib/sublime_dsl/tools/regexp_wannabe.rb, line 28
def initialize(source, backref = nil)
  # encode in utf-8, remove escaped newlines
  @source = source.encode('utf-8').gsub(/(^|[^\\](\\\\)*+)\\\n/, '\1')
  @backref = backref
  @regexp = nil
  @error = nil
  @warnings = []
  try_conversion
  @error = message_only(@error) if @error
  @source.gsub!("\t", "  ") if extended?
end

Public Instance Methods

fixme_comments(indent = ' ') click to toggle source

Returns the fixme comments for this regexp. If no fixme, returns an empty string.

# File lib/sublime_dsl/tools/regexp_wannabe.rb, line 91
def fixme_comments(indent = '  ')
  fixmes.map { |c| "#{indent}#{c}\n" }.join
end
fixmes() click to toggle source

Returns an array of fixme comments.

# File lib/sublime_dsl/tools/regexp_wannabe.rb, line 78
def fixmes
  comments = []
  error and
    comments << "# FIXME: (error) #{error}"
  source =~ /#[@${]/ and
    comments << "# FIXME: (error) '#$&' will be interpreted as interpolation: escape '#' as '\\#'"
  comments.concat warnings.map { |w| "# FIXME: (warning) #{w}" }
  comments
end
inspect(rform = false) click to toggle source

Returns the ruby syntax.

If source contains a '/', tries to return an r-form %r'...' or %r:...: or %r!...!. If it does not, or if the source contains all 3 characters ':!, return /.../ if rform is false (the default), or %r/.../ if rform is true. (The latter syntax avoids warnings when the regexp is the first argument to a method and parentheses are not used around arguments.)

# File lib/sublime_dsl/tools/regexp_wannabe.rb, line 48
def inspect(rform = false)
  str = extended? ? source : source.gsub("\t", '\t').gsub("\n", '\n')
  s = rform ? '%r' : ''
  return "#{s}/#{str}/" unless str.include?('/')
  %w(' : !).each { |c| return "%r#{c}#{str}#{c}" unless str.include?(c) }
  # replace / with \/ unless already escaped
  escaped = str.gsub( %r"(^|[^\\](\\\\)*+)(?=/)", '\1\\')
  "#{s}/#{escaped}/"
end
to_s(for_json = false) click to toggle source

Returns the string syntax.

  • If json == false, the string is suitable for PList.

  • If json == true, the string is suitable for JSON output with String#inspect.

# File lib/sublime_dsl/tools/regexp_wannabe.rb, line 62
def to_s(for_json = false)
  if regexp
    # remove \/ escapes
    str = regexp.source.gsub( %r"(^|[^\\](\\\\)*+)\\(?=/)", '\1')
    str = str.gsub("\t", '\t').gsub("\n", '\n') unless extended?
    if for_json
      str.gsub!( %r"(^|[^\\](\\\\)*+)\\n", "\\1\n")
      str.gsub!( %r"(^|[^\\](\\\\)*+)\\t", "\\1\t")
    end
  else
    str = source
  end
  str.gsub(/§(?=[1-9])/, '\\')
end

Private Instance Methods

capture_warnings() { || ... } click to toggle source
# File lib/sublime_dsl/tools/regexp_wannabe.rb, line 131
def capture_warnings
  prev_verbose, $VERBOSE = $VERBOSE, 2
  prev_stderr, $stderr = $stderr, StringIO.new('', 'wb:utf-8')
  yield
  msg = $stderr.string
  msg.lines.grep(/ warning: /).map do |line|
    line = line[/.*? warning: (.*)/, 1]
    message_only(line)
  end
ensure
  $stderr = prev_stderr
  $VERBOSE = prev_verbose
end
extended?() click to toggle source
# File lib/sublime_dsl/tools/regexp_wannabe.rb, line 97
def extended?
  source =~ /\(\?[im]*x(:|[-im]*\))/
end
message_only(msg) click to toggle source
# File lib/sublime_dsl/tools/regexp_wannabe.rb, line 145
def message_only(msg)
  line = msg.lines.first.strip
  line =~ /(.*?):/ and line = $1.strip
  line
end
try_conversion() click to toggle source
# File lib/sublime_dsl/tools/regexp_wannabe.rb, line 101
def try_conversion
  @warnings = capture_warnings { @regexp = Regexp.new(source.dup) }
  @backref = nil unless source =~ /§[1-9]/
rescue RegexpError => ex
  msg = ex.message
  unless backref && msg =~ /^invalid backref|^invalid pattern in look-behind/
    @error = msg
    return
  end

  if backref.error
    warnings << 'contains references to a Regexp with an error'
    return
  end

  # replace \n with §n (but \\n remains \\n, while \\\n => \\§n, etc.)
  source.gsub!(/((^|[^\\])(\\\\)*)\\(?=[1-9])/, '\1§')

  unless backref.warnings.empty?
    warnings << 'contains references to a Regexp with warning(s)'
    return
  end

  begin
    @warnings = capture_warnings { @regexp = Regexp.new(source.dup) }
  rescue RegexpError => ex
    @error = ex.message
  end
end