1# :markup: markdown
2
3##
4# Outputs parsed markup as Markdown
5
6class RDoc::Markup::ToMarkdown < RDoc::Markup::ToRdoc
7
8  ##
9  # Creates a new formatter that will output Markdown format text
10
11  def initialize markup = nil
12    super
13
14    @headings[1] = ['# ',      '']
15    @headings[2] = ['## ',     '']
16    @headings[3] = ['### ',    '']
17    @headings[4] = ['#### ',   '']
18    @headings[5] = ['##### ',  '']
19    @headings[6] = ['###### ', '']
20
21    add_special_RDOCLINK
22    add_special_TIDYLINK
23
24    @hard_break = "  \n"
25  end
26
27  ##
28  # Maps attributes to HTML sequences
29
30  def init_tags
31    add_tag :BOLD, '**', '**'
32    add_tag :EM,   '*',  '*'
33    add_tag :TT,   '`',  '`'
34  end
35
36  ##
37  # Adds a newline to the output
38
39  def handle_special_HARD_BREAK special
40    "  \n"
41  end
42
43  ##
44  # Finishes consumption of `list`
45
46  def accept_list_end list
47    @res << "\n"
48
49    super
50  end
51
52  ##
53  # Finishes consumption of `list_item`
54
55  def accept_list_item_end list_item
56    width = case @list_type.last
57            when :BULLET then
58              4
59            when :NOTE, :LABEL then
60              use_prefix
61
62              4
63            else
64              @list_index[-1] = @list_index.last.succ
65              4
66            end
67
68    @indent -= width
69  end
70
71  ##
72  # Prepares the visitor for consuming `list_item`
73
74  def accept_list_item_start list_item
75    type = @list_type.last
76
77    case type
78    when :NOTE, :LABEL then
79      bullets = Array(list_item.label).map do |label|
80        attributes(label).strip
81      end.join "\n"
82
83      bullets << "\n:"
84
85      @prefix = ' ' * @indent
86      @indent += 4
87      @prefix << bullets + (' ' * (@indent - 1))
88    else
89      bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
90      @prefix = (' ' * @indent) + bullet.ljust(4)
91
92      @indent += 4
93    end
94  end
95
96  ##
97  # Prepares the visitor for consuming `list`
98
99  def accept_list_start list
100    case list.type
101    when :BULLET, :LABEL, :NOTE then
102      @list_index << nil
103    when :LALPHA, :NUMBER, :UALPHA then
104      @list_index << 1
105    else
106      raise RDoc::Error, "invalid list type #{list.type}"
107    end
108
109    @list_width << 4
110    @list_type << list.type
111  end
112
113  ##
114  # Adds `rule` to the output
115
116  def accept_rule rule
117    use_prefix or @res << ' ' * @indent
118    @res << '-' * 3
119    @res << "\n"
120  end
121
122  ##
123  # Outputs `verbatim` indented 4 columns
124
125  def accept_verbatim verbatim
126    indent = ' ' * (@indent + 4)
127
128    verbatim.parts.each do |part|
129      @res << indent unless part == "\n"
130      @res << part
131    end
132
133    @res << "\n" unless @res =~ /\n\z/
134  end
135
136  ##
137  # Creates a Markdown-style URL from +url+ with +text+.
138
139  def gen_url url, text
140    scheme, url, = parse_url url
141
142    "[#{text.sub(%r{^#{scheme}:/*}i, '')}](#{url})"
143  end
144
145  ##
146  # Handles <tt>rdoc-</tt> type links for footnotes.
147
148  def handle_rdoc_link url
149    case url
150    when /\Ardoc-ref:/ then
151      $'
152    when /\Ardoc-label:footmark-(\d+)/ then
153      "[^#{$1}]:"
154    when /\Ardoc-label:foottext-(\d+)/ then
155      "[^#{$1}]"
156    when /\Ardoc-label:label-/ then
157      gen_url url, $'
158    when /\Ardoc-[a-z]+:/ then
159      $'
160    end
161  end
162
163  ##
164  # Converts the RDoc markup tidylink into a Markdown.style link.
165
166  def handle_special_TIDYLINK special
167    text = special.text
168
169    return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
170
171    label = $1
172    url   = $2
173
174    if url =~ /^rdoc-label:foot/ then
175      handle_rdoc_link url
176    else
177      gen_url url, label
178    end
179  end
180
181  ##
182  # Converts the rdoc-...: links into a Markdown.style links.
183
184  def handle_special_RDOCLINK special
185    handle_rdoc_link special.text
186  end
187
188end
189
190