Release 0.41.92
[vala-gnome.git] / libvaladoc / html / htmlrenderer.vala
blob9a8c1713e4fa153756247a47b81067638a612116
1 /* htmlrenderer.vala
3 * Copyright (C) 2008-20014 Florian Brosch, Didier Villevalois
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 * Author:
20 * Didier 'Ptitjes Villevalois <ptitjes@free.fr>
23 using GLib;
24 using Valadoc.Content;
26 public class Valadoc.Html.HtmlRenderer : ContentRenderer {
28 protected Documentation? _container;
29 protected Documentation? _owner;
30 protected unowned MarkupWriter writer;
31 protected Html.CssClassResolver cssresolver;
32 protected LinkHelper linker;
33 protected Settings settings;
35 public HtmlRenderer (Settings settings, LinkHelper linker, CssClassResolver cssresolver) {
36 this.cssresolver = cssresolver;
37 this.settings = settings;
38 this.linker = linker;
41 public void set_container (Documentation? container) {
42 _container = container;
45 public void set_owner (Documentation? owner) {
46 _owner = owner;
49 public void set_writer (MarkupWriter writer) {
50 this.writer = writer;
53 public override void render (ContentElement element) {
54 element.accept (this);
57 public override void render_children (ContentElement element) {
58 element.accept_children (this);
61 private string get_url (Documentation symbol) {
62 return linker.get_relative_link (_container, symbol, settings);
65 private void write_unresolved_symbol_link (string given_symbol_name, InlineContent? label_owner = null) {
66 if (label_owner == null || label_owner.content.size == 0) {
67 writer.start_tag ("code");
68 writer.text (given_symbol_name);
69 writer.end_tag ("code");
70 } else {
71 writer.start_tag ("i");
72 label_owner.accept_children (this);
73 writer.end_tag ("i");
77 private void write_resolved_symbol_link (Api.Node symbol, string? given_symbol_name, InlineContent? label_owner = null) {
78 var symbol_name = (given_symbol_name == null || given_symbol_name == "") ? symbol.get_full_name () : given_symbol_name;
79 string href = (symbol == _container || symbol == _owner)? null : get_url (symbol);
80 string css_class = cssresolver.resolve (symbol);
81 string end_tag_name;
84 // Start Tag:
85 if (href != null) {
86 writer.start_tag ("a", {"href", href, "class", css_class});
87 end_tag_name = "a";
88 } else {
89 writer.start_tag ("span", {"class", css_class});
90 end_tag_name = "span";
94 // Content:
95 if (label_owner != null && label_owner.content.size > 0) {
96 label_owner.accept_children (this);
97 } else {
98 writer.text (symbol_name);
102 // End Tag:
103 writer.end_tag (end_tag_name);
106 private delegate void Write ();
107 private delegate void TagletWrite (Taglet taglet);
109 private void write_taglets (Write header, Write footer, Write separator,
110 Vala.List<Taglet> taglets, TagletWrite write) {
111 if (taglets.size > 0) {
112 header ();
113 bool first = true;
114 foreach (var taglet in taglets) {
115 if (!first) {
116 separator ();
118 write (taglet);
119 first = false;
121 footer ();
125 public override void visit_comment (Comment element) {
126 Vala.List<Taglet> taglets;
128 taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.Deprecated));
129 write_taglets (
130 () => {
131 writer.start_tag ("p", {"class", "main_title"});
132 writer.start_tag ("b")
133 .text ("Deprecated: ")
134 .end_tag ("b");
136 () => {
137 writer.end_tag ("p");
139 () => {},
140 taglets,
141 (taglet) => {
142 var deprecated = taglet as Taglets.Deprecated;
143 deprecated.accept_children (this);
146 // Write description
147 element.accept_children (this);
149 taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.Param));
150 taglets.sort ((_a, _b) => {
151 Taglets.Param a = _a as Taglets.Param;
152 Taglets.Param b = _b as Taglets.Param;
154 if (a.position < 0 && b.position < 0) {
155 int cmp = a.parameter_name.ascii_casecmp (b.parameter_name);
156 if (cmp == 0) {
157 return 0;
160 if (a.parameter_name == "...") {
161 return 1;
164 if (b.parameter_name == "...") {
165 return -1;
168 return cmp;
171 if (a.position < 0) {
172 return 1;
175 if (b.position < 0) {
176 return -1;
179 return a.position - b.position;
182 write_taglets (
183 () => {
184 writer.start_tag ("h2", {"class", "main_title"})
185 .text ("Parameters:")
186 .end_tag ("h2");
187 writer.start_tag ("table", {"class", "main_parameter_table"});
189 () => {
190 writer.end_tag ("table");
192 () => {},
193 taglets,
194 (taglet) => {
195 var param = taglet as Taglets.Param;
196 string[]? unknown_parameter_css = null;
197 if (param.parameter == null && !param.is_this) {
198 unknown_parameter_css = {"class", "main_parameter_table_unknown_parameter"};
201 writer.start_tag ("tr", unknown_parameter_css);
202 writer.start_tag ("td", {"class", "main_parameter_table_name"})
203 .text (param.parameter_name)
204 .end_tag ("td");
205 writer.start_tag ("td");
206 param.accept_children (this);
207 writer.end_tag ("td");
208 writer.end_tag ("tr");
211 taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.Return));
212 write_taglets (
213 () => {
214 writer.start_tag ("h2", {"class", "main_title"})
215 .text ("Returns:")
216 .end_tag ("h2");
217 writer.start_tag ("table", {"class", "main_parameter_table"});
219 () => {
220 writer.end_tag ("table");
222 () => {},
223 taglets,
224 (taglet) => {
225 var param = taglet as Taglets.Return;
226 writer.start_tag ("tr");
227 writer.start_tag ("td");
228 param.accept_children (this);
229 writer.end_tag ("td");
230 writer.end_tag ("tr");
233 taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.Throws));
234 write_taglets (
235 () => {
236 writer.start_tag ("h2", {"class", "main_title"})
237 .text ("Exceptions:")
238 .end_tag ("h2");
239 writer.start_tag ("table", {"class", "main_parameter_table"});
241 () => {
242 writer.end_tag ("table");
244 () => {},
245 taglets,
246 (taglet) => {
247 var exception = taglet as Taglets.Throws;
248 writer.start_tag ("tr");
249 writer.start_tag ("td", {"class", "main_parameter_table_name"})
250 .text (exception.error_domain_name)
251 .end_tag ("td");
252 writer.start_tag ("td");
253 exception.accept_children (this);
254 writer.end_tag ("td");
255 writer.end_tag ("tr");
258 taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.Since));
259 write_taglets (
260 () => {
261 writer.start_tag ("h2", {"class", "main_title"})
262 .text ("Since:")
263 .end_tag ("h2");
264 writer.start_tag ("p");
266 () => {
267 writer.end_tag ("p");
269 () => {},
270 taglets,
271 (taglet) => {
272 var since = taglet as Taglets.Since;
273 writer.text (since.version);
276 taglets = element.find_taglets ((Api.Node) _container, typeof (Taglets.See));
277 write_taglets (
278 () => {
279 writer.start_tag ("h2", {"class", "main_title"})
280 .text ("See also:")
281 .end_tag ("h2");
282 writer.start_tag ("p");
284 () => {
285 writer.end_tag ("p");
287 () => {
288 writer.text (", ");
290 taglets,
291 (taglet) => {
292 var see = taglet as Taglets.See;
293 if (see.symbol == null) {
294 write_unresolved_symbol_link (see.symbol_name);
295 } else {
296 write_resolved_symbol_link (see.symbol, see.symbol_name);
301 public override void visit_embedded (Embedded element) {
302 var caption = element.caption;
304 var absolute_path = Path.build_filename (settings.path, element.package.name, "img",
305 Path.get_basename (element.url));
306 var relative_path = Path.build_filename ("img", Path.get_basename (element.url));
308 copy_file (element.url, absolute_path);
310 writer.image (relative_path, (caption == null || caption == "") ? "" : caption);
313 public override void visit_headline (Headline element) {
314 writer.start_tag ("h%d".printf (element.level));
315 element.accept_children (this);
316 writer.end_tag ("h%d".printf (element.level));
319 public override void visit_wiki_link (WikiLink element) {
320 if (element.page != null) {
321 writer.start_tag ("a", {"href", get_url (element.page)});
324 if (element.content.size > 0) {
325 element.accept_children (this);
326 } else {
327 writer.text (element.name.substring (0, element.name.last_index_of_char ('.')));
330 if (element.page != null) {
331 writer.end_tag ("a");
335 public override void visit_link (Link element) {
336 if (Uri.parse_scheme (element.url) != null) {
337 writer.start_tag ("a", {"href", element.url, "target", "_blank"});
338 } else {
339 writer.start_tag ("a", {"href", element.url});
342 if (element.content.size > 0) {
343 element.accept_children (this);
344 } else {
345 writer.text (element.url);
348 writer.end_tag ("a");
351 public override void visit_symbol_link (SymbolLink element) {
352 if (element.symbol == null) {
353 write_unresolved_symbol_link (element.given_symbol_name, element);
354 } else {
355 write_resolved_symbol_link (element.symbol, element.given_symbol_name, element);
359 public override void visit_list (Content.List element) {
360 string list_type = null;
361 string bullet_type = null;
362 string css_class = null;
363 switch (element.bullet) {
364 case Content.List.Bullet.NONE:
365 list_type = "ul";
366 css_class = "no_bullet";
367 break;
368 case Content.List.Bullet.UNORDERED:
369 list_type = "ul";
370 break;
371 case Content.List.Bullet.ORDERED:
372 list_type = "ol";
373 break;
374 case Content.List.Bullet.ORDERED_NUMBER:
375 list_type = "ol";
376 bullet_type = "1";
377 break;
378 case Content.List.Bullet.ORDERED_LOWER_CASE_ALPHA:
379 list_type = "ol";
380 bullet_type = "a";
381 break;
382 case Content.List.Bullet.ORDERED_UPPER_CASE_ALPHA:
383 list_type = "ol";
384 bullet_type = "A";
385 break;
386 case Content.List.Bullet.ORDERED_LOWER_CASE_ROMAN:
387 list_type = "ol";
388 bullet_type = "i";
389 break;
390 case Content.List.Bullet.ORDERED_UPPER_CASE_ROMAN:
391 list_type = "ol";
392 bullet_type = "I";
393 break;
395 writer.start_tag (list_type, {"class", css_class, "type", bullet_type});
396 element.accept_children (this);
397 writer.end_tag (list_type);
400 public override void visit_list_item (ListItem element) {
401 writer.start_tag ("li");
402 Paragraph? first_para = (element.content.size > 0)? element.content[0] as Paragraph : null;
403 if (first_para != null) {
404 // We do not pick up alignments in gir-files.
405 first_para.accept_children (this);
406 bool first_entry = true;
407 foreach (var item in element.content) {
408 if (!first_entry) {
409 item.accept (this);
411 first_entry = false;
413 } else {
414 element.accept_children (this);
416 writer.end_tag ("li");
419 public override void visit_page (Page element) {
420 element.accept_children (this);
423 public override void visit_paragraph (Paragraph element) {
424 //FIXME: the extra-field is just a workarround for the current codegen ...
425 switch (element.horizontal_align) {
426 case HorizontalAlign.CENTER:
427 writer.start_tag ("p", {"style", "text-align: center;"});
428 break;
429 case HorizontalAlign.RIGHT:
430 writer.start_tag ("p", {"style", "text-align: right;"});
431 break;
432 default:
433 writer.start_tag ("p");
434 break;
436 element.accept_children (this);
437 writer.end_tag ("p");
440 private void visit_notification_block (BlockContent element, string headline) {
441 writer.start_tag ("div", {"class", "main_notification_block"});
442 writer.start_tag ("span", {"class", "main_block_headline"})
443 .text (headline)
444 .end_tag ("span")
445 .text (" ");
446 writer.start_tag ("div", {"class", "main_block_content"});
447 element.accept_children (this);
448 writer.end_tag ("div");
449 writer.end_tag ("div");
452 public override void visit_warning (Warning element) {
453 visit_notification_block (element, "Warning:");
456 public override void visit_note (Note element) {
457 visit_notification_block (element, "Note:");
460 public override void visit_run (Run element) {
461 string tag = null;
462 string css_type = null;
463 switch (element.style) {
464 case Run.Style.BOLD:
465 tag = "b";
466 break;
467 case Run.Style.ITALIC:
468 tag = "i";
469 break;
470 case Run.Style.UNDERLINED:
471 tag = "u";
472 break;
473 case Run.Style.MONOSPACED:
474 tag = "code";
475 break;
476 case Run.Style.STROKE:
477 tag = "stroke";
478 break;
479 case Run.Style.LANG_KEYWORD:
480 tag = "span";
481 css_type = "main_keyword";
482 break;
483 case Run.Style.LANG_ESCAPE:
484 tag = "span";
485 css_type = "main_escape";
486 break;
487 case Run.Style.LANG_LITERAL:
488 tag = "span";
489 css_type = "main_literal";
490 break;
491 case Run.Style.LANG_BASIC_TYPE:
492 tag = "span";
493 css_type = "main_basic_type";
494 break;
495 case Run.Style.LANG_TYPE:
496 tag = "span";
497 css_type = "main_type";
498 break;
499 case Run.Style.LANG_COMMENT:
500 tag = "span";
501 css_type = "main_comment";
502 break;
503 case Run.Style.LANG_PREPROCESSOR:
504 tag = "span";
505 css_type = "main_preprocessor";
506 break;
508 case Run.Style.XML_ESCAPE:
509 tag = "span";
510 css_type = "xml_escape";
511 break;
513 case Run.Style.XML_ELEMENT:
514 tag = "span";
515 css_type = "xml_element";
516 break;
518 case Run.Style.XML_ATTRIBUTE:
519 tag = "span";
520 css_type = "xml_attribute";
521 break;
523 case Run.Style.XML_ATTRIBUTE_VALUE:
524 tag = "span";
525 css_type = "xml_attribute_value";
526 break;
528 case Run.Style.XML_COMMENT:
529 tag = "span";
530 css_type = "xml_comment";
531 break;
533 case Run.Style.XML_CDATA:
534 tag = "span";
535 css_type = "xml_cdata";
536 break;
538 if (tag != null) {
539 writer.start_tag (tag, {"class", css_type});
541 element.accept_children (this);
542 if (tag != null) {
543 writer.end_tag (tag);
547 public override void visit_source_code (SourceCode element) {
548 writer.set_wrap (false);
549 writer.start_tag ("pre", {"class", "main_source"});
550 element.accept_children (this);
551 writer.end_tag ("pre");
552 writer.set_wrap (true);
555 public override void visit_table (Table element) {
556 writer.start_tag ("table", {"class", "main_table"});
557 element.accept_children (this);
558 writer.end_tag ("table");
561 public override void visit_table_cell (TableCell element) {
562 string style = "";
564 if (element.horizontal_align != HorizontalAlign.NONE) {
565 style += "text-align: "+element.horizontal_align.to_string ()+"; ";
568 if (element.vertical_align != VerticalAlign.NONE) {
569 style += "vertical-align: "+element.vertical_align.to_string ()+"; ";
572 writer.start_tag ("td", {"class", "main_table",
573 "colspan", element.colspan.to_string (),
574 "rowspan", element.rowspan.to_string (),
575 "style", style});
576 element.accept_children (this);
577 writer.end_tag ("td");
580 public override void visit_table_row (TableRow element) {
581 writer.start_tag ("tr");
582 element.accept_children (this);
583 writer.end_tag ("tr");
586 public override void visit_taglet (Taglet element) {
589 public override void visit_text (Text element) {
590 write_string (element.content);
593 private void write_string (string content) {
594 unichar chr = content[0];
595 long lpos = 0;
596 int i = 0;
598 for (i = 0; chr != '\0' ; i++, chr = content[i]) {
599 switch (chr) {
600 case '\n':
601 writer.text (content.substring (lpos, i-lpos));
602 writer.simple_tag ("br");
603 lpos = i+1;
604 break;
605 case '<':
606 writer.text (content.substring (lpos, i-lpos));
607 writer.text ("&lt;");
608 lpos = i+1;
609 break;
610 case '>':
611 writer.text (content.substring (lpos, i-lpos));
612 writer.text ("&gt;");
613 lpos = i+1;
614 break;
615 case '&':
616 writer.text (content.substring (lpos, i-lpos));
617 writer.text ("&amp;");
618 lpos = i+1;
619 break;
622 writer.text (content.substring (lpos, i-lpos));