Chris@909: module CodeRay Chris@909: module Scanners Chris@909: Chris@909: load :ruby Chris@909: load :html Chris@909: load :java_script Chris@909: Chris@909: class HAML < Scanner Chris@909: Chris@909: register_for :haml Chris@909: title 'HAML Template' Chris@909: Chris@909: KINDS_NOT_LOC = HTML::KINDS_NOT_LOC Chris@909: Chris@909: protected Chris@909: Chris@909: def setup Chris@909: super Chris@909: @ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true Chris@909: @embedded_ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true, :state => @ruby_scanner.interpreted_string_state Chris@909: @html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true Chris@909: end Chris@909: Chris@909: def scan_tokens encoder, options Chris@909: Chris@909: match = nil Chris@909: code = '' Chris@909: Chris@909: until eos? Chris@909: Chris@909: if bol? Chris@909: if match = scan(/!!!.*/) Chris@909: encoder.text_token match, :doctype Chris@909: next Chris@909: end Chris@909: Chris@909: if match = scan(/(?>( *)(\/(?!\[if)|-\#|:javascript|:ruby|:\w+) *)(?=\n)/) Chris@909: encoder.text_token match, :comment Chris@909: Chris@909: code = self[2] Chris@909: if match = scan(/(?:\n+#{self[1]} .*)+/) Chris@909: case code Chris@909: when '/', '-#' Chris@909: encoder.text_token match, :comment Chris@909: when ':javascript' Chris@909: # TODO: recognize #{...} snippets inside JavaScript Chris@909: @java_script_scanner ||= CodeRay.scanner :java_script, :tokens => @tokens, :keep_tokens => true Chris@909: @java_script_scanner.tokenize match, :tokens => encoder Chris@909: when ':ruby' Chris@909: @ruby_scanner.tokenize match, :tokens => encoder Chris@909: when /:\w+/ Chris@909: encoder.text_token match, :comment Chris@909: else Chris@909: raise 'else-case reached: %p' % [code] Chris@909: end Chris@909: end Chris@909: end Chris@909: Chris@909: if match = scan(/ +/) Chris@909: encoder.text_token match, :space Chris@909: end Chris@909: Chris@909: if match = scan(/\/.*/) Chris@909: encoder.text_token match, :comment Chris@909: next Chris@909: end Chris@909: Chris@909: if match = scan(/\\/) Chris@909: encoder.text_token match, :plain Chris@909: if match = scan(/.+/) Chris@909: @html_scanner.tokenize match, :tokens => encoder Chris@909: end Chris@909: next Chris@909: end Chris@909: Chris@909: tag = false Chris@909: Chris@909: if match = scan(/%[\w:]+\/?/) Chris@909: encoder.text_token match, :tag Chris@909: # if match = scan(/( +)(.+)/) Chris@909: # encoder.text_token self[1], :space Chris@909: # @embedded_ruby_scanner.tokenize self[2], :tokens => encoder Chris@909: # end Chris@909: tag = true Chris@909: end Chris@909: Chris@909: while match = scan(/([.#])[-\w]*\w/) Chris@909: encoder.text_token match, self[1] == '#' ? :constant : :class Chris@909: tag = true Chris@909: end Chris@909: Chris@909: if tag && match = scan(/(\()([^)]+)?(\))?/) Chris@909: # TODO: recognize title=@title, class="widget_#{@widget.number}" Chris@909: encoder.text_token self[1], :plain Chris@909: @html_scanner.tokenize self[2], :tokens => encoder, :state => :attribute if self[2] Chris@909: encoder.text_token self[3], :plain if self[3] Chris@909: end Chris@909: Chris@909: if tag && match = scan(/\{/) Chris@909: encoder.text_token match, :plain Chris@909: Chris@909: code = '' Chris@909: level = 1 Chris@909: while true Chris@909: code << scan(/([^\{\},\n]|, *\n?)*/) Chris@909: case match = getch Chris@909: when '{' Chris@909: level += 1 Chris@909: code << match Chris@909: when '}' Chris@909: level -= 1 Chris@909: if level > 0 Chris@909: code << match Chris@909: else Chris@909: break Chris@909: end Chris@909: when "\n", ",", nil Chris@909: break Chris@909: end Chris@909: end Chris@909: @ruby_scanner.tokenize code, :tokens => encoder unless code.empty? Chris@909: Chris@909: encoder.text_token match, :plain if match Chris@909: end Chris@909: Chris@909: if tag && match = scan(/(\[)([^\]\n]+)?(\])?/) Chris@909: encoder.text_token self[1], :plain Chris@909: @ruby_scanner.tokenize self[2], :tokens => encoder if self[2] Chris@909: encoder.text_token self[3], :plain if self[3] Chris@909: end Chris@909: Chris@909: if tag && match = scan(/\//) Chris@909: encoder.text_token match, :tag Chris@909: end Chris@909: Chris@909: if scan(/(>? encoder Chris@909: else Chris@909: @ruby_scanner.tokenize self[4], :tokens => encoder Chris@909: end Chris@909: end Chris@909: elsif match = scan(/((?:<|> encoder if self[2] Chris@909: end Chris@909: Chris@909: elsif match = scan(/.+/) Chris@909: @html_scanner.tokenize match, :tokens => encoder Chris@909: Chris@909: end Chris@909: Chris@909: if match = scan(/\n/) Chris@909: encoder.text_token match, :space Chris@909: end Chris@909: end Chris@909: Chris@909: encoder Chris@909: Chris@909: end Chris@909: Chris@909: end Chris@909: Chris@909: end Chris@909: end