summaryrefslogtreecommitdiff
path: root/lib/rdoc/markup/formatter.rb
blob: 993e523f0c26052268864f9f7173e1b4d9ab0b99 (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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
require 'rdoc/markup'

##
# Base class for RDoc markup formatters
#
# Formatters use a visitor pattern to convert content into output.

class RDoc::Markup::Formatter

  InlineTag = Struct.new(:bit, :on, :off)

  ##
  # Creates a new Formatter

  def initialize
    @markup = RDoc::Markup.new
    @am = @markup.attribute_manager
    @attr_tags = []

    @in_tt = 0
    @tt_bit = RDoc::Markup::Attribute.bitmap_for :TT
  end

  ##
  # Add a new set of tags for an attribute. We allow separate start and end
  # tags for flexibility

  def add_tag(name, start, stop)
    attr = RDoc::Markup::Attribute.bitmap_for name
    @attr_tags << InlineTag.new(attr, start, stop)
  end

  ##
  # Allows +tag+ to be decorated with additional information.

  def annotate(tag)
    tag
  end

  ##
  # Marks up +content+

  def convert(content)
    @markup.convert content, self
  end

  ##
  # Converts flow items +flow+

  def convert_flow(flow)
    res = []

    flow.each do |item|
      case item
      when String then
        res << convert_string(item)
      when RDoc::Markup::AttrChanger then
        off_tags res, item
        on_tags res, item
      when RDoc::Markup::Special then
        res << convert_special(item)
      else
        raise "Unknown flow element: #{item.inspect}"
      end
    end

    res.join
  end

  ##
  # Converts added specials.  See RDoc::Markup#add_special

  def convert_special(special)
    handled = false

    RDoc::Markup::Attribute.each_name_of special.type do |name|
      method_name = "handle_special_#{name}"

      if respond_to? method_name then
        special.text = send method_name, special
        handled = true
      end
    end

    raise "Unhandled special: #{special}" unless handled

    special.text
  end

  ##
  # Converts a string to be fancier if desired

  def convert_string string
    string
  end

  ##
  # Are we currently inside tt tags?

  def in_tt?
    @in_tt > 0
  end

  def on_tags res, item
    attr_mask = item.turn_on
    return if attr_mask.zero?

    @attr_tags.each do |tag|
      if attr_mask & tag.bit != 0 then
        res << annotate(tag.on)
        @in_tt += 1 if tt? tag
      end
    end
  end

  def off_tags res, item
    attr_mask = item.turn_off
    return if attr_mask.zero?

    @attr_tags.reverse_each do |tag|
      if attr_mask & tag.bit != 0 then
        @in_tt -= 1 if tt? tag
        res << annotate(tag.off)
      end
    end
  end

  ##
  # Is +tag+ a tt tag?

  def tt? tag
    tag.bit == @tt_bit
  end

end

class RDoc::Markup
  autoload :ToAnsi,         'rdoc/markup/to_ansi'
  autoload :ToBs,           'rdoc/markup/to_bs'
  autoload :ToHtml,         'rdoc/markup/to_html'
  autoload :ToHtmlCrossref, 'rdoc/markup/to_html_crossref'
  autoload :ToRdoc,         'rdoc/markup/to_rdoc'
end