summaryrefslogtreecommitdiff
path: root/lib/coderay
diff options
context:
space:
mode:
authormurphy <murphy@rubychan.de>2008-09-18 01:12:44 +0000
committermurphy <murphy@rubychan.de>2008-09-18 01:12:44 +0000
commit41acfacb91970c8fa4e8b34f35c718eb329a3733 (patch)
treef11afbc209873285165934749e47b74e6936f25f /lib/coderay
parente46b6cbe74db250d743e7f194bfc7514529a82cc (diff)
downloadcoderay-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')
-rw-r--r--lib/coderay/scanners/css.rb181
-rw-r--r--lib/coderay/styles/cycnus.rb5
-rwxr-xr-xlib/coderay/token_classes.rb3
3 files changed, 188 insertions, 1 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
diff --git a/lib/coderay/styles/cycnus.rb b/lib/coderay/styles/cycnus.rb
index 7b68a66..d552d50 100644
--- a/lib/coderay/styles/cycnus.rb
+++ b/lib/coderay/styles/cycnus.rb
@@ -80,6 +80,7 @@ ol.CodeRay li { white-space: pre }
.il .il .il { background: #ccc }
.il .idl { font-weight: bold; color: #888 }
+.im { color:#f00; }
.in { color:#B2B; font-weight:bold }
.iv { color:#33B }
.la { color:#970; font-weight:bold }
@@ -89,11 +90,13 @@ ol.CodeRay li { white-space: pre }
.op { }
.pc { color:#038; font-weight:bold }
.pd { color:#369; font-weight:bold }
-.pp { color:#579 }
+.pp { color:#579; }
+.ps { color:#00C; font-weight: bold; }
.pt { color:#339; font-weight:bold }
.r, .kw { color:#080; font-weight:bold }
.ke { color: #808; }
+.vl { color: #088; }
.rx { background-color:#fff0ff }
.rx .k { color:#808 }
diff --git a/lib/coderay/token_classes.rb b/lib/coderay/token_classes.rb
index c352e48..9b5e83c 100755
--- a/lib/coderay/token_classes.rb
+++ b/lib/coderay/token_classes.rb
@@ -29,6 +29,7 @@ module CodeRay
:function => 'fu',
:global_variable => 'gv',
:hex => 'hx',
+ :important => 'im',
:include => 'ic',
:inline => 'il',
:inline_delimiter => 'idl',
@@ -46,6 +47,7 @@ module CodeRay
:pre_type => 'pt',
:predefined => 'pd',
:preprocessor => 'pp',
+ :pseudo_class => 'ps',
:regexp => 'rx',
:reserved => 'r',
:shell => 'sh',
@@ -56,6 +58,7 @@ module CodeRay
:tag_special => 'ts',
:type => 'ty',
:variable => 'v',
+ :value => 'vl',
:xml_text => 'xt',
:ident => :NO_HIGHLIGHT, # 'id'