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 / vendor / gems / coderay-0.9.7 / lib / coderay / scanners / css.rb @ 442:753f1380d6bc

History | View | Annotate | Download (5.36 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,
13
      :error, :important,
14
    ]
15
    
16
    module RE
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})*"?/  # FIXME: buggy regexp
24
      String2 = /'(?:[^\n\r\f\\']|\\#{NL}|#{Escape})*'?/  # FIXME: 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)\((?:[^)\n\r\f]|\\\))*\)?/
44

    
45
      Id = /##{Name}/
46
      Class = /\.#{Name}/
47
      PseudoClass = /:#{Name}/
48
      AttributeSelector = /\[[^\]]*\]?/
49

    
50
    end
51

    
52
    def scan_tokens tokens, options
53
      
54
      value_expected = nil
55
      states = [:initial]
56

    
57
      until eos?
58

    
59
        kind = nil
60
        match = nil
61

    
62
        if scan(/\s+/)
63
          kind = :space
64

    
65
        elsif case states.last
66
          when :initial, :media
67
            if scan(/(?>#{RE::Ident})(?!\()|\*/ox)
68
              kind = :type
69
            elsif scan RE::Class
70
              kind = :class
71
            elsif scan RE::Id
72
              kind = :constant
73
            elsif scan RE::PseudoClass
74
              kind = :pseudo_class
75
            elsif match = scan(RE::AttributeSelector)
76
              # TODO: Improve highlighting inside of attribute selectors.
77
              tokens << [:open, :string]
78
              tokens << [match[0,1], :delimiter]
79
              tokens << [match[1..-2], :content] if match.size > 2
80
              tokens << [match[-1,1], :delimiter] if match[-1] == ?]
81
              tokens << [:close, :string]
82
              next
83
            elsif match = scan(/@media/)
84
              kind = :directive
85
              states.push :media_before_name
86
            end
87
          
88
          when :block
89
            if scan(/(?>#{RE::Ident})(?!\()/ox)
90
              if value_expected
91
                kind = :value
92
              else
93
                kind = :key
94
              end
95
            end
96

    
97
          when :media_before_name
98
            if scan RE::Ident
99
              kind = :type
100
              states[-1] = :media_after_name
101
            end
102
          
103
          when :media_after_name
104
            if scan(/\{/)
105
              kind = :operator
106
              states[-1] = :media
107
            end
108
          
109
          when :comment
110
            if scan(/(?:[^*\s]|\*(?!\/))+/)
111
              kind = :comment
112
            elsif scan(/\*\//)
113
              kind = :comment
114
              states.pop
115
            elsif scan(/\s+/)
116
              kind = :space
117
            end
118

    
119
          else
120
            raise_inspect 'Unknown state', tokens
121

    
122
          end
123

    
124
        elsif scan(/\/\*/)
125
          kind = :comment
126
          states.push :comment
127

    
128
        elsif scan(/\{/)
129
          value_expected = false
130
          kind = :operator
131
          states.push :block
132

    
133
        elsif scan(/\}/)
134
          value_expected = false
135
          if states.last == :block || states.last == :media
136
            kind = :operator
137
            states.pop
138
          else
139
            kind = :error
140
          end
141

    
142
        elsif match = scan(/#{RE::String}/o)
143
          tokens << [:open, :string]
144
          tokens << [match[0, 1], :delimiter]
145
          tokens << [match[1..-2], :content] if match.size > 2
146
          tokens << [match[-1, 1], :delimiter] if match.size >= 2
147
          tokens << [:close, :string]
148
          next
149

    
150
        elsif match = scan(/#{RE::Function}/o)
151
          tokens << [:open, :string]
152
          start = match[/^\w+\(/]
153
          tokens << [start, :delimiter]
154
          if match[-1] == ?)
155
            tokens << [match[start.size..-2], :content]
156
            tokens << [')', :delimiter]
157
          else
158
            tokens << [match[start.size..-1], :content]
159
          end
160
          tokens << [:close, :string]
161
          next
162

    
163
        elsif scan(/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/ox)
164
          kind = :float
165

    
166
        elsif scan(/#{RE::Color}/o)
167
          kind = :color
168

    
169
        elsif scan(/! *important/)
170
          kind = :important
171

    
172
        elsif scan(/rgb\([^()\n]*\)?/)
173
          kind = :color
174

    
175
        elsif scan(/#{RE::AtKeyword}/o)
176
          kind = :directive
177

    
178
        elsif match = scan(/ [+>:;,.=()\/] /x)
179
          if match == ':'
180
            value_expected = true
181
          elsif match == ';'
182
            value_expected = false
183
          end
184
          kind = :operator
185

    
186
        else
187
          getch
188
          kind = :error
189

    
190
        end
191

    
192
        match ||= matched
193
        if $CODERAY_DEBUG and not kind
194
          raise_inspect 'Error token %p in line %d' %
195
            [[match, kind], line], tokens
196
        end
197
        raise_inspect 'Empty token', tokens unless match
198

    
199
        tokens << [match, kind]
200

    
201
      end
202

    
203
      tokens
204
    end
205

    
206
  end
207

    
208
end
209
end