diff vendor/plugins/coderay-0.9.2/lib/coderay/encoders/html.rb @ 0:513646585e45

* Import Redmine trunk SVN rev 3859
author Chris Cannam
date Fri, 23 Jul 2010 15:52:44 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/plugins/coderay-0.9.2/lib/coderay/encoders/html.rb	Fri Jul 23 15:52:44 2010 +0100
@@ -0,0 +1,305 @@
+require 'set'
+
+module CodeRay
+module Encoders
+
+  # = HTML Encoder
+  #
+  # This is CodeRay's most important highlighter:
+  # It provides save, fast XHTML generation and CSS support.
+  #
+  # == Usage
+  #
+  #  require 'coderay'
+  #  puts CodeRay.scan('Some /code/', :ruby).html  #-> a HTML page
+  #  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,
+  #    :css => :style
+  #  )
+  #  #-> <span class="no">1</span>  <span style="color:#036; font-weight:bold;">Some</span> code
+  #
+  # == Options
+  #
+  # === :tab_width
+  # Convert \t characters to +n+ spaces (a number.)
+  # Default: 8
+  #
+  # === :css
+  # How to include the styles; can be :class or :style.
+  #
+  # Default: :class
+  #
+  # === :wrap
+  # Wrap in :page, :div, :span or nil.
+  #
+  # You can also use Encoders::Div and Encoders::Span.
+  #
+  # Default: nil
+  #
+  # === :title
+  # 
+  # The title of the HTML page (works only when :wrap is set to :page.)
+  #
+  # Default: 'CodeRay output'
+  #
+  # === :line_numbers
+  # Include line numbers in :table, :inline, :list or nil (no line numbers)
+  #
+  # Default: nil
+  #
+  # === :line_number_start
+  # Where to start with line number counting.
+  #
+  # Default: 1
+  #
+  # === :bold_every
+  # Make every +n+-th number appear bold.
+  #
+  # 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.
+  #
+  # Default: nil
+  #
+  # === :hint
+  # Include some information into the output using the title attribute.
+  # Can be :info (show token type on mouse-over), :info_long (with full path)
+  # or :debug (via inspect).
+  #
+  # Default: false
+  class HTML < Encoder
+
+    include Streamable
+    register_for :html
+
+    FILE_EXTENSION = 'html'
+
+    DEFAULT_OPTIONS = {
+      :tab_width => 8,
+
+      :css => :class,
+
+      :style => :cycnus,
+      :wrap => nil,
+      :title => 'CodeRay output',
+
+      :line_numbers => nil,
+      :line_number_start => 1,
+      :bold_every => 10,
+      :highlight_lines => nil,
+
+      :hint => false,
+    }
+
+    helper :output, :css
+
+    attr_reader :css
+
+  protected
+
+    HTML_ESCAPE = {  #:nodoc:
+      '&' => '&amp;',
+      '"' => '&quot;',
+      '>' => '&gt;',
+      '<' => '&lt;',
+    }
+
+    # 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]/
+
+    TOKEN_KIND_TO_INFO = Hash.new { |h, kind|
+      h[kind] =
+        case kind
+        when :pre_constant
+          'Predefined constant'
+        else
+          kind.to_s.gsub(/_/, ' ').gsub(/\b\w/) { $&.capitalize }
+        end
+    }
+
+    TRANSPARENT_TOKEN_KINDS = [
+      :delimiter, :modifier, :content, :escape, :inline_delimiter,
+    ].to_set
+
+    # Generate a hint about the given +classes+ in a +hint+ style.
+    #
+    # +hint+ may be :info, :info_long or :debug.
+    def self.token_path_to_hint hint, classes
+      title =
+        case hint
+        when :info
+          TOKEN_KIND_TO_INFO[classes.first]
+        when :info_long
+          classes.reverse.map { |kind| TOKEN_KIND_TO_INFO[kind] }.join('/')
+        when :debug
+          classes.inspect
+        end
+      title ? " title=\"#{title}\"" : ''
+    end
+
+    def setup options
+      super
+
+      @HTML_ESCAPE = HTML_ESCAPE.dup
+      @HTML_ESCAPE["\t"] = ' ' * options[:tab_width]
+
+      @opened = [nil]
+      @css = CSS.new options[:style]
+
+      hint = options[:hint]
+      if hint and not [:debug, :info, :info_long].include? hint
+        raise ArgumentError, "Unknown value %p for :hint; \
+          expected :info, :debug, false, or nil." % hint
+      end
+
+      case options[:css]
+
+      when :class
+        @css_style = Hash.new do |h, k|
+          c = CodeRay::Tokens::ClassOfKind[k.first]
+          if c == :NO_HIGHLIGHT and not hint
+            h[k.dup] = false
+          else
+            title = if hint
+              HTML.token_path_to_hint(hint, k[1..-1] << k.first)
+            else
+              ''
+            end
+            if c == :NO_HIGHLIGHT
+              h[k.dup] = '<span%s>' % [title]
+            else
+              h[k.dup] = '<span%s class="%s">' % [title, c]
+            end
+          end
+        end
+
+      when :style
+        @css_style = Hash.new do |h, k|
+          if k.is_a? ::Array
+            styles = k.dup
+          else
+            styles = [k]
+          end
+          type = styles.first
+          classes = styles.map { |c| Tokens::ClassOfKind[c] }
+          if classes.first == :NO_HIGHLIGHT and not hint
+            h[k] = false
+          else
+            styles.shift if TRANSPARENT_TOKEN_KINDS.include? styles.first
+            title = HTML.token_path_to_hint hint, styles
+            style = @css[*classes]
+            h[k] =
+              if style
+                '<span%s style="%s">' % [title, style]
+              else
+                false
+              end
+          end
+        end
+
+      else
+        raise ArgumentError, "Unknown value %p for :css." % options[:css]
+
+      end
+    end
+
+    def finish options
+      not_needed = @opened.shift
+      @out << '</span>' * @opened.size
+      unless @opened.empty?
+        warn '%d tokens still open: %p' % [@opened.size, @opened]
+      end
+
+      @out.extend Output
+      @out.css = @css
+      @out.numerize! options[:line_numbers], options
+      @out.wrap! options[:wrap]
+      @out.apply_title! options[:title]
+
+      super
+    end
+
+    def token text, type = :plain
+      case text
+      
+      when nil
+        # raise 'Token with nil as text was given: %p' % [[text, type]] 
+      
+      when String
+        if text =~ /#{HTML_ESCAPE_PATTERN}/o
+          text = text.gsub(/#{HTML_ESCAPE_PATTERN}/o) { |m| @HTML_ESCAPE[m] }
+        end
+        @opened[0] = type
+        if text != "\n" && style = @css_style[@opened]
+          @out << style << text << '</span>'
+        else
+          @out << text
+        end
+        
+      
+      # token groups, eg. strings
+      when :open
+        @opened[0] = type
+        @out << (@css_style[@opened] || '<span>')
+        @opened << type
+      when :close
+        if @opened.empty?
+          # nothing to close
+        else
+          if $CODERAY_DEBUG and (@opened.size == 1 or @opened.last != type)
+            raise 'Malformed token stream: Trying to close a token (%p) \
+              that is not open. Open are: %p.' % [type, @opened[1..-1]]
+          end
+          @out << '</span>'
+          @opened.pop
+        end
+      
+      # whole lines to be highlighted, eg. a deleted line in a diff
+      when :begin_line
+        @opened[0] = type
+        if style = @css_style[@opened]
+          @out << style.sub('<span', '<div')
+        else
+          @out << '<div>'
+        end
+        @opened << type
+      when :end_line
+        if @opened.empty?
+          # nothing to close
+        else
+          if $CODERAY_DEBUG and (@opened.size == 1 or @opened.last != type)
+            raise 'Malformed token stream: Trying to close a line (%p) \
+              that is not open. Open are: %p.' % [type, @opened[1..-1]]
+          end
+          @out << '</div>'
+          @opened.pop
+        end
+      
+      else
+        raise 'unknown token kind: %p' % [text]
+        
+      end
+    end
+
+  end
+
+end
+end