comparison vendor/plugins/coderay-0.9.2/lib/coderay/scanners/.svn/text-base/css.rb.svn-base @ 0:513646585e45

* Import Redmine trunk SVN rev 3859
author Chris Cannam
date Fri, 23 Jul 2010 15:52:44 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:513646585e45
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 NonASCII = /[\x80-\xFF]/
18 Hex = /[0-9a-fA-F]/
19 Unicode = /\\#{Hex}{1,6}(?:\r\n|\s)?/ # differs from standard because it allows uppercase hex too
20 Escape = /#{Unicode}|\\[^\r\n\f0-9a-fA-F]/
21 NMChar = /[-_a-zA-Z0-9]|#{NonASCII}|#{Escape}/
22 NMStart = /[_a-zA-Z]|#{NonASCII}|#{Escape}/
23 NL = /\r\n|\r|\n|\f/
24 String1 = /"(?:[^\n\r\f\\"]|\\#{NL}|#{Escape})*"?/ # FIXME: buggy regexp
25 String2 = /'(?:[^\n\r\f\\']|\\#{NL}|#{Escape})*'?/ # FIXME: buggy regexp
26 String = /#{String1}|#{String2}/
27
28 HexColor = /#(?:#{Hex}{6}|#{Hex}{3})/
29 Color = /#{HexColor}/
30
31 Num = /-?(?:[0-9]+|[0-9]*\.[0-9]+)/
32 Name = /#{NMChar}+/
33 Ident = /-?#{NMStart}#{NMChar}*/
34 AtKeyword = /@#{Ident}/
35 Percentage = /#{Num}%/
36
37 reldimensions = %w[em ex px]
38 absdimensions = %w[in cm mm pt pc]
39 Unit = Regexp.union(*(reldimensions + absdimensions))
40
41 Dimension = /#{Num}#{Unit}/
42
43 Comment = %r! /\* (?: .*? \*/ | .* ) !mx
44 Function = /(?:url|alpha)\((?:[^)\n\r\f]|\\\))*\)?/
45
46 Id = /##{Name}/
47 Class = /\.#{Name}/
48 PseudoClass = /:#{Name}/
49 AttributeSelector = /\[[^\]]*\]?/
50
51 end
52
53 def scan_tokens tokens, options
54
55 value_expected = nil
56 states = [:initial]
57
58 until eos?
59
60 kind = nil
61 match = nil
62
63 if scan(/\s+/)
64 kind = :space
65
66 elsif case states.last
67 when :initial, :media
68 if scan(/(?>#{RE::Ident})(?!\()|\*/ox)
69 kind = :type
70 elsif scan RE::Class
71 kind = :class
72 elsif scan RE::Id
73 kind = :constant
74 elsif scan RE::PseudoClass
75 kind = :pseudo_class
76 elsif match = scan(RE::AttributeSelector)
77 # TODO: Improve highlighting inside of attribute selectors.
78 tokens << [:open, :string]
79 tokens << [match[0,1], :delimiter]
80 tokens << [match[1..-2], :content] if match.size > 2
81 tokens << [match[-1,1], :delimiter] if match[-1] == ?]
82 tokens << [:close, :string]
83 next
84 elsif match = scan(/@media/)
85 kind = :directive
86 states.push :media_before_name
87 end
88
89 when :block
90 if scan(/(?>#{RE::Ident})(?!\()/ox)
91 if value_expected
92 kind = :value
93 else
94 kind = :key
95 end
96 end
97
98 when :media_before_name
99 if scan RE::Ident
100 kind = :type
101 states[-1] = :media_after_name
102 end
103
104 when :media_after_name
105 if scan(/\{/)
106 kind = :operator
107 states[-1] = :media
108 end
109
110 when :comment
111 if scan(/(?:[^*\s]|\*(?!\/))+/)
112 kind = :comment
113 elsif scan(/\*\//)
114 kind = :comment
115 states.pop
116 elsif scan(/\s+/)
117 kind = :space
118 end
119
120 else
121 raise_inspect 'Unknown state', tokens
122
123 end
124
125 elsif scan(/\/\*/)
126 kind = :comment
127 states.push :comment
128
129 elsif scan(/\{/)
130 value_expected = false
131 kind = :operator
132 states.push :block
133
134 elsif scan(/\}/)
135 value_expected = false
136 if states.last == :block || states.last == :media
137 kind = :operator
138 states.pop
139 else
140 kind = :error
141 end
142
143 elsif match = scan(/#{RE::String}/o)
144 tokens << [:open, :string]
145 tokens << [match[0, 1], :delimiter]
146 tokens << [match[1..-2], :content] if match.size > 2
147 tokens << [match[-1, 1], :delimiter] if match.size >= 2
148 tokens << [:close, :string]
149 next
150
151 elsif match = scan(/#{RE::Function}/o)
152 tokens << [:open, :string]
153 start = match[/^\w+\(/]
154 tokens << [start, :delimiter]
155 if match[-1] == ?)
156 tokens << [match[start.size..-2], :content]
157 tokens << [')', :delimiter]
158 else
159 tokens << [match[start.size..-1], :content]
160 end
161 tokens << [:close, :string]
162 next
163
164 elsif scan(/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/ox)
165 kind = :float
166
167 elsif scan(/#{RE::Color}/o)
168 kind = :color
169
170 elsif scan(/! *important/)
171 kind = :important
172
173 elsif scan(/rgb\([^()\n]*\)?/)
174 kind = :color
175
176 elsif scan(/#{RE::AtKeyword}/o)
177 kind = :directive
178
179 elsif match = scan(/ [+>:;,.=()\/] /x)
180 if match == ':'
181 value_expected = true
182 elsif match == ';'
183 value_expected = false
184 end
185 kind = :operator
186
187 else
188 getch
189 kind = :error
190
191 end
192
193 match ||= matched
194 if $CODERAY_DEBUG and not kind
195 raise_inspect 'Error token %p in line %d' %
196 [[match, kind], line], tokens
197 end
198 raise_inspect 'Empty token', tokens unless match
199
200 tokens << [match, kind]
201
202 end
203
204 tokens
205 end
206
207 end
208
209 end
210 end