diff options
author | murphy <murphy@rubychan.de> | 2008-09-18 01:12:44 +0000 |
---|---|---|
committer | murphy <murphy@rubychan.de> | 2008-09-18 01:12:44 +0000 |
commit | 41acfacb91970c8fa4e8b34f35c718eb329a3733 (patch) | |
tree | f11afbc209873285165934749e47b74e6936f25f /lib/coderay/scanners/css.rb | |
parent | e46b6cbe74db250d743e7f194bfc7514529a82cc (diff) | |
download | coderay-41acfacb91970c8fa4e8b34f35c718eb329a3733.tar.gz |
New: *CSS Scanner* (closes #29).
* Based on Stefan Walk's implementation, with fixes, enhancements and speedups.
* It seems to be fairly fast and highlights nicely.
* I added the styles for Ignis Draconis, S5, and YUI as example code for testing.
More changes:
* Added three new token classes, :important, :pseudo_class, and :value, along with CSS styles.
Diffstat (limited to 'lib/coderay/scanners/css.rb')
-rw-r--r-- | lib/coderay/scanners/css.rb | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/lib/coderay/scanners/css.rb b/lib/coderay/scanners/css.rb new file mode 100644 index 0000000..897e27a --- /dev/null +++ b/lib/coderay/scanners/css.rb @@ -0,0 +1,181 @@ +module CodeRay +module Scanners + + class CSS < Scanner + + register_for :css + + module RE + NonASCII = /[\x80-\xFF]/ + Hex = /[0-9a-fA-F]/ + Unicode = /\\#{Hex}{1,6}(?:\r\n|\s)?/ # differs from standard because it allows uppercase hex too + Escape = /#{Unicode}|\\[^\r\n\f0-9a-fA-F]/ + NMChar = /[-_a-zA-Z0-9]|#{NonASCII}|#{Escape}/ + NMStart = /[_a-zA-Z]|#{NonASCII}|#{Escape}/ + NL = /\r\n|\r|\n|\f/ + String1 = /"(?:[^\n\r\f\\"]|\\#{NL}|#{Escape})*"?/ # FIXME: buggy regexp + String2 = /'(?:[^\n\r\f\\']|\\#{NL}|#{Escape})*'?/ # FIXME: buggy regexp + String = /#{String1}|#{String2}/ + + HexColor = /#(?:#{Hex}{6}|#{Hex}{3})/ + Color = /#{HexColor}/ + + Num = /-?(?:[0-9]+|[0-9]*\.[0-9]+)/ + Name = /#{NMChar}+/ + Ident = /-?#{NMStart}#{NMChar}*/ + AtKeyword = /@#{Ident}/ + Percentage = /#{Num}%/ + + reldimensions = %w[em ex px] + absdimensions = %w[in cm mm pt pc] + Unit = Regexp.union(*(reldimensions + absdimensions)) + + Dimension = /#{Num}#{Unit}/ + + Comment = %r! /\* (?: .*? \*/ | .* ) !mx + Function = /(?:url|alpha)\((?:[^)\n\r\f]|\\\))*\)?/ + + Id = /##{Name}/ + Class = /\.#{Name}/ + PseudoClass = /:#{Name}/ + + end + + def scan_tokens tokens, options + + value_expected = nil + states = [:initial] + + until eos? + + kind = nil + match = nil + + if scan(/\s+/) + kind = :space + + elsif case states.last + when :initial + if scan(/#{RE::Ident}|\*/ox) + kind = :keyword + elsif scan RE::Class + kind = :class + elsif scan RE::Id + kind = :constant + elsif scan RE::PseudoClass + kind = :pseudo_class + elsif scan RE::Name + kind = :identifier + end + + when :block + if scan(/(?>#{RE::Ident})(?!\()/ox) + if value_expected + kind = :value + else + kind = :key + end + end + + when :comment + if scan(/(?:[^*\s]|\*(?!\/))+/) + kind = :comment + elsif scan(/\*\//) + kind = :comment + states.pop + elsif scan(/\s+/) + kind = :space + end + + else + raise_inspect 'Unknown state', tokens + + end + + elsif scan(/\/\*/) + kind = :comment + states.push :comment + + elsif scan(/\{/) + value_expected = false + kind = :operator + states.push :block + + elsif scan(/\}/) + value_expected = false + if states.last == :block + kind = :operator + states.pop + else + kind = :error + end + + elsif match = scan(/#{RE::String}/o) + tokens << [:open, :string] + tokens << [match[0, 1], :delimiter] + tokens << [match[1..-2], :content] if match.size > 2 + tokens << [match[-1, 1], :delimiter] if match.size >= 2 + tokens << [:close, :string] + next + + elsif match = scan(/#{RE::Function}/o) + tokens << [:open, :string] + start = match[/^\w+\(/] + tokens << [start, :delimiter] + if match[-1] == ?) + tokens << [match[start.size..-2], :content] + tokens << [')', :delimiter] + else + tokens << [match[start.size..-1], :content] + end + tokens << [:close, :string] + next + + elsif scan(/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/ox) + kind = :float + + elsif scan(/#{RE::Color}/o) + kind = :color + + elsif scan(/! *important/) + kind = :important + + elsif scan(/rgb\([^()\n]*\)?/) + kind = :color + + elsif scan(/#{RE::AtKeyword}/o) + kind = :directive + + elsif match = scan(/ [+>:;,.=()\/] /x) + if match == ':' + value_expected = true + elsif match == ';' + value_expected = false + end + kind = :operator + + else + getch + kind = :error + + end + + match ||= matched + if $DEBUG and not kind + raise_inspect 'Error token %p in line %d' % + [[match, kind], line], tokens + end + raise_inspect 'Empty token', tokens unless match + + tokens << [match, kind] + # tokens << [states.inspect, :error] + + end + + tokens + end + + end + +end +end |