From 3756b83b8d5099ab2bd78db010d25320358c7fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Grubbstr=C3=B6m=20=28Grubba=29?= <grubba@grubba.org> Date: Wed, 16 Nov 2011 22:11:50 +0100 Subject: [PATCH] Refdoc: Moved make_html and tree-split-autodoc to Tools.Standalone. --- .../Standalone.pmod/autodoc_to_html.pike | 1442 +++++++++++++++++ .../autodoc_to_split_html.pike | 775 +++++++++ refdoc/presentation/make_html.pike | 1376 +--------------- refdoc/presentation/tree-split-autodoc.pike | 729 +-------- 4 files changed, 2223 insertions(+), 2099 deletions(-) create mode 100644 lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_html.pike create mode 100644 lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_split_html.pike diff --git a/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_html.pike b/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_html.pike new file mode 100644 index 0000000000..db295dbd68 --- /dev/null +++ b/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_html.pike @@ -0,0 +1,1442 @@ +//! AutoDoc XML to HTML converter. +//! +//! @seealso +//! @[tree_split] + +constant description = "AutoDoc XML to HTML converter."; + +#define Node Parser.XML.Tree.SimpleNode +#define XML_COMMENT Parser.XML.Tree.XML_COMMENT +#define XML_ELEMENT Parser.XML.Tree.XML_ELEMENT +#define XML_TEXT Parser.XML.Tree.XML_TEXT +#define DEBUG + +Tools.AutoDoc.Flags flags = Tools.AutoDoc.FLAG_NORMAL; + +int verbosity = Tools.AutoDoc.FLAG_VERBOSE; //NORMAL; + +function resolve_reference; +string image_path = "images/"; +string dest_path; +string default_ns; + +//! Layout template headers and trailers. +mapping lay = ([ + "docgroup" : "\n\n<hr clear='all' size='1' noshadow='noshadow' />\n<dl>", + "_docgroup" : "</dl>\n", + "ndochead" : "<dd><p>", + "_ndochead" : "</p></dd>\n", + + "dochead" : "\n<dt style='font-family: sans-serif'>", + "_dochead" : "</dt>\n", + "typehead" : "\n<dt style='font-family: sans-serif'>", + "_typehead" : "</dt>\n", + "docbody" : "<dd style='font-family: sans-serif'>", + "_docbody" : "</dd>", + "fixmehead" : "<dt style='font-family: sans-serif; color: red'>", + "_fixmehead" : "</dt>\n", + "fixmebody" : "<dd style='font-family: sans-serif; color: red'>", + "_fixmebody" : "</dd>", + + "parameter" : "<tt><font color='#8000F0'>", + "_parameter" : "</font></tt>", + "example" : "<dd><pre>", + "_example" : "</pre></dd>", + + "pre" : "<font face='courier'><pre>", + "_pre" : "</pre></font>", + "code" : "<font face='courier'><pre><code>", + "_code" : "</code></pre></font>", + "expr" : "<font face='courier'><code>", + "_expr" : "</code></font>", + +]); + +//! Path to where in the destination filesystem the images are. +string image_prefix() +{ + return image_path; +} + +class Position { + string file; + int line; + + void update(Node n) { + mapping m = n->get_attributes(); + if(!m->file || !m["first-line"]) { + werror("Missing attribute in source-position element. %O\n", m); + return; + } + file = m->file; + line = (int) m["first-line"]; + } + + string get() { + return file + ":" + line; + } +} + +Position position = Position(); + +string quote(string in) { + if(in-" "-"\t"=="") return ""; + if(String.trim_all_whites(in)=="") return "\n"; + return Parser.XML.Tree.text_quote(in); +} + +Node get_first_element(Node n) { + foreach(n->get_children(), Node c) + if(c->get_node_type()==XML_ELEMENT) { + if(c->get_any_name()!="source-position") + return c; + else + position->update(c); + } + error( "Node had no element child.\n" ); +} + +int section, subsection; + +string low_parse_chapter(Node n, int chapter) { + string ret = ""; + Node dummy = Node(XML_ELEMENT, "dummy", ([]), ""); + foreach(n->get_elements(), Node c) + switch(c->get_any_name()) { + + case "p": + dummy->replace_children( ({ c }) ); + ret += parse_text(dummy); + break; + + case "example": + ret += "<p><pre>" + c->value_of_node() + "</pre></p>\n"; + break; + + case "ul": + case "ol": + ret += (string)c; + break; + + case "dl": + ret += "<dl>\n"; + foreach(c->get_elements(), Node cc) { + string tag = cc->get_any_name(); + if(tag!="dt" && tag!="dd") + error("dl contained element %O.\n", tag); + ret += "<" + tag + ">" + parse_text(cc) + "</" + tag + ">\n"; + } + ret += "</dl>"; + break; + + case "matrix": + ret += layout_matrix( map(c->get_elements("r")->get_elements("c"), map, parse_text) ); + break; + + case "section": + if(section) + error("Section inside section.\n"); + if(subsection) + error("Section inside subsection.\n"); + section = (int)c->get_attributes()->number; + ret += "</dd>\n<dt><a name='" + section + "'></a>\n" + "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" + "<td bgcolor='#EEEEEE'><font size='+3'> " + chapter + "." + section + + ". " + quote(c->get_attributes()->title || + // The following for bug compat. + c->get_attributes()->name) + + "</font></td></tr></table><br />\n" + "</dt>\n<dd>"; + ret += low_parse_chapter(c, chapter); + section = 0; + break; + + case "subsection": + if(!section) + error("Subsection outside section.\n"); + if(subsection) + error("Subsection inside subsection.\n"); + subsection = (int)c->get_attributes()->number; + ret += "</dd><dt>" + "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" + "<td bgcolor='#EEEEEE'><font size='+3'> " + chapter + "." + section + "." + subsection + + ". " + quote(c->get_attributes()->title) + + "</font></td></tr></table><br />\n" + "</dt><dd>"; + ret += low_parse_chapter(c, chapter); + subsection = 0; + break; + + case "docgroup": + ret += parse_docgroup(c); + break; + + case "autodoc": + ret += parse_autodoc(c); + break; + + case "namespace": + ret += parse_namespace(c); + break; + + case "module": + ret += parse_module(c); + break; + + case "class": + ret += parse_class(c); + break; + + case "enum": + ret += parse_enum(c); + break; + + default: + if (!(flags & Tools.AutoDoc.FLAG_KEEP_GOING)) { + error("Unknown element %O.\n", c->get_any_name()); + } + break; + } + return ret; +} + +string parse_chapter(Node n, void|int noheader) { + string ret = ""; + if(!noheader) { + ret += "<dl><dt>" + "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" + "<td bgcolor='#EEEEEE'><font size='+3'> "; + if(n->get_attributes()->number) + ret += n->get_attributes()->number + ". "; + ret += quote(n->get_attributes()->title) + + "</font></td></tr></table><br />\n" + "</dt><dd>"; + } + + ret += low_parse_chapter(n, (int)n->get_attributes()->number); + + if(!noheader) + ret = ret + "</dd></dl>"; + + return ret; +} + +string parse_autodoc(Node n) +{ + string ret =""; + + mapping m = n->get_attributes(); + + Node c = n->get_first_element("doc"); + if(c) + ret += "<dl>" + parse_doc(c) + "</dl>"; + + if((sizeof(n->get_elements("doc"))>1) && + ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) == + Tools.AutoDoc.FLAG_DEBUG)) { + error( "More than one doc element in autodoc node.\n" ); + } + + ret += parse_children(n, "docgroup", parse_docgroup); + ret += parse_children(n, "namespace", parse_namespace); + + return ret; +} + +string parse_namespace(Node n, void|int noheader) +{ + string ret = ""; + + mapping m = n->get_attributes(); + int(0..1) header = !noheader && !(m->hidden) && m->name!=default_ns; + if(header) + ret += "<dl><dt>" + "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" + "<td bgcolor='#EEEEEE'><font size='+3'> Namespace <b>" + + m->name + "::</b></font></td></tr></table><br />\n" + "</dt><dd>"; + + Node c = n->get_first_element("doc"); + if(c) + ret += "<dl>" + parse_doc(c) + "</dl>"; + + if((sizeof(n->get_elements("doc"))>1) && + ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) == + Tools.AutoDoc.FLAG_DEBUG)) { + error( "More than one doc element in namespace node.\n" ); + } + + ret += parse_children(n, "docgroup", parse_docgroup, noheader); + ret += parse_children(n, "enum", parse_enum, noheader); + ret += parse_children(n, "class", parse_class, noheader); + ret += parse_children(n, "module", parse_module, noheader); + + if(header) + ret += "</dd></dl>"; + + return ret; +} + +string parse_module(Node n, void|int noheader) { + string ret =""; + + mapping m = n->get_attributes(); + int(0..1) header = !noheader && !(m->hidden); + if(header) + ret += "<dl><dt>" + "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" + "<td bgcolor='#EEEEEE'><font size='+3'> Module <b>" + + m->class_path + m->name + "</b></font></td></tr></table><br />\n" + "</dt><dd>"; + + Node c = n->get_first_element("doc"); + if(c) + ret += "<dl>" + parse_doc(c) + "</dl>"; + + if((sizeof(n->get_elements("doc"))>1) && + ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) == + Tools.AutoDoc.FLAG_DEBUG)) { + error( "More than one doc element in module node.\n" ); + } + + ret += parse_children(n, "docgroup", parse_docgroup, noheader); + ret += parse_children(n, "enum", parse_enum, noheader); + ret += parse_children(n, "class", parse_class, noheader); + ret += parse_children(n, "module", parse_module, noheader); + + if(header) + ret += "</dd></dl>"; + + return ret; +} + +string parse_class(Node n, void|int noheader) { + string ret =""; + if(!noheader) + ret += "<dl><dt>" + "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" + "<td bgcolor='#EEEEEE'><font size='+3'> CLASS <b><font color='#005080'>" + + n->get_attributes()->class_path + n->get_attributes()->name + + "</font></b></font></td></tr></table><br />\n" + "</dt><dd>"; + + Node c = n->get_first_element("doc"); + + if(c) + ret += "<dl>" + parse_doc(c) + "</dl>"; + + if((sizeof(n->get_elements("doc"))>1) && + ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) == + Tools.AutoDoc.FLAG_DEBUG)) { + error( "More than one doc element in class node.\n" ); + } + + ret += parse_children(n, "docgroup", parse_docgroup); + ret += parse_children(n, "enum", parse_enum); + ret += parse_children(n, "class", parse_class, noheader); + + if(!noheader) + ret += "</dd></dl>"; + return ret; +} + +string parse_enum(Node n, void|int noheader) { + string ret =""; + if(!noheader) + ret += "<dl><dt>" + "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" + "<td bgcolor='#EEEEEE'><font size='+3'> ENUM <b><font color='#005080'>" + + n->get_attributes()->class_path + n->get_attributes()->name + + "</font></b></font></td></tr></table><br />\n" + "</dt><dd>"; + + Node c = n->get_first_element("doc"); + + if(c) + ret += "<dl>" + parse_doc(c) + "</dl>"; + + if((sizeof(n->get_elements("doc"))>1) && + ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) == + Tools.AutoDoc.FLAG_DEBUG)) { + error( "More than one doc element in enum node.\n" ); + } + + ret += parse_children(n, "docgroup", parse_docgroup); + + if(!noheader) + ret += "</dd></dl>"; + return ret; +} + +string layout_matrix( array(array(string)) rows ) { + string ret = "<table bgcolor='black' border='0' cellspacing='0' cellpadding='0'><tr><td>\n" + "<table cellspacing='1' cellpadding='3' border='0' bgcolor='black'>\n"; + + int dim; + foreach(rows, array row) + dim = max(dim, sizeof(row)); + + foreach(rows, array row) { + ret += "<tr valign='top'>"; + if(sizeof(row)<dim) + ret += "<td bgcolor='white'>" + row[..sizeof(row)-2]*"</td><td bgcolor='white'>" + + "</td><td bgcolor='white' colspan='"+ (dim-sizeof(row)) + "'>" + row[-1] + "</td>"; + else + ret += "<td bgcolor='white'>" + row*"</td><td bgcolor='white'>" + "</td>"; + ret += "</tr>\n"; + } + + return ret + "</table></td></tr></table><br />\n"; +} + +// ({ ({ array(string)+, void|string })* }) +void nicebox(array rows, String.Buffer ret) { + ret->add( "<table bgcolor='black' border='0' cellspacing='0' cellpadding='0'><tr><td>\n" + "<table cellspacing='1' cellpadding='3' border='0' bgcolor='black'>\n" ); + + int dim; + foreach(rows, array row) + dim = max(dim, sizeof(row)); + + foreach(rows, array row) { + if(sizeof(row)==1) { + if(stringp(row[0])) + ret->add( "<tr valign='top'><td bgcolor='white' colspan='", (string)dim, "'>", + row[0], "</td></tr>\n" ); + else + foreach(row[0], string elem) + ret->add( "<tr valign='top'><td bgcolor='white'><tt>", elem, "</tt></td>", + (dim==2?"<td bgcolor='white'> </td>":""), "</tr>\n" ); + } + else if(sizeof(row[0])==1) + ret->add( "<tr valign='top'><td bgcolor='white'><tt>", row[0][0], + "</tt></td><td bgcolor='white'>", row[1], "</td></tr>\n" ); + else { + ret->add( "<tr valign='top'><td bgcolor='white'><tt>", row[0][0], + "</tt></td><td bgcolor='white' rowspan='", (string)sizeof(row[0]), "'>", + row[1], "</td></tr>\n" ); + foreach(row[0][1..], string elem) + ret->add( "<tr valign='top'><td bgcolor='white'><tt>", elem, "</tt></td></tr>\n" ); + } + } + + ret->add( "</table></td></tr></table><br />\n" ); +} + +void build_box(Node n, String.Buffer ret, string first, string second, function layout, void|string header) { + array rows = ({}); + if(header) rows += ({ ({ header }) }); + foreach(n->get_elements(first), Node d) { + array elems = ({}); + foreach(d->get_elements(second), Node e) + elems += ({ layout(e) }); + if(d->get_first_element("text")) + rows += ({ ({ elems, parse_text(d->get_first_element("text")) }) }); + else + rows += ({ ({ elems }) }); + } + nicebox(rows, ret); +} + +string parse_text(Node n, void|String.Buffer ret) { + if(n->get_node_type()==XML_TEXT && n->get_text()) { + if(ret) + ret->add(quote(n->get_test())); + else + return quote(n->get_text()); + } + + int cast; + if(!ret) { + ret = String.Buffer(); + cast = 1; + } + + foreach(n->get_children(), Node c) { + int node_type = c->get_node_type(); + if(c->get_node_type()==XML_TEXT) { + // Don't use quote() here since we don't want to strip whitespace. + ret->add(Parser.XML.Tree.text_quote (c->get_text())); + continue; + } + + if(c->get_node_type()==XML_COMMENT) + continue; + + if((c->get_node_type()!=XML_ELEMENT) && + ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) == + Tools.AutoDoc.FLAG_DEBUG)) { + error( "Forbidden node type " + c->get_node_type() + " in doc node.\n" ); + } + + array rows; + string name = c->get_any_name(); + switch(name) { + case "text": + ret->add("<dd>"); + parse_text(c, ret); + ret->add("</dd>\n"); + break; + + case "p": + case "b": + case "i": + case "tt": + case "sub": + case "sup": + ret->add("<", name, ">"); + parse_text(c, ret); + ret->add("</", name, ">"); + break; + + case "pre": + ret->add(lay->pre); + parse_text(c, ret); + ret->add(lay->_pre); + break; + + case "code": + ret->add(lay->code); + parse_text(c, ret); + ret->add(lay->_code); + break; + + case "expr": + ret->add(lay->expr, replace(parse_text(c), " ", " "), lay->_expr); + break; + + case "ref": + if(resolve_reference) { + ret->add(resolve_reference(parse_text(c), c->get_attributes())); + break; + } + string ref; + //ref = c->get_attributes()->resolved; + if(!ref) ref = parse_text(c); + ret->add("<font face='courier'>", ref, "</font>"); + break; + + case "dl": + ret->add("<dl>", map(c->get_elements("group"), parse_text)*"", "</dl>"); + break; + + case "item": + if(c->get_attributes()->name) + ret->add("<dt>", c->get_attributes()->name, "</dt>\n"); + if(c->count_children() && + ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) == + Tools.AutoDoc.FLAG_DEBUG)) { + error( "dl item has a child.\n" ); + } + break; + + case "mapping": + build_box(c, ret, "group", "member", + lambda(Node n) { + return "<font color='green'>" + + parse_text(n->get_first_element("index")) + + "</font> : " + + parse_type(get_first_element(n->get_first_element("type"))); + }); + break; + + case "array": + build_box(c, ret, "group", "elem", + lambda(Node n) { + string index =""; + if(n->get_first_element("index")) + index = parse_text(n->get_first_element("index")); + else { + if(n->get_first_element("minindex")) + index = parse_text(n->get_first_element("minindex")); + index += ".."; + if(n->get_first_element("maxindex")) + index += parse_text(n->get_first_element("maxindex")); + } + return parse_type(get_first_element(n->get_first_element("type"))) + + " <font color='green'>" + index + "</font>"; }, "Array" ); + break; + + case "int": + build_box(c, ret, "group", "value", + lambda(Node n) { + string tmp = "<font color='green'>"; + Node min = n->get_first_element("minvalue"); + Node max = n->get_first_element("maxvalue"); + if(min || max) { + if(min) tmp += parse_text(min); + tmp += ".."; + if(max) tmp += parse_text(max); + } + else + tmp += parse_text(n); + return tmp + "</font>"; + } ); + break; + + case "mixed": + if(c->get_attributes()->name) + ret->add("<tt>", c->get_attributes()->name, "</tt> can have any of the following types:<br />"); + rows = ({}); + foreach(c->get_elements("group"), Node d) + rows += ({ ({ + ({ parse_type(get_first_element(d->get_first_element("type"))) }), + parse_text(d->get_first_element("text")) + }) }); + nicebox(rows, ret); + break; + + case "string": // Not in XSLT + build_box(c, ret, "group", "value", + lambda(Node n) { + return "<font color='green'>" + parse_text(n) + "</font>"; + } ); + break; + + case "multiset": // Not in XSLT + build_box(c, ret, "group", "index", + lambda(Node n) { + return "<font color='green'>" + + parse_text(n->get_first_element("value")) + "</font>"; + } ); + break; + + case "image": // Not in XSLT + mapping m = c->get_attributes(); + m->src = image_prefix() + m_delete(m, "file"); + ret->add( sprintf("<img%{ %s='%s'%} />", (array)m) ); + break; + + case "url": // Not in XSLT + if((c->count_children()!=1 && c->get_node_type()!=XML_ELEMENT) && + ((flags & (Tools.AutoDoc.FLAG_KEEP_GOING|Tools.AutoDoc.FLAG_DEBUG)) == + Tools.AutoDoc.FLAG_DEBUG)) { + error( "url node has not one child. %O\n", c->html_of_node() ); + } + m = c->get_attributes(); + if(!m->href) + m->href=c->value_of_node(); + ret->add( sprintf("<a%{ %s='%s'%}>%s</a>", + (array)m, c->value_of_node()) ); + break; + + case "section": + if(section && !(flags & Tools.AutoDoc.FLAG_KEEP_GOING)) + error("Section inside section.\n"); + ret->add ("<h2>", quote (c->get_attributes()->title || + // The following for bug compat. + c->get_attributes()->name), + "</h2>\n"); + if (!equal (c->get_children()->get_any_name(), ({"text"})) && + !(flags & Tools.AutoDoc.FLAG_KEEP_GOING)) + error ("Expected a single <text> element inside <section>.\n"); + section = -1; + parse_text (c->get_children()[0], ret); + section = 0; + break; + + case "ul": + ret->add( "<ul>\n" ); + foreach(c->get_elements("group"), Node c) { + ret->add("<li>"); + array(Node) d = c->get_elements("item"); + if(sizeof(d->get_attributes()->name-({0}))) { + ret->add("<b>"); + ret->add( String.implode_nicely(d->get_attributes()->name-({0})) ); + ret->add("</b>"); + } + Node e = c->get_first_element("text"); + if(e) + parse_text(e, ret); + ret->add("</li>"); + } + ret->add("</ul>"); + break; + + case "ol": + ret->add("<ol>\n"); + foreach(c->get_elements("group"), Node c) { + ret->add("<li>"); + parse_text(c->get_first_element("text"), ret); + ret->add("</li>"); + } + ret->add("</ol>"); + break; + + case "source-position": + position->update(c); + break; + + case "matrix": + ret->add( layout_matrix( map(c->get_elements("r")->get_elements("c"), + map, parse_text) ) ); + break; + + case "fixme": + ret->add("<font color='red'>FIXME: "); + parse_text(c, ret); + ret->add("</font>"); + break; + + // Not really allowed + case "br": + ret->add( sprintf("<%s%{ %s='%s'%} />", c->get_any_name(), (array)c->get_attributes()) ); + break; + case "table": + case "td": + case "tr": + case "th": + ret->add( sprintf("<%s%{ %s='%s'%}>%s</%s>", + c->get_any_name(), (array)c->get_attributes(), + parse_text(c), c->get_any_name()) ); + break; + + default: + if (verbosity >= Tools.AutoDoc.FLAG_DEBUG) { + werror("\n%s\n", (string)c); + error( "Illegal element \"" + name + "\" in mode text.\n" ); + } + break; + } + } + + if(cast) + return ret->get(); +} + +string parse_doc(Node n, void|int no_text) { + string ret =""; + + Node c = n->get_first_element("text"); + if(c) + ret += lay->dochead + "Description" + lay->_dochead + + lay->docbody + parse_text(c) + lay->_docbody; + + foreach(n->get_elements("group"), Node c) { + Node header = c->get_first_element(); + string name = header->get_any_name(); + switch(name) { + case "param": + foreach(c->get_elements("param"), Node d) + ret += lay->dochead + "Parameter " + lay->parameter + + quote(d->get_attributes()->name) + lay->_parameter + lay->_dochead + + "<dd></dd>"; + if (c = c->get_first_element("text")) { + ret += lay->docbody + parse_text(c) + lay->_docbody; + } + break; + + case "seealso": + ret += lay->dochead + "See also" + lay->_dochead; + if (c = c->get_first_element("text")) { + ret += lay->docbody + parse_text(c) + lay->_docbody; + } + break; + + case "fixme": + ret += lay->fixmehead + "FIXME" + lay->_fixmehead; + if (c = c->get_first_element("text")) { + ret += lay->fixmebody + parse_text(c) + lay->_fixmebody; + } + break; + + case "deprecated": + ret += lay->dochead + String.capitalize(name) + lay->_dochead; + array(Node) replacements = header->get_elements("name"); + if (sizeof(replacements)) { + ret += lay->docbody + + "<p>Replaced by " + + String.implode_nicely(map(replacements, parse_text)) + ".</p>\n" + + lay->_docbody; + } + if (c = c->get_first_element("text")) { + ret += lay->docbody + parse_text(c) + lay->_docbody; + } + break; + + case "bugs": + case "note": + case "returns": + case "throws": + ret += lay->dochead + String.capitalize(name) + lay->_dochead; + if (c = c->get_first_element("text")) { + ret += lay->docbody + parse_text(c) + lay->_docbody; + } + break; + + case "example": + ret += lay->dochead + "Example" + lay->_dochead; + if (c = c->get_first_element("text")) { + ret += lay->example + parse_text(c) + lay->_example; + } + break; + + default: + error( "Unknown group type \"" + name + "\".\n" ); + } + } + + return ret; +} + +string parse_type(Node n, void|string debug) { + if(n->get_node_type()!=XML_ELEMENT) + return ""; + + string ret = ""; + Node c, d; + switch(n->get_any_name()) { + + case "object": + if(n->count_children()) { + if (resolve_reference) { + ret += "<font color='#005080'>" + + resolve_reference(n->value_of_node(), n->get_attributes()) + + "</font>"; + } else { + ret += "<font color='#005080'>" + n->value_of_node() + "</font>"; + } + } else + ret += "<font color='#202020'>object</font>"; + break; + + case "type": + ret += "<font color='#202020'>type</font>"; + if (n->count_children() && (c = get_first_element(n)) && + (c->get_any_name() != "mixed")) { + ret += "(" + parse_type(c) + ")"; + } + break; + + case "multiset": + ret += "<font color='#202020'>multiset</font>"; + c = n->get_first_element("indextype"); + if(c) ret += "(" + parse_type( get_first_element(c) ) + ")"; + break; + + case "array": + ret += "<font color='#202020'>array</font>"; + c = n->get_first_element("valuetype"); + if(c) ret += "(" + parse_type( get_first_element(c) ) + ")"; + break; + + case "mapping": + ret += "<font color='#202020'>mapping</font>"; + c = n->get_first_element("indextype"); + d = n->get_first_element("valuetype"); + if(c && d) + ret += "(" + parse_type( get_first_element(c) ) + ":" + + parse_type( get_first_element(d) ) + ")"; +#ifdef DEBUG + if( !c != !d ) + error( "Indextype/valuetype defined while the other is not in mapping.\n" ); +#endif + break; + + case "function": + ret += "<font color='#202020'>function</font>"; + array(Node) args = n->get_elements("argtype"); + d = n->get_first_element("returntype"); + // Doing different than the XSL here. Must both + // argtype and returntype be defined? + if(args || d) { + ret += "("; + if(args) ret += map(args->get_children() * ({}), parse_type)*", "; + ret += ":"; + if(d) ret += parse_type( get_first_element(d) ); + else ret += "<font color='#202020'>void</font>"; + ret += ")"; + } + break; + + case "varargs": +#ifdef DEBUG + if(!n->count_children()) + error( "varargs node must have child node.\n" ); +#endif + ret += parse_type(get_first_element(n)) + " ... "; + break; + + case "or": + ret += map(filter(n->get_children(), + lambda(Node n){ + return n->get_node_type()==XML_ELEMENT; + }), parse_type)*"|"; + break; + + case "string": case "void": case "program": case "mixed": case "float": + case "zero": + ret += "<font color='#202020'>" + n->get_any_name() + "</font>"; + break; + + case "int": + ret += "<font color='#202020'>int</font>"; + c = n->get_first_element("min"); + d = n->get_first_element("max"); + if(c && d) + ret += "(" + c->value_of_node() + ".." + d->value_of_node() + ")"; + else if(c) + ret += "(" + c->value_of_node() + "..)"; + else if(d) + ret += "(.." + d->value_of_node() + ")"; + break; + + case "attribute": + string attr = n->get_first_element("attribute")->value_of_node(); + string subtype = + parse_type(n->get_first_element("subtype")->get_first_element()); + if (n->get_first_element("prefix")) { + if (attr == "\"deprecated\"") { + ret += "<font color='#600000'>__deprecated__</font> " + subtype; + } else { + ret += sprintf("__attribute__(%s) %s", attr, subtype); + } + } else if (attr == "\"deprecated\"") { + ret += "<font color='#600000'>__deprecated__</font>(" + subtype + ")"; + } else { + ret += sprintf("__attribute__(%s, %s)", attr, subtype); + } + break; + + // Modifiers: + case "extern": // Not in XSLT + ret += "extern "; + break; + case "final": // Not in XSLT + case "nomask": // Not in XSLT + ret += "final "; + break; + case "inline": // Not in XSLT + case "local": // Not in XSLT + ret += "local "; + break; + case "optional": // Not in XSLT + ret += "optional "; + break; + case "private": // Not in XSLT + ret += "private "; + break; + case "protected": // Not in XSLT + case "static": // Not in XSLT + ret += "protected "; + break; + case "public": // Not in XSLT + // Ignored. + break; + case "variant": // Not in XSLT + ret += "variant "; + break; + + default: + error( "Illegal element " + n->get_any_name() + " in mode type.\n" ); + break; + } + return ret; +} + +void resolve_class_paths(Node n, string|void path, Node|void parent) +{ + if (!path) path = ""; + mapping attrs = n->get_attributes() || ([]); + string name = attrs->name; + switch(n->get_any_name()) { + case "arguments": + case "returntype": + case "type": + case "doc": + case "source-position": + case "modifiers": + case "classname": + // We're not interrested in the stuff under the above nodes. + return; + default: + if (verbosity >= Tools.AutoDoc.FLAG_NORMAL) { + werror("Unhandled node: %O path:%O name: %O, parent: %O\n", + n->get_any_name(), path, name, parent&&parent->get_any_name()); + } + // FALL_THROUGH + case "": + case "docgroup": + break; + + case "manual": + case "dir": + case "file": + case "chapter": + case "section": + case "subsection": + foreach(filter(n->get_children(), + lambda(Node n) { + return (<"manual", "dir", "file", "chapter", + "section", "subsection", "autodoc">) + [n->get_any_name()]; + }), Node child) { + resolve_class_paths(child, "", n); + } + return; + case "autodoc": + path = ""; + break; + case "namespace": + if ((<"", "lfun">)[name]) { + // Censor namespaces other than :: and lfun:: + path = name+"::"; + } else { + path = ""; + } + break; + case "class": + case "module": + attrs->class_path = path; + path += name + "."; + break; + case "enum": + // Enum names don't show up in the path. + attrs->class_path = path; + break; + case "method": + // Special case for create(). + if (name == "create") { + // Get rid of the extra dot. + attrs->class_path = path[..sizeof(path)-2]; + } + else + { + // Hide the class path for methods. + attrs->class_path = ""; + } + return; + case "constant": + case "inherit": + case "import": + case "typedef": + case "variable": + // These don't have children. + attrs->class_path = path; + return; + } + foreach(n->get_children(), Node child) + { + resolve_class_paths(child, path, n); + } +} + +#if 0 +// DEAD code (for reference only). +string render_class_path(Node n,int|void class_only) +{ + array a = reverse(n->get_ancestors(0)); + array b = a->get_any_name(); + int root; + foreach( ({ "manual", "dir", "file", "chapter", + "section", "subsection" }), string node) + root = max(root, search(b, node)); + a = a[root+1..]; + if(sizeof(a) && a[0]->get_any_name() == "autodoc") + a = a[1..]; + if (!sizeof(a)) return ""; + string ret = ""; + if ((sizeof(a) > 1) && a[-2]->get_any_name() == "enum") { + // Enum names don't show up in the path. + a = a[..sizeof(a)-3] + a[sizeof(a)-1..]; + } + if (a[0]->get_any_name() == "namespace") { + a = a->get_attributes()->name; + a[0] += "::"; + if ((<"::","lfun::">)[a[0]]) { + // Censor namespaces other than :: and lfun:: + ret = a[0]; + } + a = a[1..]; + } else { + a = a->get_attributes()->name; + } + ret += a * "."; + if (!sizeof(ret) || ret[-1] == ':') return ret; + if((<"class", "module", "enum">)[n->get_any_name()]) + return ret + "."; + + // Note we need two calls to get_parent() to skip over the docgroup. + Node parent = n->get_parent()->get_parent(); + + // Hide the enum part. + if (parent->get_any_name()=="enum") parent = parent->get_parent(); + + if(parent->get_any_name()=="class") + if( !class_only ) + return ""; //ret + "()->"; + else + return ret; + if(parent->get_any_name()=="module") + return ret + "."; + if (verbosity >= Tools.AutoDoc.FLAG_NORMAL) { + werror("Failure, raw path: %{%O, %}\n", + reverse(n->get_ancestors(0))->get_any_name()); + } + return " (could not resolve) "; +#ifdef DEBUG + error( "Parent module is " + n->get_parent()->get_any_name() + ".\n" ); +#else + return ""; +#endif +} +#endif /* 0 */ + +string parse_not_doc(Node n) { + string ret = ""; + int method, argument, variable, const, typedf; + + foreach(n->get_children(), Node c) { + + if(c->get_node_type()!=XML_ELEMENT) + continue; + + Node cc; + switch(c->get_any_name()) { + + case "doc": + continue; + + case "source-position": + position->update(c); + continue; + + case "method": + if(method++) ret += "<br />\n"; +#ifdef DEBUG + if(!c->get_first_element("returntype")) + continue; + // error( "No returntype element in method element.\n" ); +#endif + switch( c->get_attributes()->name ) + { + case "create": + ret += "<tt>" + parse_type(get_first_element(c->get_first_element("returntype"))); // Check for more children + ret += " "; + ret += c->get_attributes()->class_path+"<b>(</b>"; + ret += parse_not_doc( c->get_first_element("arguments") ); + ret += "<b>)</b></tt>"; + break; + default: + ret += "<tt>"; + cc = c->get_first_element("modifiers"); + if(cc) ret += map(cc->get_children(), parse_type)*" " + " "; + ret += parse_type(get_first_element(c->get_first_element("returntype"))); // Check for more children + ret += " "; + ret += c->get_attributes()->class_path; + ret += "<b><font color='#000066'>" + c->get_attributes()->name + "</font>(</b>"; + ret += parse_not_doc( c->get_first_element("arguments") ); + ret += "<b>)</b></tt>"; + break; + } + break; + + case "argument": + if(argument++) ret += ", "; + cc = c->get_first_element("value"); + if(cc) ret += "<font color='green'>" + cc->value_of_node() + "</font>"; + else if( !c->count_children() ); + else if( get_first_element(c)->get_any_name()=="type") { + ret += parse_type(get_first_element(get_first_element(c))); + if(c->get_attributes()->name) + ret += " <font color='#005080'>" + + c->get_attributes()->name + "</font>"; + } + else + error( "Malformed argument element.\n" + c->html_of_node() + "\n" ); + break; + + case "variable": + if(variable++) ret += "<br />\n"; + ret += "<tt>"; + cc = c->get_first_element("modifiers"); + if(cc) ret += map(cc->get_children(), parse_type)*" " + " "; + ret += parse_type(get_first_element(c->get_first_element("type")), "variable") + " " + + c->get_attributes()->class_path + "<b><font color='#F000F0'>" + c->get_attributes()->name + + "</font></b></tt>"; + break; + + case "constant": + if(const++) ret += "<br />\n"; + ret += "<tt>"; + cc = c->get_first_element("modifiers"); + if(cc) ret += map(cc->get_children(), parse_type)*" " + " "; + ret += "constant "; + if (Node type = c->get_first_element ("type")) + ret += parse_type (get_first_element (type), "constant") + " "; + ret += c->get_attributes()->class_path; + ret += "<font color='#F000F0'>" + c->get_attributes()->name + "</font>"; + cc = c->get_first_element("typevalue"); + if(cc) ret += " = " + parse_type(get_first_element(cc)); + ret += "</tt>"; + break; + + case "typedef": + if(typedf++) ret += "<br />\n"; + ret += "<tt>"; + cc = c->get_first_element("modifiers"); + if(cc) ret += map(cc->get_children(), parse_type)*" " + " "; + ret += "typedef "; + ret += parse_type(get_first_element(c->get_first_element("type")), "typedef") + " " + + c->get_attributes()->class_path + "<font color='#F000F0'>" + c->get_attributes()->name + + "</font></tt>"; + break; + + case "inherit": + ret += "<font face='courier'>"; + cc = c->get_first_element("modifiers"); + if(cc) ret += map(cc->get_children(), parse_type)*" " + " "; + ret += "inherit "; + Node n = c->get_first_element("classname"); + if (resolve_reference) { + ret += "</font>" + + resolve_reference(n->value_of_node(), n->get_attributes()); + } else { + ret += Parser.encode_html_entities(n->value_of_node()) + "</font>"; + } + if (c->get_attributes()->name) { + ret += "<font face='courier'> : " + "<font color='#F000F0'>" + + Parser.encode_html_entities(c->get_attributes()->name) + + "</font></font>"; + } + break; + + // We don't need import information. + case "import": + break; + + default: + error( "Illegal element " + c->get_any_name() + " in !doc.\n" ); + break; + } + } + + return ret; +} + +int foo; + +string parse_docgroup(Node n) { + mapping m = n->get_attributes(); + string ret = lay->docgroup; + + if(lay->typehead) { + ret += lay->typehead; + if(m["homogen-type"]) { + string type = "<font face='Helvetica'>" + + quote(String.capitalize(m["homogen-type"])) + + "</font>\n"; + if(m["homogen-name"]) { + ret += type + "<font size='+1'><b>" + + quote((m->belongs?m->belongs+" ":"") + m["homogen-name"]) + + "</b></font>\n"; + } else { + array(string) names = + Array.uniq(map(n->get_elements(m["homogen-type"]), + lambda(Node child) { + return child->get_attributes()->name || + child->value_of_node(); + })); + foreach(names, string name) + ret += type + "<font size='+1'><b>" + name + "</b></font><br />\n"; + } + } + else + ret += "Syntax"; + ret += lay->_typehead; + } + + ret += lay->ndochead; + + ret += parse_not_doc(n); + + ret += lay->_ndochead; + + foreach(n->get_elements("doc"), Node c) + ret += parse_doc(c); + + return ret + lay->_docgroup; +} + +string parse_children(Node n, string tag, function cb, mixed ... args) { + string ret = ""; + foreach(n->get_elements(tag), Node c) + ret += cb(c, @args); + + return ret; +} + +string manual_title = "Pike Reference Manual"; +string frame_html(string res, void|string title) { + title = title || manual_title; + return "<html><head><title>" + quote(title) + "</title></head>\n" + "<body bgcolor='white' text='black'>\n" + res + + "</body></html>"; +} + +string layout_toploop(Node n, Git.Export|void exporter) { + string res = ""; + foreach(n->get_elements(), Node c) + switch(c->get_any_name()) { + + case "dir": + if (exporter) { + layout_toploop(c, exporter); + break; + } + string cwd; + if(dest_path) + { + cwd=getcwd(); + cd(dest_path); + } + Stdio.mkdirhier(c->get_attributes()->name); + layout_toploop(c); + if(cwd) + cd(cwd); + break; + + case "file": + if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) { + werror("\t%s\n", c->get_attributes()->name); + } + if (exporter) { + exporter->filemodify(Git.MODE_FILE, c->get_attributes()->name); + exporter->data(frame_html(layout_toploop(c))); + break; + } + if(dest_path) + { + cwd=getcwd(); + cd(dest_path); + } + Stdio.write_file( c->get_attributes()->name, + frame_html(layout_toploop(c)) ); + if(cwd) + cd(cwd); + break; + + case "chapter": + res += parse_chapter(c); + break; + + default: + error("Unknown element %O.\n", c->get_any_name()); + } + return res; +} + +int low_main(string title, string input_file, string|void output_file, + Git.Export|void exporter) +{ + int t = time(); + string file = Stdio.read_file(input_file); + if(!file) + error( "Could not read %s.\n", input_file ); + if(!sizeof(file)) + error( "%s is empty.\n", input_file ); + + // We are only interested in what's in the + // module container. + if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) { + werror("Parsing %O...\n", input_file); + } + Node n = Parser.XML.Tree.simple_parse_input(file); + + Node n2 = n->get_first_element("manual"); + if(!n2) { + n2 = n->get_first_element("autodoc"); + if(!n2) { + if (verbosity >= Tools.AutoDoc.FLAG_NORMAL) { + werror("Not an autodoc XML file.\n"); + } + return 1; + } + + Node chap = Node(XML_ELEMENT, "chapter", + (["title":title||"Documentation"]), 0); + chap->add_child( n2 ); + + string fn = output_file || "index.html"; + Node file = Node(XML_ELEMENT, "file", (["name":fn]), 0); + file->add_child( chap ); + + Node top = Node(XML_ELEMENT, "manual", ([]), 0); + top->add_child( file ); + n = top; + } + else + n = n2; + + if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) { + werror("Resolving class paths...\n"); + } + resolve_class_paths(n); + + mapping m = n->get_attributes(); + + if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) { + werror("Layouting...\n"); + } + manual_title = m->title || (m->version?"Reference Manual for "+m->version:"Pike Reference Manual"); + layout_toploop(n, exporter); + + if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) { + werror("Took %d seconds.\n\n", time()-t); + } + + return 0; +} + +int main(int num, array args) { + + string title; + + foreach(Getopt.find_all_options(args, ({ + ({ "img", Getopt.HAS_ARG, "--img" }), + ({ "dest", Getopt.HAS_ARG, "--dest" }), + ({ "title", Getopt.HAS_ARG, "--title" }), + ({ "defns", Getopt.HAS_ARG, "--default-ns" }), + ({ "verbose", Getopt.NO_ARG, "-v,--verbose"/"," }), + ({ "quiet", Getopt.NO_ARG, "-q,--quiet"/"," }), + ({ "help", Getopt.NO_ARG, "--help" }), + })), array opt) + switch(opt[0]) { + case "img": + image_path = opt[1]; + break; + case "dest": + dest_path = opt[1]; + break; + case "title": + title = opt[1]; + break; + case "defns": + default_ns = opt[1]; + break; + case "verbose": + if (verbosity < Tools.AutoDoc.FLAG_DEBUG) { + verbosity += 1; + flags = (flags & ~Tools.AutoDoc.FLAG_VERB_MASK) | verbosity; + } + break; + case "quiet": + flags &= ~Tools.AutoDoc.FLAG_VERB_MASK; + verbosity = Tools.AutoDoc.FLAG_QUIET; + break; + case "help": + write(#"pike -x autodoc_to_html [args] <input file> [<output file>] +--img=<image path> +--dest=<destination path> + +--title=<document title> +"); + break; + } + args = Getopt.get_args(args)[1..]; + + if(!sizeof(args)) + error( "No input file given.\n" ); + + return low_main(title, @args); +} diff --git a/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_split_html.pike b/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_split_html.pike new file mode 100644 index 0000000000..5785e425ee --- /dev/null +++ b/lib/modules/Tools.pmod/Standalone.pmod/autodoc_to_split_html.pike @@ -0,0 +1,775 @@ +/* + */ + +inherit .autodoc_to_html; + +mapping (string:Node) refs = ([ ]); +string default_namespace; + +string extra_prefix = ""; +string image_prefix() +{ + return extra_prefix + ::image_prefix(); +} + +int unresolved; +mapping profiling = ([]); +#define PROFILE int profilet=gethrtime +#define ENDPROFILE(X) profiling[(X)] += gethrtime()-profilet; + +string cquote(string n) +{ + string ret=""; + // n = replace(n, ([ ">":">", "<":"<", "&":"&" ])); + while(sscanf((string)n,"%[._a-zA-Z0-9]%c%s",string safe, int c, n)==3) { + switch(c) { + default: ret += sprintf("%s_%02X",safe,c); break; + case '+': ret += sprintf("%s_add",safe); break; + case '`': ret += sprintf("%s_backtick",safe); break; + case '=': ret += sprintf("%s_eq",safe); break; + } + } + ret += n; + return ret; +} + +string create_reference(string from, string to, string text) { + array a = (to/"::"); + switch(sizeof(a)) { + case 2: + if (sizeof(a[1])) { + // Split trailing module path. + a = a[0..0] + a[1]/"."; + } else { + // Get rid of trailing "". + a = a[0..0]; + } + a[0] += "::"; + break; + case 1: + a = a[0]/"."; + break; + default: + error("Bad reference: %O\n", to); + } + return "<font face='courier'><a href='" + + "../"*max(sizeof(from/"/") - 2, 0) + map(a, cquote)*"/" + ".html'>" + + text + + "</a></font>"; +} + +multiset missing = (< "foreach", "catch", "throw", "sscanf", "gauge", "typeof" >); + + +class Node +{ + string type; + string name; + string data; + array(Node) class_children = ({ }); + array(Node) module_children = ({ }); + array(Node) enum_children = ({ }); + array(Node) method_children = ({ }); + + Node parent; + + string _sprintf() { + return sprintf("Node(%O,%O,%d)", type, name, data?sizeof(data):0); + } + + void create(string _type, string _name, string _data, void|Node _parent) + { + if(!_type || !_name) throw( ({ "No type or name\n", backtrace() }) ); + type = _type; + name = _name; + parent = _parent; + data = get_parser()->finish( _data )->read(); + + string path = raw_class_path(); + refs[path] = this_object(); + + sort(class_children->name, class_children); + sort(module_children->name, module_children); + sort(enum_children->name, enum_children); + sort(method_children->name, method_children); + + method_children = check_uniq(method_children); + + /* + foreach(method_children, Node m) + if( (<"create","destroy">)[m->name] ) { + method_children -= ({ m }); + string d; + sscanf(m->data, "%*s<docgroup%s/docgroup>", d); + if(d) + data += "<docgroup" + d + "/docgroup>"; + } + */ + + data = make_faked_wrapper(data); + } + + array(Node) check_uniq(array children) { + array names = children->name; + foreach(Array.uniq(names), string n) + if(sizeof(filter(names, lambda(string in) { return in==n; }))!=1) { + string d=""; + Parser.HTML parser = Parser.HTML(); + parser->case_insensitive_tag(1); + parser->xml_tag_syntax(3); + parser->add_container("docgroup", + lambda(Parser.HTML p, mapping m, string c) { + d += sprintf("<docgroup%{ %s='%s'%}>%s</docgroup>", + (array)m, c); + return ""; + }); + + foreach(children, Node c) + if(c->name==n) parser->feed(c->data); + parser->finish(); + d = make_faked_wrapper(d); + + foreach(children; int i; Node c) + if(c->name==n) { + if(d) { + c->data = d; + d = 0; + } + else children[i] = 0; + } + children -= ({ 0 }); + } + return children; + } + + protected string parse_node(Parser.HTML p, mapping m, string c) { + if(!m->name) error("Unnamed %O %O\n", p->tag_name(), m); + this_object()[p->tag_name()+"_children"] += + ({ Node( p->tag_name(), m->name, c, this_object() ) }); + return ""; + } + + array(string) my_parse_docgroup(Parser.HTML p, mapping m, string c) + { + foreach(({"homogen-name", "belongs"}), string attr) { + if (m[attr]) m[attr] = Parser.parse_html_entities(m[attr]); + } + if(m["homogen-type"]) { + switch( m["homogen-type"] ) { + + case "method": + if( m["homogen-name"] ) { + string name = m["homogen-name"]; + if(m->belongs) { + if(m->belongs[-1]==':') name = m->belongs + name; + else name = m->belongs + "." + name; + } + method_children += + ({ Node( "method", name, c, this_object() ) }); + return ({ "" }); + } + + // Several different methods documented with the same blurb. + array names = ({}); + Parser.HTML parser = Parser.HTML(); + parser->case_insensitive_tag(1); + parser->xml_tag_syntax(0); + parser->add_tag("method", + lambda(Parser.HTML p, mapping m) { + names += ({ Parser.parse_html_entities(m->name) }); + } ); + parser->finish(c); + foreach(Array.uniq(names) - ({ 0, "" }), string name) { + method_children += + ({ Node( "method", name, c, this_object() ) }); + } + return ({ "" }); + break; + + case "constant": + case "variable": + case "inherit": + string path = raw_class_path(); + if(sizeof(path) && (path[-1] != ':')) path += "."; + if(!m["homogen-name"]) { + Parser.HTML()->add_tags + ( ([ "constant": + lambda(Parser.HTML p, mapping m, string c) { + string name = Parser.parse_html_entities(m->name); + refs[path + name] = + Node( "constant", name, "", this_object()); + }, + "variable": + lambda(Parser.HTML p, mapping m, string c) { + string name = Parser.parse_html_entities(m->name); + refs[path + name] = + Node( "variable", name, "", this_object()); + }, + "inherit": + lambda(Parser.HTML p, mapping m, string c) { + if (m->name) { + string name = Parser.parse_html_entities(m->name); + refs[path + name] = + Node( "inherit", name, "", this_object()); + } + }, + ]) )->finish(c); + } + else + refs[path + m["homogen-name"]] = + Node( m["homogen-type"], m["homogen-name"], "", this_object()); + break; + + } + } + + return 0; + } + + protected Parser.HTML get_parser() { + Parser.HTML parser = Parser.HTML(); + + parser->case_insensitive_tag(1); + parser->xml_tag_syntax(3); + + parser->add_container("docgroup", my_parse_docgroup); + parser->add_container("module", parse_node); + parser->add_container("class", parse_node); + parser->add_container("enum", parse_node); + + return parser; + } + + string make_faked_wrapper(string s) + { + if(type=="method") + s = sprintf("<docgroup homogen-type='method' homogen-name='%s'>\n" + "%s\n</docgroup>\n", + Parser.encode_html_entities(name), s); + else + s = sprintf("<%s name='%s'>\n%s\n</%s>\n", + type, Parser.encode_html_entities(name), s, type); + if(parent) + return parent->make_faked_wrapper(s); + else + return s; + } + + string _make_filename_low; + string make_filename_low() + { + if(_make_filename_low) return _make_filename_low; + if (type == "namespace") { + _make_filename_low = parent->make_filename_low()+"/"+cquote(name+"::"); + } else { + _make_filename_low = parent->make_filename_low()+"/"+cquote(name); + } + return _make_filename_low; + } + + string make_filename() + { + return make_filename_low()[5..]+".html"; + } + + string make_link(Node to) + { + // FIXME: Optimize the length of relative links + int num_segments = sizeof(make_filename()/"/") - 1; + return ("../"*num_segments)+to->make_filename(); + } + + array(Node) get_ancestors() + { + PROFILE(); + array tmp = ({ this_object() }) + parent->get_ancestors(); + ENDPROFILE("get_ancestors"); + return tmp; + } + + string my_resolve_reference(string _reference, mapping vars) + { + array(string) resolved = vars->resolved && vars->resolved/"\0"; + if(default_namespace && has_prefix(_reference, default_namespace+"::")) + _reference = _reference[sizeof(default_namespace)+2..]; + + if(vars->param) + return "<font face='courier'>" + _reference + "</font>"; + + if (resolved) { + foreach (resolved, string resolution) { + Node res_obj; + + if(res_obj = refs[resolution]) { + while(res_obj && (<"constant","variable">)[res_obj->type]) { + res_obj = res_obj->parent; + } + if (!res_obj && verbosity) { + werror("Found no page to link to for reference %O (%O)\n", + _reference, resolution); + return sprintf("<font face='courier'>" + _reference + "</font>"); + } + // FIXME: Assert that the reference is correct? + return create_reference(make_filename(), + res_obj->raw_class_path(), + _reference); + } + + // Might be a reference to a parameter. + // Try cutting of the last section, and retry. + array(string) tmp = resolution/"."; + if ((sizeof(tmp) > 1) && (res_obj = refs[tmp[..sizeof(tmp)-2]*"."])) { + if (res_obj == this_object()) { + return sprintf("<font face='courier'>" + _reference + "</font>"); + } + return create_reference(make_filename(), + res_obj->raw_class_path(), + _reference); + } + + if (!zero_type(refs[resolution]) && + (verbosity >= Tools.AutoDoc.FLAG_VERBOSE)) { + werror("Reference %O (%O) is %O!\n", + _reference, resolution, refs[resolution]); + } + } + if (!missing[vars->resolved] && !has_prefix(_reference, "lfun::") && + verbosity) { + werror("Missed reference %O (%{%O, %}) in %s\n", + _reference, resolved||({}), make_class_path()); +#if 0 + werror("Potential refs:%O\n", + sort(map(resolved, + lambda (string resolution) { + return filter(indices(refs), + glob(resolution[..sizeof(resolution)/2]+ + "*", indices(refs)[*])); + }) * ({}))); +#endif /* 0 */ + } + unresolved++; + } + return "<font face='courier'>" + _reference + "</font>"; + } + + string _make_class_path; + string _raw_class_path; + string make_class_path(void|int(0..1) header) + { + if(_make_class_path) return _make_class_path; + array a = reverse(parent->get_ancestors()); + + _make_class_path = ""; + _raw_class_path = ""; + foreach(a, Node n) + { + // Hide most namepaces from the class path. + if (n->type == "namespace") { + _raw_class_path += n->name + "::"; + if ((<"","lfun">)[n->name]) { + _make_class_path += n->name + "::"; + } + } else { + _raw_class_path += n->name + "."; + _make_class_path += n->name; + if(n->type=="class") + _make_class_path += "()->"; + else if(n->type=="module") + _make_class_path += "."; + } + } + _make_class_path += name; + _raw_class_path += name; + + if(type=="method") { + _make_class_path += "()"; + } else if (type == "namespace") { + _make_class_path += "::"; + _raw_class_path += "::"; + } + + return _make_class_path; + } + string raw_class_path(void|int(0..1) header) + { + if(_raw_class_path) return _raw_class_path; + make_class_path(header); + return _raw_class_path; + } + + string make_navbar_really_low(array(Node) children, string what) + { + if(!sizeof(children)) return ""; + + String.Buffer res = String.Buffer(3000); + res->add("<tr><td nowrap='nowrap'><br /><b>", what, "</b></td></tr>\n"); + + foreach(children, Node node) + { + string my_name = Parser.encode_html_entities(node->name); + if(node->type=="method") + my_name+="()"; + else if (node->type == "namespace") { + my_name="<b>"+my_name+"::</b>"; + } + else + my_name="<b>"+my_name+"</b>"; + + res->add("<tr><td nowrap='nowrap'> "); + if(node==this_object()) + res->add( my_name ); + else + res->add( "<a href='", make_link(node), "'>", my_name, "</a>" ); + res->add("</td></tr>\n"); + } + return (string)res; + } + + string make_hier_list(Node node) + { + string res=""; + + if(node) + { + if(node->type=="namespace" && node->name==default_namespace) + node = node->parent; + res += make_hier_list(node->parent); + + string my_class_path = + (node->is_TopNode)?"[Top]":node->make_class_path(); + + if(node == this_object()) + res += sprintf("<b>%s</b><br />\n", + Parser.encode_html_entities(my_class_path)); + else + res += sprintf("<a href='%s'><b>%s</b></a><br />\n", + make_link(node), + Parser.encode_html_entities(my_class_path)); + } + return res; + } + + string make_navbar_low(Node root) + { + string res=""; + + res += make_hier_list(root); + + res+="<table border='0' cellpadding='1' cellspacing='0' class='sidebar'>"; + + res += make_navbar_really_low(root->module_children, "Modules"); + + res += make_navbar_really_low(root->class_children, "Classes"); + + if(root->is_TopNode) + res += make_navbar_really_low(root->namespace_children, "Namespaces"); + else { + res += make_navbar_really_low(root->enum_children, "Enums"); + res += make_navbar_really_low(root->method_children, "Methods"); + } + + return res+"</table>"; + } + + string make_navbar() + { + if(type=="method") + return make_navbar_low(parent); + else + return make_navbar_low(this_object()); + } + + array(Node) find_siblings() + { + return + parent->class_children+ + parent->module_children+ + parent->enum_children+ + parent->method_children; + } + + array(Node) find_children() + { + return + class_children+ + module_children+ + enum_children+ + method_children; + } + + Node find_prev_node() + { + array(Node) siblings = find_siblings(); + int index = search( siblings, this_object() ); + + Node tmp; + + if(index==0 || index == -1) + return parent; + + tmp = siblings[index-1]; + + while(sizeof(tmp->find_children())) + tmp = tmp->find_children()[-1]; + + return tmp; + } + + Node find_next_node(void|int dont_descend) + { + if(!dont_descend && sizeof(find_children())) + return find_children()[0]; + + array(Node) siblings = find_siblings(); + int index = search( siblings, this_object() ); + + Node tmp; + if(index==sizeof(siblings)-1) + tmp = parent->find_next_node(1); + else + tmp = siblings[index+1]; + return tmp; + } + + protected string make_content() { + PROFILE(); + string err; + Parser.XML.Tree.Node n; + if(err = catch( n = Parser.XML.Tree.parse_input(data)[0] )) { + werror(err + "\n" + data); + exit(1); + } + ENDPROFILE("XML.Tree"); + + resolve_reference = my_resolve_reference; + + String.Buffer contents = String.Buffer(100000); + resolve_class_paths(n); + contents->add( parse_children(n, "docgroup", parse_docgroup, 1) ); + contents->add( parse_children(n, "namespace", parse_namespace, 1) ); + contents->add( parse_children(n, "module", parse_module, 1) ); + contents->add( parse_children(n, "class", parse_class, 1) ); + contents->add( parse_children(n, "enum", parse_enum, 1) ); + + n->zap_tree(); + + return (string)contents; + } + + void make_html(string template, string path, Git.Export|void exporter) + { + class_children->make_html(template, path, exporter); + module_children->make_html(template, path, exporter); + enum_children->make_html(template, path, exporter); + method_children->make_html(template, path, exporter); + + int num_segments = sizeof(make_filename()/"/")-1; + string style = ("../"*num_segments)+"style.css"; + extra_prefix = "../"*num_segments; + + Node prev = find_prev_node(); + Node next = find_next_node(); + string next_url="", next_title="", prev_url="", prev_title=""; + if(next) { + next_title = next->make_class_path(); + next_url = make_link(next); + } + if(prev) { + prev_title = prev->make_class_path(); + prev_url = make_link(prev); + } + + string res = replace(template, + (["$navbar$": make_navbar(), + "$contents$": make_content(), + "$prev_url$": prev_url, + "$prev_title$": _Roxen.html_encode_string(prev_title), + "$next_url$": next_url, + "$next_title$": _Roxen.html_encode_string(next_title), + "$type$": String.capitalize(type), + "$title$": _Roxen.html_encode_string(make_class_path(1)), + "$style$": style, + "$dotdot$": sizeof(extra_prefix)?extra_prefix:".", + "$imagedir$":image_prefix(), + "$filename$": _Roxen.html_encode_string(make_filename()), + ])); + + if (exporter) { + exporter->filemodify(Git.MODE_FILE, path + "/" + make_filename()); + exporter->data(res); + } else { + Stdio.mkdirhier(combine_path(path+"/"+make_filename(), "../")); + Stdio.write_file(path+"/"+make_filename(), res); + } + } +} + +class TopNode { + inherit Node; + + constant is_TopNode = 1; + array(Node) namespace_children = ({ }); + + void create(string _data) { + PROFILE(); + Parser.HTML parser = Parser.HTML(); + parser->case_insensitive_tag(1); + parser->xml_tag_syntax(3); + parser->add_container("autodoc", + lambda(Parser.HTML p, mapping args, string c) { + return ({ c }); }); + + _data = parser->finish(_data)->read(); + ::create("autodoc", "", _data); + sort(namespace_children->name, namespace_children); + foreach(namespace_children, Node x) + if(x->type=="namespace" && x->name==default_namespace) { + // namespace_children -= ({ x }); + class_children += x->class_children; + module_children += x->module_children; + enum_children += x->enum_children; + method_children += x->method_children; + } + type = "autodoc"; + ENDPROFILE("top_create"); + } + + Parser.HTML get_parser() { + Parser.HTML parser = ::get_parser(); + parser->add_container("namespace", parse_node); + return parser; + } + + string make_filename_low() { return "__index"; } + string make_filename() { return "index.html"; } + array(Node) get_ancestors() { return ({ }); } + int(0..0) find_prev_node() { return 0; } + int(0..0) find_next_node() { return 0; } + string make_class_path(void|int(0..1) header) { + if(header && sizeof(method_children)) { + if(default_namespace) + return "namespace "+default_namespace; + else + return "Namespaces"; + } + return ""; + } + string raw_class_path(void|int(0..1) header) { + return ""; + } + + string make_method_page(array(Node) children) + { + String.Buffer res = String.Buffer(3500); + foreach(children, Node node) + res->add(" <a href='", make_link(node), "'>", + Parser.encode_html_entities(node->name), + "()</a><br />\n"); + return (string)res; + } + + string make_content() { + resolve_reference = my_resolve_reference; + if(!sizeof(method_children)) return ""; + + string contents = "<table class='sidebar'><tr>"; + foreach(method_children/( sizeof(method_children)/4.0 ), + array(Node) children) + contents += "<td nowrap='nowrap' valign='top'>" + + make_method_page(children) + "</td>"; + + contents += "</tr><tr><td colspan='4' nowrap='nowrap'>" + + parse_children(Parser.XML.Tree.parse_input(data), + "docgroup", parse_docgroup, 1) + + "</td></tr></table>"; + + return contents; + } + + void make_html(string template, string path, Git.Export|void exporter) { + PROFILE(); + namespace_children->make_html(template, path, exporter); + ::make_html(template, path, exporter); + ENDPROFILE("top_make_html"); + } +} + +int low_main(string doc_file, string template_file, string outdir, + Git.Export|void exporter) +{ + PROFILE(); + if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) + werror("Reading refdoc blob %s...\n", doc_file); + string doc = Stdio.read_file(doc_file); + if(!doc) { + werror("Failed to load refdoc blob %s.\n", doc_file); + return 1; + } + + if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) + werror("Reading template file %s...\n", template_file); + string template = Stdio.read_file(template_file); + if(!template) { + werror("Failed to load template %s.\n", template_file); + return 1; + } + mapping m = localtime(time()); + template = replace(template, + ([ "$version$":version(), + "$date$":sprintf("%4d-%02d-%02d", + m->year+1900, m->mon+1, m->mday), + ]) ); + + if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) + werror("Splitting to destination directory %s...\n", outdir); + TopNode top = TopNode(doc); + top->make_html(template, outdir, exporter); + ENDPROFILE("main"); + + if (verbosity >= Tools.AutoDoc.FLAG_VERBOSE) + foreach(sort(indices(profiling)), string f) + werror("%s: %.1f\n", f, profiling[f]/1000000.0); + if (verbosity >= Tools.AutoDoc.FLAG_NORMAL) { + werror("%d unresolved references.\n", unresolved); + werror("%d documented constants/variables/functions/classes/modules.\n", + sizeof(refs)); + } + return 0; +} + +int main(int argc, array(string) argv) +{ + foreach(Getopt.find_all_options(argv, ({ + ({ "verbose", Getopt.NO_ARG, "-v,--verbose"/"," }), + ({ "quiet", Getopt.NO_ARG, "-q,--quiet"/"," }), + ({ "help", Getopt.NO_ARG, "--help" }), + })), array opt) + switch(opt[0]) { + case "verbose": + if (verbosity < Tools.AutoDoc.FLAG_DEBUG) { + verbosity += 1; + flags = (flags & ~Tools.AutoDoc.FLAG_VERB_MASK) | verbosity; + } + break; + case "quiet": + flags &= ~Tools.AutoDoc.FLAG_VERB_MASK; + verbosity = Tools.AutoDoc.FLAG_QUIET; + break; + case "help": + write("pike -x autodoc_to_split_html [-v|--verbose] [-q|--quiet]\n" + "\t[--help] <input-file> <template-file> <output-dir>" + " [<namespace>]\n"); + return 0; + } + argv = Getopt.get_args(argv)[1..]; + if(sizeof(argv)<4) { + werror("Too few arguments.\n" + "pike -x autodoc_to_split_html [-v|--verbose] [-q|--quiet]\n" + "\t[--help] <input-file> <template-file> <output-dir>" + " [<namespace>]\n"); + return 1; + } + if(argc>4) default_namespace=argv[4]; + + return low_main(argv[1], argv[2], argv[3]); +} diff --git a/refdoc/presentation/make_html.pike b/refdoc/presentation/make_html.pike index 2e251e05c9..53b9475d0a 100644 --- a/refdoc/presentation/make_html.pike +++ b/refdoc/presentation/make_html.pike @@ -1,1373 +1,5 @@ -#define Node Parser.XML.Tree.SimpleNode -#define XML_COMMENT Parser.XML.Tree.XML_COMMENT -#define XML_ELEMENT Parser.XML.Tree.XML_ELEMENT -#define XML_TEXT Parser.XML.Tree.XML_TEXT -#define DEBUG +/* + * Compat place-holder. + */ -function resolve_reference; -string image_path = "images/"; -string dest_path; -string default_ns; - -mapping lay = ([ - "docgroup" : "\n\n<hr clear='all' size='1' noshadow='noshadow' />\n<dl>", - "_docgroup" : "</dl>\n", - "ndochead" : "<dd><p>", - "_ndochead" : "</p></dd>\n", - - "dochead" : "\n<dt style='font-family: sans-serif'>", - "_dochead" : "</dt>\n", - "typehead" : "\n<dt style='font-family: sans-serif'>", - "_typehead" : "</dt>\n", - "docbody" : "<dd style='font-family: sans-serif'>", - "_docbody" : "</dd>", - "fixmehead" : "<dt style='font-family: sans-serif; color: red'>", - "_fixmehead" : "</dt>\n", - "fixmebody" : "<dd style='font-family: sans-serif; color: red'>", - "_fixmebody" : "</dd>", - - "parameter" : "<tt><font color='#8000F0'>", - "_parameter" : "</font></tt>", - "example" : "<dd><pre>", - "_example" : "</pre></dd>", - - "pre" : "<font face='courier'><pre>", - "_pre" : "</pre></font>", - "code" : "<font face='courier'><pre><code>", - "_code" : "</code></pre></font>", - "expr" : "<font face='courier'><code>", - "_expr" : "</code></font>", - -]); - -string image_prefix() -{ - return image_path; -} - -class Position { - string file; - int line; - - void update(Node n) { - mapping m = n->get_attributes(); - if(!m->file || !m["first-line"]) { - werror("Missing attribute in source-position element. %O\n", m); - return; - } - file = m->file; - line = (int) m["first-line"]; - } - - string get() { - return file + ":" + line; - } -} - -Position position = Position(); - -string quote(string in) { - if(in-" "-"\t"=="") return ""; - if(String.trim_all_whites(in)=="") return "\n"; - return Parser.XML.Tree.text_quote(in); -} - -Node get_first_element(Node n) { - foreach(n->get_children(), Node c) - if(c->get_node_type()==XML_ELEMENT) { - if(c->get_any_name()!="source-position") - return c; - else - position->update(c); - } - error( "Node had no element child.\n" ); -} - -int section, subsection; - -string low_parse_chapter(Node n, int chapter) { - string ret = ""; - Node dummy = Node(XML_ELEMENT, "dummy", ([]), ""); - foreach(n->get_elements(), Node c) - switch(c->get_any_name()) { - - case "p": - dummy->replace_children( ({ c }) ); - ret += parse_text(dummy); - break; - - case "example": - ret += "<p><pre>" + c->value_of_node() + "</pre></p>\n"; - break; - - case "ul": - case "ol": - ret += (string)c; - break; - - case "dl": - ret += "<dl>\n"; - foreach(c->get_elements(), Node cc) { - string tag = cc->get_any_name(); - if(tag!="dt" && tag!="dd") - error("dl contained element %O.\n", tag); - ret += "<" + tag + ">" + parse_text(cc) + "</" + tag + ">\n"; - } - ret += "</dl>"; - break; - - case "matrix": - ret += layout_matrix( map(c->get_elements("r")->get_elements("c"), map, parse_text) ); - break; - - case "section": - if(section) - error("Section inside section.\n"); - if(subsection) - error("Section inside subsection.\n"); - section = (int)c->get_attributes()->number; - ret += "</dd>\n<dt><a name='" + section + "'></a>\n" - "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" - "<td bgcolor='#EEEEEE'><font size='+3'> " + chapter + "." + section + - ". " + quote(c->get_attributes()->title || - // The following for bug compat. - c->get_attributes()->name) + - "</font></td></tr></table><br />\n" - "</dt>\n<dd>"; - ret += low_parse_chapter(c, chapter); - section = 0; - break; - - case "subsection": - if(!section) - error("Subsection outside section.\n"); - if(subsection) - error("Subsection inside subsection.\n"); - subsection = (int)c->get_attributes()->number; - ret += "</dd><dt>" - "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" - "<td bgcolor='#EEEEEE'><font size='+3'> " + chapter + "." + section + "." + subsection + - ". " + quote(c->get_attributes()->title) + - "</font></td></tr></table><br />\n" - "</dt><dd>"; - ret += low_parse_chapter(c, chapter); - subsection = 0; - break; - - case "docgroup": - ret += parse_docgroup(c); - break; - - case "autodoc": - ret += parse_autodoc(c); - break; - - case "namespace": - ret += parse_namespace(c); - break; - - case "module": - ret += parse_module(c); - break; - - case "class": - ret += parse_class(c); - break; - - case "enum": - ret += parse_enum(c); - break; - - default: - error("Unknown element %O.\n", c->get_any_name()); - } - return ret; -} - -string parse_chapter(Node n, void|int noheader) { - string ret = ""; - if(!noheader) { - ret += "<dl><dt>" - "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" - "<td bgcolor='#EEEEEE'><font size='+3'> "; - if(n->get_attributes()->number) - ret += n->get_attributes()->number + ". "; - ret += quote(n->get_attributes()->title) + - "</font></td></tr></table><br />\n" - "</dt><dd>"; - } - - ret += low_parse_chapter(n, (int)n->get_attributes()->number); - - if(!noheader) - ret = ret + "</dd></dl>"; - - return ret; -} - -string parse_autodoc(Node n) -{ - string ret =""; - - mapping m = n->get_attributes(); - - Node c = n->get_first_element("doc"); - if(c) - ret += "<dl>" + parse_doc(c) + "</dl>"; - -#ifdef DEBUG - if(sizeof(n->get_elements("doc"))>1) - error( "More than one doc element in autodoc node.\n" ); -#endif - - ret += parse_children(n, "docgroup", parse_docgroup); - ret += parse_children(n, "namespace", parse_namespace); - - return ret; -} - -string parse_namespace(Node n, void|int noheader) -{ - string ret = ""; - - mapping m = n->get_attributes(); - int(0..1) header = !noheader && !(m->hidden) && m->name!=default_ns; - if(header) - ret += "<dl><dt>" - "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" - "<td bgcolor='#EEEEEE'><font size='+3'> Namespace <b>" + - m->name + "::</b></font></td></tr></table><br />\n" - "</dt><dd>"; - - Node c = n->get_first_element("doc"); - if(c) - ret += "<dl>" + parse_doc(c) + "</dl>"; - -#ifdef DEBUG - if(sizeof(n->get_elements("doc"))>1) - error( "More than one doc element in namespace node.\n" ); -#endif - - ret += parse_children(n, "docgroup", parse_docgroup, noheader); - ret += parse_children(n, "enum", parse_enum, noheader); - ret += parse_children(n, "class", parse_class, noheader); - ret += parse_children(n, "module", parse_module, noheader); - - if(header) - ret += "</dd></dl>"; - - return ret; -} - -string parse_module(Node n, void|int noheader) { - string ret =""; - - mapping m = n->get_attributes(); - int(0..1) header = !noheader && !(m->hidden); - if(header) - ret += "<dl><dt>" - "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" - "<td bgcolor='#EEEEEE'><font size='+3'> Module <b>" + - m->class_path + m->name + "</b></font></td></tr></table><br />\n" - "</dt><dd>"; - - Node c = n->get_first_element("doc"); - if(c) - ret += "<dl>" + parse_doc(c) + "</dl>"; - -#ifdef DEBUG - if(sizeof(n->get_elements("doc"))>1) - error( "More than one doc element in module node.\n" ); -#endif - - ret += parse_children(n, "docgroup", parse_docgroup, noheader); - ret += parse_children(n, "enum", parse_enum, noheader); - ret += parse_children(n, "class", parse_class, noheader); - ret += parse_children(n, "module", parse_module, noheader); - - if(header) - ret += "</dd></dl>"; - - return ret; -} - -string parse_class(Node n, void|int noheader) { - string ret =""; - if(!noheader) - ret += "<dl><dt>" - "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" - "<td bgcolor='#EEEEEE'><font size='+3'> CLASS <b><font color='#005080'>" + - n->get_attributes()->class_path + n->get_attributes()->name + - "</font></b></font></td></tr></table><br />\n" - "</dt><dd>"; - - Node c = n->get_first_element("doc"); - - if(c) - ret += "<dl>" + parse_doc(c) + "</dl>"; - -#ifdef DEBUG - if(sizeof(n->get_elements("doc"))>1) - error( "More than one doc element in class node.\n" ); -#endif - - ret += parse_children(n, "docgroup", parse_docgroup); - ret += parse_children(n, "enum", parse_enum); - ret += parse_children(n, "class", parse_class, noheader); - - if(!noheader) - ret += "</dd></dl>"; - return ret; -} - -string parse_enum(Node n, void|int noheader) { - string ret =""; - if(!noheader) - ret += "<dl><dt>" - "<table width='100%' cellpadding='3' cellspacing='0' border='0'><tr>" - "<td bgcolor='#EEEEEE'><font size='+3'> ENUM <b><font color='#005080'>" + - n->get_attributes()->class_path + n->get_attributes()->name + - "</font></b></font></td></tr></table><br />\n" - "</dt><dd>"; - - Node c = n->get_first_element("doc"); - - if(c) - ret += "<dl>" + parse_doc(c) + "</dl>"; - -#ifdef DEBUG - if(sizeof(n->get_elements("doc"))>1) - error( "More than one doc element in enum node.\n" ); -#endif - - ret += parse_children(n, "docgroup", parse_docgroup); - - if(!noheader) - ret += "</dd></dl>"; - return ret; -} - -string layout_matrix( array(array(string)) rows ) { - string ret = "<table bgcolor='black' border='0' cellspacing='0' cellpadding='0'><tr><td>\n" - "<table cellspacing='1' cellpadding='3' border='0' bgcolor='black'>\n"; - - int dim; - foreach(rows, array row) - dim = max(dim, sizeof(row)); - - foreach(rows, array row) { - ret += "<tr valign='top'>"; - if(sizeof(row)<dim) - ret += "<td bgcolor='white'>" + row[..sizeof(row)-2]*"</td><td bgcolor='white'>" + - "</td><td bgcolor='white' colspan='"+ (dim-sizeof(row)) + "'>" + row[-1] + "</td>"; - else - ret += "<td bgcolor='white'>" + row*"</td><td bgcolor='white'>" + "</td>"; - ret += "</tr>\n"; - } - - return ret + "</table></td></tr></table><br />\n"; -} - -// ({ ({ array(string)+, void|string })* }) -void nicebox(array rows, String.Buffer ret) { - ret->add( "<table bgcolor='black' border='0' cellspacing='0' cellpadding='0'><tr><td>\n" - "<table cellspacing='1' cellpadding='3' border='0' bgcolor='black'>\n" ); - - int dim; - foreach(rows, array row) - dim = max(dim, sizeof(row)); - - foreach(rows, array row) { - if(sizeof(row)==1) { - if(stringp(row[0])) - ret->add( "<tr valign='top'><td bgcolor='white' colspan='", (string)dim, "'>", - row[0], "</td></tr>\n" ); - else - foreach(row[0], string elem) - ret->add( "<tr valign='top'><td bgcolor='white'><tt>", elem, "</tt></td>", - (dim==2?"<td bgcolor='white'> </td>":""), "</tr>\n" ); - } - else if(sizeof(row[0])==1) - ret->add( "<tr valign='top'><td bgcolor='white'><tt>", row[0][0], - "</tt></td><td bgcolor='white'>", row[1], "</td></tr>\n" ); - else { - ret->add( "<tr valign='top'><td bgcolor='white'><tt>", row[0][0], - "</tt></td><td bgcolor='white' rowspan='", (string)sizeof(row[0]), "'>", - row[1], "</td></tr>\n" ); - foreach(row[0][1..], string elem) - ret->add( "<tr valign='top'><td bgcolor='white'><tt>", elem, "</tt></td></tr>\n" ); - } - } - - ret->add( "</table></td></tr></table><br />\n" ); -} - -void build_box(Node n, String.Buffer ret, string first, string second, function layout, void|string header) { - array rows = ({}); - if(header) rows += ({ ({ header }) }); - foreach(n->get_elements(first), Node d) { - array elems = ({}); - foreach(d->get_elements(second), Node e) - elems += ({ layout(e) }); - if(d->get_first_element("text")) - rows += ({ ({ elems, parse_text(d->get_first_element("text")) }) }); - else - rows += ({ ({ elems }) }); - } - nicebox(rows, ret); -} - -string parse_text(Node n, void|String.Buffer ret) { - if(n->get_node_type()==XML_TEXT && n->get_text()) { - if(ret) - ret->add(quote(n->get_test())); - else - return quote(n->get_text()); - } - - int cast; - if(!ret) { - ret = String.Buffer(); - cast = 1; - } - - foreach(n->get_children(), Node c) { - int node_type = c->get_node_type(); - if(c->get_node_type()==XML_TEXT) { - // Don't use quote() here since we don't want to strip whitespace. - ret->add(Parser.XML.Tree.text_quote (c->get_text())); - continue; - } - - if(c->get_node_type()==XML_COMMENT) - continue; - -#ifdef DEBUG - if(c->get_node_type()!=XML_ELEMENT) { - error( "Forbidden node type " + c->get_node_type() + " in doc node.\n" ); - } -#endif - - array rows; - string name = c->get_any_name(); - switch(name) { - case "text": - ret->add("<dd>"); - parse_text(c, ret); - ret->add("</dd>\n"); - break; - - case "p": - case "b": - case "i": - case "tt": - case "sub": - case "sup": - ret->add("<", name, ">"); - parse_text(c, ret); - ret->add("</", name, ">"); - break; - - case "pre": - ret->add(lay->pre); - parse_text(c, ret); - ret->add(lay->_pre); - break; - - case "code": - ret->add(lay->code); - parse_text(c, ret); - ret->add(lay->_code); - break; - - case "expr": - ret->add(lay->expr, replace(parse_text(c), " ", " "), lay->_expr); - break; - - case "ref": - if(resolve_reference) { - ret->add(resolve_reference(parse_text(c), c->get_attributes())); - break; - } - string ref; - //ref = c->get_attributes()->resolved; - if(!ref) ref = parse_text(c); - ret->add("<font face='courier'>", ref, "</font>"); - break; - - case "dl": - ret->add("<dl>", map(c->get_elements("group"), parse_text)*"", "</dl>"); - break; - - case "item": - if(c->get_attributes()->name) - ret->add("<dt>", c->get_attributes()->name, "</dt>\n"); -#ifdef DEBUG - if(c->count_children()) - error( "dl item has a child.\n" ); -#endif - break; - - case "mapping": - build_box(c, ret, "group", "member", - lambda(Node n) { - return "<font color='green'>" + - parse_text(n->get_first_element("index")) + - "</font> : " + - parse_type(get_first_element(n->get_first_element("type"))); - }); - break; - - case "array": - build_box(c, ret, "group", "elem", - lambda(Node n) { - string index =""; - if(n->get_first_element("index")) - index = parse_text(n->get_first_element("index")); - else { - if(n->get_first_element("minindex")) - index = parse_text(n->get_first_element("minindex")); - index += ".."; - if(n->get_first_element("maxindex")) - index += parse_text(n->get_first_element("maxindex")); - } - return parse_type(get_first_element(n->get_first_element("type"))) + - " <font color='green'>" + index + "</font>"; }, "Array" ); - break; - - case "int": - build_box(c, ret, "group", "value", - lambda(Node n) { - string tmp = "<font color='green'>"; - Node min = n->get_first_element("minvalue"); - Node max = n->get_first_element("maxvalue"); - if(min || max) { - if(min) tmp += parse_text(min); - tmp += ".."; - if(max) tmp += parse_text(max); - } - else - tmp += parse_text(n); - return tmp + "</font>"; - } ); - break; - - case "mixed": - if(c->get_attributes()->name) - ret->add("<tt>", c->get_attributes()->name, "</tt> can have any of the following types:<br />"); - rows = ({}); - foreach(c->get_elements("group"), Node d) - rows += ({ ({ - ({ parse_type(get_first_element(d->get_first_element("type"))) }), - parse_text(d->get_first_element("text")) - }) }); - nicebox(rows, ret); - break; - - case "string": // Not in XSLT - build_box(c, ret, "group", "value", - lambda(Node n) { - return "<font color='green'>" + parse_text(n) + "</font>"; - } ); - break; - - case "multiset": // Not in XSLT - build_box(c, ret, "group", "index", - lambda(Node n) { - return "<font color='green'>" + - parse_text(n->get_first_element("value")) + "</font>"; - } ); - break; - - case "image": // Not in XSLT - mapping m = c->get_attributes(); - m->src = image_prefix() + m_delete(m, "file"); - ret->add( sprintf("<img%{ %s='%s'%} />", (array)m) ); - break; - - case "url": // Not in XSLT -#ifdef DEBUG - if(c->count_children()!=1 && c->get_node_type()!=XML_ELEMENT) - error( "url node has not one child. %O\n", c->html_of_node() ); -#endif - m = c->get_attributes(); - if(!m->href) - m->href=c->value_of_node(); - ret->add( sprintf("<a%{ %s='%s'%}>%s</a>", - (array)m, c->value_of_node()) ); - break; - - case "section": - if(section) - error("Section inside section.\n"); - ret->add ("<h2>", quote (c->get_attributes()->title || - // The following for bug compat. - c->get_attributes()->name), - "</h2>\n"); - if (!equal (c->get_children()->get_any_name(), ({"text"}))) - error ("Expected a single <text> element inside <section>.\n"); - section = -1; - parse_text (c->get_children()[0], ret); - section = 0; - break; - - case "ul": - ret->add( "<ul>\n" ); - foreach(c->get_elements("group"), Node c) { - ret->add("<li>"); - array(Node) d = c->get_elements("item"); - if(sizeof(d->get_attributes()->name-({0}))) { - ret->add("<b>"); - ret->add( String.implode_nicely(d->get_attributes()->name-({0})) ); - ret->add("</b>"); - } - Node e = c->get_first_element("text"); - if(e) - parse_text(e, ret); - ret->add("</li>"); - } - ret->add("</ul>"); - break; - - case "ol": - ret->add("<ol>\n"); - foreach(c->get_elements("group"), Node c) { - ret->add("<li>"); - parse_text(c->get_first_element("text"), ret); - ret->add("</li>"); - } - ret->add("</ol>"); - break; - - case "source-position": - position->update(c); - break; - - case "matrix": - ret->add( layout_matrix( map(c->get_elements("r")->get_elements("c"), - map, parse_text) ) ); - break; - - case "fixme": - ret->add("<font color='red'>FIXME: "); - parse_text(c, ret); - ret->add("</font>"); - break; - - // Not really allowed - case "br": - ret->add( sprintf("<%s%{ %s='%s'%} />", c->get_any_name(), (array)c->get_attributes()) ); - break; - case "table": - case "td": - case "tr": - case "th": - ret->add( sprintf("<%s%{ %s='%s'%}>%s</%s>", - c->get_any_name(), (array)c->get_attributes(), - parse_text(c), c->get_any_name()) ); - break; - - default: -#ifdef DEBUG - werror("\n%s\n", (string)c); - error( "Illegal element \"" + name + "\" in mode text.\n" ); -#endif - break; - } - } - - if(cast) - return ret->get(); -} - -string parse_doc(Node n, void|int no_text) { - string ret =""; - - Node c = n->get_first_element("text"); - if(c) - ret += lay->dochead + "Description" + lay->_dochead + - lay->docbody + parse_text(c) + lay->_docbody; - - foreach(n->get_elements("group"), Node c) { - Node header = c->get_first_element(); - string name = header->get_any_name(); - switch(name) { - case "param": - foreach(c->get_elements("param"), Node d) - ret += lay->dochead + "Parameter " + lay->parameter + - quote(d->get_attributes()->name) + lay->_parameter + lay->_dochead + - "<dd></dd>"; - if (c = c->get_first_element("text")) { - ret += lay->docbody + parse_text(c) + lay->_docbody; - } - break; - - case "seealso": - ret += lay->dochead + "See also" + lay->_dochead; - if (c = c->get_first_element("text")) { - ret += lay->docbody + parse_text(c) + lay->_docbody; - } - break; - - case "fixme": - ret += lay->fixmehead + "FIXME" + lay->_fixmehead; - if (c = c->get_first_element("text")) { - ret += lay->fixmebody + parse_text(c) + lay->_fixmebody; - } - break; - - case "deprecated": - ret += lay->dochead + String.capitalize(name) + lay->_dochead; - array(Node) replacements = header->get_elements("name"); - if (sizeof(replacements)) { - ret += lay->docbody + - "<p>Replaced by " + - String.implode_nicely(map(replacements, parse_text)) + ".</p>\n" + - lay->_docbody; - } - if (c = c->get_first_element("text")) { - ret += lay->docbody + parse_text(c) + lay->_docbody; - } - break; - - case "bugs": - case "note": - case "returns": - case "throws": - ret += lay->dochead + String.capitalize(name) + lay->_dochead; - if (c = c->get_first_element("text")) { - ret += lay->docbody + parse_text(c) + lay->_docbody; - } - break; - - case "example": - ret += lay->dochead + "Example" + lay->_dochead; - if (c = c->get_first_element("text")) { - ret += lay->example + parse_text(c) + lay->_example; - } - break; - - default: - error( "Unknown group type \"" + name + "\".\n" ); - } - } - - return ret; -} - -string parse_type(Node n, void|string debug) { - if(n->get_node_type()!=XML_ELEMENT) - return ""; - - string ret = ""; - Node c, d; - switch(n->get_any_name()) { - - case "object": - if(n->count_children()) { - if (resolve_reference) { - ret += "<font color='#005080'>" + - resolve_reference(n->value_of_node(), n->get_attributes()) + - "</font>"; - } else { - ret += "<font color='#005080'>" + n->value_of_node() + "</font>"; - } - } else - ret += "<font color='#202020'>object</font>"; - break; - - case "type": - ret += "<font color='#202020'>type</font>"; - if (n->count_children() && (c = get_first_element(n)) && - (c->get_any_name() != "mixed")) { - ret += "(" + parse_type(c) + ")"; - } - break; - - case "multiset": - ret += "<font color='#202020'>multiset</font>"; - c = n->get_first_element("indextype"); - if(c) ret += "(" + parse_type( get_first_element(c) ) + ")"; - break; - - case "array": - ret += "<font color='#202020'>array</font>"; - c = n->get_first_element("valuetype"); - if(c) ret += "(" + parse_type( get_first_element(c) ) + ")"; - break; - - case "mapping": - ret += "<font color='#202020'>mapping</font>"; - c = n->get_first_element("indextype"); - d = n->get_first_element("valuetype"); - if(c && d) - ret += "(" + parse_type( get_first_element(c) ) + ":" + - parse_type( get_first_element(d) ) + ")"; -#ifdef DEBUG - if( !c != !d ) - error( "Indextype/valuetype defined while the other is not in mapping.\n" ); -#endif - break; - - case "function": - ret += "<font color='#202020'>function</font>"; - array(Node) args = n->get_elements("argtype"); - d = n->get_first_element("returntype"); - // Doing different than the XSL here. Must both - // argtype and returntype be defined? - if(args || d) { - ret += "("; - if(args) ret += map(args->get_children() * ({}), parse_type)*", "; - ret += ":"; - if(d) ret += parse_type( get_first_element(d) ); - else ret += "<font color='#202020'>void</font>"; - ret += ")"; - } - break; - - case "varargs": -#ifdef DEBUG - if(!n->count_children()) - error( "varargs node must have child node.\n" ); -#endif - ret += parse_type(get_first_element(n)) + " ... "; - break; - - case "or": - ret += map(filter(n->get_children(), - lambda(Node n){ - return n->get_node_type()==XML_ELEMENT; - }), parse_type)*"|"; - break; - - case "string": case "void": case "program": case "mixed": case "float": - case "zero": - ret += "<font color='#202020'>" + n->get_any_name() + "</font>"; - break; - - case "int": - ret += "<font color='#202020'>int</font>"; - c = n->get_first_element("min"); - d = n->get_first_element("max"); - if(c && d) - ret += "(" + c->value_of_node() + ".." + d->value_of_node() + ")"; - else if(c) - ret += "(" + c->value_of_node() + "..)"; - else if(d) - ret += "(.." + d->value_of_node() + ")"; - break; - - case "attribute": - string attr = n->get_first_element("attribute")->value_of_node(); - string subtype = - parse_type(n->get_first_element("subtype")->get_first_element()); - if (n->get_first_element("prefix")) { - if (attr == "\"deprecated\"") { - ret += "<font color='#600000'>__deprecated__</font> " + subtype; - } else { - ret += sprintf("__attribute__(%s) %s", attr, subtype); - } - } else if (attr == "\"deprecated\"") { - ret += "<font color='#600000'>__deprecated__</font>(" + subtype + ")"; - } else { - ret += sprintf("__attribute__(%s, %s)", attr, subtype); - } - break; - - // Modifiers: - case "extern": // Not in XSLT - ret += "extern "; - break; - case "final": // Not in XSLT - case "nomask": // Not in XSLT - ret += "final "; - break; - case "inline": // Not in XSLT - case "local": // Not in XSLT - ret += "local "; - break; - case "optional": // Not in XSLT - ret += "optional "; - break; - case "private": // Not in XSLT - ret += "private "; - break; - case "protected": // Not in XSLT - case "static": // Not in XSLT - ret += "protected "; - break; - case "public": // Not in XSLT - // Ignored. - break; - case "variant": // Not in XSLT - ret += "variant "; - break; - - default: - error( "Illegal element " + n->get_any_name() + " in mode type.\n" ); - break; - } - return ret; -} - -void resolve_class_paths(Node n, string|void path, Node|void parent) -{ - if (!path) path = ""; - mapping attrs = n->get_attributes() || ([]); - string name = attrs->name; - switch(n->get_any_name()) { - case "arguments": - case "returntype": - case "type": - case "doc": - case "source-position": - case "modifiers": - case "classname": - // We're not interrested in the stuff under the above nodes. - return; - default: - werror("Unhandled node: %O path:%O name: %O, parent: %O\n", - n->get_any_name(), path, name, parent&&parent->get_any_name()); - // FALL_THROUGH - case "": - case "docgroup": - break; - - case "manual": - case "dir": - case "file": - case "chapter": - case "section": - case "subsection": - foreach(filter(n->get_children(), - lambda(Node n) { - return (<"manual", "dir", "file", "chapter", - "section", "subsection", "autodoc">) - [n->get_any_name()]; - }), Node child) { - resolve_class_paths(child, "", n); - } - return; - case "autodoc": - path = ""; - break; - case "namespace": - if ((<"", "lfun">)[name]) { - // Censor namespaces other than :: and lfun:: - path = name+"::"; - } else { - path = ""; - } - break; - case "class": - case "module": - attrs->class_path = path; - path += name + "."; - break; - case "enum": - // Enum names don't show up in the path. - attrs->class_path = path; - break; - case "method": - // Special case for create(). - if (name == "create") { - // Get rid of the extra dot. - attrs->class_path = path[..sizeof(path)-2]; - } - else - { - // Hide the class path for methods. - attrs->class_path = ""; - } - return; - case "constant": - case "inherit": - case "import": - case "typedef": - case "variable": - // These don't have children. - attrs->class_path = path; - return; - } - foreach(n->get_children(), Node child) - { - resolve_class_paths(child, path, n); - } -} - -#if 0 -// DEAD code (for reference only). -string render_class_path(Node n,int|void class_only) -{ - array a = reverse(n->get_ancestors(0)); - array b = a->get_any_name(); - int root; - foreach( ({ "manual", "dir", "file", "chapter", - "section", "subsection" }), string node) - root = max(root, search(b, node)); - a = a[root+1..]; - if(sizeof(a) && a[0]->get_any_name() == "autodoc") - a = a[1..]; - if (!sizeof(a)) return ""; - string ret = ""; - if ((sizeof(a) > 1) && a[-2]->get_any_name() == "enum") { - // Enum names don't show up in the path. - a = a[..sizeof(a)-3] + a[sizeof(a)-1..]; - } - if (a[0]->get_any_name() == "namespace") { - a = a->get_attributes()->name; - a[0] += "::"; - if ((<"::","lfun::">)[a[0]]) { - // Censor namespaces other than :: and lfun:: - ret = a[0]; - } - a = a[1..]; - } else { - a = a->get_attributes()->name; - } - ret += a * "."; - if (!sizeof(ret) || ret[-1] == ':') return ret; - if((<"class", "module", "enum">)[n->get_any_name()]) - return ret + "."; - - // Note we need two calls to get_parent() to skip over the docgroup. - Node parent = n->get_parent()->get_parent(); - - // Hide the enum part. - if (parent->get_any_name()=="enum") parent = parent->get_parent(); - - if(parent->get_any_name()=="class") - if( !class_only ) - return ""; //ret + "()->"; - else - return ret; - if(parent->get_any_name()=="module") - return ret + "."; - werror("Failure, raw path: %{%O, %}\n", - reverse(n->get_ancestors(0))->get_any_name()); - return " (could not resolve) "; -#ifdef DEBUG - error( "Parent module is " + n->get_parent()->get_any_name() + ".\n" ); -#else - return ""; -#endif -} -#endif /* 0 */ - -string parse_not_doc(Node n) { - string ret = ""; - int method, argument, variable, const, typedf; - - foreach(n->get_children(), Node c) { - - if(c->get_node_type()!=XML_ELEMENT) - continue; - - Node cc; - switch(c->get_any_name()) { - - case "doc": - continue; - - case "source-position": - position->update(c); - continue; - - case "method": - if(method++) ret += "<br />\n"; -#ifdef DEBUG - if(!c->get_first_element("returntype")) - continue; - // error( "No returntype element in method element.\n" ); -#endif - switch( c->get_attributes()->name ) - { - case "create": - ret += "<tt>" + parse_type(get_first_element(c->get_first_element("returntype"))); // Check for more children - ret += " "; - ret += c->get_attributes()->class_path+"<b>(</b>"; - ret += parse_not_doc( c->get_first_element("arguments") ); - ret += "<b>)</b></tt>"; - break; - default: - ret += "<tt>"; - cc = c->get_first_element("modifiers"); - if(cc) ret += map(cc->get_children(), parse_type)*" " + " "; - ret += parse_type(get_first_element(c->get_first_element("returntype"))); // Check for more children - ret += " "; - ret += c->get_attributes()->class_path; - ret += "<b><font color='#000066'>" + c->get_attributes()->name + "</font>(</b>"; - ret += parse_not_doc( c->get_first_element("arguments") ); - ret += "<b>)</b></tt>"; - break; - } - break; - - case "argument": - if(argument++) ret += ", "; - cc = c->get_first_element("value"); - if(cc) ret += "<font color='green'>" + cc->value_of_node() + "</font>"; - else if( !c->count_children() ); - else if( get_first_element(c)->get_any_name()=="type") { - ret += parse_type(get_first_element(get_first_element(c))); - if(c->get_attributes()->name) - ret += " <font color='#005080'>" + - c->get_attributes()->name + "</font>"; - } - else - error( "Malformed argument element.\n" + c->html_of_node() + "\n" ); - break; - - case "variable": - if(variable++) ret += "<br />\n"; - ret += "<tt>"; - cc = c->get_first_element("modifiers"); - if(cc) ret += map(cc->get_children(), parse_type)*" " + " "; - ret += parse_type(get_first_element(c->get_first_element("type")), "variable") + " " + - c->get_attributes()->class_path + "<b><font color='#F000F0'>" + c->get_attributes()->name + - "</font></b></tt>"; - break; - - case "constant": - if(const++) ret += "<br />\n"; - ret += "<tt>"; - cc = c->get_first_element("modifiers"); - if(cc) ret += map(cc->get_children(), parse_type)*" " + " "; - ret += "constant "; - if (Node type = c->get_first_element ("type")) - ret += parse_type (get_first_element (type), "constant") + " "; - ret += c->get_attributes()->class_path; - ret += "<font color='#F000F0'>" + c->get_attributes()->name + "</font>"; - cc = c->get_first_element("typevalue"); - if(cc) ret += " = " + parse_type(get_first_element(cc)); - ret += "</tt>"; - break; - - case "typedef": - if(typedf++) ret += "<br />\n"; - ret += "<tt>"; - cc = c->get_first_element("modifiers"); - if(cc) ret += map(cc->get_children(), parse_type)*" " + " "; - ret += "typedef "; - ret += parse_type(get_first_element(c->get_first_element("type")), "typedef") + " " + - c->get_attributes()->class_path + "<font color='#F000F0'>" + c->get_attributes()->name + - "</font></tt>"; - break; - - case "inherit": - ret += "<font face='courier'>"; - cc = c->get_first_element("modifiers"); - if(cc) ret += map(cc->get_children(), parse_type)*" " + " "; - ret += "inherit "; - Node n = c->get_first_element("classname"); - if (resolve_reference) { - ret += "</font>" + - resolve_reference(n->value_of_node(), n->get_attributes()); - } else { - ret += Parser.encode_html_entities(n->value_of_node()) + "</font>"; - } - if (c->get_attributes()->name) { - ret += "<font face='courier'> : " + "<font color='#F000F0'>" + - Parser.encode_html_entities(c->get_attributes()->name) + - "</font></font>"; - } - break; - - // We don't need import information. - case "import": - break; - - default: - error( "Illegal element " + c->get_any_name() + " in !doc.\n" ); - break; - } - } - - return ret; -} - -int foo; - -string parse_docgroup(Node n) { - mapping m = n->get_attributes(); - string ret = lay->docgroup; - - if(lay->typehead) { - ret += lay->typehead; - if(m["homogen-type"]) { - string type = "<font face='Helvetica'>" + - quote(String.capitalize(m["homogen-type"])) + - "</font>\n"; - if(m["homogen-name"]) { - ret += type + "<font size='+1'><b>" + - quote((m->belongs?m->belongs+" ":"") + m["homogen-name"]) + - "</b></font>\n"; - } else { - array(string) names = - Array.uniq(map(n->get_elements(m["homogen-type"]), - lambda(Node child) { - return child->get_attributes()->name || - child->value_of_node(); - })); - foreach(names, string name) - ret += type + "<font size='+1'><b>" + name + "</b></font><br />\n"; - } - } - else - ret += "Syntax"; - ret += lay->_typehead; - } - - ret += lay->ndochead; - - ret += parse_not_doc(n); - - ret += lay->_ndochead; - - foreach(n->get_elements("doc"), Node c) - ret += parse_doc(c); - - return ret + lay->_docgroup; -} - -string parse_children(Node n, string tag, function cb, mixed ... args) { - string ret = ""; - foreach(n->get_elements(tag), Node c) - ret += cb(c, @args); - - return ret; -} - -string manual_title = "Pike Reference Manual"; -string frame_html(string res, void|string title) { - title = title || manual_title; - return "<html><head><title>" + quote(title) + "</title></head>\n" - "<body bgcolor='white' text='black'>\n" + res + - "</body></html>"; -} - -string layout_toploop(Node n) { - string res = ""; - foreach(n->get_elements(), Node c) - switch(c->get_any_name()) { - - case "dir": - string cwd; - if(dest_path) - { - cwd=getcwd(); - cd(dest_path); - } - Stdio.mkdirhier(c->get_attributes()->name); - layout_toploop(c); - if(cwd) - cd(cwd); - break; - - case "file": - if(dest_path) - { - cwd=getcwd(); - cd(dest_path); - } - Stdio.write_file( c->get_attributes()->name, - frame_html(layout_toploop(c)) ); - if(cwd) - cd(cwd); - break; - - case "chapter": - res += parse_chapter(c); - break; - - default: - error("Unknown element %O.\n", c->get_any_name()); - } - return res; -} - -int main(int num, array args) { - - int t = time(); - string title; - - foreach(Getopt.find_all_options(args, ({ - ({ "img", Getopt.HAS_ARG, "--img" }), - ({ "dest", Getopt.HAS_ARG, "--dest" }), - ({ "title", Getopt.HAS_ARG, "--title" }), - ({ "defns", Getopt.HAS_ARG, "--default-ns" }), - ({ "help", Getopt.NO_ARG, "--help" }), - })), array opt) - switch(opt[0]) { - case "img": - image_path = opt[1]; - break; - case "dest": - dest_path = opt[1]; - break; - case "title": - title = opt[1]; - break; - case "defns": - default_ns = opt[1]; - break; - case "help": - write(#"make_html.pike [args] <input file> [<output file>] ---img=<image path> ---dest=<destination path> - ---title=<document title> -"); - break; - } - args = Getopt.get_args(args)[1..]; - - if(!sizeof(args)) - error( "No input file given.\n" ); - - string file = Stdio.read_file(args[0]); - if(!file) - error( "Could not read %s.\n", args[0] ); - if(!sizeof(file)) - error( "%s is empty.\n", args[0] ); - - // We are only interested in what's in the - // module container. - werror("Parsing %O...\n", args[0]); - Node n = Parser.XML.Tree.simple_parse_input(file); - - Node n2 = n->get_first_element("manual"); - if(!n2) { - n2 = n->get_first_element("autodoc"); - if(!n2) { - werror("Not an autodoc XML file.\n"); - return 0; - } - - Node chap = Node(XML_ELEMENT, "chapter", - (["title":title||"Documentation"]), 0); - chap->add_child( n2 ); - - string fn = sizeof(args)>1 ? args[1] : "index.html"; - Node file = Node(XML_ELEMENT, "file", (["name":fn]), 0); - file->add_child( chap ); - - Node top = Node(XML_ELEMENT, "manual", ([]), 0); - top->add_child( file ); - n = top; - } - else - n = n2; - - werror("Resolving class paths...\n"); - resolve_class_paths(n); - - mapping m = n->get_attributes(); - - werror("Layouting...\n"); - manual_title = m->title || (m->version?"Reference Manual for "+m->version:"Pike Reference Manual"); - layout_toploop(n); - werror("Took %d seconds.\n\n", time()-t); - - return 0; -} +inherit Tools.Standalone.autodoc_to_html; diff --git a/refdoc/presentation/tree-split-autodoc.pike b/refdoc/presentation/tree-split-autodoc.pike index 799d51f505..0aa83cd205 100644 --- a/refdoc/presentation/tree-split-autodoc.pike +++ b/refdoc/presentation/tree-split-autodoc.pike @@ -1,730 +1,5 @@ /* + * Compat place-holder. */ -inherit "make_html.pike"; - -mapping (string:Node) refs = ([ ]); -string default_namespace; - -string extra_prefix = ""; -string image_prefix() -{ - return extra_prefix + ::image_prefix(); -} - -int unresolved; -mapping profiling = ([]); -#define PROFILE int profilet=gethrtime -#define ENDPROFILE(X) profiling[(X)] += gethrtime()-profilet; - -string cquote(string n) -{ - string ret=""; - // n = replace(n, ([ ">":">", "<":"<", "&":"&" ])); - while(sscanf((string)n,"%[._a-zA-Z0-9]%c%s",string safe, int c, n)==3) { - switch(c) { - default: ret += sprintf("%s_%02X",safe,c); break; - case '+': ret += sprintf("%s_add",safe); break; - case '`': ret += sprintf("%s_backtick",safe); break; - case '=': ret += sprintf("%s_eq",safe); break; - } - } - ret += n; - return ret; -} - -string create_reference(string from, string to, string text) { - array a = (to/"::"); - switch(sizeof(a)) { - case 2: - if (sizeof(a[1])) { - // Split trailing module path. - a = a[0..0] + a[1]/"."; - } else { - // Get rid of trailing "". - a = a[0..0]; - } - a[0] += "::"; - break; - case 1: - a = a[0]/"."; - break; - default: - error("Bad reference: %O\n", to); - } - return "<font face='courier'><a href='" + - "../"*max(sizeof(from/"/") - 2, 0) + map(a, cquote)*"/" + ".html'>" + - text + - "</a></font>"; -} - -multiset missing = (< "foreach", "catch", "throw", "sscanf", "gauge", "typeof" >); - - -class Node -{ - string type; - string name; - string data; - array(Node) class_children = ({ }); - array(Node) module_children = ({ }); - array(Node) enum_children = ({ }); - array(Node) method_children = ({ }); - - Node parent; - - string _sprintf() { - return sprintf("Node(%O,%O,%d)", type, name, data?sizeof(data):0); - } - - void create(string _type, string _name, string _data, void|Node _parent) - { - if(!_type || !_name) throw( ({ "No type or name\n", backtrace() }) ); - type = _type; - name = _name; - parent = _parent; - data = get_parser()->finish( _data )->read(); - - string path = raw_class_path(); - refs[path] = this_object(); - - sort(class_children->name, class_children); - sort(module_children->name, module_children); - sort(enum_children->name, enum_children); - sort(method_children->name, method_children); - - method_children = check_uniq(method_children); - - /* - foreach(method_children, Node m) - if( (<"create","destroy">)[m->name] ) { - method_children -= ({ m }); - string d; - sscanf(m->data, "%*s<docgroup%s/docgroup>", d); - if(d) - data += "<docgroup" + d + "/docgroup>"; - } - */ - - data = make_faked_wrapper(data); - } - - array(Node) check_uniq(array children) { - array names = children->name; - foreach(Array.uniq(names), string n) - if(sizeof(filter(names, lambda(string in) { return in==n; }))!=1) { - string d=""; - Parser.HTML parser = Parser.HTML(); - parser->case_insensitive_tag(1); - parser->xml_tag_syntax(3); - parser->add_container("docgroup", - lambda(Parser.HTML p, mapping m, string c) { - d += sprintf("<docgroup%{ %s='%s'%}>%s</docgroup>", - (array)m, c); - return ""; - }); - - foreach(children, Node c) - if(c->name==n) parser->feed(c->data); - parser->finish(); - d = make_faked_wrapper(d); - - foreach(children; int i; Node c) - if(c->name==n) { - if(d) { - c->data = d; - d = 0; - } - else children[i] = 0; - } - children -= ({ 0 }); - } - return children; - } - - protected string parse_node(Parser.HTML p, mapping m, string c) { - if(!m->name) error("Unnamed %O %O\n", p->tag_name(), m); - this_object()[p->tag_name()+"_children"] += - ({ Node( p->tag_name(), m->name, c, this_object() ) }); - return ""; - } - - array(string) my_parse_docgroup(Parser.HTML p, mapping m, string c) - { - foreach(({"homogen-name", "belongs"}), string attr) { - if (m[attr]) m[attr] = Parser.parse_html_entities(m[attr]); - } - if(m["homogen-type"]) { - switch( m["homogen-type"] ) { - - case "method": - if( m["homogen-name"] ) { - string name = m["homogen-name"]; - if(m->belongs) { - if(m->belongs[-1]==':') name = m->belongs + name; - else name = m->belongs + "." + name; - } - method_children += - ({ Node( "method", name, c, this_object() ) }); - return ({ "" }); - } - - // Several different methods documented with the same blurb. - array names = ({}); - Parser.HTML parser = Parser.HTML(); - parser->case_insensitive_tag(1); - parser->xml_tag_syntax(0); - parser->add_tag("method", - lambda(Parser.HTML p, mapping m) { - names += ({ Parser.parse_html_entities(m->name) }); - } ); - parser->finish(c); - foreach(Array.uniq(names) - ({ 0, "" }), string name) { - method_children += - ({ Node( "method", name, c, this_object() ) }); - } - return ({ "" }); - break; - - case "constant": - case "variable": - case "inherit": - string path = raw_class_path(); - if(sizeof(path) && (path[-1] != ':')) path += "."; - if(!m["homogen-name"]) { - Parser.HTML()->add_tags - ( ([ "constant": - lambda(Parser.HTML p, mapping m, string c) { - string name = Parser.parse_html_entities(m->name); - refs[path + name] = - Node( "constant", name, "", this_object()); - }, - "variable": - lambda(Parser.HTML p, mapping m, string c) { - string name = Parser.parse_html_entities(m->name); - refs[path + name] = - Node( "variable", name, "", this_object()); - }, - "inherit": - lambda(Parser.HTML p, mapping m, string c) { - if (m->name) { - string name = Parser.parse_html_entities(m->name); - refs[path + name] = - Node( "inherit", name, "", this_object()); - } - }, - ]) )->finish(c); - } - else - refs[path + m["homogen-name"]] = - Node( m["homogen-type"], m["homogen-name"], "", this_object()); - break; - - } - } - - return 0; - } - - protected Parser.HTML get_parser() { - Parser.HTML parser = Parser.HTML(); - - parser->case_insensitive_tag(1); - parser->xml_tag_syntax(3); - - parser->add_container("docgroup", my_parse_docgroup); - parser->add_container("module", parse_node); - parser->add_container("class", parse_node); - parser->add_container("enum", parse_node); - - return parser; - } - - string make_faked_wrapper(string s) - { - if(type=="method") - s = sprintf("<docgroup homogen-type='method' homogen-name='%s'>\n" - "%s\n</docgroup>\n", - Parser.encode_html_entities(name), s); - else - s = sprintf("<%s name='%s'>\n%s\n</%s>\n", - type, Parser.encode_html_entities(name), s, type); - if(parent) - return parent->make_faked_wrapper(s); - else - return s; - } - - string _make_filename_low; - string make_filename_low() - { - if(_make_filename_low) return _make_filename_low; - if (type == "namespace") { - _make_filename_low = parent->make_filename_low()+"/"+cquote(name+"::"); - } else { - _make_filename_low = parent->make_filename_low()+"/"+cquote(name); - } - return _make_filename_low; - } - - string make_filename() - { - return make_filename_low()[5..]+".html"; - } - - string make_link(Node to) - { - // FIXME: Optimize the length of relative links - int num_segments = sizeof(make_filename()/"/") - 1; - return ("../"*num_segments)+to->make_filename(); - } - - array(Node) get_ancestors() - { - PROFILE(); - array tmp = ({ this_object() }) + parent->get_ancestors(); - ENDPROFILE("get_ancestors"); - return tmp; - } - - string my_resolve_reference(string _reference, mapping vars) - { - array(string) resolved = vars->resolved && vars->resolved/"\0"; - if(default_namespace && has_prefix(_reference, default_namespace+"::")) - _reference = _reference[sizeof(default_namespace)+2..]; - - if(vars->param) - return "<font face='courier'>" + _reference + "</font>"; - - if (resolved) { - foreach (resolved, string resolution) { - Node res_obj; - - if(res_obj = refs[resolution]) { - while(res_obj && (<"constant","variable">)[res_obj->type]) { - res_obj = res_obj->parent; - } - if (!res_obj) { - werror("Found no page to link to for reference %O (%O)\n", - _reference, resolution); - return sprintf("<font face='courier'>" + _reference + "</font>"); - } - // FIXME: Assert that the reference is correct? - return create_reference(make_filename(), - res_obj->raw_class_path(), - _reference); - } - - // Might be a reference to a parameter. - // Try cutting of the last section, and retry. - array(string) tmp = resolution/"."; - if ((sizeof(tmp) > 1) && (res_obj = refs[tmp[..sizeof(tmp)-2]*"."])) { - if (res_obj == this_object()) { - return sprintf("<font face='courier'>" + _reference + "</font>"); - } - return create_reference(make_filename(), - res_obj->raw_class_path(), - _reference); - } - - if (!zero_type(refs[resolution])) { - werror("Reference %O (%O) is %O!\n", - _reference, resolution, refs[resolution]); - } - } - if (!missing[vars->resolved] && !has_prefix(_reference, "lfun::")) { - werror("Missed reference %O (%{%O, %}) in %s\n", - _reference, resolved||({}), make_class_path()); -#if 0 - werror("Potential refs:%O\n", - sort(map(resolved, - lambda (string resolution) { - return filter(indices(refs), - glob(resolution[..sizeof(resolution)/2]+ - "*", indices(refs)[*])); - }) * ({}))); -#endif /* 0 */ - } - unresolved++; - } - return "<font face='courier'>" + _reference + "</font>"; - } - - string _make_class_path; - string _raw_class_path; - string make_class_path(void|int(0..1) header) - { - if(_make_class_path) return _make_class_path; - array a = reverse(parent->get_ancestors()); - - _make_class_path = ""; - _raw_class_path = ""; - foreach(a, Node n) - { - // Hide most namepaces from the class path. - if (n->type == "namespace") { - _raw_class_path += n->name + "::"; - if ((<"","lfun">)[n->name]) { - _make_class_path += n->name + "::"; - } - } else { - _raw_class_path += n->name + "."; - _make_class_path += n->name; - if(n->type=="class") - _make_class_path += "()->"; - else if(n->type=="module") - _make_class_path += "."; - } - } - _make_class_path += name; - _raw_class_path += name; - - if(type=="method") { - _make_class_path += "()"; - } else if (type == "namespace") { - _make_class_path += "::"; - _raw_class_path += "::"; - } - - return _make_class_path; - } - string raw_class_path(void|int(0..1) header) - { - if(_raw_class_path) return _raw_class_path; - make_class_path(header); - return _raw_class_path; - } - - string make_navbar_really_low(array(Node) children, string what) - { - if(!sizeof(children)) return ""; - - String.Buffer res = String.Buffer(3000); - res->add("<tr><td nowrap='nowrap'><br /><b>", what, "</b></td></tr>\n"); - - foreach(children, Node node) - { - string my_name = Parser.encode_html_entities(node->name); - if(node->type=="method") - my_name+="()"; - else if (node->type == "namespace") { - my_name="<b>"+my_name+"::</b>"; - } - else - my_name="<b>"+my_name+"</b>"; - - res->add("<tr><td nowrap='nowrap'> "); - if(node==this_object()) - res->add( my_name ); - else - res->add( "<a href='", make_link(node), "'>", my_name, "</a>" ); - res->add("</td></tr>\n"); - } - return (string)res; - } - - string make_hier_list(Node node) - { - string res=""; - - if(node) - { - if(node->type=="namespace" && node->name==default_namespace) - node = node->parent; - res += make_hier_list(node->parent); - - string my_class_path = - (node->is_TopNode)?"[Top]":node->make_class_path(); - - if(node == this_object()) - res += sprintf("<b>%s</b><br />\n", - Parser.encode_html_entities(my_class_path)); - else - res += sprintf("<a href='%s'><b>%s</b></a><br />\n", - make_link(node), - Parser.encode_html_entities(my_class_path)); - } - return res; - } - - string make_navbar_low(Node root) - { - string res=""; - - res += make_hier_list(root); - - res+="<table border='0' cellpadding='1' cellspacing='0' class='sidebar'>"; - - res += make_navbar_really_low(root->module_children, "Modules"); - - res += make_navbar_really_low(root->class_children, "Classes"); - - if(root->is_TopNode) - res += make_navbar_really_low(root->namespace_children, "Namespaces"); - else { - res += make_navbar_really_low(root->enum_children, "Enums"); - res += make_navbar_really_low(root->method_children, "Methods"); - } - - return res+"</table>"; - } - - string make_navbar() - { - if(type=="method") - return make_navbar_low(parent); - else - return make_navbar_low(this_object()); - } - - array(Node) find_siblings() - { - return - parent->class_children+ - parent->module_children+ - parent->enum_children+ - parent->method_children; - } - - array(Node) find_children() - { - return - class_children+ - module_children+ - enum_children+ - method_children; - } - - Node find_prev_node() - { - array(Node) siblings = find_siblings(); - int index = search( siblings, this_object() ); - - Node tmp; - - if(index==0 || index == -1) - return parent; - - tmp = siblings[index-1]; - - while(sizeof(tmp->find_children())) - tmp = tmp->find_children()[-1]; - - return tmp; - } - - Node find_next_node(void|int dont_descend) - { - if(!dont_descend && sizeof(find_children())) - return find_children()[0]; - - array(Node) siblings = find_siblings(); - int index = search( siblings, this_object() ); - - Node tmp; - if(index==sizeof(siblings)-1) - tmp = parent->find_next_node(1); - else - tmp = siblings[index+1]; - return tmp; - } - - protected string make_content() { - PROFILE(); - string err; - Parser.XML.Tree.Node n; - if(err = catch( n = Parser.XML.Tree.parse_input(data)[0] )) { - werror(err + "\n" + data); - exit(1); - } - ENDPROFILE("XML.Tree"); - - resolve_reference = my_resolve_reference; - - String.Buffer contents = String.Buffer(100000); - resolve_class_paths(n); - contents->add( parse_children(n, "docgroup", parse_docgroup, 1) ); - contents->add( parse_children(n, "namespace", parse_namespace, 1) ); - contents->add( parse_children(n, "module", parse_module, 1) ); - contents->add( parse_children(n, "class", parse_class, 1) ); - contents->add( parse_children(n, "enum", parse_enum, 1) ); - - n->zap_tree(); - - return (string)contents; - } - - void make_html(string template, string path) - { - class_children->make_html(template, path); - module_children->make_html(template, path); - enum_children->make_html(template, path); - method_children->make_html(template, path); - - int num_segments = sizeof(make_filename()/"/")-1; - string style = ("../"*num_segments)+"style.css"; - extra_prefix = "../"*num_segments; - - Node prev = find_prev_node(); - Node next = find_next_node(); - string next_url="", next_title="", prev_url="", prev_title=""; - if(next) { - next_title = next->make_class_path(); - next_url = make_link(next); - } - if(prev) { - prev_title = prev->make_class_path(); - prev_url = make_link(prev); - } - - string res = replace(template, - (["$navbar$": make_navbar(), - "$contents$": make_content(), - "$prev_url$": prev_url, - "$prev_title$": _Roxen.html_encode_string(prev_title), - "$next_url$": next_url, - "$next_title$": _Roxen.html_encode_string(next_title), - "$type$": String.capitalize(type), - "$title$": _Roxen.html_encode_string(make_class_path(1)), - "$style$": style, - "$dotdot$": sizeof(extra_prefix)?extra_prefix:".", - "$imagedir$":image_prefix(), - "$filename$": _Roxen.html_encode_string(make_filename()), - ])); - - Stdio.mkdirhier(combine_path(path+"/"+make_filename(), "../")); - Stdio.write_file(path+"/"+make_filename(), res); - } -} - -class TopNode { - inherit Node; - - constant is_TopNode = 1; - array(Node) namespace_children = ({ }); - - void create(string _data) { - PROFILE(); - Parser.HTML parser = Parser.HTML(); - parser->case_insensitive_tag(1); - parser->xml_tag_syntax(3); - parser->add_container("autodoc", - lambda(Parser.HTML p, mapping args, string c) { - return ({ c }); }); - - _data = parser->finish(_data)->read(); - ::create("autodoc", "", _data); - sort(namespace_children->name, namespace_children); - foreach(namespace_children, Node x) - if(x->type=="namespace" && x->name==default_namespace) { - // namespace_children -= ({ x }); - class_children += x->class_children; - module_children += x->module_children; - enum_children += x->enum_children; - method_children += x->method_children; - } - type = "autodoc"; - ENDPROFILE("top_create"); - } - - Parser.HTML get_parser() { - Parser.HTML parser = ::get_parser(); - parser->add_container("namespace", parse_node); - return parser; - } - - string make_filename_low() { return "__index"; } - string make_filename() { return "index.html"; } - array(Node) get_ancestors() { return ({ }); } - int(0..0) find_prev_node() { return 0; } - int(0..0) find_next_node() { return 0; } - string make_class_path(void|int(0..1) header) { - if(header && sizeof(method_children)) { - if(default_namespace) - return "namespace "+default_namespace; - else - return "Namespaces"; - } - return ""; - } - string raw_class_path(void|int(0..1) header) { - return ""; - } - - string make_method_page(array(Node) children) - { - String.Buffer res = String.Buffer(3500); - foreach(children, Node node) - res->add(" <a href='", make_link(node), "'>", - Parser.encode_html_entities(node->name), - "()</a><br />\n"); - return (string)res; - } - - string make_content() { - resolve_reference = my_resolve_reference; - if(!sizeof(method_children)) return ""; - - string contents = "<table class='sidebar'><tr>"; - foreach(method_children/( sizeof(method_children)/4.0 ), - array(Node) children) - contents += "<td nowrap='nowrap' valign='top'>" + - make_method_page(children) + "</td>"; - - contents += "</tr><tr><td colspan='4' nowrap='nowrap'>" + - parse_children(Parser.XML.Tree.parse_input(data), - "docgroup", parse_docgroup, 1) + - "</td></tr></table>"; - - return contents; - } - - void make_html(string template, string path) { - PROFILE(); - namespace_children->make_html(template, path); - ::make_html(template, path); - ENDPROFILE("top_make_html"); - } -} - -int main(int argc, array(string) argv) -{ - PROFILE(); - if(argc<4) { - werror("Too few arguments. (in-file, template, out-dir (, namespace))\n"); - return 1; - } - if(argc>4) default_namespace=argv[4]; - - werror("Reading refdoc blob %s...\n", argv[1]); - string doc = Stdio.read_file(argv[1]); - if(!doc) { - werror("Failed to load refdoc blob %s.\n", argv[1]); - return 1; - } - - werror("Reading template file %s...\n", argv[2]); - string template = Stdio.read_file(argv[2]); - if(!template) { - werror("Failed to load template %s.\n", argv[2]); - return 1; - } - mapping m = localtime(time()); - template = replace(template, - ([ "$version$":version(), - "$date$":sprintf("%4d-%02d-%02d", - m->year+1900, m->mon+1, m->mday), - ]) ); - - werror("Splitting to destination directory %s...\n", argv[3]); - TopNode top = TopNode(doc); - top->make_html(template, argv[3]); - ENDPROFILE("main"); - - foreach(sort(indices(profiling)), string f) - werror("%s: %.1f\n", f, profiling[f]/1000000.0); - werror("%d unresolved references.\n", unresolved); - werror("%d documented constants/variables/functions/classes/modules.\n", - sizeof(refs)); - return 0; -} +inherit Tools.Standalone.autodoc_to_split_html; -- GitLab