To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / .svn / pristine / 56 / 56493cc79ece7ebee244c20a035044b3abd7b20d.svn-base @ 1297:0a574315af3e

History | View | Annotate | Download (5.76 KB)

1
module CodeRay
2
module Scanners
3

    
4
  class CSS < Scanner
5
    
6
    register_for :css
7
    
8
    KINDS_NOT_LOC = [
9
      :comment,
10
      :class, :pseudo_class, :type,
11
      :constant, :directive,
12
      :key, :value, :operator, :color, :float, :string,
13
      :error, :important,
14
    ]  # :nodoc:
15
    
16
    module RE  # :nodoc:
17
      Hex = /[0-9a-fA-F]/
18
      Unicode = /\\#{Hex}{1,6}(?:\r\n|\s)?/ # differs from standard because it allows uppercase hex too
19
      Escape = /#{Unicode}|\\[^\r\n\f0-9a-fA-F]/
20
      NMChar = /[-_a-zA-Z0-9]|#{Escape}/
21
      NMStart = /[_a-zA-Z]|#{Escape}/
22
      NL = /\r\n|\r|\n|\f/
23
      String1 = /"(?:[^\n\r\f\\"]|\\#{NL}|#{Escape})*"?/  # TODO: buggy regexp
24
      String2 = /'(?:[^\n\r\f\\']|\\#{NL}|#{Escape})*'?/  # TODO: buggy regexp
25
      String = /#{String1}|#{String2}/
26
      
27
      HexColor = /#(?:#{Hex}{6}|#{Hex}{3})/
28
      Color = /#{HexColor}/
29
      
30
      Num = /-?(?:[0-9]+|[0-9]*\.[0-9]+)/
31
      Name = /#{NMChar}+/
32
      Ident = /-?#{NMStart}#{NMChar}*/
33
      AtKeyword = /@#{Ident}/
34
      Percentage = /#{Num}%/
35
      
36
      reldimensions = %w[em ex px]
37
      absdimensions = %w[in cm mm pt pc]
38
      Unit = Regexp.union(*(reldimensions + absdimensions))
39
      
40
      Dimension = /#{Num}#{Unit}/
41
      
42
      Comment = %r! /\* (?: .*? \*/ | .* ) !mx
43
      Function = /(?:url|alpha|attr|counters?)\((?:[^)\n\r\f]|\\\))*\)?/
44
      
45
      Id = /##{Name}/
46
      Class = /\.#{Name}/
47
      PseudoClass = /:#{Name}/
48
      AttributeSelector = /\[[^\]]*\]?/
49
    end
50
    
51
  protected
52
    
53
    def scan_tokens encoder, options
54
      
55
      value_expected = nil
56
      states = [:initial]
57
      
58
      until eos?
59
        
60
        if match = scan(/\s+/)
61
          encoder.text_token match, :space
62
          
63
        elsif case states.last
64
          when :initial, :media
65
            if match = scan(/(?>#{RE::Ident})(?!\()|\*/ox)
66
              encoder.text_token match, :type
67
              next
68
            elsif match = scan(RE::Class)
69
              encoder.text_token match, :class
70
              next
71
            elsif match = scan(RE::Id)
72
              encoder.text_token match, :constant
73
              next
74
            elsif match = scan(RE::PseudoClass)
75
              encoder.text_token match, :pseudo_class
76
              next
77
            elsif match = scan(RE::AttributeSelector)
78
              # TODO: Improve highlighting inside of attribute selectors.
79
              encoder.text_token match[0,1], :operator
80
              encoder.text_token match[1..-2], :attribute_name if match.size > 2
81
              encoder.text_token match[-1,1], :operator if match[-1] == ?]
82
              next
83
            elsif match = scan(/@media/)
84
              encoder.text_token match, :directive
85
              states.push :media_before_name
86
              next
87
            end
88
          
89
          when :block
90
            if match = scan(/(?>#{RE::Ident})(?!\()/ox)
91
              if value_expected
92
                encoder.text_token match, :value
93
              else
94
                encoder.text_token match, :key
95
              end
96
              next
97
            end
98
            
99
          when :media_before_name
100
            if match = scan(RE::Ident)
101
              encoder.text_token match, :type
102
              states[-1] = :media_after_name
103
              next
104
            end
105
          
106
          when :media_after_name
107
            if match = scan(/\{/)
108
              encoder.text_token match, :operator
109
              states[-1] = :media
110
              next
111
            end
112
          
113
          else
114
            #:nocov:
115
            raise_inspect 'Unknown state', encoder
116
            #:nocov:
117
            
118
          end
119
          
120
        elsif match = scan(/\/\*(?:.*?\*\/|\z)/m)
121
          encoder.text_token match, :comment
122
          
123
        elsif match = scan(/\{/)
124
          value_expected = false
125
          encoder.text_token match, :operator
126
          states.push :block
127
          
128
        elsif match = scan(/\}/)
129
          value_expected = false
130
          if states.last == :block || states.last == :media
131
            encoder.text_token match, :operator
132
            states.pop
133
          else
134
            encoder.text_token match, :error
135
          end
136
          
137
        elsif match = scan(/#{RE::String}/o)
138
          encoder.begin_group :string
139
          encoder.text_token match[0, 1], :delimiter
140
          encoder.text_token match[1..-2], :content if match.size > 2
141
          encoder.text_token match[-1, 1], :delimiter if match.size >= 2
142
          encoder.end_group :string
143
          
144
        elsif match = scan(/#{RE::Function}/o)
145
          encoder.begin_group :string
146
          start = match[/^\w+\(/]
147
          encoder.text_token start, :delimiter
148
          if match[-1] == ?)
149
            encoder.text_token match[start.size..-2], :content
150
            encoder.text_token ')', :delimiter
151
          else
152
            encoder.text_token match[start.size..-1], :content
153
          end
154
          encoder.end_group :string
155
          
156
        elsif match = scan(/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/ox)
157
          encoder.text_token match, :float
158
          
159
        elsif match = scan(/#{RE::Color}/o)
160
          encoder.text_token match, :color
161
          
162
        elsif match = scan(/! *important/)
163
          encoder.text_token match, :important
164
          
165
        elsif match = scan(/(?:rgb|hsl)a?\([^()\n]*\)?/)
166
          encoder.text_token match, :color
167
          
168
        elsif match = scan(RE::AtKeyword)
169
          encoder.text_token match, :directive
170
          
171
        elsif match = scan(/ [+>:;,.=()\/] /x)
172
          if match == ':'
173
            value_expected = true
174
          elsif match == ';'
175
            value_expected = false
176
          end
177
          encoder.text_token match, :operator
178
          
179
        else
180
          encoder.text_token getch, :error
181
          
182
        end
183
        
184
      end
185
      
186
      encoder
187
    end
188
    
189
  end
190
  
191
end
192
end