lib/markdown: merge processor and emitter
[nit.git] / src / doc / doc_down.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # Handle markdown formatting in Nit comments.
16 module doc_down
17
18 import markdown
19 import highlight
20 private import parser_util
21
22 redef class MDoc
23
24 # Synopsis HTML escaped.
25 var synopsis: String is lazy do return content.first.html_escape
26
27 # Comment without synopsis HTML escaped
28 var comment: String is lazy do
29 var lines = content.to_a
30 if not lines.is_empty then lines.shift
31 return lines.join("\n")
32 end
33
34 # Full comment HTML escaped.
35 var documentation: String is lazy do return content.join("\n")
36
37 private var markdown_proc: MarkdownProcessor is lazy, writable do
38 return original_mentity.as(not null).model.nitdoc_md_processor
39 end
40
41 private var inline_proc: MarkdownProcessor is lazy, writable do
42 return original_mentity.as(not null).model.nitdoc_inline_processor
43 end
44
45 # Renders the synopsis as a HTML comment block.
46 var html_synopsis: Writable is lazy do
47 var res = new Template
48 var syn = inline_proc.process(content.first)
49 res.add "<span class=\"synopsys nitdoc\">{syn}</span>"
50 return res
51
52 end
53
54 # Renders the comment without the synopsis as a HTML comment block.
55 var html_comment: Writable is lazy do
56 var lines = content.to_a
57 if not lines.is_empty then lines.shift
58 return lines_to_html(lines)
59 end
60
61 # Renders the synopsis and the comment as a HTML comment block.
62 var html_documentation: Writable is lazy do return lines_to_html(content.to_a)
63
64 # Renders markdown line as a HTML comment block.
65 private fun lines_to_html(lines: Array[String]): Writable do
66 var res = new Template
67 var decorator = markdown_proc.decorator.as(NitdocDecorator)
68 decorator.current_mdoc = self
69 res.add "<div class=\"nitdoc\">"
70 # do not use DocUnit as synopsys
71 if not lines.is_empty then
72 if not lines.first.has_prefix(" ") and
73 not lines.first.has_prefix("\t") then
74 # parse synopsys
75 var syn = inline_proc.process(lines.shift)
76 res.add "<h1 class=\"synopsys\">{syn}</h1>"
77 end
78 end
79 # check for annotations
80 for i in [0 .. lines.length[ do
81 var line = lines[i]
82 if line.to_upper.has_prefix("ENSURE") or line.to_upper.has_prefix("REQUIRE") then
83 var html = inline_proc.process(line)
84 lines[i] = "<p class=\"contract\">{html}</p>"
85 else if line.to_upper.has_prefix("TODO") or line.to_upper.has_prefix("FIXME") then
86 var html = inline_proc.process(line)
87 lines[i] = "<p class=\"todo\">{html}</p>"
88 end
89 end
90 # add other lines
91 res.add markdown_proc.process(lines.join("\n"))
92 res.add "</div>"
93 decorator.current_mdoc = null
94 return res
95
96 end
97 end
98
99 # The specific markdown decorator used internally to process MDoc object.
100 #
101 # You should use the various methods of `MDoc` like `MDoc::html_documentation`
102 #
103 # The class is public so specific behavior can be plugged on it.
104 class NitdocDecorator
105 super HTMLDecorator
106
107 private var toolcontext = new ToolContext
108
109 # The currently processed mdoc.
110 #
111 # Unfortunately, this seems to be the simpler way to get the currently processed `MDoc` object.
112 var current_mdoc: nullable MDoc = null
113
114 redef fun add_code(v, block) do
115 var meta = block.meta or else "nit"
116
117 # Do not try to highlight non-nit code.
118 if meta != "nit" and meta != "nitish" then
119 v.add "<pre class=\"{meta}\"><code>"
120 v.emit_in block
121 v.add "</code></pre>\n"
122 return
123 end
124 # Try to parse code
125 var code = block.raw_content
126 var ast = toolcontext.parse_something(code)
127 if ast isa AError then
128 v.add "<pre class=\"{meta}\"><code>"
129 v.emit_in block
130 v.add "</code></pre>\n"
131 return
132 end
133 v.add "<pre class=\"nitcode\"><code>"
134 var hl = new HighlightVisitor
135 hl.line_id_prefix = ""
136 hl.enter_visit(ast)
137 v.add(hl.html)
138 v.add "</code></pre>\n"
139 end
140
141 redef fun add_span_code(v, text, from, to) do
142 # Try to parse it
143 var code = code_from_text(text, from, to)
144 var ast = toolcontext.parse_something(code)
145
146 if ast isa AError then
147 v.add "<code class=\"rawcode\">"
148 append_code(v, text, from, to)
149 else
150 v.add "<code class=\"nitcode\">"
151 var hl = new HighlightVisitor
152 hl.line_id_prefix = ""
153 hl.enter_visit(ast)
154 v.add(hl.html)
155 end
156 v.add "</code>"
157 end
158
159 private fun code_from_text(buffer: Text, from, to: Int): String do
160 var out = new FlatBuffer
161 for i in [from..to[ do out.add buffer[i]
162 return out.write_to_string
163 end
164 end
165
166 # Decorator for span elements.
167 #
168 # Because inline comments can appear as span elements,
169 # InlineDecorator do not decorate things like paragraphs or headers.
170 private class InlineDecorator
171 super NitdocDecorator
172
173 redef fun add_paragraph(v, block) do
174 v.emit_in block
175 end
176
177 redef fun add_headline(v, block) do
178 v.emit_in block
179 end
180
181 redef fun add_code(v, block) do
182 # Try to parse code
183 var ast = toolcontext.parse_something(block.block.text.to_s)
184 if ast isa AError then
185 v.add "<code>"
186 v.emit_in block
187 v.add "</code>"
188 return
189 end
190 v.add "<code class=\"nitcode\">"
191 var hl = new HighlightVisitor
192 hl.enter_visit(ast)
193 v.add(hl.html)
194 v.add "</code>"
195 end
196 end
197
198 redef class Model
199 # Get a markdown processor for Nitdoc comments.
200 var nitdoc_md_processor: MarkdownProcessor is lazy, writable do
201 var proc = new MarkdownProcessor
202 proc.decorator = new NitdocDecorator
203 return proc
204 end
205
206 # Get a markdown inline processor for Nitdoc comments.
207 #
208 # This processor is specificaly designed to inlinable doc elements like synopsys.
209 var nitdoc_inline_processor: MarkdownProcessor is lazy, writable do
210 var proc = new MarkdownProcessor
211 proc.decorator = new InlineDecorator
212 return proc
213 end
214 end