summaryrefslogtreecommitdiff
path: root/lib/coderay/encoders/html/numerization.rb
blob: 1590ad0eba0dc03a4b618fd0405ceadc7f45b5ed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
module CodeRay
module Encoders

  class HTML

    module Output

      def numerize *args
        clone.numerize!(*args)
      end

      def numerize! mode = :table, options = {}
        return self unless mode

        options = DEFAULT_OPTIONS.merge options

        start = options[:line_number_start]
        unless start.is_a? Integer
          raise ArgumentError, "Invalid value %p for :line_number_start; Integer expected." % start
        end
        
        anchor_prefix = options[:line_number_anchors]
        anchor_prefix = 'line' if anchor_prefix == true
        anchor_prefix = anchor_prefix.to_s[/\w+/] if anchor_prefix
        anchoring =
          if anchor_prefix
            proc do |line|
              line = line.to_s
              anchor = anchor_prefix + line
              "<a href=\"##{anchor}\" name=\"#{anchor}\">#{line}</a>"
            end
          else
            proc { |line| line.to_s }
          end
        
        bold_every = options[:bold_every]
        highlight_lines = options[:highlight_lines]
        bolding =
          if bold_every == false && highlight_lines == nil
            anchoring
          elsif highlight_lines.is_a? Enumerable
            highlight_lines = highlight_lines.to_set
            proc do |line|
              if highlight_lines.include? line
                "<strong class=\"highlighted\">#{anchoring[line]}</strong>"  # highlighted line numbers in bold
              else
                anchoring[line]
              end
            end
          elsif bold_every.is_a? Integer
            raise ArgumentError, ":bolding can't be 0." if bold_every == 0
            proc do |line|
              if line % bold_every == 0
                "<strong>#{anchoring[line]}</strong>"  # every bold_every-th number in bold
              else
                anchoring[line]
              end
            end
          else
            raise ArgumentError, 'Invalid value %p for :bolding; false or Integer expected.' % bold_every
          end
        
        case mode
        when :inline
          max_width = (start + line_count).to_s.size
          line_number = start
          opened_tags = []
          gsub!(/^.*$\n?/) do |line|
            line.chomp!
            open = opened_tags.join
            line.scan(%r!<(/)?span[^>]*>?!) do |close,|
              if close
                opened_tags.pop
              else
                opened_tags << $&
              end
            end
            close = '</span>' * opened_tags.size
            
            line_number_text = bolding.call line_number
            indent = ' ' * (max_width - line_number.to_s.size)  # TODO: Optimize (10^x)
            line_number += 1
            "<span class=\"no\">#{indent}#{line_number_text}</span>#{open}#{line}#{close}\n"
          end

        when :table
          line_numbers = (start ... start + line_count).to_a.map(&bolding).join("\n")
          line_numbers << "\n"

          line_numbers_table_template = TABLE.apply('LINE_NUMBERS', line_numbers)
          gsub!(/<\/div>\n/) { '</div>' }
          wrap_in! line_numbers_table_template
          @wrapped_in = :div

        when :list
          raise NotImplementedError, 'The :list option is no longer available. Use :table.'

        else
          raise ArgumentError, 'Unknown value %p for mode: expected one of %p' %
            [mode, [:table, :inline]]
        end

        self
      end

      def line_count
        line_count = count("\n")
        position_of_last_newline = rindex(?\n)
        if position_of_last_newline
          after_last_newline = self[position_of_last_newline + 1 .. -1]
          ends_with_newline = after_last_newline[/\A(?:<\/span>)*\z/]
          line_count += 1 if not ends_with_newline
        end
        line_count
      end

    end

  end

end
end