diff --git a/lib/coderay/encoders/html.rb b/lib/coderay/encoders/html.rb
index 1b33e921..fc6678c3 100644
--- a/lib/coderay/encoders/html.rb
+++ b/lib/coderay/encoders/html.rb
@@ -2,7 +2,7 @@
module CodeRay
module Encoders
-
+
# = HTML Encoder
#
# This is CodeRay's most important highlighter:
@@ -15,7 +15,7 @@ module Encoders
# puts CodeRay.scan('Some /code/', :ruby).html(:wrap => :span)
# #-> Some /code/
# puts CodeRay.scan('Some /code/', :ruby).span #-> the same
- #
+ #
# puts CodeRay.scan('Some code', :ruby).html(
# :wrap => nil,
# :line_numbers => :inline,
@@ -27,7 +27,7 @@ module Encoders
# === :tab_width
# Convert \t characters to +n+ spaces (a number or false.)
# false will keep tab characters untouched.
- #
+ #
# Default: 8
#
# === :css
@@ -35,6 +35,11 @@ module Encoders
#
# Default: :class
#
+ # === :css_class_prefix
+ # Optional prefix for CSS classes when :css is set to :class.
+ #
+ # Default: nil
+ #
# === :wrap
# Wrap in :page, :div, :span or nil.
#
@@ -43,13 +48,13 @@ module Encoders
# Default: nil
#
# === :title
- #
+ #
# The title of the HTML page (works only when :wrap is set to :page.)
#
# Default: 'CodeRay output'
#
# === :break_lines
- #
+ #
# Split multiline blocks at line breaks.
# Forced to true if :line_numbers option is set to :inline.
#
@@ -79,10 +84,10 @@ module Encoders
# Default: 10
#
# === :highlight_lines
- #
+ #
# Highlights certain line numbers.
# Can be any Enumerable, typically just an Array or Range, of numbers.
- #
+ #
# Bolding is deactivated when :highlight_lines is set. It only makes sense
# in combination with :line_numbers.
#
@@ -95,38 +100,38 @@ module Encoders
#
# Default: false
class HTML < Encoder
-
+
register_for :html
-
+
FILE_EXTENSION = 'snippet.html'
-
+
DEFAULT_OPTIONS = {
:tab_width => 8,
-
+
:css => :class,
:style => :alpha,
:wrap => nil,
:title => 'CodeRay output',
-
+
:break_lines => false,
-
+
:line_numbers => nil,
:line_number_anchors => 'n',
:line_number_start => 1,
:bold_every => 10,
:highlight_lines => nil,
-
+
:hint => false,
}
-
+
autoload :Output, CodeRay.coderay_path('encoders', 'html', 'output')
autoload :CSS, CodeRay.coderay_path('encoders', 'html', 'css')
autoload :Numbering, CodeRay.coderay_path('encoders', 'html', 'numbering')
-
+
attr_reader :css
-
+
protected
-
+
def self.make_html_escape_hash
{
'&' => '&',
@@ -139,18 +144,18 @@ def self.make_html_escape_hash
(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
-
+
TRANSPARENT_TOKEN_KINDS = Set[
:delimiter, :modifier, :content, :escape, :inline_delimiter,
]
-
+
# Generate a hint about the given +kinds+ in a +hint+ style.
#
# +hint+ may be :info, :info_long or :debug.
@@ -168,36 +173,36 @@ def self.token_path_to_hint hint, kinds
end
title ? " title=\"#{title}\"" : ''
end
-
+
def setup options
super
-
+
check_options! options
-
+
if options[:wrap] || options[:line_numbers]
@real_out = @out
@out = ''.dup
end
-
+
@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]
-
+
@span_for_kinds = make_span_for_kinds(options[:css], options[:hint])
-
+
@set_last_opened = options[:hint] || options[:css] == :style
end
-
+
def finish options
unless @opened.empty?
@out << '' while @opened.pop
@last_opened = nil
end
-
+
if @out.respond_to? :to_str
@out.extend Output
@out.css = @css
@@ -207,42 +212,42 @@ def finish options
@out.wrap! options[:wrap]
@out.apply_title! options[:title]
end
-
+
if defined?(@real_out) && @real_out
@real_out << @out
@out = @real_out
end
-
+
super
end
-
+
public
-
+
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 = break_lines(text, style) if @break_lines && (style || @opened.size > 0) && text.index("\n")
-
+
if style
@out << style << text << ''
else
@out << text
end
end
-
+
# token groups, eg. strings
def begin_group kind
@out << (@span_for_kinds[@last_opened ? [kind, *@opened] : kind] || '')
@opened << kind
@last_opened = kind if @set_last_opened
end
-
+
def end_group kind
check_group_nesting 'token group', kind if $CODERAY_DEBUG
close_span
end
-
+
# whole lines to be highlighted, eg. a deleted line in a diff
def begin_line kind
if style = @span_for_kinds[@last_opened ? [kind, *@opened] : kind]
@@ -257,47 +262,48 @@ def begin_line kind
@opened << kind
@last_opened = kind if @options[:css] == :style
end
-
+
def end_line kind
check_group_nesting 'line', kind if $CODERAY_DEBUG
close_span
end
-
+
protected
-
+
def check_options! options
unless [false, nil, :debug, :info, :info_long].include? options[:hint]
raise ArgumentError, "Unknown value %p for :hint; expected :info, :info_long, :debug, false, or nil." % [options[:hint]]
end
-
+
unless [:class, :style].include? options[:css]
raise ArgumentError, 'Unknown value %p for :css.' % [options[:css]]
end
-
+
options[:break_lines] = true if options[:line_numbers] == :inline
end
-
+
def css_class_for_kinds kinds
TokenKinds[kinds.is_a?(Symbol) ? kinds : kinds.first]
end
-
+
def style_for_kinds kinds
css_classes = kinds.is_a?(Array) ? kinds.map { |c| TokenKinds[c] } : [TokenKinds[kinds]]
@css.get_style_for_css_classes css_classes
end
-
+
def make_span_for_kinds method, hint
+ css_class_prefix = options[:css_class_prefix]
Hash.new do |h, kinds|
begin
css_class = css_class_for_kinds(kinds)
title = HTML.token_path_to_hint hint, kinds if hint
-
+
if css_class || title
if method == :style
style = style_for_kinds(kinds)
""
else
- ""
+ ""
end
end
end.tap do |span|
@@ -306,13 +312,13 @@ def make_span_for_kinds method, hint
end
end
end
-
+
def check_group_nesting name, kind
if @opened.empty? || @opened.last != kind
warn "Malformed token stream: Trying to close a #{name} (%p) that is not open. Open are: %p." % [kind, @opened[1..-1]]
end
end
-
+
def break_lines text, style
reopen = ''.dup
@opened.each_with_index do |kind, index|
@@ -320,7 +326,7 @@ def break_lines text, style
end
text.gsub("\n", "#{'' * @opened.size}#{'' if style}\n#{reopen}#{style}")
end
-
+
def close_span
if @opened.pop
@out << ''
@@ -328,6 +334,6 @@ def close_span
end
end
end
-
+
end
end
diff --git a/lib/coderay/tokens_proxy.rb b/lib/coderay/tokens_proxy.rb
index 31ff39be..55770d63 100644
--- a/lib/coderay/tokens_proxy.rb
+++ b/lib/coderay/tokens_proxy.rb
@@ -1,13 +1,13 @@
module CodeRay
-
+
# The result of a scan operation is a TokensProxy, but should act like Tokens.
- #
+ #
# This proxy makes it possible to use the classic CodeRay.scan.encode API
# while still providing the benefits of direct streaming.
class TokensProxy
-
+
attr_accessor :input, :lang, :options, :block
-
+
# Create a new TokensProxy with the arguments of CodeRay.scan.
def initialize input, lang, options = {}, block = nil
@input = input
@@ -15,7 +15,7 @@ def initialize input, lang, options = {}, block = nil
@options = options
@block = block
end
-
+
# Call CodeRay.encode if +encoder+ is a Symbol;
# otherwise, convert the receiver to tokens and call encoder.encode_tokens.
def encode encoder, options = {}
@@ -25,7 +25,7 @@ def encode encoder, options = {}
encoder.encode_tokens tokens, options
end
end
-
+
# Tries to call encode;
# delegates to tokens otherwise.
def method_missing method, *args, &blk
@@ -33,23 +33,23 @@ def method_missing method, *args, &blk
rescue PluginHost::PluginNotFound
tokens.send(method, *args, &blk)
end
-
+
# The (cached) result of the tokenized input; a Tokens instance.
def tokens
@tokens ||= scanner.tokenize(input)
end
-
+
# A (cached) scanner instance to use for the scan task.
def scanner
@scanner ||= CodeRay.scanner(lang, options, &block)
end
-
+
# Overwrite Struct#each.
def each *args, &blk
tokens.each(*args, &blk)
self
end
-
+
end
-
+
end
diff --git a/test/unit/html.rb b/test/unit/html.rb
index 00726351..451d9383 100644
--- a/test/unit/html.rb
+++ b/test/unit/html.rb
@@ -2,35 +2,41 @@
require 'coderay'
class HtmlTest < Test::Unit::TestCase
-
+
+ def test_css_class_prefix
+ tokens = CodeRay.scan 'x = 1', :ruby
+ html = tokens.div(css_class_prefix: 'xyz-', css: :class)
+ assert html.include?('x = 1')
+ end
+
def test_break_lines_option
snippets = {}
-
+
snippets[:ruby] = {}
-
+
snippets[:ruby][:in] = <<-RUBY
ruby_inside = <<-RUBY_INSIDE
This is tricky,
isn't it?
RUBY_INSIDE
RUBY
-
+
snippets[:ruby][:expected_with_option_off] = <<-HTML_OPT_INDEPENDENT_LINES_OFF
ruby_inside = <<-RUBY_INSIDE
This is tricky,
isn't it?
RUBY_INSIDE
HTML_OPT_INDEPENDENT_LINES_OFF
-
+
snippets[:ruby][:expected_with_option_on] = <<-HTML_OPT_INDEPENDENT_LINES_ON
ruby_inside = <<-RUBY_INSIDE
This is tricky,
isn't it?
RUBY_INSIDE
HTML_OPT_INDEPENDENT_LINES_ON
-
+
snippets[:java] = {}
-
+
snippets[:java][:in] = <<-JAVA
import java.lang.*;
@@ -51,7 +57,7 @@ def test_break_lines_option
}
}
JAVA
-
+
snippets[:java][:expected_with_option_off] = <<-HTML_OPT_INDEPENDENT_LINES_OFF
import java.lang.*;
@@ -71,7 +77,7 @@ def test_break_lines_option
}
}
HTML_OPT_INDEPENDENT_LINES_OFF
-
+
snippets[:java][:expected_with_option_on] = <<-HTML_OPT_INDEPENDENT_LINES_ON
import java.lang.*;
@@ -91,10 +97,10 @@ def test_break_lines_option
}
}
HTML_OPT_INDEPENDENT_LINES_ON
-
+
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)