comparison vendor/gems/coderay-1.0.0/lib/coderay/scanners/css.rb @ 909:cbb26bc654de redmine-1.3

Update to Redmine 1.3-stable branch (Redmine SVN rev 8964)
author Chris Cannam
date Fri, 24 Feb 2012 19:09:32 +0000
parents
children
comparison
equal deleted inserted replaced
908:c6c2cbd0afee 909:cbb26bc654de
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