From c1b6c6339f602b8ecf69c8cb330319d99e039063 Mon Sep 17 00:00:00 2001 From: murphy Date: Sat, 10 Dec 2005 04:20:30 +0000 Subject: Plugin System extended: - helper method - path names are expanded encoders/html.rb, div.rb, span.rb, ruby.rb: - using new methods (fixes problems with rdoc and test tasks): renamed scanners/ruby/helper.rb to patterns.rb encoders/statistic.rb: sorting made deterministic (for unit testing) --- lib/coderay/encoders/div.rb | 3 +- lib/coderay/encoders/html.rb | 4 +- lib/coderay/encoders/span.rb | 3 +- lib/coderay/encoders/statistic.rb | 2 +- lib/coderay/helpers/plugin.rb | 72 +++--------- lib/coderay/scanners/ruby.rb | 2 +- lib/coderay/scanners/ruby/helper.rb | 213 ---------------------------------- lib/coderay/scanners/ruby/patterns.rb | 213 ++++++++++++++++++++++++++++++++++ 8 files changed, 237 insertions(+), 275 deletions(-) delete mode 100644 lib/coderay/scanners/ruby/helper.rb create mode 100644 lib/coderay/scanners/ruby/patterns.rb (limited to 'lib/coderay') diff --git a/lib/coderay/encoders/div.rb b/lib/coderay/encoders/div.rb index d3e595d..2379a4c 100644 --- a/lib/coderay/encoders/div.rb +++ b/lib/coderay/encoders/div.rb @@ -1,6 +1,7 @@ module CodeRay module Encoders - require 'coderay/encoders/html' + load :html + class Div < HTML FILE_EXTENSION = 'div.html' diff --git a/lib/coderay/encoders/html.rb b/lib/coderay/encoders/html.rb index e456356..a755b47 100644 --- a/lib/coderay/encoders/html.rb +++ b/lib/coderay/encoders/html.rb @@ -82,9 +82,7 @@ module Encoders :hint => false, } - require 'coderay/encoders/html/classes' - require 'coderay/encoders/html/output' - require 'coderay/encoders/html/css' + helper :classes, :output, :css attr_reader :css diff --git a/lib/coderay/encoders/span.rb b/lib/coderay/encoders/span.rb index abd8a39..e222889 100644 --- a/lib/coderay/encoders/span.rb +++ b/lib/coderay/encoders/span.rb @@ -1,6 +1,7 @@ module CodeRay module Encoders - require 'coderay/encoders/html' + load :html + class Span < HTML FILE_EXTENSION = 'span.html' diff --git a/lib/coderay/encoders/statistic.rb b/lib/coderay/encoders/statistic.rb index cd26272..b8905ff 100644 --- a/lib/coderay/encoders/statistic.rb +++ b/lib/coderay/encoders/statistic.rb @@ -64,7 +64,7 @@ Token Types (%d): @type_stats.each do |type, stat| stat.size /= stat.count.to_f end - types_stats = @type_stats.sort_by { |k, v| -v.count }.map do |k, v| + types_stats = @type_stats.sort_by { |k, v| [-v.count, k.to_s] }.map do |k, v| TOKEN_TYPES_ROW % [k, v.count, 100.0 * v.count / all_count, v.size] end.join STATS % [ diff --git a/lib/coderay/helpers/plugin.rb b/lib/coderay/helpers/plugin.rb index f88fe7f..233dfd6 100644 --- a/lib/coderay/helpers/plugin.rb +++ b/lib/coderay/helpers/plugin.rb @@ -54,6 +54,12 @@ module PluginHost # Alias for +[]+. alias load [] + def require_helper plugin_id, helper_name + path = path_to File.join(plugin_id, helper_name) + #$stderr.puts 'Loading helper: ' + path + require path + end + class << self # Adds the module/class to the PLUGIN_HOSTS list. @@ -85,7 +91,7 @@ module PluginHost # The path where the plugins can be found. def plugin_path *args unless args.empty? - @plugin_path = File.join(*args) + @plugin_path = File.expand_path File.join(*args) load_map end @plugin_path @@ -264,6 +270,16 @@ module Plugin self::PLUGIN_HOST end + def helper *helpers + for helper in helpers + self::PLUGIN_HOST.require_helper plugin_id, helper.to_s + end + end + + def plugin_id + name[/[\w_]+$/].downcase + end + end @@ -280,57 +296,3 @@ def require_plugin path "No host for #{host_id.inspect} found." unless host host.load plugin_id end - -__END__ -# this is no good test. -if $0 == __FILE__ - $VERBOSE = $DEBUG = true - eval DATA.read, nil, $0, __LINE__+4 -end - -__END__ - -require 'test/unit' - -class TC_PLUGINS < Test::Unit::TestCase - - class Generators - extend PluginHost - plugin_path File.dirname(__FILE__) - map :foo => :bar, - :bar => :fancy - end - - class Generator - extend Plugin - plugin_host Generators - end - - class FancyGenerator < Generator - register_for :fancy - end - - def test_plugin - assert_nothing_raised do - Generators[:fancy] - end - assert_equal FancyGenerator, Generators[:fancy] - end - - def test_require - assert_nothing_raised do - require_plugin('TC_PLUGINS::Generators/fancy') - end - assert_equal FancyGenerator, - require_plugin('TC_PLUGINS::Generators/fancy') - end - - def test_map - assert_nothing_raised do - require_plugin('TC_PLUGINS::Generators/foo') - end - assert_equal FancyGenerator, - require_plugin('TC_PLUGINS::Generators/foo') - end - -end diff --git a/lib/coderay/scanners/ruby.rb b/lib/coderay/scanners/ruby.rb index a4d042f..6b4c8ea 100644 --- a/lib/coderay/scanners/ruby.rb +++ b/lib/coderay/scanners/ruby.rb @@ -18,7 +18,7 @@ module CodeRay module Scanners register_for :ruby - require 'coderay/scanners/ruby/helper' + helper :patterns DEFAULT_OPTIONS = { :parse_regexps => true, diff --git a/lib/coderay/scanners/ruby/helper.rb b/lib/coderay/scanners/ruby/helper.rb deleted file mode 100644 index ad649da..0000000 --- a/lib/coderay/scanners/ruby/helper.rb +++ /dev/null @@ -1,213 +0,0 @@ -module CodeRay module Scanners - - class Ruby - - RESERVED_WORDS = %w[ - and def end in or unless begin - defined? ensure module redo super until - BEGIN break do next rescue then - when END case else for retry - while alias class elsif if not return - undef yield - ] - - DEF_KEYWORDS = %w[ def ] - UNDEF_KEYWORDS = %w[ undef ] - MODULE_KEYWORDS = %w[class module] - DEF_NEW_STATE = WordList.new(:initial). - add(DEF_KEYWORDS, :def_expected). - add(UNDEF_KEYWORDS, :undef_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__ - ] - - IDENT_KIND = WordList.new(:ident). - add(RESERVED_WORDS, :reserved). - add(PREDEFINED_CONSTANTS, :pre_constant) - - IDENT = /[a-z_][\w_]*/i - - METHOD_NAME = / #{IDENT} [?!]? /ox - METHOD_NAME_OPERATOR = / - \*\*? # multiplication and power - | [-+]@? # plus, minus - | [\/%&|^`~] # division, modulo or format strings, &and, |or, ^xor, `system`, tilde - | \[\]=? # array getter and setter - | << | >> # append or shift left, shift right - | <=?>? | >=? # comparison, rocket operator - | ===? # simple equality and case equality - /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 - VARIABLE = / @?@? #{IDENT} | #{GLOBAL_VARIABLE} /ox - - QUOTE_TO_TYPE = { - '`' => :shell, - '/'=> :regexp, - } - QUOTE_TO_TYPE.default = :string - - REGEXP_MODIFIERS = /[mixounse]*/ - REGEXP_SYMBOLS = /[|?*+?(){}\[\].^$]/ - - DECIMAL = /\d+(?:_\d+)*/ - OCTAL = /0_?[0-7]+(?:_[0-7]+)*/ - HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/ - BINARY = /0b[01]+(?:_[01]+)*/ - - 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 - - SYMBOL = / - : - (?: - #{METHOD_NAME_EX} - | #{PREFIX_VARIABLE} - | ['"] - ) - /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} | . ) ) - # assert_equal(225, ?\M-a) - # assert_equal(129, ?\M-\C-a) - ESCAPE = / - [abefnrstv] - | M-\\C-|C-\\M-|M-\\c|c\\M-|c|C-|M- - | [0-7]{1,3} - | x[0-9A-Fa-f]{1,2} - | . - /mx - - CHARACTER = / - \? - (?: - [^\s\\] - | \\ #{ESCAPE} - ) - /mx - - # NOTE: This is not completely correct, but - # nobody needs heredoc delimiters ending with \n. - HEREDOC_OPEN = / - << (-)? # $1 = float - (?: - ( [A-Za-z_0-9]+ ) # $2 = delim - | - ( ["'`] ) # $3 = quote, type - ( [^\n]*? ) \3 # $4 = delim - ) - /mx - - RUBYDOC = / - =begin (?!\S) - .*? - (?: \Z | ^=end (?!\S) [^\n]* ) - /mx - - DATA = / - __END__$ - .*? - (?: \Z | (?=^\#CODE) ) - /mx - - RUBYDOC_OR_DATA = / #{RUBYDOC} | #{DATA} /xo - - RDOC_DATA_START = / ^=begin (?!\S) | ^__END__$ /x - - FANCY_START = / % ( [qQwWxsr] | (?![\w\s=]) ) (.) /mox - - FancyStringType = { - 'q' => [:string, false], - 'Q' => [:string, true], - 'r' => [:regexp, true], - 's' => [:symbol, false], - 'x' => [:shell, true], - 'w' => [:string, :word], - 'W' => [:string, :word], - } - FancyStringType['w'] = FancyStringType['q'] - FancyStringType['W'] = FancyStringType[''] = FancyStringType['Q'] - - class StringState < Struct.new :type, :interpreted, :delim, :heredoc, - :paren, :paren_depth, :pattern, :next_state - - CLOSING_PAREN = Hash[ *%w[ - ( ) - [ ] - < > - { } - ] ] - - CLOSING_PAREN.values.each { |o| o.freeze } # debug, if I try to change it with << - OPENING_PAREN = CLOSING_PAREN.invert - - STRING_PATTERN = Hash.new { |h, k| - delim, interpreted = *k - delim_pattern = Regexp.escape(delim.dup) - if starter = OPENING_PAREN[delim] - delim_pattern << Regexp.escape(starter) - end - - - special_escapes = - case interpreted - when :regexp_symbols - '| ' + REGEXP_SYMBOLS.source - when :words - '| \s' - end - - h[k] = - if interpreted and not delim == '#' - / (?= [#{delim_pattern}\\] | \# [{$@] #{special_escapes} ) /mx - else - / (?= [#{delim_pattern}\\] #{special_escapes} ) /mx - end - } - - HEREDOC_PATTERN = Hash.new { |h, k| - delim, interpreted, indented = *k - delim_pattern = Regexp.escape(delim.dup) - delim_pattern = / \n #{ '(?>[\ \t]*)' if indented } #{ Regexp.new delim_pattern } $ /x - h[k] = - if interpreted - / (?= #{delim_pattern}() | \\ | \# [{$@] ) /mx # $1 set == end of heredoc - else - / (?= #{delim_pattern}() | \\ ) /mx - end - } - - def initialize kind, interpreted, delim, heredoc = false - if paren = CLOSING_PAREN[delim] - delim, paren = paren, delim - paren_depth = 1 - end - if heredoc - pattern = HEREDOC_PATTERN[ [delim, interpreted, heredoc == :indented] ] - delim = nil - else - pattern = STRING_PATTERN[ [delim, interpreted] ] - end - super kind, interpreted, delim, heredoc, paren, paren_depth, pattern, :initial - end - end unless defined? StringState - - end - -end end diff --git a/lib/coderay/scanners/ruby/patterns.rb b/lib/coderay/scanners/ruby/patterns.rb new file mode 100644 index 0000000..ad649da --- /dev/null +++ b/lib/coderay/scanners/ruby/patterns.rb @@ -0,0 +1,213 @@ +module CodeRay module Scanners + + class Ruby + + RESERVED_WORDS = %w[ + and def end in or unless begin + defined? ensure module redo super until + BEGIN break do next rescue then + when END case else for retry + while alias class elsif if not return + undef yield + ] + + DEF_KEYWORDS = %w[ def ] + UNDEF_KEYWORDS = %w[ undef ] + MODULE_KEYWORDS = %w[class module] + DEF_NEW_STATE = WordList.new(:initial). + add(DEF_KEYWORDS, :def_expected). + add(UNDEF_KEYWORDS, :undef_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__ + ] + + IDENT_KIND = WordList.new(:ident). + add(RESERVED_WORDS, :reserved). + add(PREDEFINED_CONSTANTS, :pre_constant) + + IDENT = /[a-z_][\w_]*/i + + METHOD_NAME = / #{IDENT} [?!]? /ox + METHOD_NAME_OPERATOR = / + \*\*? # multiplication and power + | [-+]@? # plus, minus + | [\/%&|^`~] # division, modulo or format strings, &and, |or, ^xor, `system`, tilde + | \[\]=? # array getter and setter + | << | >> # append or shift left, shift right + | <=?>? | >=? # comparison, rocket operator + | ===? # simple equality and case equality + /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 + VARIABLE = / @?@? #{IDENT} | #{GLOBAL_VARIABLE} /ox + + QUOTE_TO_TYPE = { + '`' => :shell, + '/'=> :regexp, + } + QUOTE_TO_TYPE.default = :string + + REGEXP_MODIFIERS = /[mixounse]*/ + REGEXP_SYMBOLS = /[|?*+?(){}\[\].^$]/ + + DECIMAL = /\d+(?:_\d+)*/ + OCTAL = /0_?[0-7]+(?:_[0-7]+)*/ + HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/ + BINARY = /0b[01]+(?:_[01]+)*/ + + 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 + + SYMBOL = / + : + (?: + #{METHOD_NAME_EX} + | #{PREFIX_VARIABLE} + | ['"] + ) + /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} | . ) ) + # assert_equal(225, ?\M-a) + # assert_equal(129, ?\M-\C-a) + ESCAPE = / + [abefnrstv] + | M-\\C-|C-\\M-|M-\\c|c\\M-|c|C-|M- + | [0-7]{1,3} + | x[0-9A-Fa-f]{1,2} + | . + /mx + + CHARACTER = / + \? + (?: + [^\s\\] + | \\ #{ESCAPE} + ) + /mx + + # NOTE: This is not completely correct, but + # nobody needs heredoc delimiters ending with \n. + HEREDOC_OPEN = / + << (-)? # $1 = float + (?: + ( [A-Za-z_0-9]+ ) # $2 = delim + | + ( ["'`] ) # $3 = quote, type + ( [^\n]*? ) \3 # $4 = delim + ) + /mx + + RUBYDOC = / + =begin (?!\S) + .*? + (?: \Z | ^=end (?!\S) [^\n]* ) + /mx + + DATA = / + __END__$ + .*? + (?: \Z | (?=^\#CODE) ) + /mx + + RUBYDOC_OR_DATA = / #{RUBYDOC} | #{DATA} /xo + + RDOC_DATA_START = / ^=begin (?!\S) | ^__END__$ /x + + FANCY_START = / % ( [qQwWxsr] | (?![\w\s=]) ) (.) /mox + + FancyStringType = { + 'q' => [:string, false], + 'Q' => [:string, true], + 'r' => [:regexp, true], + 's' => [:symbol, false], + 'x' => [:shell, true], + 'w' => [:string, :word], + 'W' => [:string, :word], + } + FancyStringType['w'] = FancyStringType['q'] + FancyStringType['W'] = FancyStringType[''] = FancyStringType['Q'] + + class StringState < Struct.new :type, :interpreted, :delim, :heredoc, + :paren, :paren_depth, :pattern, :next_state + + CLOSING_PAREN = Hash[ *%w[ + ( ) + [ ] + < > + { } + ] ] + + CLOSING_PAREN.values.each { |o| o.freeze } # debug, if I try to change it with << + OPENING_PAREN = CLOSING_PAREN.invert + + STRING_PATTERN = Hash.new { |h, k| + delim, interpreted = *k + delim_pattern = Regexp.escape(delim.dup) + if starter = OPENING_PAREN[delim] + delim_pattern << Regexp.escape(starter) + end + + + special_escapes = + case interpreted + when :regexp_symbols + '| ' + REGEXP_SYMBOLS.source + when :words + '| \s' + end + + h[k] = + if interpreted and not delim == '#' + / (?= [#{delim_pattern}\\] | \# [{$@] #{special_escapes} ) /mx + else + / (?= [#{delim_pattern}\\] #{special_escapes} ) /mx + end + } + + HEREDOC_PATTERN = Hash.new { |h, k| + delim, interpreted, indented = *k + delim_pattern = Regexp.escape(delim.dup) + delim_pattern = / \n #{ '(?>[\ \t]*)' if indented } #{ Regexp.new delim_pattern } $ /x + h[k] = + if interpreted + / (?= #{delim_pattern}() | \\ | \# [{$@] ) /mx # $1 set == end of heredoc + else + / (?= #{delim_pattern}() | \\ ) /mx + end + } + + def initialize kind, interpreted, delim, heredoc = false + if paren = CLOSING_PAREN[delim] + delim, paren = paren, delim + paren_depth = 1 + end + if heredoc + pattern = HEREDOC_PATTERN[ [delim, interpreted, heredoc == :indented] ] + delim = nil + else + pattern = STRING_PATTERN[ [delim, interpreted] ] + end + super kind, interpreted, delim, heredoc, paren, paren_depth, pattern, :initial + end + end unless defined? StringState + + end + +end end -- cgit v1.2.1