nitweb: use config file
[nit.git] / src / web / api_docdown.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 # Nitdoc specific Markdown format handling for Nitweb
16 module api_docdown
17
18 import api_graph
19 intrude import doc_down
20 intrude import markdown::wikilinks
21 import doc_commands
22
23 # Docdown handler accept docdown as POST data and render it as HTML
24 class APIDocdown
25 super APIHandler
26
27 # Specific Markdown processor to use within Nitweb
28 var md_processor: MarkdownProcessor is lazy do
29 var proc = new MarkdownProcessor
30 proc.emitter.decorator = new NitwebDecorator(view, config.modelbuilder)
31 return proc
32 end
33
34 redef fun post(req, res) do
35 res.html md_processor.process(req.body)
36 end
37 end
38
39 # Specific Markdown decorator for Nitweb
40 #
41 # We reuse all the implementation of the NitdocDecorator and add the wikilinks handling.
42 class NitwebDecorator
43 super NitdocDecorator
44
45 # View used by wikilink commands to find model entities
46 var view: ModelView
47
48 # Modelbuilder used to access code
49 var modelbuilder: ModelBuilder
50
51 redef fun add_wikilink(v, token) do
52 var link = token.link
53 if link == null then return
54 var cmd = new DocCommand(link.write_to_string)
55 cmd.render(v, token, view)
56 end
57 end
58
59 # Same as `InlineDecorator` but with wikilink commands handling
60 class NitwebInlineDecorator
61 super InlineDecorator
62
63 # View used by wikilink commands to find model entities
64 var view: ModelView
65
66 # Modelbuilder used to access code
67 var modelbuilder: ModelBuilder
68
69 redef fun add_wikilink(v, token) do
70 var link = token.link
71 if link == null then return
72 var cmd = new DocCommand(link.write_to_string)
73 cmd.render(v, token, view)
74 end
75 end
76
77 redef interface DocCommand
78
79 # Emit the HTML related to the execution of this doc command
80 fun render(v: MarkdownEmitter, token: TokenWikiLink, model: ModelView) do
81 write_error(v, "Not yet implemented command `{token.link or else "null"}`")
82 end
83
84 # Find the MEntity ` with `full_name`.
85 fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
86 if full_name == null then return null
87 return model.mentity_by_full_name(full_name.from_percent_encoding)
88 end
89
90 # Write a warning in the output
91 fun write_warning(v: MarkdownEmitter, text: String) do
92 v.emit_text "<p class='text-warning'>Warning: {text}</p>"
93 end
94
95 # Write an error in the output
96 fun write_error(v: MarkdownEmitter, text: String) do
97 v.emit_text "<p class='text-danger'>Error: {text}</p>"
98 end
99
100 # Write a link to a mentity in the output
101 fun write_mentity_link(v: MarkdownEmitter, mentity: MEntity) do
102 var link = mentity.web_url
103 var name = mentity.name
104 var mdoc = mentity.mdoc_or_fallback
105 var comment = null
106 if mdoc != null then comment = mdoc.synopsis
107 v.decorator.add_link(v, link, name, comment)
108 end
109 end
110
111 redef class UnknownCommand
112 redef fun render(v, token, model) do
113 var link = token.link
114 if link == null then
115 write_error(v, "Empty command")
116 return
117 end
118 var full_name = link.write_to_string
119 var mentity = find_mentity(model, full_name)
120 if mentity == null then
121 write_error(v, "Unknown command `{link}`")
122 return
123 end
124 write_mentity_link(v, mentity)
125 end
126 end
127
128 redef class ArticleCommand
129 redef fun render(v, token, model) do
130 if args.is_empty then
131 write_error(v, "Expected one arg: the MEntity name")
132 return
133 end
134 var name = args.first
135 var mentity = find_mentity(model, name)
136 if mentity == null then
137 write_error(v, "No MEntity found for name `{name}`")
138 return
139 end
140 var mdoc = mentity.mdoc_or_fallback
141 if mdoc == null then
142 write_warning(v, "No MDoc for mentity `{name}`")
143 return
144 end
145 v.add "<h3>"
146 write_mentity_link(v, mentity)
147 v.add " - "
148 v.emit_text mdoc.synopsis
149 v.add "</h3>"
150 v.add v.processor.process(mdoc.comment).write_to_string
151 end
152 end
153
154 redef class CommentCommand
155 redef fun render(v, token, model) do
156 if args.is_empty then
157 write_error(v, "Expected one arg: the MEntity name")
158 return
159 end
160 var name = args.first
161 var mentity = find_mentity(model, name)
162 if mentity == null then
163 write_error(v, "No MEntity found for name `{name}`")
164 return
165 end
166 var mdoc = mentity.mdoc_or_fallback
167 if mdoc == null then
168 write_warning(v, "No MDoc for mentity `{name}`")
169 return
170 end
171 v.add v.processor.process(mdoc.comment).write_to_string
172 end
173 end
174
175 redef class ListCommand
176 redef fun render(v, token, model) do
177 if args.is_empty then
178 write_error(v, "Expected one arg: the MEntity name")
179 return
180 end
181 var name = args.first
182 var mentity = find_mentity(model, name)
183 if mentity isa MPackage then
184 write_list(v, mentity.mgroups)
185 else if mentity isa MGroup then
186 var res = new Array[MEntity]
187 res.add_all mentity.in_nesting.smallers
188 res.add_all mentity.mmodules
189 write_list(v, res)
190 else if mentity isa MModule then
191 write_list(v, mentity.mclassdefs)
192 else if mentity isa MClass then
193 write_list(v, mentity.collect_intro_mproperties(model))
194 else if mentity isa MClassDef then
195 write_list(v, mentity.mpropdefs)
196 else if mentity isa MProperty then
197 write_list(v, mentity.mpropdefs)
198 else
199 write_error(v, "No list found for name `{name}`")
200 end
201 end
202
203 # Write a mentity list in the output
204 fun write_list(v: MarkdownEmitter, mentities: Collection[MEntity]) do
205 v.add "<ul>"
206 for mentity in mentities do
207 var mdoc = mentity.mdoc_or_fallback
208 v.add "<li>"
209 write_mentity_link(v, mentity)
210 if mdoc != null then
211 v.add " - "
212 v.emit_text mdoc.synopsis
213 end
214 v.add "</li>"
215 end
216 v.add "</ul>"
217 end
218 end
219
220 redef class CodeCommand
221 redef fun render(v, token, model) do
222 if args.is_empty then
223 write_error(v, "Expected one arg: the MEntity name")
224 return
225 end
226 var name = args.first
227 var mentity = find_mentity(model, name)
228 if mentity == null then
229 write_error(v, "No MEntity found for name `{name}`")
230 return
231 end
232 if mentity isa MClass then mentity = mentity.intro
233 if mentity isa MProperty then mentity = mentity.intro
234 var source = render_source(mentity, v.decorator.as(NitwebDecorator).modelbuilder)
235 if source == null then
236 write_error(v, "No source for MEntity `{name}`")
237 return
238 end
239 v.add "<pre>"
240 v.add source
241 v.add "</pre>"
242 end
243
244 # Highlight `mentity` source code.
245 private fun render_source(mentity: MEntity, modelbuilder: ModelBuilder): nullable HTMLTag do
246 var node = modelbuilder.mentity2node(mentity)
247 if node == null then return null
248 var hl = new HighlightVisitor
249 hl.enter_visit node
250 return hl.html
251 end
252 end
253
254 redef class GraphCommand
255 redef fun render(v, token, model) do
256 if args.is_empty then
257 write_error(v, "Expected one arg: the MEntity name")
258 return
259 end
260 var name = args.first
261 var mentity = find_mentity(model, name)
262 if mentity == null then
263 write_error(v, "No MEntity found for name `{name}`")
264 return
265 end
266 var g = new InheritanceGraph(mentity, model)
267 v.add g.draw(3, 3).to_svg
268 end
269 end