-#
# License:: LGPL / ask the author
-# Version:: 1.0 (2006-Feb-3)
+# Version:: 1.1 (2006-Oct-19)
#
# A WordList is a Hash with some additional features.
# It is intended to be used for keyword recognition.
#
# WordList is highly optimized to be used in Scanners,
-# typically to decide whether a given ident is a keyword.
+# typically to decide whether a given ident is a special token.
#
# For case insensitive words use CaseIgnoringWordList.
#
@@ -47,25 +51,30 @@
# ...
class WordList < Hash
- # Create a WordList for the given +words+.
- #
- # This WordList responds to [] with +true+, if the word is
- # in +words+, and with +false+ otherwise.
- def self.for words
- new.add words
- end
-
# Creates a new WordList with +default+ as default value.
- def initialize default = false, &block
- super default, &block
+ #
+ # You can activate +caching+ to store the results for every [] request.
+ #
+ # With caching, methods like +include?+ or +delete+ may no longer behave
+ # as you expect. Therefore, it is recommended to use the [] method only.
+ def initialize default = false, caching = false, &block
+ if block
+ raise ArgumentError, 'Can\'t combine block with caching.' if caching
+ super(&block)
+ else
+ if caching
+ super() do |h, k|
+ h[k] = h.fetch k, default
+ end
+ else
+ super default
+ end
+ end
end
- # Checks if a word is included.
- def include? word
- has_key? word
- end
-
# Add words to the list and associate them with +kind+.
+ #
+ # Returns +self+, so you can concat add calls.
def add words, kind = true
words.each do |word|
self[word] = kind
@@ -78,24 +87,30 @@
# A CaseIgnoringWordList is like a WordList, only that
# keys are compared case-insensitively.
+#
+# Ignoring the text case is realized by sending the +downcase+ message to
+# all keys.
+#
+# Caching usually makes a CaseIgnoringWordList faster, but it has to be
+# activated explicitely.
class CaseIgnoringWordList < WordList
- # Creates a new WordList with +default+ as default value.
- #
- # Text case is ignored.
- def initialize default = false, &block
- block ||= proc do |h, k|
- h[k] = h.fetch k.downcase, default
+ # Creates a new case-insensitive WordList with +default+ as default value.
+ #
+ # You can activate caching to store the results for every [] request.
+ def initialize default = false, caching = false
+ if caching
+ super(default, false) do |h, k|
+ h[k] = h.fetch k.downcase, default
+ end
+ else
+ def self.[] key # :nodoc:
+ super(key.downcase)
+ end
end
- super default
end
- # Checks if a word is included.
- def include? word
- has_key? word.downcase
- end
-
- # Add words to the list and associate them with +kind+.
+ # Add +words+ to the list and associate them with +kind+.
def add words, kind = true
words.each do |word|
self[word.downcase] = kind
@@ -104,3 +119,5 @@
end
end
+
+end
\ No newline at end of file
Index: lib/coderay/styles/cycnus.rb
===================================================================
--- lib/coderay/styles/cycnus.rb (revision 200)
+++ lib/coderay/styles/cycnus.rb (revision 250)
@@ -42,12 +42,14 @@
MAIN
TOKEN_COLORS = <<-'TOKENS'
+.debug { color:white ! important; background:blue ! important; }
+
.af { color:#00C }
.an { color:#007 }
.av { color:#700 }
.aw { color:#C00 }
.bi { color:#509; font-weight:bold }
-.c { color:#888 }
+.c { color:#666; }
.ch { color:#04D }
.ch .k { color:#04D }
@@ -83,7 +85,7 @@
.la { color:#970; font-weight:bold }
.lv { color:#963 }
.oc { color:#40E; font-weight:bold }
-.on { color:#000; font-weight:bold }
+.of { color:#000; font-weight:bold }
.op { }
.pc { color:#038; font-weight:bold }
.pd { color:#369; font-weight:bold }
Index: lib/coderay/styles/murphy.rb
===================================================================
--- lib/coderay/styles/murphy.rb (revision 200)
+++ lib/coderay/styles/murphy.rb (revision 250)
@@ -47,7 +47,7 @@
.av { color:#700; }
.aw { color:#C00; }
.bi { color:#509; font-weight:bold; }
-.c { color:#666; }
+.c { color:#555; background-color: black; }
.ch { color:#88F; }
.ch .k { color:#04D; }
@@ -77,7 +77,7 @@
.la { color:#970; font-weight:bold; }
.lv { color:#963; }
.oc { color:#40E; font-weight:bold; }
-.on { color:#000; font-weight:bold; }
+.of { color:#000; font-weight:bold; }
.op { }
.pc { color:#08f; font-weight:bold; }
.pd { color:#369; font-weight:bold; }
Index: lib/coderay/tokens.rb
===================================================================
--- lib/coderay/tokens.rb (revision 200)
+++ lib/coderay/tokens.rb (revision 250)
@@ -115,7 +115,7 @@
# tokens.each_text_token { |text, kind| text.replace html_escape(text) }
def each_text_token
each do |text, kind|
- next unless text.respond_to? :to_str
+ next unless text.is_a? ::String
yield text, kind
end
end
@@ -252,7 +252,7 @@
#
# You can configure the level of compression,
# but the default value 7 should be what you want
- # in most cases as it is a good comprimise between
+ # in most cases as it is a good compromise between
# speed and compression rate.
#
# See GZip module.
@@ -267,9 +267,20 @@
# Should be equal to the input size before
# scanning.
def text_size
- map { |t, k| t }.join.size
+ size = 0
+ each_text_token do |t, k|
+ size + t.size
+ end
+ size
end
+ # The total size of the tokens.
+ # Should be equal to the input size before
+ # scanning.
+ def text
+ map { |t, k| t if t.is_a? ::String }.join
+ end
+
# Include this module to give an object an #undump
# method.
#
@@ -342,7 +353,7 @@
#
# Returns self.
def << token
- @callback.call token
+ @callback.call(*token)
@size += 1
self
end
@@ -365,4 +376,8 @@
end
+
+ # Token name abbreviations
+ require 'coderay/token_classes'
+
end
Index: lib/coderay/duo.rb
===================================================================
--- lib/coderay/duo.rb (revision 200)
+++ lib/coderay/duo.rb (revision 250)
@@ -4,26 +4,84 @@
#
# $Id: scanner.rb 123 2006-03-21 14:46:34Z murphy $
#
- # TODO: Doc.
+ # A Duo is a convenient way to use CodeRay. You just create a Duo,
+ # giving it a lang (language of the input code) and a format (desired
+ # output format), and call Duo#highlight with the code.
+ #
+ # Duo makes it easy to re-use both scanner and encoder for a repetitive
+ # task. It also provides a very easy interface syntax:
+ #
+ # require 'coderay'
+ # CodeRay::Duo[:python, :div].highlight 'import this'
+ #
+ # Until you want to do uncommon things with CodeRay, I recommend to use
+ # this method, since it takes care of everything.
class Duo
- attr_accessor :scanner, :encoder
-
- def initialize lang, format, options = {}
- @scanner = CodeRay.scanner lang, CodeRay.get_scanner_options(options)
- @encoder = CodeRay.encoder format, options
+ attr_accessor :lang, :format, :options
+
+ # Create a new Duo, holding a lang and a format to highlight code.
+ #
+ # simple:
+ # CodeRay::Duo[:ruby, :page].highlight 'bla 42'
+ #
+ # streaming:
+ # CodeRay::Duo[:ruby, :page].highlight 'bar 23', :stream => true
+ #
+ # with options:
+ # CodeRay::Duo[:ruby, :html, :hint => :debug].highlight '????::??'
+ #
+ # alternative syntax without options:
+ # CodeRay::Duo[:ruby => :statistic].encode 'class << self; end'
+ #
+ # alternative syntax with options:
+ # CodeRay::Duo[{ :ruby => :statistic }, :do => :something].encode 'abc'
+ #
+ # The options are forwarded to scanner and encoder
+ # (see CodeRay.get_scanner_options).
+ def initialize lang = nil, format = nil, options = {}
+ if format == nil and lang.is_a? Hash and lang.size == 1
+ @lang = lang.keys.first
+ @format = lang[@lang]
+ else
+ @lang = lang
+ @format = format
+ end
+ @options = options
end
class << self
+ # To allow calls like Duo[:ruby, :html].highlight.
alias [] new
end
- def encode code
- @scanner.string = code
- @encoder.encode_tokens(scanner.tokenize)
+ # The scanner of the duo. Only created once.
+ def scanner
+ @scanner ||= CodeRay.scanner @lang, CodeRay.get_scanner_options(@options)
end
+
+ # The encoder of the duo. Only created once.
+ def encoder
+ @encoder ||= CodeRay.encoder @format, @options
+ end
+
+ # Tokenize and highlight the code using +scanner+ and +encoder+.
+ #
+ # If the :stream option is set, the Duo will go into streaming mode,
+ # saving memory for the cost of time.
+ def encode code, options = { :stream => false }
+ stream = options.delete :stream
+ options = @options.merge options
+ if stream
+ encoder.encode_stream(code, @lang, options)
+ else
+ scanner.code = code
+ encoder.encode_tokens(scanner.tokenize, options)
+ end
+ end
alias highlight encode
end
end
+
Index: lib/coderay/scanner.rb
===================================================================
--- lib/coderay/scanner.rb (revision 200)
+++ lib/coderay/scanner.rb (revision 250)
@@ -66,8 +66,18 @@
end
def normify code
- code = code.to_s.to_unix
+ code = code.to_s
+ code.force_encoding 'binary' if code.respond_to? :force_encoding
+ code.to_unix
end
+
+ def file_extension extension = nil
+ if extension
+ @file_extension = extension.to_s
+ else
+ @file_extension ||= plugin_id.to_s
+ end
+ end
end
@@ -117,9 +127,6 @@
setup
end
- # More mnemonic accessor name for the input string.
- alias code string
-
def reset
super
reset_instance
@@ -131,6 +138,10 @@
reset_instance
end
+ # More mnemonic accessor name for the input string.
+ alias code string
+ alias code= string=
+
# Scans the code and returns all tokens in a Tokens object.
def tokenize new_string=nil, options = {}
options = @options.merge(options)
@@ -148,6 +159,11 @@
def tokens
@cached_tokens ||= tokenize
end
+
+ # Whether the scanner is in streaming mode.
+ def streaming?
+ !!@options[:stream]
+ end
# Traverses the tokens.
def each &block
@@ -195,7 +211,7 @@
raise ScanError, <<-EOE % [
-***ERROR in %s: %s
+***ERROR in %s: %s (after %d tokens)
tokens:
%s
@@ -211,13 +227,14 @@
***ERROR***
EOE
- File.basename(caller[0]),
- msg,
- tokens.last(10).map { |t| t.inspect }.join("\n"),
- line, pos,
- matched, state, bol?, eos?,
- string[pos-ambit,ambit],
- string[pos,ambit],
+ File.basename(caller[0]),
+ msg,
+ tokens.size,
+ tokens.last(10).map { |t| t.inspect }.join("\n"),
+ line, pos,
+ matched, state, bol?, eos?,
+ string[pos-ambit,ambit],
+ string[pos,ambit],
]
end
Index: lib/coderay/for_redcloth.rb
===================================================================
--- lib/coderay/for_redcloth.rb (revision 0)
+++ lib/coderay/for_redcloth.rb (revision 250)
@@ -0,0 +1,72 @@
+module CodeRay # :nodoc:
+
+ # A little hack to enable CodeRay highlighting in RedCloth.
+ #
+ # Usage:
+ # require 'coderay'
+ # require 'coderay/for_redcloth'
+ # RedCloth.new('@[ruby]puts "Hello, World!"@').to_html
+ #
+ # Make sure you have RedCloth 4.0.3 activated, for example by calling
+ # require 'rubygems'
+ # before RedCloth is loaded and before calling CodeRay.for_redcloth.
+ module ForRedCloth
+
+ def self.install
+ gem 'RedCloth', '>= 4.0.3' rescue nil
+ require 'redcloth'
+ raise 'CodeRay.for_redcloth needs RedCloth 4.0.3 or later.' unless RedCloth::VERSION.to_s >= '4.0.3'
+ RedCloth::TextileDoc.send :include, ForRedCloth::TextileDoc
+ RedCloth::Formatters::HTML.module_eval do
+ def unescape(html)
+ replacements = {
+ '&' => '&',
+ '"' => '"',
+ '>' => '>',
+ '<' => '<',
+ }
+ html.gsub(/&(?:amp|quot|[gl]t);/) { |entity| replacements[entity] }
+ end
+ undef_method :code, :bc_open, :bc_close, :escape_pre
+ def code(opts) # :nodoc:
+ opts[:block] = true
+ if opts[:lang] && !filter_coderay
+ require 'coderay'
+ @in_bc ||= nil
+ format = @in_bc ? :div : :span
+ highlighted_code = CodeRay.encode opts[:text], opts[:lang], format, :stream => true
+ highlighted_code.sub!(/\A<(span|div)/) { |m| m + pba(@in_bc || opts) }
+ highlighted_code = unescape(highlighted_code) unless @in_bc
+ highlighted_code
+ else
+ "#{opts[:text]}
"
+ end
+ end
+ def bc_open(opts) # :nodoc:
+ opts[:block] = true
+ @in_bc = opts
+ opts[:lang] ? '' : ""
+ end
+ def bc_close(opts) # :nodoc:
+ @in_bc = nil
+ opts[:lang] ? '' : "
\n"
+ end
+ def escape_pre(text)
+ if @in_bc ||= nil
+ text
+ else
+ html_esc(text, :html_escape_preformatted)
+ end
+ end
+ end
+ end
+
+ module TextileDoc # :nodoc:
+ attr_accessor :filter_coderay
+ end
+
+ end
+
+end
+
+CodeRay::ForRedCloth.install
\ No newline at end of file
Index: lib/coderay/scanners/ruby/patterns.rb
===================================================================
--- lib/coderay/scanners/ruby/patterns.rb (revision 200)
+++ lib/coderay/scanners/ruby/patterns.rb (revision 250)
@@ -14,19 +14,14 @@
DEF_KEYWORDS = %w[ def ]
UNDEF_KEYWORDS = %w[ undef ]
+ ALIAS_KEYWORDS = %w[ alias ]
MODULE_KEYWORDS = %w[class module]
DEF_NEW_STATE = WordList.new(:initial).
add(DEF_KEYWORDS, :def_expected).
add(UNDEF_KEYWORDS, :undef_expected).
+ add(ALIAS_KEYWORDS, :alias_expected).
add(MODULE_KEYWORDS, :module_expected)
- IDENTS_ALLOWING_REGEXP = %w[
- and or not while until unless if then elsif when sub sub! gsub gsub!
- scan slice slice! split
- ]
- REGEXP_ALLOWED = WordList.new(false).
- add(IDENTS_ALLOWING_REGEXP, :set)
-
PREDEFINED_CONSTANTS = %w[
nil true false self
DATA ARGV ARGF __FILE__ __LINE__
@@ -41,19 +36,20 @@
METHOD_NAME = / #{IDENT} [?!]? /ox
METHOD_NAME_OPERATOR = /
\*\*? # multiplication and power
- | [-+]@? # plus, minus
- | [\/%&|^`~] # division, modulo or format strings, &and, |or, ^xor, `system`, tilde
+ | [-+~]@? # plus, minus, tilde with and without @
+ | [\/%&|^`] # division, modulo or format strings, &and, |or, ^xor, `system`
| \[\]=? # array getter and setter
| << | >> # append or shift left, shift right
| <=?>? | >=? # comparison, rocket operator
- | ===? # simple equality and case equality
+ | ===? | =~ # simple equality, case equality, match
+ | ![~=@]? # negation with and without @, not-equal and not-match
/ox
METHOD_NAME_EX = / #{IDENT} (?:[?!]|=(?!>))? | #{METHOD_NAME_OPERATOR} /ox
INSTANCE_VARIABLE = / @ #{IDENT} /ox
CLASS_VARIABLE = / @@ #{IDENT} /ox
OBJECT_VARIABLE = / @@? #{IDENT} /ox
GLOBAL_VARIABLE = / \$ (?: #{IDENT} | [1-9]\d* | 0\w* | [~&+`'=\/,;_.<>!@$?*":\\] | -[a-zA-Z_0-9] ) /ox
- PREFIX_VARIABLE = / #{GLOBAL_VARIABLE} |#{OBJECT_VARIABLE} /ox
+ PREFIX_VARIABLE = / #{GLOBAL_VARIABLE} | #{OBJECT_VARIABLE} /ox
VARIABLE = / @?@? #{IDENT} | #{GLOBAL_VARIABLE} /ox
QUOTE_TO_TYPE = {
@@ -73,7 +69,7 @@
EXPONENT = / [eE] [+-]? #{DECIMAL} /ox
FLOAT_SUFFIX = / #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? /ox
FLOAT_OR_INT = / #{DECIMAL} (?: #{FLOAT_SUFFIX} () )? /ox
- NUMERIC = / [-+]? (?: (?=0) (?: #{OCTAL} | #{HEXADECIMAL} | #{BINARY} ) | #{FLOAT_OR_INT} ) /ox
+ NUMERIC = / (?: (?=0) (?: #{OCTAL} | #{HEXADECIMAL} | #{BINARY} ) | #{FLOAT_OR_INT} ) /ox
SYMBOL = /
:
@@ -83,6 +79,7 @@
| ['"]
)
/ox
+ METHOD_NAME_OR_SYMBOL = / #{METHOD_NAME_EX} | #{SYMBOL} /ox
# TODO investigste \M, \c and \C escape sequences
# (?: M-\\C-|C-\\M-|M-\\c|c\\M-|c|C-|M-)? (?: \\ (?: [0-7]{3} | x[0-9A-Fa-f]{2} | . ) )
@@ -111,7 +108,7 @@
(?:
( [A-Za-z_0-9]+ ) # $2 = delim
|
- ( ["'`] ) # $3 = quote, type
+ ( ["'`\/] ) # $3 = quote, type
( [^\n]*? ) \3 # $4 = delim
)
/mx
@@ -129,15 +126,14 @@
/mx
# Checks for a valid value to follow. This enables
- # fancy_allowed in method calls.
+ # value_expected in method calls without parentheses.
VALUE_FOLLOWS = /
- \s+
+ (?>[ \t\f\v]+)
(?:
[%\/][^\s=]
- |
- <<-?\S
- |
- #{CHARACTER}
+ | <<-?\S
+ | [-+] \d
+ | #{CHARACTER}
)
/x
Index: lib/coderay/scanners/ruby.rb
===================================================================
--- lib/coderay/scanners/ruby.rb (revision 200)
+++ lib/coderay/scanners/ruby.rb (revision 250)
@@ -18,6 +18,7 @@
include Streamable
register_for :ruby
+ file_extension 'rb'
helper :patterns
@@ -90,15 +91,15 @@
end
when '#'
- case peek(1)[0]
- when ?{
+ case peek(1)
+ when '{'
inline_block_stack << [state, depth, heredocs]
value_expected = true
state = :initial
depth = 1
tokens << [:open, :inline]
tokens << [match + getch, :inline_delimiter]
- when ?$, ?@
+ when '$', '@'
tokens << [match, :escape]
last_state = state # scan one token as normal code, then return here
state = :initial
@@ -121,36 +122,37 @@
# }}}
else
# {{{
- if match = scan(/ [ \t\f]+ | \\? \n | \# .* /x) or
- ( bol? and match = scan(/#{patterns::RUBYDOC_OR_DATA}/o) )
- case m = match[0]
- when ?\s, ?\t, ?\f
- match << scan(/\s*/) unless eos? or heredocs
- kind = :space
- when ?\n, ?\\
- kind = :space
- if m == ?\n
- value_expected = true # FIXME not quite true
- state = :initial if state == :undef_comma_expected
- end
- if heredocs
- unscan # heredoc scanning needs \n at start
- state = heredocs.shift
- tokens << [:open, state.type]
- heredocs = nil if heredocs.empty?
- next
- else
- match << scan(/\s*/) unless eos?
- end
- when ?#, ?=, ?_
- kind = :comment
- value_expected = true
+ if match = scan(/[ \t\f]+/)
+ kind = :space
+ match << scan(/\s*/) unless eos? || heredocs
+ value_expected = true if match.index(?\n) # FIXME not quite true
+ tokens << [match, kind]
+ next
+
+ elsif match = scan(/\\?\n/)
+ kind = :space
+ if match == "\n"
+ value_expected = true # FIXME not quite true
+ state = :initial if state == :undef_comma_expected
+ end
+ if heredocs
+ unscan # heredoc scanning needs \n at start
+ state = heredocs.shift
+ tokens << [:open, state.type]
+ heredocs = nil if heredocs.empty?
+ next
else
- raise_inspect 'else-case _ reached, because case %p was
- not handled' % [matched[0].chr], tokens
+ match << scan(/\s*/) unless eos?
end
tokens << [match, kind]
next
+
+ elsif match = scan(/\#.*/) or
+ ( bol? and match = scan(/#{patterns::RUBYDOC_OR_DATA}/o) )
+ kind = :comment
+ value_expected = true
+ tokens << [match, kind]
+ next
elsif state == :initial
@@ -167,19 +169,19 @@
end
end
## experimental!
- value_expected = :set if
- patterns::REGEXP_ALLOWED[match] or check(/#{patterns::VALUE_FOLLOWS}/o)
+ value_expected = :set if check(/#{patterns::VALUE_FOLLOWS}/o)
elsif last_token_dot and match = scan(/#{patterns::METHOD_NAME_OPERATOR}/o)
kind = :ident
value_expected = :set if check(/#{patterns::VALUE_FOLLOWS}/o)
# OPERATORS #
- elsif not last_token_dot and match = scan(/ ==?=? | \.\.?\.? | [\(\)\[\]\{\}] | :: | , /x)
+ # TODO: match (), [], {} as one single operator
+ elsif not last_token_dot and match = scan(/ \.\.\.? | (?:\.|::)() | [,\(\)\[\]\{\}] | ==?=? /x)
if match !~ / [.\)\]\}] /x or match =~ /\.\.\.?/
value_expected = :set
end
- last_token_dot = :set if match == '.' or match == '::'
+ last_token_dot = :set if self[1]
kind = :operator
unless inline_block_stack.empty?
case match
@@ -210,8 +212,9 @@
interpreted = true
state = patterns::StringState.new :regexp, interpreted, match
- elsif match = scan(/#{patterns::NUMERIC}/o)
- kind = if self[1] then :float else :integer end
+ # elsif match = scan(/[-+]?#{patterns::NUMERIC}/o)
+ elsif match = value_expected ? scan(/[-+]?#{patterns::NUMERIC}/o) : scan(/#{patterns::NUMERIC}/o)
+ kind = self[1] ? :float : :integer
elsif match = scan(/#{patterns::SYMBOL}/o)
case delim = match[1]
@@ -285,6 +288,18 @@
next
end
+ elsif state == :module_expected
+ if match = scan(/<)
+ kind = :operator
+ else
+ state = :initial
+ if match = scan(/ (?:#{patterns::IDENT}::)* #{patterns::IDENT} /ox)
+ kind = :class
+ else
+ next
+ end
+ end
+
elsif state == :undef_expected
state = :undef_comma_expected
if match = scan(/#{patterns::METHOD_NAME_EX}/o)
@@ -306,6 +321,15 @@
next
end
+ elsif state == :alias_expected
+ if match = scan(/(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/o)
+ tokens << [self[1], (self[1][0] == ?: ? :symbol : :method)]
+ tokens << [self[2], :space]
+ tokens << [self[3], (self[3][0] == ?: ? :symbol : :method)]
+ end
+ state = :initial
+ next
+
elsif state == :undef_comma_expected
if match = scan(/,/)
kind = :operator
@@ -315,24 +339,14 @@
next
end
- elsif state == :module_expected
- if match = scan(/<)
- kind = :operator
- else
- state = :initial
- if match = scan(/ (?:#{patterns::IDENT}::)* #{patterns::IDENT} /ox)
- kind = :class
- else
- next
- end
- end
-
end
# }}}
+
+ unless kind == :error
+ value_expected = value_expected == :set
+ last_token_dot = last_token_dot == :set
+ end
- value_expected = value_expected == :set
- last_token_dot = last_token_dot == :set
-
if $DEBUG and not kind
raise_inspect 'Error token %p in line %d' %
[[match, kind], line], tokens, state
Index: lib/coderay/scanners/c.rb
===================================================================
--- lib/coderay/scanners/c.rb (revision 200)
+++ lib/coderay/scanners/c.rb (revision 250)
@@ -4,6 +4,8 @@
class C < Scanner
register_for :c
+
+ include Streamable
RESERVED_WORDS = [
'asm', 'break', 'case', 'continue', 'default', 'do', 'else',
@@ -42,7 +44,7 @@
kind = nil
match = nil
-
+
case state
when :initial
Index: lib/coderay/scanners/scheme.rb
===================================================================
--- lib/coderay/scanners/scheme.rb (revision 0)
+++ lib/coderay/scanners/scheme.rb (revision 250)
@@ -0,0 +1,142 @@
+module CodeRay
+ module Scanners
+
+ # Scheme scanner for CodeRay (by closure).
+ # Thanks to murphy for putting CodeRay into public.
+ class Scheme < Scanner
+
+ register_for :scheme
+ file_extension :scm
+
+ CORE_FORMS = %w[
+ lambda let let* letrec syntax-case define-syntax let-syntax
+ letrec-syntax begin define quote if or and cond case do delay
+ quasiquote set! cons force call-with-current-continuation call/cc
+ ]
+
+ IDENT_KIND = CaseIgnoringWordList.new(:ident).
+ add(CORE_FORMS, :reserved)
+
+ #IDENTIFIER_INITIAL = /[a-z!@\$%&\*\/\:<=>\?~_\^]/i
+ #IDENTIFIER_SUBSEQUENT = /#{IDENTIFIER_INITIAL}|\d|\.|\+|-/
+ #IDENTIFIER = /#{IDENTIFIER_INITIAL}#{IDENTIFIER_SUBSEQUENT}*|\+|-|\.{3}/
+ IDENTIFIER = /[a-zA-Z!@$%&*\/:<=>?~_^][\w!@$%&*\/:<=>?~^.+\-]*|[+-]|\.\.\./
+ DIGIT = /\d/
+ DIGIT10 = DIGIT
+ DIGIT16 = /[0-9a-f]/i
+ DIGIT8 = /[0-7]/
+ DIGIT2 = /[01]/
+ RADIX16 = /\#x/i
+ RADIX8 = /\#o/i
+ RADIX2 = /\#b/i
+ RADIX10 = /\#d/i
+ EXACTNESS = /#i|#e/i
+ SIGN = /[\+-]?/
+ EXP_MARK = /[esfdl]/i
+ EXP = /#{EXP_MARK}#{SIGN}#{DIGIT}+/
+ SUFFIX = /#{EXP}?/
+ PREFIX10 = /#{RADIX10}?#{EXACTNESS}?|#{EXACTNESS}?#{RADIX10}?/
+ PREFIX16 = /#{RADIX16}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX16}/
+ PREFIX8 = /#{RADIX8}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX8}/
+ PREFIX2 = /#{RADIX2}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX2}/
+ UINT10 = /#{DIGIT10}+#*/
+ UINT16 = /#{DIGIT16}+#*/
+ UINT8 = /#{DIGIT8}+#*/
+ UINT2 = /#{DIGIT2}+#*/
+ DECIMAL = /#{DIGIT10}+#+\.#*#{SUFFIX}|#{DIGIT10}+\.#{DIGIT10}*#*#{SUFFIX}|\.#{DIGIT10}+#*#{SUFFIX}|#{UINT10}#{EXP}/
+ UREAL10 = /#{UINT10}\/#{UINT10}|#{DECIMAL}|#{UINT10}/
+ UREAL16 = /#{UINT16}\/#{UINT16}|#{UINT16}/
+ UREAL8 = /#{UINT8}\/#{UINT8}|#{UINT8}/
+ UREAL2 = /#{UINT2}\/#{UINT2}|#{UINT2}/
+ REAL10 = /#{SIGN}#{UREAL10}/
+ REAL16 = /#{SIGN}#{UREAL16}/
+ REAL8 = /#{SIGN}#{UREAL8}/
+ REAL2 = /#{SIGN}#{UREAL2}/
+ IMAG10 = /i|#{UREAL10}i/
+ IMAG16 = /i|#{UREAL16}i/
+ IMAG8 = /i|#{UREAL8}i/
+ IMAG2 = /i|#{UREAL2}i/
+ COMPLEX10 = /#{REAL10}@#{REAL10}|#{REAL10}\+#{IMAG10}|#{REAL10}-#{IMAG10}|\+#{IMAG10}|-#{IMAG10}|#{REAL10}/
+ COMPLEX16 = /#{REAL16}@#{REAL16}|#{REAL16}\+#{IMAG16}|#{REAL16}-#{IMAG16}|\+#{IMAG16}|-#{IMAG16}|#{REAL16}/
+ COMPLEX8 = /#{REAL8}@#{REAL8}|#{REAL8}\+#{IMAG8}|#{REAL8}-#{IMAG8}|\+#{IMAG8}|-#{IMAG8}|#{REAL8}/
+ COMPLEX2 = /#{REAL2}@#{REAL2}|#{REAL2}\+#{IMAG2}|#{REAL2}-#{IMAG2}|\+#{IMAG2}|-#{IMAG2}|#{REAL2}/
+ NUM10 = /#{PREFIX10}?#{COMPLEX10}/
+ NUM16 = /#{PREFIX16}#{COMPLEX16}/
+ NUM8 = /#{PREFIX8}#{COMPLEX8}/
+ NUM2 = /#{PREFIX2}#{COMPLEX2}/
+ NUM = /#{NUM10}|#{NUM16}|#{NUM8}|#{NUM2}/
+
+ private
+ def scan_tokens tokens,options
+
+ state = :initial
+ ident_kind = IDENT_KIND
+
+ until eos?
+ kind = match = nil
+
+ case state
+ when :initial
+ if scan(/ \s+ | \\\n /x)
+ kind = :space
+ elsif scan(/['\(\[\)\]]|#\(/)
+ kind = :operator_fat
+ elsif scan(/;.*/)
+ kind = :comment
+ elsif scan(/#\\(?:newline|space|.?)/)
+ kind = :char
+ elsif scan(/#[ft]/)
+ kind = :pre_constant
+ elsif scan(/#{IDENTIFIER}/o)
+ kind = ident_kind[matched]
+ elsif scan(/\./)
+ kind = :operator
+ elsif scan(/"/)
+ tokens << [:open, :string]
+ state = :string
+ tokens << ['"', :delimiter]
+ next
+ elsif scan(/#{NUM}/o) and not matched.empty?
+ kind = :integer
+ elsif getch
+ kind = :error
+ end
+
+ when :string
+ if scan(/[^"\\]+/) or scan(/\\.?/)
+ kind = :content
+ elsif scan(/"/)
+ tokens << ['"', :delimiter]
+ tokens << [:close, :string]
+ state = :initial
+ next
+ else
+ raise_inspect "else case \" reached; %p not handled." % peek(1),
+ tokens, state
+ end
+
+ else
+ raise "else case reached"
+ 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, state unless match
+
+ tokens << [match, kind]
+
+ end # until eos
+
+ if state == :string
+ tokens << [:close, :string]
+ end
+
+ tokens
+
+ end #scan_tokens
+ end #class
+ end #module scanners
+end #module coderay
\ No newline at end of file
Index: lib/coderay/scanners/delphi.rb
===================================================================
--- lib/coderay/scanners/delphi.rb (revision 200)
+++ lib/coderay/scanners/delphi.rb (revision 250)
@@ -29,13 +29,18 @@
'virtual', 'write', 'writeonly'
]
- IDENT_KIND = CaseIgnoringWordList.new(:ident).
+ IDENT_KIND = CaseIgnoringWordList.new(:ident, caching=true).
add(RESERVED_WORDS, :reserved).
add(DIRECTIVES, :directive)
+
+ NAME_FOLLOWS = CaseIgnoringWordList.new(false, caching=true).
+ add(%w(procedure function .))
+ private
def scan_tokens tokens, options
state = :initial
+ last_token = ''
until eos?
@@ -45,19 +50,29 @@
if state == :initial
if scan(/ \s+ /x)
- kind = :space
+ tokens << [matched, :space]
+ next
elsif scan(%r! \{ \$ [^}]* \}? | \(\* \$ (?: .*? \*\) | .* ) !mx)
- kind = :preprocessor
+ tokens << [matched, :preprocessor]
+ next
elsif scan(%r! // [^\n]* | \{ [^}]* \}? | \(\* (?: .*? \*\) | .* ) !mx)
- kind = :comment
+ tokens << [matched, :comment]
+ next
- elsif scan(/ [-+*\/=<>:;,.@\^|\(\)\[\]]+ /x)
+ elsif match = scan(/ <[>=]? | >=? | :=? | [-+=*\/;,@\^|\(\)\[\]] | \.\. /x)
kind = :operator
+
+ elsif match = scan(/\./)
+ kind = :operator
+ if last_token == 'end'
+ tokens << [match, kind]
+ next
+ end
elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
- kind = IDENT_KIND[match]
+ kind = NAME_FOLLOWS[last_token] ? :ident : IDENT_KIND[match]
elsif match = scan(/ ' ( [^\n']|'' ) (?:'|$) /x)
tokens << [:open, :char]
@@ -101,6 +116,7 @@
state = :initial
next
elsif scan(/\n/)
+ tokens << [:close, :string]
kind = :error
state = :initial
else
@@ -119,6 +135,7 @@
end
raise_inspect 'Empty token', tokens unless match
+ last_token = match
tokens << [match, kind]
end
Index: lib/coderay/scanners/debug.rb
===================================================================
--- lib/coderay/scanners/debug.rb (revision 0)
+++ lib/coderay/scanners/debug.rb (revision 250)
@@ -0,0 +1,60 @@
+module CodeRay
+module Scanners
+
+ # = Debug Scanner
+ class Debug < Scanner
+
+ include Streamable
+ register_for :debug
+
+ protected
+ def scan_tokens tokens, options
+
+ opened_tokens = []
+
+ until eos?
+
+ kind = nil
+ match = nil
+
+ if scan(/\s+/)
+ tokens << [matched, :space]
+ next
+
+ elsif scan(/ (\w+) \( ( [^\)\\]* ( \\. [^\)\\]* )* ) \) /x)
+ kind = self[1].to_sym
+ match = self[2].gsub(/\\(.)/, '\1')
+
+ elsif scan(/ (\w+) < /x)
+ kind = self[1].to_sym
+ opened_tokens << kind
+ match = :open
+
+ elsif scan(/ > /x)
+ kind = opened_tokens.pop
+ match = :close
+
+ else
+ kind = :error
+ getch
+
+ 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]
+
+ end
+
+ tokens
+ end
+
+ end
+
+end
+end
Index: lib/coderay/scanners/rhtml.rb
===================================================================
--- lib/coderay/scanners/rhtml.rb (revision 200)
+++ lib/coderay/scanners/rhtml.rb (revision 250)
@@ -51,10 +51,10 @@
start_tag = match[/\A<%[-=]?/]
end_tag = match[/-?%?>?\z/]
tokens << [:open, :inline]
- tokens << [start_tag, :delimiter]
+ tokens << [start_tag, :inline_delimiter]
code = match[start_tag.size .. -1 - end_tag.size]
@ruby_scanner.tokenize code
- tokens << [end_tag, :delimiter] unless end_tag.empty?
+ tokens << [end_tag, :inline_delimiter] unless end_tag.empty?
tokens << [:close, :inline]
else
Index: lib/coderay/scanners/nitro_xhtml.rb
===================================================================
--- lib/coderay/scanners/nitro_xhtml.rb (revision 200)
+++ lib/coderay/scanners/nitro_xhtml.rb (revision 250)
@@ -95,20 +95,20 @@
delimiter = CLOSING_PAREN[start_tag[1,1]]
end_tag = match[-1,1] == delimiter ? delimiter : ''
tokens << [:open, :inline]
- tokens << [start_tag, :delimiter]
+ tokens << [start_tag, :inline_delimiter]
code = match[start_tag.size .. -1 - end_tag.size]
@ruby_scanner.tokenize code
- tokens << [end_tag, :delimiter] unless end_tag.empty?
+ tokens << [end_tag, :inline_delimiter] unless end_tag.empty?
tokens << [:close, :inline]
elsif match = scan(/#{NITRO_RUBY_BLOCK}/o)
start_tag = '' ? '?>' : ''
tokens << [:open, :inline]
- tokens << [start_tag, :delimiter]
+ tokens << [start_tag, :inline_delimiter]
code = match[start_tag.size .. -(end_tag.size)-1]
@ruby_scanner.tokenize code
- tokens << [end_tag, :delimiter] unless end_tag.empty?
+ tokens << [end_tag, :inline_delimiter] unless end_tag.empty?
tokens << [:close, :inline]
elsif entity = scan(/#{NITRO_ENTITY}/o)
Index: lib/coderay/scanners/plaintext.rb
===================================================================
--- lib/coderay/scanners/plaintext.rb (revision 200)
+++ lib/coderay/scanners/plaintext.rb (revision 250)
@@ -4,6 +4,8 @@
class Plaintext < Scanner
register_for :plaintext, :plain
+
+ include Streamable
def scan_tokens tokens, options
text = (scan_until(/\z/) || '')
Index: lib/coderay.rb
===================================================================
--- lib/coderay.rb (revision 200)
+++ lib/coderay.rb (revision 250)
@@ -24,8 +24,8 @@
#
# == Usage
#
-# Remember you need RubyGems to use CodeRay. Run Ruby with -rubygems option
-# if required.
+# Remember you need RubyGems to use CodeRay, unless you have it in your load path. Run Ruby with
+# -rubygems option if required.
#
# === Highlight Ruby code in a string as html
#
@@ -44,19 +44,15 @@
#
# You can include this div in your page. The used CSS styles can be printed with
#
-# % ruby -rcoderay -e "print CodeRay::Encoders[:html]::CSS"
+# % coderay_stylesheet
#
# === Highlight without typing too much
-#
+#
# If you are one of the hasty (or lazy, or extremely curious) people, just run this file:
-#
-# % ruby -rubygems coderay.rb
#
-# If the output was to fast for you, try
+# % ruby -rubygems /path/to/coderay/coderay.rb > example.html
#
-# % ruby -rubygems coderay.rb > example.html
-#
-# and look at the file it created.
+# and look at the file it created in your browser.
#
# = CodeRay Module
#
@@ -111,7 +107,7 @@
#
# CodeRay.scan_stream:: Scan in stream mode.
#
-# == All-in-One Encoding
+# == All-in-One Encoding
#
# CodeRay.encode:: Highlight a string with a given input and output format.
#
@@ -121,11 +117,16 @@
# for this Encoder must only be done once.
#
# CodeRay.encoder:: Create an Encoder instance with format and options.
+# CodeRay.scanner:: Create an Scanner instance for lang, with '' as default code.
#
-# There is no CodeRay.scanner method because Scanners are bound to an input string
-# on creation; you can't re-use them with another string.
+# To make use of CodeRay.scanner, use CodeRay::Scanner::code=.
#
-# The scanning methods provide more flexibility; we recommend to use these.
+# The scanning methods provide more flexibility; we recommend to use these.
+#
+# == Reusing Scanners and Encoders
+#
+# If you want to re-use scanners and encoders (because that is faster), see
+# CodeRay::Duo for the most convenient (and recommended) interface.
module CodeRay
# Version: Major.Minor.Teeny[.Revision]
@@ -133,7 +134,7 @@
# Minor: odd for beta, even for stable
# Teeny: development state
# Revision: Subversion Revision number (generated on rake)
- VERSION = '0.7.4'
+ VERSION = '0.7.9'
require 'coderay/tokens'
require 'coderay/scanner'
@@ -170,7 +171,7 @@
def scan_file filename, lang = :auto, options = {}, &block
file = IO.read filename
if lang == :auto
- require 'coderay/helpers/filetype'
+ require 'coderay/helpers/file_type'
lang = FileType.fetch filename, :plaintext, true
end
scan file, lang, options = {}, &block
@@ -314,6 +315,7 @@
# Run a test script.
if $0 == __FILE__
$stderr.print 'Press key to print demo.'; gets
- code = File.read($0)[/module CodeRay.*/m]
+ # Just use this file as an example of Ruby code.
+ code = File.read(__FILE__)[/module CodeRay.*/m]
print CodeRay.scan(code, :ruby).html
end
Property changes on: lib
___________________________________________________________________
Added: svn:externals
+ term http://term-ansicolor.rubyforge.org/svn/trunk/lib/term/