Chris@909: module CodeRay Chris@909: module Encoders Chris@909: Chris@909: class HTML Chris@909: Chris@909: module Numbering # :nodoc: Chris@909: Chris@909: def self.number! output, mode = :table, options = {} Chris@909: return self unless mode Chris@909: Chris@909: options = DEFAULT_OPTIONS.merge options Chris@909: Chris@909: start = options[:line_number_start] Chris@909: unless start.is_a? Integer Chris@909: raise ArgumentError, "Invalid value %p for :line_number_start; Integer expected." % start Chris@909: end Chris@909: Chris@909: anchor_prefix = options[:line_number_anchors] Chris@909: anchor_prefix = 'line' if anchor_prefix == true Chris@909: anchor_prefix = anchor_prefix.to_s[/\w+/] if anchor_prefix Chris@909: anchoring = Chris@909: if anchor_prefix Chris@909: proc do |line| Chris@909: line = line.to_s Chris@909: anchor = anchor_prefix + line Chris@909: "#{line}" Chris@909: end Chris@909: else Chris@909: proc { |line| line.to_s } # :to_s.to_proc in Ruby 1.8.7+ Chris@909: end Chris@909: Chris@909: bold_every = options[:bold_every] Chris@909: highlight_lines = options[:highlight_lines] Chris@909: bolding = Chris@909: if bold_every == false && highlight_lines == nil Chris@909: anchoring Chris@909: elsif highlight_lines.is_a? Enumerable Chris@909: highlight_lines = highlight_lines.to_set Chris@909: proc do |line| Chris@909: if highlight_lines.include? line Chris@909: "#{anchoring[line]}" # highlighted line numbers in bold Chris@909: else Chris@909: anchoring[line] Chris@909: end Chris@909: end Chris@909: elsif bold_every.is_a? Integer Chris@909: raise ArgumentError, ":bolding can't be 0." if bold_every == 0 Chris@909: proc do |line| Chris@909: if line % bold_every == 0 Chris@909: "#{anchoring[line]}" # every bold_every-th number in bold Chris@909: else Chris@909: anchoring[line] Chris@909: end Chris@909: end Chris@909: else Chris@909: raise ArgumentError, 'Invalid value %p for :bolding; false or Integer expected.' % bold_every Chris@909: end Chris@909: Chris@909: line_count = output.count("\n") Chris@909: position_of_last_newline = output.rindex(RUBY_VERSION >= '1.9' ? /\n/ : ?\n) Chris@909: if position_of_last_newline Chris@909: after_last_newline = output[position_of_last_newline + 1 .. -1] Chris@909: ends_with_newline = after_last_newline[/\A(?:<\/span>)*\z/] Chris@909: line_count += 1 if not ends_with_newline Chris@909: end Chris@909: Chris@909: case mode Chris@909: when :inline Chris@909: max_width = (start + line_count).to_s.size Chris@909: line_number = start Chris@909: nesting = [] Chris@909: output.gsub!(/^.*$\n?/) do |line| Chris@909: line.chomp! Chris@909: open = nesting.join Chris@909: line.scan(%r!<(/)?span[^>]*>?!) do |close,| Chris@909: if close Chris@909: nesting.pop Chris@909: else Chris@909: nesting << $& Chris@909: end Chris@909: end Chris@909: close = '' * nesting.size Chris@909: Chris@909: line_number_text = bolding.call line_number Chris@909: indent = ' ' * (max_width - line_number.to_s.size) # TODO: Optimize (10^x) Chris@909: line_number += 1 Chris@909: "#{indent}#{line_number_text}#{open}#{line}#{close}\n" Chris@909: end Chris@909: Chris@909: when :table Chris@909: line_numbers = (start ... start + line_count).map(&bolding).join("\n") Chris@909: line_numbers << "\n" Chris@909: line_numbers_table_template = Output::TABLE.apply('LINE_NUMBERS', line_numbers) Chris@909: Chris@909: output.gsub!(/<\/div>\n/, '') Chris@909: output.wrap_in! line_numbers_table_template Chris@909: output.wrapped_in = :div Chris@909: Chris@909: when :list Chris@909: raise NotImplementedError, 'The :list option is no longer available. Use :table.' Chris@909: Chris@909: else Chris@909: raise ArgumentError, 'Unknown value %p for mode: expected one of %p' % Chris@909: [mode, [:table, :inline]] Chris@909: end Chris@909: Chris@909: output Chris@909: end Chris@909: Chris@909: end Chris@909: Chris@909: end Chris@909: Chris@909: end Chris@909: end