1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Markdown rendering of Markdown documents
16 module markdown_md_rendering
18 import markdown_rendering
19 import markdown_github
20 import markdown_wikilinks
22 # Markdown document renderer to Markdown
23 class MarkdownRenderer
26 # Markdown output under construction
27 private var md
: Buffer is noinit
29 # Render `node` as Markdown
30 redef fun render
(node
) do
33 return md
.write_to_string
36 redef fun visit
(node
) do node
.render_md
(self)
38 # Reset internal state
43 # Current indentation level
44 private var indent
= 0
46 # Are we currently in a blockquote?
49 # Add a `md` string to the output
50 fun add_raw
(md
: String) do self.md
.append
(md
)
52 # Add a blank line to the output
53 fun add_line
do add_raw
"\n"
55 # Add an indentation depending on `ident` level
61 private class TextLengthVisitor
66 redef fun visit
(node
) do node
.process_len
(self)
71 # Render `self` as Markdown
72 fun render_md
(v
: MarkdownRenderer) do visit_all
(v
)
74 private fun process_len
(v
: TextLengthVisitor) do visit_all
(v
)
77 redef class MdDocument
78 redef fun render_md
(v
) do
79 var node
= first_child
92 redef class MdBlockQuote
93 redef fun render_md
(v
) do
95 var node
= first_child
106 redef class MdIndentedCodeBlock
107 redef fun render_md
(v
) do
108 var literal
= self.literal
109 if literal
== null then return
111 var lines
= literal
.split
("\n")
112 for i
in [0..lines
.length
[ do
113 if i
== lines
.length
- 1 then continue
115 if line
.is_empty
then
131 redef class MdFencedCodeBlock
132 redef fun render_md
(v
) do
135 v
.add_raw fence_char
.to_s
* fence_length
136 v
.add_raw info
or else ""
137 for line
in (literal
or else "").split
("\n") do
139 if not line
.is_empty
then
145 v
.add_raw fence_char
.to_s
* fence_length
150 redef class MdHeading
151 redef fun render_md
(v
) do
154 var length_visitor
= new TextLengthVisitor
155 length_visitor
.enter_visit
(self)
158 v
.add_raw
"=" * length_visitor
.length
160 v
.add_raw
"-" * length_visitor
.length
163 v
.add_raw
"#" * level
166 if has_atx_trailing
then
168 v
.add_raw
"#" * level
175 redef class MdOrderedList
177 private var md_numbering
: Int = start_number
is lazy
180 redef class MdListItem
181 redef fun render_md
(v
) do
182 var parent
= self.parent
183 var is_tight
= parent
.as(MdListBlock).is_tight
186 if parent
isa MdUnorderedList then
187 v
.add_raw parent
.bullet_marker
.to_s
189 else if parent
isa MdOrderedList then
190 v
.add_raw
"{parent.md_numbering}{parent.delimiter.to_s}"
194 var node
= first_child
200 while node
!= null do
203 if node
!= null and not is_tight
then
208 if next
!= null and not is_tight
then
212 if parent
isa MdUnorderedList then
214 else if parent
isa MdOrderedList then
215 parent
.md_numbering
+= 1
221 redef class MdParagraph
222 redef fun render_md
(v
) do
223 if not parent
isa MdBlockQuote and not parent
isa MdListItem or prev
!= null then
226 # if parent isa MdBlockQuote then
228 # var node = first_child
229 # while node != null do
230 # v.enter_visit(node)
231 # if node isa MdSoftLineBreak or node isa MdHardLineBreak then
244 redef class MdThematicBreak
245 redef fun render_md
(v
) do
246 v
.add_raw original_pattern
251 redef class MdHtmlBlock
252 redef fun render_md
(v
) do
253 v
.add_raw literal
or else ""
260 redef class MdHardLineBreak
261 redef fun render_md
(v
) do
262 if has_backslash
then
269 v
.add_raw
"> " * v
.in_quote
272 redef fun process_len
(v
) do
278 redef class MdSoftLineBreak
279 redef fun render_md
(v
) do
282 v
.add_raw
"> " * v
.in_quote
285 redef fun process_len
(v
) do
292 redef fun render_md
(v
) do
298 redef fun process_len
(v
) do
300 v
.length
+= delimiter
.length
304 redef class MdDelimited
305 redef fun render_md
(v
) do
311 redef fun process_len
(v
) do
313 v
.length
+= delimiter
.length
* 2
317 redef class MdHtmlInline
318 redef fun render_md
(v
) do
322 redef fun process_len
(v
) do
323 v
.length
+= literal
.length
327 redef class MdLinkOrImage
328 redef fun render_md
(v
) do
329 var title
= self.title
337 v
.add_raw destination
341 if title
!= null and not title
.is_empty
then
343 v.add_raw title.replace("\
"", "\\\"")
352 redef fun render_md
(v
) do
359 redef fun render_md
(v
) do
362 v
.add_raw destination
371 redef fun render_md
(v
) do
375 redef fun process_len
(v
) do
376 v
.length
+= literal
.length
382 redef class MdWikilink
383 redef fun render_md
(v
) do
385 var title
= self.title
386 if title
!= null then
387 v
.add_raw
"{title} | "