summaryrefslogtreecommitdiff
path: root/lib/coderay/encoders
diff options
context:
space:
mode:
Diffstat (limited to 'lib/coderay/encoders')
-rw-r--r--lib/coderay/encoders/debug.rb3
-rw-r--r--lib/coderay/encoders/html.rb167
-rw-r--r--lib/coderay/encoders/html/css.rb14
-rw-r--r--lib/coderay/encoders/terminal.rb141
4 files changed, 160 insertions, 165 deletions
diff --git a/lib/coderay/encoders/debug.rb b/lib/coderay/encoders/debug.rb
index 95d6138..c03d3fb 100644
--- a/lib/coderay/encoders/debug.rb
+++ b/lib/coderay/encoders/debug.rb
@@ -24,11 +24,12 @@ module Encoders
end
def text_token text, kind
+ raise 'empty token' if $CODERAY_DEBUG && text.empty?
if kind == :space
@out << text
else
# TODO: Escape (
- text = text.gsub(/[)\\]/, '\\\\\0') # escape ) and \
+ text = text.gsub(/[)\\]/, '\\\\\0') if text.index(/[)\\]/)
@out << kind.to_s << '(' << text << ')'
end
end
diff --git a/lib/coderay/encoders/html.rb b/lib/coderay/encoders/html.rb
index 635a4d8..b897f5e 100644
--- a/lib/coderay/encoders/html.rb
+++ b/lib/coderay/encoders/html.rb
@@ -126,22 +126,21 @@ module Encoders
protected
- HTML_ESCAPE = { #:nodoc:
- '&' => '&amp;',
- '"' => '&quot;',
- '>' => '&gt;',
- '<' => '&lt;',
- }
+ 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
- # This was to prevent illegal HTML.
- # Strange chars should still be avoided in codes.
- evil_chars = Array(0x00...0x20) - [?\n, ?\t, ?\s]
- evil_chars.each { |i| HTML_ESCAPE[i.chr] = ' ' }
- #ansi_chars = Array(0x7f..0xff)
- #ansi_chars.each { |i| HTML_ESCAPE[i.chr] = '&#%d;' % i }
- # \x9 (\t) and \xA (\n) not included
- #HTML_ESCAPE_PATTERN = /[\t&"><\0-\x8\xB-\x1f\x7f-\xff]/
- HTML_ESCAPE_PATTERN = /[\t"&><\0-\x8\xB-\x1f]/
+ 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 }
@@ -172,59 +171,22 @@ module Encoders
def setup options
super
+ check_options! options
+
if options[:wrap] || options[:line_numbers]
@real_out = @out
@out = ''
end
- options[:break_lines] = true if options[:line_numbers] == :inline
-
@break_lines = (options[:break_lines] == true)
- @HTML_ESCAPE = HTML_ESCAPE.dup
- @HTML_ESCAPE["\t"] = ' ' * options[:tab_width]
+ @HTML_ESCAPE = HTML_ESCAPE.merge("\t" => ' ' * options[:tab_width])
@opened = []
@last_opened = nil
@css = CSS.new options[:style]
- hint = options[:hint]
- if hint && ![:debug, :info, :info_long].include?(hint)
- raise ArgumentError, "Unknown value %p for :hint; \
- expected :info, :info_long, :debug, false, or nil." % hint
- end
-
- css_classes = TokenKinds
- case options[:css]
- when :class
- @span_for_kind = Hash.new do |h, k|
- if k.is_a? ::Symbol
- kind = k_dup = k
- else
- kind = k.first
- k_dup = k.dup
- end
- if kind != :space && (hint || css_class = css_classes[kind])
- title = HTML.token_path_to_hint hint, k if hint
- css_class ||= css_classes[kind]
- h[k_dup] = "<span#{title}#{" class=\"#{css_class}\"" if css_class}>"
- else
- h[k_dup] = nil
- end
- end
- when :style
- @span_for_kind = Hash.new do |h, k|
- kind = k.is_a?(Symbol) ? k : k.first
- h[k.is_a?(Symbol) ? k : k.dup] =
- if kind != :space && (hint || css_classes[kind])
- title = HTML.token_path_to_hint hint, k if hint
- style = @css.get_style Array(k).map { |c| css_classes[c] }
- "<span#{title}#{" style=\"#{style}\"" if style}>"
- end
- end
- else
- raise ArgumentError, "Unknown value %p for :css." % options[:css]
- end
+ @span_for_kinds = make_span_for_kinds(options[:css], options[:hint])
@set_last_opened = options[:hint] || options[:css] == :style
end
@@ -255,20 +217,10 @@ module Encoders
public
def text_token text, kind
- if text =~ /#{HTML_ESCAPE_PATTERN}/o
- text = text.gsub(/#{HTML_ESCAPE_PATTERN}/o) { |m| @HTML_ESCAPE[m] }
- end
+ style = @span_for_kinds[@last_opened ? [kind, *@opened] : kind]
- style = @span_for_kind[@last_opened ? [kind, *@opened] : kind]
-
- if @break_lines && (i = text.index("\n")) && (c = @opened.size + (style ? 1 : 0)) > 0
- close = '</span>' * c
- reopen = ''
- @opened.each_with_index do |k, index|
- reopen << (@span_for_kind[index > 0 ? [k, *@opened[0 ... index ]] : k] || '<span>')
- end
- text[i .. -1] = text[i .. -1].gsub("\n", "#{close}\n#{reopen}#{style}")
- end
+ 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>'
@@ -279,25 +231,19 @@ module Encoders
# token groups, eg. strings
def begin_group kind
- @out << (@span_for_kind[@last_opened ? [kind, *@opened] : kind] || '<span>')
+ @out << (@span_for_kinds[@last_opened ? [kind, *@opened] : kind] || '<span>')
@opened << kind
@last_opened = kind if @set_last_opened
end
def end_group kind
- if $CODERAY_DEBUG && (@opened.empty? || @opened.last != kind)
- warn 'Malformed token stream: Trying to close a token (%p) ' \
- 'that is not open. Open are: %p.' % [kind, @opened[1..-1]]
- end
- if @opened.pop
- @out << '</span>'
- @last_opened = @opened.last if @last_opened
- end
+ 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_kind[@last_opened ? [kind, *@opened] : kind]
+ if style = @span_for_kinds[@last_opened ? [kind, *@opened] : kind]
if style['class="']
@out << style.sub('class="', 'class="line ')
else
@@ -311,16 +257,71 @@ module Encoders
end
def end_line kind
- if $CODERAY_DEBUG && (@opened.empty? || @opened.last != kind)
- warn 'Malformed token stream: Trying to close a line (%p) ' \
- 'that is not open. Open are: %p.' % [kind, @opened[1..-1]]
+ 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
+ Hash.new do |h, kinds|
+ h[kinds.is_a?(Symbol) ? kinds : kinds.dup] = 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}>"
+ end
+ end
+ 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 = ''
+ @opened.each_with_index do |k, index|
+ reopen << (@span_for_kinds[index > 0 ? [k, *@opened[0...index]] : k] || '<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
diff --git a/lib/coderay/encoders/html/css.rb b/lib/coderay/encoders/html/css.rb
index 6de4b46..164d7f8 100644
--- a/lib/coderay/encoders/html/css.rb
+++ b/lib/coderay/encoders/html/css.rb
@@ -11,7 +11,7 @@ module Encoders
end
def initialize style = :default
- @classes = Hash.new
+ @styles = Hash.new
style = CSS.load_stylesheet style
@stylesheet = [
style::CSS_MAIN_STYLES,
@@ -20,12 +20,12 @@ module Encoders
parse style::TOKEN_COLORS
end
- def get_style styles
- cl = @classes[styles.first]
+ def get_style_for_css_classes css_classes
+ cl = @styles[css_classes.first]
return '' unless cl
style = ''
- 1.upto styles.size do |offset|
- break if style = cl[styles[offset .. -1]]
+ 1.upto css_classes.size do |offset|
+ break if style = cl[css_classes[offset .. -1]]
end
# warn 'Style not found: %p' % [styles] if style.empty?
return style
@@ -52,8 +52,8 @@ module Encoders
for selector in selectors.split(',')
classes = selector.scan(/[-\w]+/)
cl = classes.pop
- @classes[cl] ||= Hash.new
- @classes[cl][classes] = style.to_s.strip.delete(' ').chomp(';')
+ @styles[cl] ||= Hash.new
+ @styles[cl][classes] = style.to_s.strip.delete(' ').chomp(';')
end
end
end
diff --git a/lib/coderay/encoders/terminal.rb b/lib/coderay/encoders/terminal.rb
index a0ceb3c..9894b91 100644
--- a/lib/coderay/encoders/terminal.rb
+++ b/lib/coderay/encoders/terminal.rb
@@ -19,73 +19,73 @@ module CodeRay
register_for :terminal
TOKEN_COLORS = {
- :annotation => '35',
- :attribute_name => '33',
- :attribute_value => '31',
- :binary => '1;35',
+ :annotation => "\e[35m",
+ :attribute_name => "\e[33m",
+ :attribute_value => "\e[31m",
+ :binary => "\e[1;35m",
:char => {
- :self => '36', :delimiter => '1;34'
+ :self => "\e[36m", :delimiter => "\e[1;34m"
},
- :class => '1;35',
- :class_variable => '36',
- :color => '32',
- :comment => '37',
- :complex => '1;34',
- :constant => ['1;34', '4'],
- :decoration => '35',
- :definition => '1;32',
- :directive => ['32', '4'],
- :doc => '46',
- :doctype => '1;30',
- :doc_string => ['31', '4'],
- :entity => '33',
- :error => ['1;33', '41'],
- :exception => '1;31',
- :float => '1;35',
- :function => '1;34',
- :global_variable => '42',
- :hex => '1;36',
- :include => '33',
- :integer => '1;34',
- :key => '35',
- :label => '1;15',
- :local_variable => '33',
- :octal => '1;35',
- :operator_name => '1;29',
- :predefined_constant => '1;36',
- :predefined_type => '1;30',
- :predefined => ['4', '1;34'],
- :preprocessor => '36',
- :pseudo_class => '1;34',
+ :class => "\e[1;35m",
+ :class_variable => "\e[36m",
+ :color => "\e[32m",
+ :comment => "\e[37m",
+ :complex => "\e[1;34m",
+ :constant => "\e[1;34m\e[4m",
+ :decoration => "\e[35m",
+ :definition => "\e[1;32m",
+ :directive => "\e[32m\e[4m",
+ :doc => "\e[46m",
+ :doctype => "\e[1;30m",
+ :docstring => "\e[31m\e[4m",
+ :entity => "\e[33m",
+ :error => "\e[1;33m\e[41m",
+ :exception => "\e[1;31m",
+ :float => "\e[1;35m",
+ :function => "\e[1;34m",
+ :global_variable => "\e[42m",
+ :hex => "\e[1;36m",
+ :include => "\e[33m",
+ :integer => "\e[1;34m",
+ :key => "\e[35m",
+ :label => "\e[1;15m",
+ :local_variable => "\e[33m",
+ :octal => "\e[1;35m",
+ :operator_name => "\e[1;29m",
+ :predefined_constant => "\e[1;36m",
+ :predefined_type => "\e[1;30m",
+ :predefined => "\e[4m\e[1;34m",
+ :preprocessor => "\e[36m",
+ :pseudo_class => "\e[1;34m",
:regexp => {
- :self => '31',
- :content => '31',
- :delimiter => '1;29',
- :modifier => '35',
+ :self => "\e[31m",
+ :content => "\e[31m",
+ :delimiter => "\e[1;29m",
+ :modifier => "\e[35m",
},
- :reserved => '1;31',
+ :reserved => "\e[1;31m",
:shell => {
- :self => '42',
- :content => '1;29',
- :delimiter => '37',
+ :self => "\e[42m",
+ :content => "\e[1;29m",
+ :delimiter => "\e[37m",
},
:string => {
- :self => '32',
- :modifier => '1;32',
- :escape => '1;36',
- :delimiter => '1;32',
- :char => '1;36',
+ :self => "\e[32m",
+ :modifier => "\e[1;32m",
+ :escape => "\e[1;36m",
+ :delimiter => "\e[1;32m",
+ :char => "\e[1;36m",
},
- :symbol => '1;32',
- :tag => '1;34',
- :type => '1;34',
- :value => '36',
- :variable => '1;34',
+ :symbol => "\e[1;32m",
+ :tag => "\e[1;34m",
+ :type => "\e[1;34m",
+ :value => "\e[36m",
+ :variable => "\e[1;34m",
- :insert => '42',
- :delete => '41',
- :change => '44',
- :head => '45'
+ :insert => "\e[42m",
+ :delete => "\e[41m",
+ :change => "\e[44m",
+ :head => "\e[45m"
}
TOKEN_COLORS[:keyword] = TOKEN_COLORS[:reserved]
TOKEN_COLORS[:method] = TOKEN_COLORS[:function]
@@ -114,10 +114,10 @@ module CodeRay
end
end
- @out << ansi_colorize(color)
- @out << text.gsub("\n", ansi_clear + "\n" + ansi_colorize(color))
- @out << ansi_clear
- @out << ansi_colorize(@subcolors[:self]) if @subcolors && @subcolors[:self]
+ @out << color
+ @out << text.gsub("\n", "\e[0m\n" + color)
+ @out << "\e[0m"
+ @out << @subcolors[:self] if @subcolors
else
@out << text
end
@@ -134,7 +134,7 @@ module CodeRay
# nothing to close
else
@opened.pop
- @out << ansi_clear
+ @out << "\e[0m"
@out << open_token(@opened.last)
end
end
@@ -146,7 +146,7 @@ module CodeRay
@opened.pop
# whole lines to be highlighted,
# eg. added/modified/deleted lines in a diff
- @out << "\t" * 100 + ansi_clear
+ @out << (@line_filler ||= "\t" * 100 + "\e[0m")
@out << open_token(@opened.last)
end
end
@@ -157,23 +157,16 @@ module CodeRay
if color = TOKEN_COLORS[kind]
if Hash === color
@subcolors = color
- ansi_colorize(color[:self]) if color[:self]
+ color[:self]
else
@subcolors = {}
- ansi_colorize(color)
+ color
end
else
@subcolors = nil
''
end
end
-
- def ansi_colorize(color)
- Array(color).map { |c| "\e[#{c}m" }.join
- end
- def ansi_clear
- ansi_colorize(0)
- end
end
end
-end \ No newline at end of file
+end