Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed up HTML encoder for 1.2 #153

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions coderay.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ Gem::Specification.new do |s|
s.rubyforge_project = s.name
s.rdoc_options = '-SNw2', "-m#{readme_file}", '-t CodeRay Documentation'
s.extra_rdoc_files = readme_file

s.add_dependency 'escape_utils', '>= 1.0'
end
23 changes: 3 additions & 20 deletions lib/coderay/encoders/html.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'set'
require 'escape_utils'

module CodeRay
module Encoders
Expand Down Expand Up @@ -127,22 +128,6 @@ class HTML < Encoder

protected

def self.make_html_escape_hash
{
'&' => '&amp;',
'"' => '&quot;',
'>' => '&gt;',
'<' => '&lt;',
# "\t" => will be set to ' ' * options[:tab_width] during setup
}.tap do |hash|
# Escape ASCII control codes except \x9 == \t and \xA == \n.
(Array(0x00..0x8) + Array(0xB..0x1F)).each { |invalid| hash[invalid.chr] = ' ' }
end
end

HTML_ESCAPE = make_html_escape_hash
HTML_ESCAPE_PATTERN = /[\t"&><\0-\x8\xB-\x1F]/

TOKEN_KIND_TO_INFO = Hash.new do |h, kind|
h[kind] = kind.to_s.gsub(/_/, ' ').gsub(/\b\w/) { $&.capitalize }
end
Expand Down Expand Up @@ -181,8 +166,6 @@ def setup options

@break_lines = (options[:break_lines] == true)

@HTML_ESCAPE = HTML_ESCAPE.merge("\t" => options[:tab_width] ? ' ' * options[:tab_width] : "\t")

@opened = []
@last_opened = nil
@css = CSS.new options[:style]
Expand All @@ -198,7 +181,7 @@ def finish options
@last_opened = nil
end

if @out.respond_to? :to_str
if options[:wrap] || options[:line_numbers]
@out.extend Output
@out.css = @css
if options[:line_numbers]
Expand All @@ -221,7 +204,7 @@ def finish options
def text_token text, kind
style = @span_for_kinds[@last_opened ? [kind, *@opened] : kind]

text = text.gsub(/#{HTML_ESCAPE_PATTERN}/o) { |m| @HTML_ESCAPE[m] } if text =~ /#{HTML_ESCAPE_PATTERN}/o
text = EscapeUtils.escape_html text, false
text = break_lines(text, style) if @break_lines && (style || @opened.size > 0) && text.index("\n")

if style
Expand Down
38 changes: 19 additions & 19 deletions lib/coderay/encoders/html/css.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,23 @@ module Encoders

class HTML
class CSS # :nodoc:
def initialize style_name = :default
@style_name = style_name
end

attr :stylesheet

def CSS.load_stylesheet style = nil
CodeRay::Styles[style]
def style
@style ||= CodeRay::Styles[@style_name]
end

def initialize style = :default
@styles = Hash.new
style = CSS.load_stylesheet style
@stylesheet = [
def stylesheet
@css ||= [
style::CSS_MAIN_STYLES,
style::TOKEN_COLORS.gsub(/^(?!$)/, '.CodeRay ')
].join("\n")
parse style::TOKEN_COLORS
end

def get_style_for_css_classes css_classes
cl = @styles[css_classes.first]
cl = styles[css_classes.first]
return '' unless cl
style = ''
1.upto css_classes.size do |offset|
Expand All @@ -31,7 +29,7 @@ def get_style_for_css_classes css_classes
return style
end

private
private

CSS_CLASS_PATTERN = /
( # $1 = selectors
Expand All @@ -46,14 +44,16 @@ def get_style_for_css_classes css_classes
|
( [^\n]+ ) # $3 = error
/mx
def parse stylesheet
stylesheet.scan CSS_CLASS_PATTERN do |selectors, style, error|
raise "CSS parse error: '#{error.inspect}' not recognized" if error
for selector in selectors.split(',')
classes = selector.scan(/[-\w]+/)
cl = classes.pop
@styles[cl] ||= Hash.new
@styles[cl][classes] = style.to_s.strip.delete(' ').chomp(';')
def styles
@styles ||= Hash.new.tap do |styles|
style::TOKEN_COLORS.scan CSS_CLASS_PATTERN do |selectors, style, error|
raise "CSS parse error: '#{error.inspect}' not recognized" if error
for selector in selectors.split(',')
classes = selector.scan(/[-\w]+/)
cl = classes.pop
styles[cl] ||= Hash.new
styles[cl][classes] = style.to_s.strip.delete(' ').chomp(';')
end
end
end
end
Expand Down
20 changes: 15 additions & 5 deletions lib/coderay/helpers/plugin_host.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,21 @@ def load_all
# Example:
# yaml_plugin = MyPluginHost[:yaml]
def [] id, *args, &blk
plugin = validate_id(id)
begin
plugin = plugin_hash.[](plugin, *args, &blk)
end while plugin.is_a? String
plugin
if !args.empty? || blk
plugin = validate_id(id)
begin
plugin = plugin_hash.[](plugin, *args, &blk)
end while plugin.is_a? String
plugin
else
(@cache ||= Hash.new do |cache, key|
plugin = validate_id(key)
begin
plugin = plugin_hash.[](plugin)
end while plugin.is_a? String
cache[key] = plugin
end)[id]
end
end

alias load []
Expand Down
13 changes: 9 additions & 4 deletions test/functional/basic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

class BasicTest < Test::Unit::TestCase

def normalize_html html
html.gsub('&#39;', "'").gsub('&quot;', '"')
end

def test_version
assert_nothing_raised do
assert_match(/\A\d\.\d\.\d?\z/, CodeRay::VERSION)
Expand Down Expand Up @@ -46,11 +50,11 @@ def test_simple_scan
end
end

RUBY_TEST_HTML = 'puts <span class="string"><span class="delimiter">&quot;</span>' +
'<span class="content">Hello, World!</span><span class="delimiter">&quot;</span></span>'
RUBY_TEST_HTML = 'puts <span class="string"><span class="delimiter">"</span>' +
'<span class="content">Hello, World!</span><span class="delimiter">"</span></span>'
def test_simple_highlight
assert_nothing_raised do
assert_equal RUBY_TEST_HTML, CodeRay.scan(RUBY_TEST_CODE, :ruby).html
assert_equal RUBY_TEST_HTML, normalize_html(CodeRay.scan(RUBY_TEST_CODE, :ruby).html)
end
end

Expand All @@ -75,7 +79,8 @@ def test_highlight
end

def test_highlight_file
assert_match "require <span class=\"string\"><span class=\"delimiter\">'</span><span class=\"content\">test/unit</span><span class=\"delimiter\">'</span></span>\n", CodeRay.highlight_file(__FILE__)
assert_match "require <span class=\"string\"><span class=\"delimiter\">'</span><span class=\"content\">test/unit</span><span class=\"delimiter\">'</span></span>\n",
normalize_html(CodeRay.highlight_file(__FILE__))
end

def test_duo
Expand Down
22 changes: 13 additions & 9 deletions test/functional/examples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@

class ExamplesTest < Test::Unit::TestCase

def normalize_html html
html.gsub('&#39;', "'").gsub('&quot;', '"')
end

def test_examples
# output as HTML div (using inline CSS styles)
div = CodeRay.scan('puts "Hello, world!"', :ruby).div
assert_equal <<-DIV, div
assert_equal <<-DIV, normalize_html(div)
<div class="CodeRay">
<div class="code"><pre>puts <span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">&quot;</span><span style="color:#D20">Hello, world!</span><span style="color:#710">&quot;</span></span></pre></div>
<div class="code"><pre>puts <span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">"</span><span style="color:#D20">Hello, world!</span><span style="color:#710">"</span></span></pre></div>
</div>
DIV

Expand All @@ -20,7 +24,7 @@ def test_examples
puts 'Hello, world!'
end
CODE
assert_equal <<-DIV, div
assert_equal <<-DIV, normalize_html(div)
<table class="CodeRay"><tr>
<td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
<a href="#n2" name="n2">2</a>
Expand All @@ -34,13 +38,13 @@ def test_examples

# output as standalone HTML page (using CSS classes)
page = CodeRay.scan('puts "Hello, world!"', :ruby).page
assert_match <<-PAGE, page
assert_match <<-PAGE, normalize_html(page)
<body>

<table class="CodeRay"><tr>
<td class="line-numbers"><pre><a href="#n1" name="n1">1</a>
</pre></td>
<td class="code"><pre>puts <span class="string"><span class="delimiter">&quot;</span><span class="content">Hello, world!</span><span class="delimiter">&quot;</span></span></pre></td>
<td class="code"><pre>puts <span class="string"><span class="delimiter">"</span><span class="content">Hello, world!</span><span class="delimiter">"</span></span></pre></td>
</tr></table>

</body>
Expand Down Expand Up @@ -90,9 +94,9 @@ def test_examples

# produce a HTML div, but with CSS classes
div = tokens.div(:css => :class)
assert_equal <<-DIV, div
assert_equal <<-DIV, normalize_html(div)
<div class="CodeRay">
<div class="code"><pre>{ <span class="key"><span class="delimiter">&quot;</span><span class="content">just</span><span class="delimiter">&quot;</span></span>: <span class="string"><span class="delimiter">&quot;</span><span class="content">an</span><span class="delimiter">&quot;</span></span>, <span class="key"><span class="delimiter">&quot;</span><span class="content">example</span><span class="delimiter">&quot;</span></span>: <span class="integer">42</span> }</pre></div>
<div class="code"><pre>{ <span class="key"><span class="delimiter">"</span><span class="content">just</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">an</span><span class="delimiter">"</span></span>, <span class="key"><span class="delimiter">"</span><span class="content">example</span><span class="delimiter">"</span></span>: <span class="integer">42</span> }</pre></div>
</div>
DIV

Expand All @@ -119,9 +123,9 @@ def test_examples
# re-using scanner and encoder
ruby_highlighter = CodeRay::Duo[:ruby, :div]
div = ruby_highlighter.encode('puts "Hello, world!"')
assert_equal <<-DIV, div
assert_equal <<-DIV, normalize_html(div)
<div class="CodeRay">
<div class="code"><pre>puts <span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">&quot;</span><span style="color:#D20">Hello, world!</span><span style="color:#710">&quot;</span></span></pre></div>
<div class="code"><pre>puts <span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">"</span><span style="color:#D20">Hello, world!</span><span style="color:#710">"</span></span></pre></div>
</div>
DIV
end
Expand Down
12 changes: 8 additions & 4 deletions test/functional/for_redcloth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@

class BasicTest < Test::Unit::TestCase

def normalize_html html
html.gsub('&#39;', "'").gsub('&quot;', '"')
end

def test_for_redcloth
require 'coderay/for_redcloth'
assert_equal "<p><span lang=\"ruby\" class=\"CodeRay\">puts <span style=\"background-color:hsla(0,100%,50%,0.05)\"><span style=\"color:#710\">&quot;</span><span style=\"color:#D20\">Hello, World!</span><span style=\"color:#710\">&quot;</span></span></span></p>",
RedCloth.new('@[ruby]puts "Hello, World!"@').to_html
assert_equal "<p><span lang=\"ruby\" class=\"CodeRay\">puts <span style=\"background-color:hsla(0,100%,50%,0.05)\"><span style=\"color:#710\">\"</span><span style=\"color:#D20\">Hello, World!</span><span style=\"color:#710\">\"</span></span></span></p>",
normalize_html(RedCloth.new('@[ruby]puts "Hello, World!"@').to_html)
assert_equal <<-BLOCKCODE.chomp,
<div lang="ruby" class="CodeRay">
<div class="code"><pre>puts <span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">&quot;</span><span style="color:#D20">Hello, World!</span><span style="color:#710">&quot;</span></span></pre></div>
<div class="code"><pre>puts <span style="background-color:hsla(0,100%,50%,0.05)"><span style="color:#710">"</span><span style="color:#D20">Hello, World!</span><span style="color:#710">"</span></span></pre></div>
</div>
BLOCKCODE
RedCloth.new('bc[ruby]. puts "Hello, World!"').to_html
normalize_html(RedCloth.new('bc[ruby]. puts "Hello, World!"').to_html)
end

def test_for_redcloth_no_lang
Expand Down
14 changes: 9 additions & 5 deletions test/unit/html.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

class HtmlTest < Test::Unit::TestCase

def normalize_html html
html.gsub('&#39;', "'").gsub('&quot;', '"')
end

def test_break_lines_option
snippets = {}

Expand Down Expand Up @@ -60,7 +64,7 @@ def test_break_lines_option
* used to test the
*/</span>
<span class=\"directive\">public</span> <span class=\"type\">class</span> <span class=\"class\">Test</span> {
<span class=\"directive\">public</span> <span class=\"directive\">static</span> <span class=\"directive\">final</span> <span class=\"predefined-type\">String</span> MESSAGE = <span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">My message To the world</span><span class=\"delimiter\">&quot;</span></span>;
<span class=\"directive\">public</span> <span class=\"directive\">static</span> <span class=\"directive\">final</span> <span class=\"predefined-type\">String</span> MESSAGE = <span class=\"string\"><span class=\"delimiter\">\"</span><span class=\"content\">My message To the world</span><span class=\"delimiter\">\"</span></span>;

<span class=\"directive\">static</span> <span class=\"type\">void</span> main() {
<span class=\"comment\">/*
Expand All @@ -80,7 +84,7 @@ def test_break_lines_option
<span class=\"comment\"> * used to test the</span>
<span class=\"comment\"> */</span>
<span class=\"directive\">public</span> <span class=\"type\">class</span> <span class=\"class\">Test</span> {
<span class=\"directive\">public</span> <span class=\"directive\">static</span> <span class=\"directive\">final</span> <span class=\"predefined-type\">String</span> MESSAGE = <span class=\"string\"><span class=\"delimiter\">&quot;</span><span class=\"content\">My message To the world</span><span class=\"delimiter\">&quot;</span></span>;
<span class=\"directive\">public</span> <span class=\"directive\">static</span> <span class=\"directive\">final</span> <span class=\"predefined-type\">String</span> MESSAGE = <span class=\"string\"><span class=\"delimiter\">\"</span><span class=\"content\">My message To the world</span><span class=\"delimiter\">\"</span></span>;

<span class=\"directive\">static</span> <span class=\"type\">void</span> main() {
<span class=\"comment\">/*</span>
Expand All @@ -95,9 +99,9 @@ def test_break_lines_option
for lang, code in snippets
tokens = CodeRay.scan code[:in], lang

assert_equal code[:expected_with_option_off], tokens.html
assert_equal code[:expected_with_option_off], tokens.html(:break_lines => false)
assert_equal code[:expected_with_option_on], tokens.html(:break_lines => true)
assert_equal code[:expected_with_option_off], normalize_html(tokens.html)
assert_equal code[:expected_with_option_off], normalize_html(tokens.html(:break_lines => false))
assert_equal code[:expected_with_option_on], normalize_html(tokens.html(:break_lines => true))
end
end
end