To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
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
|