class Textpow::SyntaxNode

Attributes

begin[RW]
beginCaptures[RW]
captures[RW]
content[RW]
contentName[RW]
end[RW]
endCaptures[RW]
fileTypes[RW]
firstLineMatch[RW]
foldingStartMarker[RW]
foldingStopMarker[RW]
keyEquivalent[RW]
match[RW]
name[RW]
patterns[RW]
repository[RW]
scopeName[RW]
syntax[RW]

Public Class Methods

load(file, options={}) click to toggle source
# File lib/textpow/syntax.rb, line 59
def self.load(file, options={})
  table = convert_file_to_table(file)
  SyntaxNode.new(table, options)
end
new(table, options={}) click to toggle source
# File lib/textpow/syntax.rb, line 64
def initialize(table, options={})
  @syntax = options[:syntax] || self
  @name_space = options[:name_space]

  register_in_syntaxes(table["scopeName"])
  parse_and_store_syntax_info(table)
end

Protected Class Methods

convert_file_to_table(file) click to toggle source
# File lib/textpow/syntax.rb, line 136
def self.convert_file_to_table(file)
  raise "File not found: #{file}" unless File.exist?(file)
  table = case file
  when /(\.tmSyntax|\.plist)$/
    require 'plist'
    Plist::parse_xml(file)
  else
    YAML.load_file(file)
  end
  raise "Could not parse file #{file} to a table" if table.is_a?(String)
  table
end

Public Instance Methods

parse(string, processor = RecordingProcessor.new) click to toggle source
# File lib/textpow/syntax.rb, line 76
def parse(string, processor = RecordingProcessor.new)
  processor.start_parsing scopeName
  stack = [[self, nil]]
  string.each_line do |line|
    parse_line stack, line, processor
  end
  processor.end_parsing scopeName

  processor
end
syntaxes() click to toggle source
# File lib/textpow/syntax.rb, line 72
def syntaxes
  @@syntaxes[@name_space]
end

Protected Instance Methods

create_children(patterns) click to toggle source
# File lib/textpow/syntax.rb, line 160
def create_children(patterns)
  @patterns = patterns.map do |pattern|
    if pattern["include"]
      SyntaxProxy.new(pattern["include"], syntax)
    else
      SyntaxNode.new(pattern, :syntax => syntax, :name_space => @name_space)
    end
  end
end
match_captures(name, match) click to toggle source
# File lib/textpow/syntax.rb, line 204
def match_captures name, match
  matches = []
  captures = instance_variable_get "@#{name}"
  if captures
    captures.each do |key, value|
      if key =~ /^\d*$/
        matches << [key.to_i, match.offset( key.to_i ), value["name"]] if key.to_i < match.size
      else
        matches << [match.to_index( key.to_sym ), match.offset( key.to_sym), value["name"]] if match.to_index( key.to_sym )
      end
    end
  end
  matches
end
match_end(string, match, position) click to toggle source
# File lib/textpow/syntax.rb, line 235
def match_end string, match, position
  regstring = self.end.clone
  regstring.gsub!( /\\([1-9])/ ) { |s| match[$1.to_i] }

  # in spox-textpow this is \\g in 1.9 !?
  regstring.gsub!( /\\k<(.*?)>/ ) { |s| match[$1.to_sym] }
  if Textpow::RUBY_19
    Regexp.new( regstring ).match( string, position )
  else
    Oniguruma::ORegexp.new( regstring ).match( string, position )
  end
end
match_first(string, position) click to toggle source
# File lib/textpow/syntax.rb, line 219
def match_first string, position
  if self.match
    if match = self.match.match( string, position )
      return [self, match]
    end
  elsif self.begin
    if match = self.begin.match( string, position )
      return [self, match]
    end
  elsif self.end
  else
    return match_first_son( string, position )
  end
  nil
end
match_first_son(string, position) click to toggle source

find earliest matching pattern

# File lib/textpow/syntax.rb, line 249
def match_first_son(string, position)
  return if not patterns

  earliest_match = nil
  earliest_match_offset = nil
  patterns.each do |pattern|
    next unless match = pattern.match_first(string, position)

    match_offset = match_offset(match[1]).first
    return match if match_offset == 0 # no need to look any further

    if not earliest_match or earliest_match_offset > match_offset
      earliest_match = match
      earliest_match_offset = match_offset
    end
  end

  earliest_match
end
match_offset(match) click to toggle source
# File lib/textpow/syntax.rb, line 321
def match_offset(match)
  if Textpow::RUBY_19
    match.offset(0)
  else
    match.offset
  end
end
parse_and_store_syntax_info(table) click to toggle source
# File lib/textpow/syntax.rb, line 89
def parse_and_store_syntax_info(table)
  table.each do |key, value|
    case key
    when "firstLineMatch", "foldingStartMarker", "foldingStopMarker", "match", "begin"
      instance_variable_set("@#{key}", parse_regex(value))
    when "content", "fileTypes", "name", "contentName", "end", "scopeName", "keyEquivalent"
      instance_variable_set("@#{key}", value)
    when "captures", "beginCaptures", "endCaptures"
      instance_variable_set("@#{key}", value.sort)
    when "repository"
      parse_repository value
    when "patterns"
      create_children value
    when "comment"
    else
      STDERR.puts "Ignoring: #{key} => #{value.gsub("\n", "\n>>")}" if $DEBUG
    end
  end
end
parse_captures(name, pattern, match, processor) click to toggle source
# File lib/textpow/syntax.rb, line 170
    def parse_captures name, pattern, match, processor
      captures = pattern.match_captures( name, match )
      captures.reject! { |group, range, name| ! range.first || range.first == range.last }
      starts = []
      ends = []
      captures.each do |group, range, name|
        starts << [range.first, group, name]
        ends   << [range.last, -group, name]
      end

#          STDERR.puts '-' * 100
#          starts.sort!.reverse!.each{|c| STDERR.puts c.join(', ')}
#          STDERR.puts
#          ends.sort!.reverse!.each{|c| STDERR.puts c.join(', ')}
      starts.sort!.reverse!
      ends.sort!.reverse!

      while ! starts.empty? || ! ends.empty?
        if starts.empty?
          pos, key, name = ends.pop
          processor.close_tag name, pos
        elsif ends.empty?
          pos, key, name = starts.pop
          processor.open_tag name, pos
        elsif ends.last[1].abs < starts.last[1]
          pos, key, name = ends.pop
          processor.close_tag name, pos
        else
          pos, key, name = starts.pop
          processor.open_tag name, pos
        end
      end
    end
parse_line(stack, line, processor) click to toggle source
# File lib/textpow/syntax.rb, line 269
def parse_line(stack, line, processor)
  processor.new_line line
  top, match = stack.last
  position = 0
  #@ln ||= 0
  #@ln += 1
  #STDERR.puts @ln
  loop do
    if top.patterns
      pattern, pattern_match = top.match_first_son(line, position)
    end

    if top.end
      end_match = top.match_end( line, match, position )
    end

    if end_match and (not pattern_match or match_offset(pattern_match).first >= match_offset(end_match).first)
      pattern_match = end_match
      start_pos = match_offset(pattern_match).first
      end_pos = match_offset(pattern_match).last

      processor.close_tag top.contentName, start_pos if top.contentName
      parse_captures "captures", top, pattern_match, processor
      parse_captures "endCaptures", top, pattern_match, processor
      processor.close_tag top.name, end_pos if top.name
      stack.pop
      top, match = stack.last
    else
      break unless pattern

      start_pos = match_offset(pattern_match).first
      end_pos = match_offset(pattern_match).last

      if pattern.begin
        processor.open_tag pattern.name, start_pos if pattern.name
        parse_captures "captures", pattern, pattern_match, processor
        parse_captures "beginCaptures", pattern, pattern_match, processor
        processor.open_tag pattern.contentName, end_pos if pattern.contentName
        top = pattern
        match = pattern_match
        stack << [top, match]
      elsif pattern.match
        processor.open_tag pattern.name, start_pos if pattern.name
        parse_captures "captures", pattern, pattern_match, processor
        processor.close_tag pattern.name, end_pos if pattern.name
      end
    end

    position = end_pos
  end
end
parse_regex(value) click to toggle source
# File lib/textpow/syntax.rb, line 109
def parse_regex(value)
  if Textpow::RUBY_19
    parse_regex_with_invalid_chars(value)
  else
    Oniguruma::ORegexp.new(value, :options => Oniguruma::OPTION_CAPTURE_GROUP)
  end
rescue RegexpError, ArgumentError => e
  raise ParsingError, "Parsing error in #{value}: #{e.to_s}"
end
parse_regex_with_invalid_chars(value) click to toggle source
# File lib/textpow/syntax.rb, line 119
def parse_regex_with_invalid_chars(value)
  Regexp.new(value.force_encoding('UTF-8'))
rescue RegexpError => e
  if e.message =~ /UTF-8/ or e.message =~ /invalid multibyte escape/
    puts "Ignored utf8 regex error #{$!}"
    /INVALID_UTF8/
  else
    raise e
  end
end
parse_repository(repository) click to toggle source
# File lib/textpow/syntax.rb, line 149
def parse_repository(repository)
  @repository = {}
  repository.each do |key, value|
    if value["include"]
      @repository[key] = SyntaxProxy.new(value["include"], syntax)
    else
      @repository[key] = SyntaxNode.new(value, :syntax => syntax, :name_space => @name_space)
    end
  end
end
register_in_syntaxes(scope) click to toggle source

register in global syntax list -> can be found by include

# File lib/textpow/syntax.rb, line 131
def register_in_syntaxes(scope)
  @@syntaxes[@name_space] ||= {}
  @@syntaxes[@name_space][scope] = self if scope
end