Create HTML documentation for C API
Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/fca91dce Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/fca91dce Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/fca91dce Branch: refs/heads/markdown Commit: fca91dcec74c99046a94ee9072f5525c7fc36e2b Parents: a4c0106 Author: Nick Wellnhofer <[email protected]> Authored: Sun Nov 9 23:32:16 2014 +0100 Committer: Nick Wellnhofer <[email protected]> Committed: Sun Nov 23 18:35:49 2014 +0100 ---------------------------------------------------------------------- compiler/c/cfc.c | 1 + compiler/src/CFCC.c | 186 ++++++++++--- compiler/src/CFCC.h | 5 + compiler/src/CFCCHTML.c | 602 +++++++++++++++++++++++++++++++++++++++++++ compiler/src/CFCCHTML.h | 42 +++ 5 files changed, 807 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/fca91dce/compiler/c/cfc.c ---------------------------------------------------------------------- diff --git a/compiler/c/cfc.c b/compiler/c/cfc.c index ae8828f..6460e53 100644 --- a/compiler/c/cfc.c +++ b/compiler/c/cfc.c @@ -239,6 +239,7 @@ main(int argc, char **argv) { CFCC_write_hostdefs(c_binding); if (args.num_source_dirs != 0) { CFCC_write_callbacks(c_binding); + CFCC_write_html_docs(c_binding); CFCC_write_man_pages(c_binding); } http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/fca91dce/compiler/src/CFCC.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCC.c b/compiler/src/CFCC.c index 067e84e..6bd7165 100644 --- a/compiler/src/CFCC.c +++ b/compiler/src/CFCC.c @@ -16,16 +16,21 @@ #include "charmony.h" +#include <ctype.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #define CFC_NEED_BASE_STRUCT_DEF #include "CFCBase.h" #include "CFCC.h" +#include "CFCCHTML.h" #include "CFCCMan.h" #include "CFCClass.h" #include "CFCHierarchy.h" #include "CFCMethod.h" +#include "CFCParcel.h" +#include "CFCSymbol.h" #include "CFCUtil.h" struct CFCC { @@ -46,6 +51,12 @@ static const CFCMeta CFCC_META = { static char* S_callback_decs(CFCClass *klass); +static int +S_compare_class_name(const void *va, const void *vb); + +static void +S_write_html_docs(CFCC *self, CFCParcel *parcel, CFCClass **classes); + CFCC* CFCC_new(CFCHierarchy *hierarchy, const char *header, const char *footer) { CFCC *self = (CFCC*)CFCBase_allocate(&CFCC_META); @@ -145,6 +156,151 @@ S_callback_decs(CFCClass *klass) { } void +CFCC_write_hostdefs(CFCC *self) { + const char pattern[] = + "%s\n" + "\n" + "#ifndef H_CFISH_HOSTDEFS\n" + "#define H_CFISH_HOSTDEFS 1\n" + "\n" + "#define CFISH_OBJ_HEAD \\\n" + " size_t refcount;\n" + "\n" + "#endif /* H_CFISH_HOSTDEFS */\n" + "\n" + "%s\n"; + char *content + = CFCUtil_sprintf(pattern, self->c_header, self->c_footer); + + // Unlink then write file. + const char *inc_dest = CFCHierarchy_get_include_dest(self->hierarchy); + char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfish_hostdefs.h", + inc_dest); + remove(filepath); + CFCUtil_write_file(filepath, content, strlen(content)); + FREEMEM(filepath); + + FREEMEM(content); +} + +void +CFCC_write_html_docs(CFCC *self) { + CFCHierarchy *hierarchy = self->hierarchy; + CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy); + CFCParcel **parcels = CFCParcel_all_parcels(); + + size_t num_classes = 0; + for (size_t i = 0; ordered[i] != NULL; i++) { + ++num_classes; + } + + qsort(ordered, num_classes, sizeof(*ordered), S_compare_class_name); + + for (size_t i = 0; parcels[i] != NULL; ++i) { + CFCParcel *parcel = parcels[i]; + if (!CFCParcel_included(parcel)) { + S_write_html_docs(self, parcel, ordered); + } + } + + FREEMEM(ordered); +} + +static int +S_compare_class_name(const void *va, const void *vb) { + const char *a = CFCClass_get_class_name(*(CFCClass**)va); + const char *b = CFCClass_get_class_name(*(CFCClass**)vb); + + return strcmp(a, b); +} + +static void +S_write_html_docs(CFCC *self, CFCParcel *parcel, CFCClass **classes) { + CFCHierarchy *hierarchy = self->hierarchy; + const char *prefix = CFCParcel_get_prefix(parcel); + + size_t num_classes = 0; + for (size_t i = 0; classes[i] != NULL; i++) { + CFCClass *klass = classes[i]; + if (strcmp(CFCClass_get_prefix(klass), prefix) != 0 + || !CFCSymbol_public((CFCSymbol*)klass) + ) { + continue; + } + ++num_classes; + } + + if (num_classes == 0) { return; } + + size_t max_docs = num_classes + 1; + char **html_docs = (char**)CALLOCATE(max_docs, sizeof(char*)); + size_t num_docs = 0; + + html_docs[num_docs++] = CFCCHTML_create_index_doc(parcel, classes); + + // Generate HTML docs, but don't write. That way, if there's an error + // while generating the pages, we leak memory but don't clutter up the file + // system. + for (size_t i = 0; classes[i] != NULL; i++) { + CFCClass *klass = classes[i]; + if (strcmp(CFCClass_get_prefix(klass), prefix) != 0 + || !CFCSymbol_public((CFCSymbol*)klass) + ) { + continue; + } + html_docs[num_docs++] = CFCCHTML_create_html_doc(klass); + } + + const char *dest = CFCHierarchy_get_dest(hierarchy); + char *doc_path + = CFCUtil_sprintf("%s" CHY_DIR_SEP "share" CHY_DIR_SEP "doc" + CHY_DIR_SEP "clownfish", dest); + if (!CFCUtil_is_dir(doc_path)) { + CFCUtil_make_path(doc_path); + if (!CFCUtil_is_dir(doc_path)) { + CFCUtil_die("Can't make path %s", doc_path); + } + } + + num_docs = 0; + + { + char *html_doc = html_docs[num_docs++]; + char *nickname = CFCUtil_strdup(CFCParcel_get_nickname(parcel)); + for (size_t i = 0; nickname[i]; ++i) { + nickname[i] = tolower(nickname[i]); + } + char *filename = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.html", doc_path, + nickname); + CFCUtil_write_if_changed(filename, html_doc, strlen(html_doc)); + FREEMEM(filename); + FREEMEM(nickname); + FREEMEM(html_doc); + } + + // Write out any HTML docs that have changed. + for (size_t i = 0; classes[i] != NULL; i++) { + CFCClass *klass = classes[i]; + if (strcmp(CFCClass_get_prefix(klass), prefix) != 0 + || !CFCSymbol_public((CFCSymbol*)klass) + ) { + continue; + } + + char *html_doc = html_docs[num_docs++]; + const char *full_struct_sym = CFCClass_full_struct_sym(klass); + char *filename = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.html", doc_path, + full_struct_sym); + CFCUtil_write_if_changed(filename, html_doc, strlen(html_doc)); + FREEMEM(filename); + FREEMEM(html_doc); + } + + FREEMEM(doc_path); + FREEMEM(html_docs); +} + +void CFCC_write_man_pages(CFCC *self) { CFCHierarchy *hierarchy = self->hierarchy; CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy); @@ -157,7 +313,7 @@ CFCC_write_man_pages(CFCC *self) { char **man_pages = (char**)CALLOCATE(num_classes, sizeof(char*)); // Generate man pages, but don't write. That way, if there's an error - // while generating the pages, we leak memory but don't clutter up the file + // while generating the pages, we leak memory but don't clutter up the file // system. for (size_t i = 0, j = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; @@ -201,32 +357,4 @@ CFCC_write_man_pages(CFCC *self) { FREEMEM(ordered); } -void -CFCC_write_hostdefs(CFCC *self) { - const char pattern[] = - "%s\n" - "\n" - "#ifndef H_CFISH_HOSTDEFS\n" - "#define H_CFISH_HOSTDEFS 1\n" - "\n" - "#define CFISH_OBJ_HEAD \\\n" - " size_t refcount;\n" - "\n" - "#endif /* H_CFISH_HOSTDEFS */\n" - "\n" - "%s\n"; - char *content - = CFCUtil_sprintf(pattern, self->c_header, self->c_footer); - - // Unlink then write file. - const char *inc_dest = CFCHierarchy_get_include_dest(self->hierarchy); - char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfish_hostdefs.h", - inc_dest); - remove(filepath); - CFCUtil_write_file(filepath, content, strlen(content)); - FREEMEM(filepath); - - FREEMEM(content); -} - http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/fca91dce/compiler/src/CFCC.h ---------------------------------------------------------------------- diff --git a/compiler/src/CFCC.h b/compiler/src/CFCC.h index 6f665ed..c52ebae 100644 --- a/compiler/src/CFCC.h +++ b/compiler/src/CFCC.h @@ -54,6 +54,11 @@ CFCC_write_callbacks(CFCC *self); void CFCC_write_hostdefs(CFCC *self); +/** Write the HTML documentation. + */ +void +CFCC_write_html_docs(CFCC *self); + /** Write all man pages. */ void http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/fca91dce/compiler/src/CFCCHTML.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCCHTML.c b/compiler/src/CFCCHTML.c new file mode 100644 index 0000000..12ebbab --- /dev/null +++ b/compiler/src/CFCCHTML.c @@ -0,0 +1,602 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string.h> + +#include <cmark.h> + +#include "charmony.h" +#include "CFCCHTML.h" +#include "CFCClass.h" +#include "CFCDocuComment.h" +#include "CFCFunction.h" +#include "CFCMethod.h" +#include "CFCParamList.h" +#include "CFCParcel.h" +#include "CFCSymbol.h" +#include "CFCType.h" +#include "CFCUtil.h" +#include "CFCVariable.h" + +#ifndef true + #define true 1 + #define false 0 +#endif + +// TODO: Pass HTML header and footer as option. + +static const char html_header[] = + "<!DOCTYPE html>\n" + "<html>\n" + "<head>\n" + "<meta name=\"viewport\" content=\"width=device-width\" />\n" + "<style type=\"text/css\">\n" + "body {\n" + " font-family: sans-serif;\n" + " font-size: 0.85em;\n" + " max-width: 640px;\n" + "}\n" + "dt {\n" + " font-weight: bold;\n" + "}\n" + "pre {\n" + " border: 1px solid #000;\n" + " padding: 0.2em 0.4em;\n" + " background: #f6f6f6;\n" + " font-size: 1.2em;\n" + "}\n" + "code {\n" + " font-family: \"Consolas\", \"Menlo\", monospace;\n" + "}\n" + "</style>\n" + "</head>\n" + "<body>\n"; + +static const char html_footer[] = + "</body>\n" + "</html>\n"; + +static char* +S_html_create_name(CFCClass *klass); + +static char* +S_html_create_synopsis(CFCClass *klass); + +static char* +S_html_create_description(CFCClass *klass); + +static char* +S_html_create_functions(CFCClass *klass); + +static char* +S_html_create_methods(CFCClass *klass); + +static char* +S_html_create_inherited_methods(CFCClass *klass); + +static char* +S_html_create_func(CFCClass *klass, CFCFunction *func, const char *short_sym, + const char *full_sym); + +static char* +S_html_create_param_list(CFCClass *klass, CFCFunction *func, + const char *full_sym); + +static char* +S_html_create_inheritance(CFCClass *klass); + +static char* +S_md_to_html(const char *md); + +static void +S_convert_uris(cmark_node *node); + +static void +S_convert_uri(cmark_node *link); + +static char* +S_type_to_html(CFCClass *klass, CFCType *type); + +char* +CFCCHTML_create_index_doc(CFCParcel *parcel, CFCClass **classes) { + const char *prefix = CFCParcel_get_prefix(parcel); + const char *parcel_name = CFCParcel_get_name(parcel); + char *class_list = CFCUtil_strdup(""); + + for (size_t i = 0; classes[i] != NULL; i++) { + CFCClass *klass = classes[i]; + if (strcmp(CFCClass_get_prefix(klass), prefix) != 0 + || !CFCSymbol_public((CFCSymbol*)klass) + ) { + continue; + } + + const char *struct_sym = CFCClass_full_struct_sym(klass); + const char *class_name = CFCClass_get_class_name(klass); + class_list + = CFCUtil_cat(class_list, "<li><a href=\"", struct_sym, ".html\">", + class_name, "</a></li>\n", NULL); + } + + const char pattern[] = + "%s" + "<h1>C API documentation for %s</h1>\n" + "<ul>\n" + "%s" + "</ul>\n" + "%s"; + char *html_doc + = CFCUtil_sprintf(pattern, html_header, parcel_name, class_list, + html_footer); + + FREEMEM(class_list); + + return html_doc; +} + +char* +CFCCHTML_create_html_doc(CFCClass *klass) { + const char *class_name = CFCClass_get_class_name(klass); + + // Create NAME. + char *name = S_html_create_name(klass); + + // Create SYNOPSIS. + char *synopsis = S_html_create_synopsis(klass); + + // Create DESCRIPTION. + char *description = S_html_create_description(klass); + + // Create CONSTRUCTORS. + char *functions_html = S_html_create_functions(klass); + + // Create METHODS, possibly including an ABSTRACT METHODS section. + char *methods_html = S_html_create_methods(klass); + + // Build an INHERITANCE section describing class ancestry. + char *inheritance = S_html_create_inheritance(klass); + + // Put it all together. + const char pattern[] = + "%s" + "<h1>%s</h1>\n" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s"; + char *html_doc + = CFCUtil_sprintf(pattern, html_header, class_name, name, synopsis, + description, functions_html, methods_html, + inheritance, html_footer); + + FREEMEM(name); + FREEMEM(synopsis); + FREEMEM(description); + FREEMEM(functions_html); + FREEMEM(methods_html); + FREEMEM(inheritance); + + return html_doc; +} + +static char* +S_html_create_name(CFCClass *klass) { + const char *class_name = CFCClass_get_class_name(klass); + char *md = CFCUtil_strdup(class_name);; + CFCDocuComment *docucom = CFCClass_get_docucomment(klass); + + if (docucom) { + const char *raw_brief = CFCDocuComment_get_brief(docucom); + if (raw_brief && raw_brief[0] != '\0') { + md = CFCUtil_cat(md, " - ", raw_brief, NULL); + } + } + + char *html = S_md_to_html(md); + + const char *format = + "<h2>Name</h2>\n" + "%s"; + char *result = CFCUtil_sprintf(format, html); + + FREEMEM(html); + return result; +} + +static char* +S_html_create_synopsis(CFCClass *klass) { + CHY_UNUSED_VAR(klass); + return CFCUtil_strdup(""); +} + +static char* +S_html_create_description(CFCClass *klass) { + CFCDocuComment *docucom = CFCClass_get_docucomment(klass); + char *desc = NULL; + + if (docucom) { + const char *raw_desc = CFCDocuComment_get_long(docucom); + if (raw_desc && raw_desc[0] != '\0') { + desc = S_md_to_html(raw_desc); + } + } + + if (!desc) { return CFCUtil_strdup(""); } + + char *result = CFCUtil_sprintf("<h2>Description</h2>\n%s", desc); + + FREEMEM(desc); + return result; +} + +static char* +S_html_create_functions(CFCClass *klass) { + CFCFunction **functions = CFCClass_functions(klass); + char *result = CFCUtil_strdup(""); + + for (int func_num = 0; functions[func_num] != NULL; func_num++) { + CFCFunction *func = functions[func_num]; + if (!CFCFunction_public(func)) { continue; } + + if (result[0] == '\0') { + result = CFCUtil_cat(result, "<h2>Functions</h2>\n<dl>\n", NULL); + } + + const char *micro_sym = CFCFunction_micro_sym(func); + const char *full_func_sym = CFCFunction_full_func_sym(func); + + char *func_html = S_html_create_func(klass, func, micro_sym, + full_func_sym); + result = CFCUtil_cat(result, func_html, NULL); + FREEMEM(func_html); + } + + if (result[0] != '\0') { + result = CFCUtil_cat(result, "</dl>\n", NULL); + } + + return result; +} + +static char* +S_html_create_methods(CFCClass *klass) { + CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); + char *methods_html = CFCUtil_strdup(""); + char *novel_html = CFCUtil_strdup(""); + char *result; + + for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { + CFCMethod *method = fresh_methods[meth_num]; + if (!CFCMethod_public(method) || !CFCMethod_novel(method)) { + continue; + } + + const char *macro_sym = CFCMethod_get_macro_sym(method); + char *full_method_sym = CFCMethod_full_method_sym(method, NULL); + char *method_html = S_html_create_func(klass, (CFCFunction*)method, + macro_sym, full_method_sym); + + if (CFCMethod_abstract(method)) { + if (methods_html[0] == '\0') { + methods_html = CFCUtil_cat(methods_html, + "<h3>Abstract methods</h3>\n<dl>\n", + NULL); + } + methods_html = CFCUtil_cat(methods_html, method_html, NULL); + } + else { + if (novel_html[0] == '\0') { + novel_html = CFCUtil_cat(novel_html, + "<h3>Novel methods</h3>\n<dl>\n", + NULL); + } + novel_html = CFCUtil_cat(novel_html, method_html, NULL); + } + + FREEMEM(method_html); + FREEMEM(full_method_sym); + } + + if (methods_html[0] != '\0') { + methods_html = CFCUtil_cat(methods_html, "</dl>\n", NULL); + } + if (novel_html[0] != '\0') { + novel_html = CFCUtil_cat(novel_html, "</dl>\n", NULL); + } + + methods_html = CFCUtil_cat(methods_html, novel_html, NULL); + + // Add methods from parent classes excluding Clownfish::Obj + CFCClass *parent = CFCClass_get_parent(klass); + while (parent) { + if (strcmp(CFCClass_get_class_name(parent), "Clownfish::Obj") == 0) { + break; + } + char *inherited_html = S_html_create_inherited_methods(parent); + methods_html = CFCUtil_cat(methods_html, inherited_html, NULL); + FREEMEM(inherited_html); + parent = CFCClass_get_parent(parent); + } + + if (methods_html[0] == '\0') { + result = CFCUtil_strdup(""); + } + else { + result = CFCUtil_sprintf("<h2>Methods</h2>\n%s", methods_html); + } + + FREEMEM(methods_html); + FREEMEM(novel_html); + FREEMEM(fresh_methods); + return result; +} + +static char* +S_html_create_inherited_methods(CFCClass *klass) { + CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); + char *result = CFCUtil_strdup(""); + + for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { + CFCMethod *method = fresh_methods[meth_num]; + if (!CFCMethod_public(method) || !CFCMethod_novel(method)) { + continue; + } + + if (result[0] == '\0') { + result = CFCUtil_cat(result, "<h3>Methods inherited from ", + CFCClass_get_class_name(klass), + "</h3><dl>\n", + NULL); + } + + const char *macro_sym = CFCMethod_get_macro_sym(method); + char *full_method_sym = CFCMethod_full_method_sym(method, NULL); + char *method_html = S_html_create_func(klass, (CFCFunction*)method, + macro_sym, full_method_sym); + result = CFCUtil_cat(result, method_html, NULL); + + FREEMEM(method_html); + FREEMEM(full_method_sym); + } + + if (result[0] != '\0') { + result = CFCUtil_cat(result, "</dl>\n", NULL); + } + + FREEMEM(fresh_methods); + return result; +} + +static char* +S_html_create_func(CFCClass *klass, CFCFunction *func, const char *short_sym, + const char *full_sym) { + CFCType *return_type = CFCFunction_get_return_type(func); + char *return_type_html = S_type_to_html(klass, return_type); + const char *incremented = ""; + + if (CFCType_incremented(return_type)) { + incremented = " // incremented"; + } + + char *param_list = S_html_create_param_list(klass, func, full_sym); + + const char *pattern = + "<dt>%s</dt>\n" + "<dd>\n" + "<pre><code>%s%s\n" + "%s</code></pre>\n"; + char *result = CFCUtil_sprintf(pattern, short_sym, return_type_html, + incremented, param_list); + + FREEMEM(param_list); + + // Get documentation, which may be inherited. + CFCDocuComment *docucomment = CFCFunction_get_docucomment(func); + if (!docucomment) { + const char *micro_sym = CFCFunction_micro_sym(func); + CFCClass *parent = klass; + while (NULL != (parent = CFCClass_get_parent(parent))) { + CFCFunction *parent_func + = (CFCFunction*)CFCClass_method(parent, micro_sym); + if (!parent_func) { break; } + docucomment = CFCFunction_get_docucomment(parent_func); + if (docucomment) { break; } + } + } + + if (docucomment) { + // Description + const char *raw_desc = CFCDocuComment_get_description(docucomment); + char *desc = S_md_to_html(raw_desc); + result = CFCUtil_cat(result, desc, NULL); + FREEMEM(desc); + + // Params + const char **param_names + = CFCDocuComment_get_param_names(docucomment); + const char **param_docs + = CFCDocuComment_get_param_docs(docucomment); + if (param_names[0]) { + result = CFCUtil_cat(result, "<dl>\n", NULL); + for (size_t i = 0; param_names[i] != NULL; i++) { + char *doc = S_md_to_html(param_docs[i]); + result = CFCUtil_cat(result, "<dt><emph>", param_names[i], + "</emph></dt>\n<dd>", doc, "</dd>\n", + NULL); + FREEMEM(doc); + } + result = CFCUtil_cat(result, "</dl>\n", NULL); + } + + // Return value + const char *retval_doc = CFCDocuComment_get_retval(docucomment); + if (retval_doc && strlen(retval_doc)) { + char *md = CFCUtil_sprintf("**Returns:** %s", retval_doc); + char *html = S_md_to_html(md); + result = CFCUtil_cat(result, html, NULL); + FREEMEM(html); + FREEMEM(md); + } + } + + result = CFCUtil_cat(result, "</dd>\n", NULL); + + FREEMEM(return_type_html); + return result; +} + +static char* +S_html_create_param_list(CFCClass *klass, CFCFunction *func, + const char *full_sym) { + CFCParamList *param_list = CFCFunction_get_param_list(func); + CFCVariable **variables = CFCParamList_get_variables(param_list); + + if (!variables[0]) { + return CFCUtil_sprintf("<strong>%s</strong>(void);\n", full_sym); + } + + char *result = CFCUtil_sprintf("<strong>%s</strong>(", full_sym); + + for (int i = 0; variables[i]; ++i) { + CFCVariable *variable = variables[i]; + CFCType *type = CFCVariable_get_type(variable); + char *type_html = S_type_to_html(klass, type); + const char *name = CFCVariable_micro_sym(variable); + + result = CFCUtil_cat(result, "\n ", type_html, " <strong>", name, + "</strong>", NULL); + + if (variables[i+1]) { + result = CFCUtil_cat(result, ",", NULL); + } + if (CFCType_decremented(type)) { + result = CFCUtil_cat(result, " // decremented", NULL); + } + + FREEMEM(type_html); + } + + result = CFCUtil_cat(result, "\n);\n", NULL); + + return result; +} + +static char* +S_html_create_inheritance(CFCClass *klass) { + CFCClass *ancestor = CFCClass_get_parent(klass); + char *result = CFCUtil_strdup(""); + + if (!ancestor) { return result; } + + const char *class_name = CFCClass_get_class_name(klass); + result = CFCUtil_cat(result, "<h2>Inheritance</h2>\n<p>", class_name, + NULL); + while (ancestor) { + const char *ancestor_name = CFCClass_get_class_name(ancestor); + const char *ancestor_sym = CFCClass_full_struct_sym(ancestor); + result = CFCUtil_cat(result, " is a <a href=\"", ancestor_sym, + ".html\">", ancestor_name, "</a>", NULL); + ancestor = CFCClass_get_parent(ancestor); + } + result = CFCUtil_cat(result, ".</p>\n", NULL); + + return result; +} + +static char* +S_md_to_html(const char *md) { + cmark_node *doc = cmark_parse_document(md, strlen(md)); + S_convert_uris(doc); + char *html = cmark_render_html(doc); + cmark_node_destroy(doc); + + return html; +} + +static void +S_convert_uris(cmark_node *node) { + cmark_node *cur = node; + + while (true) { + cmark_node_type type = cmark_node_get_type(cur); + + if (type == NODE_LINK) { + S_convert_uri(cur); + } + + cmark_node *child = cmark_node_first_child(cur); + if (child) { + cur = child; + continue; + } + + next_sibling: + if (cur == node) { break; } + cmark_node *next = cmark_node_next(cur); + if (next) { + cur = next; + continue; + } + cur = cmark_node_parent(cur); + goto next_sibling; + } +} + +static void +S_convert_uri(cmark_node *link) { + const char *uri = cmark_node_get_url(link); + if (!uri || memcmp(uri, "clownfish:", 10) != 0) { + return; + } + if (memcmp(uri, "clownfish:class:", 16) != 0) { + CFCUtil_die("Invalid clownfish URI: %s", uri); + } + + // Convert 'clownfish:class:{parcel}:{name}' to '{parcel}_{name}.html' + const char *src = uri + 16; + const char *colon = strchr(src, ':'); + if (!colon) { + CFCUtil_die("Invalid clownfish URI: %s", uri); + } + size_t src_len = strlen(src); + char *new_uri = (char*)MALLOCATE(src_len + 5 + 1); + strcpy(new_uri, src); + new_uri[colon-src] = '_'; + strcpy(new_uri + src_len, ".html"); + + cmark_node_set_url(link, new_uri); + FREEMEM(new_uri); +} + +static char* +S_type_to_html(CFCClass *klass, CFCType *type) { + const char *type_c = CFCType_to_c(type); + + if (CFCType_is_object(type)) { + const char *struct_sym = CFCClass_full_struct_sym(klass); + const char *specifier = CFCType_get_specifier(type); + if (strcmp(specifier, struct_sym) != 0) { + return CFCUtil_sprintf("<a href=\"%s.html\">%s</a>", specifier, + type_c); + } + } + + return CFCUtil_strdup(type_c); +} + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/fca91dce/compiler/src/CFCCHTML.h ---------------------------------------------------------------------- diff --git a/compiler/src/CFCCHTML.h b/compiler/src/CFCCHTML.h new file mode 100644 index 0000000..f340321 --- /dev/null +++ b/compiler/src/CFCCHTML.h @@ -0,0 +1,42 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef H_CFCCHTML +#define H_CFCCHTML + +#ifdef __cplusplus +extern "C" { +#endif + +struct CFCClass; +struct CFCParcel; + +/** Return the index document of the HTML documentation. + */ +char* +CFCCHTML_create_index_doc(struct CFCParcel *parcel, struct CFCClass **classes); + +/** Return the HTML documentation for the class. + */ +char* +CFCCHTML_create_html_doc(struct CFCClass *klass); + +#ifdef __cplusplus +} +#endif + +#endif /* H_CFCCHTML */ +
