Chris@0: module CodeRay Chris@0: module Scanners Chris@0: Chris@0: # YAML Scanner Chris@0: # Chris@0: # Based on the YAML scanner from Syntax by Jamis Buck. Chris@0: class YAML < Scanner Chris@0: Chris@0: register_for :yaml Chris@0: file_extension 'yml' Chris@0: Chris@0: KINDS_NOT_LOC = :all Chris@0: Chris@0: def scan_tokens tokens, options Chris@0: Chris@0: value_expected = nil Chris@0: state = :initial Chris@0: key_indent = indent = 0 Chris@0: Chris@0: until eos? Chris@0: Chris@0: kind = nil Chris@0: match = nil Chris@0: key_indent = nil if bol? Chris@0: Chris@0: if match = scan(/ +[\t ]*/) Chris@0: kind = :space Chris@0: Chris@0: elsif match = scan(/\n+/) Chris@0: kind = :space Chris@0: state = :initial if match.index(?\n) Chris@0: Chris@0: elsif match = scan(/#.*/) Chris@0: kind = :comment Chris@0: Chris@0: elsif bol? and case Chris@0: when match = scan(/---|\.\.\./) Chris@0: tokens << [:open, :head] Chris@0: tokens << [match, :head] Chris@0: tokens << [:close, :head] Chris@0: next Chris@0: when match = scan(/%.*/) Chris@0: tokens << [match, :doctype] Chris@0: next Chris@0: end Chris@0: Chris@0: elsif state == :value and case Chris@0: when !check(/(?:"[^"]*")(?=: |:$)/) && scan(/"/) Chris@0: tokens << [:open, :string] Chris@0: tokens << [matched, :delimiter] Chris@0: tokens << [matched, :content] if scan(/ [^"\\]* (?: \\. [^"\\]* )* /mx) Chris@0: tokens << [matched, :delimiter] if scan(/"/) Chris@0: tokens << [:close, :string] Chris@0: next Chris@0: when match = scan(/[|>][-+]?/) Chris@0: tokens << [:open, :string] Chris@0: tokens << [match, :delimiter] Chris@0: string_indent = key_indent || column(pos - match.size - 1) Chris@0: tokens << [matched, :content] if scan(/(?:\n+ {#{string_indent + 1}}.*)+/) Chris@0: tokens << [:close, :string] Chris@0: next Chris@0: when match = scan(/(?![!"*&]).+?(?=$|\s+#)/) Chris@0: tokens << [match, :string] Chris@0: string_indent = key_indent || column(pos - match.size - 1) Chris@0: tokens << [matched, :string] if scan(/(?:\n+ {#{string_indent + 1}}.*)+/) Chris@0: next Chris@0: end Chris@0: Chris@0: elsif case Chris@0: when match = scan(/[-:](?= |$)/) Chris@0: state = :value if state == :colon && (match == ':' || match == '-') Chris@0: state = :value if state == :initial && match == '-' Chris@0: kind = :operator Chris@0: when match = scan(/[,{}\[\]]/) Chris@0: kind = :operator Chris@0: when state == :initial && match = scan(/[\w.() ]*\S(?=: |:$)/) Chris@0: kind = :key Chris@0: key_indent = column(pos - match.size - 1) Chris@0: # tokens << [key_indent.inspect, :debug] Chris@0: state = :colon Chris@0: when match = scan(/(?:"[^"\n]*"|'[^'\n]*')(?=: |:$)/) Chris@0: tokens << [:open, :key] Chris@0: tokens << [match[0,1], :delimiter] Chris@0: tokens << [match[1..-2], :content] Chris@0: tokens << [match[-1,1], :delimiter] Chris@0: tokens << [:close, :key] Chris@0: key_indent = column(pos - match.size - 1) Chris@0: # tokens << [key_indent.inspect, :debug] Chris@0: state = :colon Chris@0: next Chris@0: when scan(/(![\w\/]+)(:([\w:]+))?/) Chris@0: tokens << [self[1], :type] Chris@0: if self[2] Chris@0: tokens << [':', :operator] Chris@0: tokens << [self[3], :class] Chris@0: end Chris@0: next Chris@0: when scan(/&\S+/) Chris@0: kind = :variable Chris@0: when scan(/\*\w+/) Chris@0: kind = :global_variable Chris@0: when scan(/<