annotate vendor/gems/coderay-0.9.7/lib/coderay/scanners/css.rb @ 855:7294e8db2515 bug_162

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