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

adds css class prefixes #221

Open
wants to merge 1 commit 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
116 changes: 61 additions & 55 deletions lib/coderay/encoders/html.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module CodeRay
module Encoders

# = HTML Encoder
#
# This is CodeRay's most important highlighter:
Expand All @@ -15,7 +15,7 @@ module Encoders
# puts CodeRay.scan('Some /code/', :ruby).html(:wrap => :span)
# #-> <span class="CodeRay"><span class="co">Some</span> /code/</span>
# puts CodeRay.scan('Some /code/', :ruby).span #-> the same
#
#
# puts CodeRay.scan('Some code', :ruby).html(
# :wrap => nil,
# :line_numbers => :inline,
Expand All @@ -27,14 +27,19 @@ module Encoders
# === :tab_width
# Convert \t characters to +n+ spaces (a number or false.)
# false will keep tab characters untouched.
#
#
# Default: 8
#
# === :css
# How to include the styles; can be :class or :style.
#
# 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.
#
Expand All @@ -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.
#
Expand Down Expand Up @@ -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.
#
Expand All @@ -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
{
'&' => '&amp;',
Expand All @@ -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.
Expand All @@ -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 << '</span>' while @opened.pop
@last_opened = nil
end

if @out.respond_to? :to_str
@out.extend Output
@out.css = @css
Expand All @@ -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 << '</span>'
else
@out << text
end
end

# token groups, eg. strings
def begin_group kind
@out << (@span_for_kinds[@last_opened ? [kind, *@opened] : kind] || '<span>')
@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]
Expand All @@ -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)
"<span#{title}#{" style=\"#{style}\"" if style}>"
else
"<span#{title}#{" class=\"#{css_class}\"" if css_class}>"
"<span#{title}#{" class=\"#{css_class_prefix}#{css_class}\"" if css_class}>"
end
end
end.tap do |span|
Expand All @@ -306,28 +312,28 @@ 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|
reopen << (@span_for_kinds[index > 0 ? [kind, *@opened[0...index]] : kind] || '<span>')
end
text.gsub("\n", "#{'</span>' * @opened.size}#{'</span>' if style}\n#{reopen}#{style}")
end

def close_span
if @opened.pop
@out << '</span>'
@last_opened = @opened.last if @last_opened
end
end
end

end
end
22 changes: 11 additions & 11 deletions lib/coderay/tokens_proxy.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
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
@lang = lang
@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 = {}
Expand All @@ -25,31 +25,31 @@ 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
encode method.to_sym, *args
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