summaryrefslogtreecommitdiff
path: root/lib/coderay/scanners/css2.rb
blob: edb9ca3f5aae41eb187d82875607568c10e6bcc9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
module CodeRay
module Scanners

  class CSS2 < RuleBasedScanner
    
    register_for :css2
    
    KINDS_NOT_LOC = [
      :comment,
      :class, :pseudo_class, :tag,
      :id, :directive,
      :key, :value, :operator, :color, :float, :string,
      :error, :important, :type,
    ]  # :nodoc:
    
    module RE  # :nodoc:
      Hex = /[0-9a-fA-F]/
      Unicode = /\\#{Hex}{1,6}\b/ # differs from standard because it allows uppercase hex too
      Escape = /#{Unicode}|\\[^\n0-9a-fA-F]/
      NMChar = /[-_a-zA-Z0-9]/
      NMStart = /[_a-zA-Z]/
      String1 = /(")((?:[^\n\\"]+|\\\n|#{Escape})+)?(")?/  # TODO: buggy regexp
      String2 = /(')((?:[^\n\\']+|\\\n|#{Escape})+)?(')?/  # TODO: buggy regexp
      String = /#{String1}|#{String2}/
      
      HexColor = /#(?:#{Hex}{6}|#{Hex}{3})/
      
      Num = /-?(?:[0-9]*\.[0-9]+|[0-9]+)n?/
      Name = /#{NMChar}+/
      Ident = /-?#{NMStart}#{NMChar}*/
      AtKeyword = /@#{Ident}/
      Percentage = /#{Num}%/
      
      reldimensions = %w[em ex px]
      absdimensions = %w[in cm mm pt pc]
      Unit = Regexp.union(*(reldimensions + absdimensions + %w[s dpi dppx deg]))
      
      Dimension = /#{Num}#{Unit}/
      
      Function = /((?:url|alpha|attr|counters?)\()((?:[^)\n]|\\\))+)?(\))?/
      
      Id = /(?!#{HexColor}\b(?!-))##{Name}/
      Class = /\.#{Name}/
      PseudoClass = /::?#{Ident}/
      AttributeSelector = /(\[)([^\]]+)?(\])?/
    end
    
  protected
    
    def setup
      @state = :initial
      @value_expected = false
      @block = false
    end
    
    state :initial do
      on %r/\s+/, :space
      
      on check_if(:block), check_if(:value_expected), %r/(?>#{RE::Ident})(?!\()/x, :value
      on check_if(:block), %r/(?>#{RE::Ident})(?!\()/x, :key
      
      on check_unless(:block), %r/(?>#{RE::Ident})(?!\()|\*/x, :tag
      on check_unless(:block), RE::Class, :class
      on check_unless(:block), RE::Id, :id
      on check_unless(:block), RE::PseudoClass, :pseudo_class
      # TODO: Improve highlighting inside of attribute selectors.
      on check_unless(:block), RE::AttributeSelector, groups(:operator, :attribute_name, :operator)
      on check_unless(:block), %r/(@media)(\s+)?(#{RE::Ident})?(\s+)?(\{)?/, groups(:directive, :space, :type, :space, :operator)
      
      on %r/\/\*(?:.*?\*\/|\z)/m, :comment
      on %r/\{/, :operator, flag_off(:value_expected), flag_on(:block)
      on %r/\}/, :operator, flag_off(:value_expected), flag_off(:block)
      on RE::String1, push(:string), groups(:delimiter, :content, :delimiter), pop
      on RE::String2, push(:string), groups(:delimiter, :content, :delimiter), pop
      on RE::Function, push(:function), groups(:delimiter, :content, :delimiter), pop
      on %r/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/x, :float
      on RE::HexColor, :color
      on %r/! *important/, :important
      on %r/(?:rgb|hsl)a?\([^()\n]*\)?/, :color
      on RE::AtKeyword, :directive
      on %r/:/, :operator, flag_on(:value_expected)
      on %r/;/, :operator, flag_off(:value_expected)
      on %r/ [+>~,.=()\/] /x, :operator
    end
    
    scan_tokens_code = <<-"RUBY"
    def scan_tokens encoder, options#{ def_line = __LINE__; nil }
      states = Array(options[:state] || @state).dup
      value_expected = @value_expected
      block = @block
      state = states.last
      
      until eos?
        
        case state
        
#{ @code.chomp.gsub(/^/, '        ') }
        else
          raise_inspect 'Unknown state: %p' % [state], encoder
          
        end
        
      end
      
      if options[:keep_state]
        @state = states
        @value_expected = value_expected
        @block = block
      end
      
      encoder
    end
    RUBY
    
    if ENV['PUTS']
      puts CodeRay.scan(scan_tokens_code, :ruby).terminal
      puts "callbacks: #{callbacks.size}"
    end
    class_eval scan_tokens_code, __FILE__, def_line
    
  end
  
end
end