lib/rouge/lexer.rb in rouge-2.0.7 vs lib/rouge/lexer.rb in rouge-2.1.0
- old
+ new
@@ -9,25 +9,21 @@
# @abstract
# A lexer transforms text into a stream of `[token, chunk]` pairs.
class Lexer
include Token::Tokens
+ @option_docs = {}
+
class << self
# Lexes `stream` with the given options. The lex is delegated to a
# new instance.
#
# @see #lex
def lex(stream, opts={}, &b)
new(opts).lex(stream, &b)
end
- def default_options(o={})
- @default_options ||= {}
- @default_options.merge!(o)
- @default_options
- end
-
# Given a string, return the correct lexer class.
def find(name)
registry[name.to_s]
end
@@ -43,23 +39,34 @@
# Lexer.find_fancy('guess', "#!/bin/bash\necho Hello, world")
#
# This is used in the Redcarpet plugin as well as Rouge's own
# markdown lexer for highlighting internal code blocks.
#
- def find_fancy(str, code=nil)
+ def find_fancy(str, code=nil, additional_options={})
+ if str && !str.include?('?') && str != 'guess'
+ lexer_class = find(str)
+ return lexer_class && lexer_class.new(additional_options)
+ end
+
name, opts = str ? str.split('?', 2) : [nil, '']
# parse the options hash from a cgi-style string
opts = CGI.parse(opts || '').map do |k, vals|
- [ k.to_sym, vals.empty? ? true : vals[0] ]
+ val = case vals.size
+ when 0 then true
+ when 1 then vals[0]
+ else vals
+ end
+
+ [ k.to_s, val ]
end
- opts = Hash[opts]
+ opts = additional_options.merge(Hash[opts])
lexer_class = case name
when 'guess', nil
- self.guess(:source => code, :mimetype => opts[:mimetype])
+ self.guess(:source => code, :mimetype => opts['mimetype'])
when String
self.find(name)
end
lexer_class && lexer_class.new(opts)
@@ -80,10 +87,18 @@
else
@desc = arg
end
end
+ def option_docs
+ @option_docs ||= InheritableHash.new(superclass.option_docs)
+ end
+
+ def option(name, desc)
+ option_docs[name.to_s] = desc
+ end
+
# Specify or get the path name containing a small demo for
# this lexer (can be overriden by {demo}).
def demo_file(arg=:absent)
return @demo_file = Pathname.new(arg) unless arg == :absent
@@ -154,12 +169,22 @@
def guess_by_source(source)
guess :source => source
end
- private
+ def enable_debug!
+ @debug_enabled = true
+ end
+ def disable_debug!
+ @debug_enabled = false
+ end
+
+ def debug_enabled?
+ !!@debug_enabled
+ end
+
protected
# @private
def register(name, lexer)
registry[name.to_s] = lexer
end
@@ -232,55 +257,117 @@
end
end
# -*- instance methods -*- #
+ attr_reader :options
# Create a new lexer with the given options. Individual lexers may
# specify extra options. The only current globally accepted option
# is `:debug`.
#
# @option opts :debug
# Prints debug information to stdout. The particular info depends
# on the lexer in question. In regex lexers, this will log the
# state stack at the beginning of each step, along with each regex
# tried and each stream consumed. Try it, it's pretty useful.
def initialize(opts={})
- options(opts)
+ @options = {}
+ opts.each { |k, v| @options[k.to_s] = v }
- @debug = option(:debug)
+ @debug = Lexer.debug_enabled? && bool_option(:debug)
end
- # get and/or specify the options for this lexer.
- def options(o={})
- (@options ||= {}).merge!(o)
+ def as_bool(val)
+ case val
+ when nil, false, 0, '0', 'off'
+ false
+ when Array
+ val.empty? ? true : as_bool(val.last)
+ else
+ true
+ end
+ end
- self.class.default_options.merge(@options)
+ def as_string(val)
+ return as_string(val.last) if val.is_a?(Array)
+
+ val ? val.to_s : nil
end
- # get or specify one option for this lexer
- def option(k, v=:absent)
- if v == :absent
- options[k]
+ def as_list(val)
+ case val
+ when Array
+ val.flat_map { |v| as_list(v) }
+ when String
+ val.split(',')
else
- options({ k => v })
+ []
end
end
- # @deprecated
- # Instead of `debug { "foo" }`, simply `puts "foo" if @debug`.
- #
- # Leave a debug message if the `:debug` option is set. The message
- # is given as a block because some debug messages contain calculated
- # information that is unnecessary for lexing in the real world.
- #
- # Calls to this method should be guarded with "if @debug" for best
- # performance when debugging is turned off.
- #
- # @example
- # debug { "hello, world!" } if @debug
- def debug
- warn "Lexer#debug is deprecated. Simply puts if @debug instead."
- puts yield if @debug
+ def as_lexer(val)
+ return as_lexer(val.last) if val.is_a?(Array)
+ return val.new(@options) if val.is_a?(Class) && val < Lexer
+
+ case val
+ when Lexer
+ val
+ when String
+ lexer_class = Lexer.find(val)
+ lexer_class && lexer_class.new(@options)
+ end
+ end
+
+ def as_token(val)
+ return as_token(val.last) if val.is_a?(Array)
+ case val
+ when Token
+ val
+ else
+ Token[val]
+ end
+ end
+
+ def bool_option(name, &default)
+ if @options.key?(name.to_s)
+ as_bool(@options[name.to_s])
+ else
+ default ? default.call : false
+ end
+ end
+
+ def string_option(name, &default)
+ as_string(@options.delete(name.to_s, &default))
+ end
+
+ def lexer_option(name, &default)
+ as_lexer(@options.delete(name.to_s, &default))
+ end
+
+ def list_option(name, &default)
+ as_list(@options.delete(name.to_s, &default))
+ end
+
+ def token_option(name, &default)
+ as_token(@options.delete(name.to_s, &default))
+ end
+
+ def hash_option(name, defaults, &val_cast)
+ name = name.to_s
+ out = defaults.dup
+
+ base = @options.delete(name.to_s)
+ base = {} unless base.is_a?(Hash)
+ base.each { |k, v| out[k.to_s] = val_cast ? val_cast.call(v) : v }
+
+ @options.keys.each do |key|
+ next unless key =~ /(\w+)\[(\w+)\]/ and $1 == name
+ value = @options.delete(key)
+
+ out[$2] = val_cast ? val_cast.call(value) : value
+ end
+
+ out
end
# @abstract
#
# Called after each lex is finished. The default implementation