From 1c1e1c71cbe76ffe758a1b063540b753a4c6efd0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 4 Oct 2019 17:39:35 -0400 Subject: [PATCH] proof of concept of stage1 doc generation This commit adds `-fgenerate-docs` CLI option, and it outputs: * doc/index.html * doc/data.js * doc/main.js In this strategy, we have 1 static html page and 1 static javascript file, which loads the semantic analysis dump directly and renders it using dom manipulation. Currently, all it does is list the declarations. But there is a lot more data available to work with. The next step would be making the declarations hyperlinks, and handling page navigation. Another strategy would be to generate a static site with no javascript, based on the semantic analysis dump that zig now provides. I invite the Zig community to take on such a project. However this version which heavily relies on javascript will also be a direction explored. I also welcome contributors to improve the html, css, and javascript of what this commit started, as well as whatever improvements are necessary to the static analysis dumping code to provide more information. See #21. --- lib/std/special/doc/index.html | 35 +++++++++ lib/std/special/doc/main.js | 126 +++++++++++++++++++++++++++++++++ src/all_types.hpp | 1 + src/codegen.cpp | 39 +++++++++- src/dump_analysis.cpp | 7 +- src/dump_analysis.hpp | 2 +- src/main.cpp | 7 +- 7 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 lib/std/special/doc/index.html create mode 100644 lib/std/special/doc/main.js diff --git a/lib/std/special/doc/index.html b/lib/std/special/doc/index.html new file mode 100644 index 000000000000..b00a8f04639a --- /dev/null +++ b/lib/std/special/doc/index.html @@ -0,0 +1,35 @@ + + + + + Documentation - Zig + + + + +

Loading...

+ + + + + + diff --git a/lib/std/special/doc/main.js b/lib/std/special/doc/main.js new file mode 100644 index 000000000000..2ce822e64b65 --- /dev/null +++ b/lib/std/special/doc/main.js @@ -0,0 +1,126 @@ +(function() { + var domStatus = document.getElementById("status"); + var domSectPkgs = document.getElementById("sectPkgs"); + var domListPkgs = document.getElementById("listPkgs"); + var domSectTypes = document.getElementById("sectTypes"); + var domListTypes = document.getElementById("listTypes"); + + var curNav = { + kind: "pkg", + index: zigAnalysis.rootPkg, + }; + + var rootIsStd = detectRootIsStd(); + var typeKindTypeId = findTypeKindType(); + var typeTypeId = findTypeTypeId(); + render(); + + function render() { + domStatus.classList.add("hidden"); + + if (curNav.kind === "pkg") { + var pkg = zigAnalysis.packages[curNav.index]; + renderPkgList(pkg); + var pkgStruct = zigAnalysis.types[pkg.main]; + renderContainer(pkgStruct); + } else { + throw new Error("TODO"); + } + } + + function renderPkgList(pkg) { + var list = []; + for (var key in pkg.table) { + if (key === "root" && rootIsStd) continue; + list.push({ + name: key, + pkg: pkg.table[key], + }); + } + list.sort(function(a, b) { + return operatorCompare(a.name.toLowerCase(), b.name.toLowerCase()); + }); + + resizeDomList(domListPkgs, list.length, '
  • '); + var domItems = domListPkgs.children; + for (var i = 0; i < list.length; i += 1) { + var domItem = domItems[i]; + domItem.textContent = list[i].name; + } + + domSectPkgs.classList.remove("hidden"); + } + + function resizeDomList(listDom, desiredLen, templateHtml) { + // add the missing dom entries + var i, ev; + for (i = listDom.childElementCount; i < desiredLen; i += 1) { + listDom.insertAdjacentHTML('beforeend', templateHtml); + } + // remove extra dom entries + while (desiredLen < listDom.childElementCount) { + listDom.removeChild(listDom.lastChild); + } + } + + function renderContainer(container) { + // Find only the types of this package + var list = []; + for (var i = 0; i < container.decls.length; i += 1) { + var decl = zigAnalysis.decls[container.decls[i]]; + if (decl.type == typeTypeId) { + list.push(decl); + } + } + list.sort(function(a, b) { + return operatorCompare(a.name.toLowerCase(), b.name.toLowerCase()); + }); + + resizeDomList(domListTypes, list.length, '
  • '); + for (var i = 0; i < list.length; i += 1) { + var domItem = domListTypes.children[i]; + var decl = list[i]; + domItem.textContent = decl.name; + } + + domSectTypes.classList.remove("hidden"); + } + + function operatorCompare(a, b) { + if (a === b) { + return 0; + } else if (a < b) { + return -1; + } else { + return 1; + } + } + + function detectRootIsStd() { + var rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; + if (rootPkg.table["std"] == null) { + // no std mapped into the root package + return false; + } + var stdPkg = zigAnalysis.packages[rootPkg.table["std"]]; + return rootPkg.file === stdPkg.file; + } + + function findTypeKindType() { + for (var i = 0; i < zigAnalysis.typeKinds.length; i += 1) { + if (zigAnalysis.typeKinds[i] === "Type") { + return i; + } + } + throw new Error("No type kind 'Type' found"); + } + + function findTypeTypeId() { + for (var i = 0; i < zigAnalysis.types.length; i += 1) { + if (zigAnalysis.types[i].kind == typeKindTypeId) { + return i; + } + } + throw new Error("No type 'type' found"); + } +})(); diff --git a/src/all_types.hpp b/src/all_types.hpp index 6ebfe934b48d..1119d2bf4e9f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2063,6 +2063,7 @@ struct CodeGen { bool have_stack_probing; bool function_sections; bool enable_dump_analysis; + bool enable_doc_generation; Buf *mmacosx_version_min; Buf *mios_version_min; diff --git a/src/codegen.cpp b/src/codegen.cpp index 0f86635646c9..88ff179ca78e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -10158,6 +10158,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->is_dummy_so); cache_bool(ch, g->function_sections); cache_bool(ch, g->enable_dump_analysis); + cache_bool(ch, g->enable_doc_generation); cache_buf_opt(ch, g->mmacosx_version_min); cache_buf_opt(ch, g->mios_version_min); cache_usize(ch, g->version_major); @@ -10347,12 +10348,48 @@ void codegen_build_and_link(CodeGen *g) { fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno)); exit(1); } - zig_print_analysis_dump(g, f); + zig_print_analysis_dump(g, f, " ", "\n"); if (fclose(f) != 0) { fprintf(stderr, "Unable to write '%s': %s\n", analysis_json_filename, strerror(errno)); exit(1); } + } + if (g->enable_doc_generation) { + Buf *doc_dir_path = buf_sprintf("%s" OS_SEP "doc", buf_ptr(g->output_dir)); + if ((err = os_make_path(doc_dir_path))) { + fprintf(stderr, "Unable to create directory %s: %s\n", buf_ptr(doc_dir_path), err_str(err)); + exit(1); + } + Buf *index_html_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "doc" OS_SEP "index.html", + buf_ptr(g->zig_std_dir)); + Buf *index_html_dest_path = buf_sprintf("%s" OS_SEP "index.html", buf_ptr(doc_dir_path)); + Buf *main_js_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "doc" OS_SEP "main.js", + buf_ptr(g->zig_std_dir)); + Buf *main_js_dest_path = buf_sprintf("%s" OS_SEP "main.js", buf_ptr(doc_dir_path)); + if ((err = os_copy_file(index_html_src_path, index_html_dest_path))) { + fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(index_html_src_path), + buf_ptr(index_html_dest_path), err_str(err)); + exit(1); + } + if ((err = os_copy_file(main_js_src_path, main_js_dest_path))) { + fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(main_js_src_path), + buf_ptr(main_js_dest_path), err_str(err)); + exit(1); + } + const char *data_js_filename = buf_ptr(buf_sprintf("%s" OS_SEP "data.js", buf_ptr(doc_dir_path))); + FILE *f = fopen(data_js_filename, "wb"); + if (f == nullptr) { + fprintf(stderr, "Unable to open '%s': %s\n", data_js_filename, strerror(errno)); + exit(1); + } + fprintf(f, "zigAnalysis="); + zig_print_analysis_dump(g, f, "", ""); + fprintf(f, ";"); + if (fclose(f) != 0) { + fprintf(stderr, "Unable to write '%s': %s\n", data_js_filename, strerror(errno)); + exit(1); + } } // If we're outputting assembly or llvm IR we skip linking. diff --git a/src/dump_analysis.cpp b/src/dump_analysis.cpp index 401fa37ded06..b110e0e03832 100644 --- a/src/dump_analysis.cpp +++ b/src/dump_analysis.cpp @@ -688,12 +688,12 @@ static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) { jw_end_object(jw); } -void zig_print_analysis_dump(CodeGen *g, FILE *f) { +void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl) { Error err; AnalDumpCtx ctx = {}; ctx.g = g; JsonWriter *jw = &ctx.jw; - jw_init(jw, f, " ", "\n"); + jw_init(jw, f, one_indent, nl); ctx.type_map.init(16); ctx.pkg_map.init(16); ctx.file_map.init(16); @@ -728,6 +728,9 @@ void zig_print_analysis_dump(CodeGen *g, FILE *f) { Buf triple_buf = BUF_INIT; target_triple_zig(&triple_buf, g->zig_target); jw_string(jw, buf_ptr(&triple_buf)); + + jw_object_field(jw, "rootName"); + jw_string(jw, buf_ptr(g->root_out_name)); } jw_end_object(jw); diff --git a/src/dump_analysis.hpp b/src/dump_analysis.hpp index 44a87290e5f0..6d1c644ea208 100644 --- a/src/dump_analysis.hpp +++ b/src/dump_analysis.hpp @@ -12,6 +12,6 @@ #include void zig_print_stack_report(CodeGen *g, FILE *f); -void zig_print_analysis_dump(CodeGen *g, FILE *f); +void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl); #endif diff --git a/src/main.cpp b/src/main.cpp index 5300938f850e..9b8bddabd79b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,7 +64,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " -fno-PIC disable Position Independent Code\n" " -ftime-report print timing diagnostics\n" " -fstack-report print stack size diagnostics\n" - " -fdump-analysis write analysis.json file for use with zig docs\n" + " -fdump-analysis write analysis.json file with type information\n" + " -fgenerate-docs create a doc/ dir with html documentation\n" " --libc [file] Provide a file which specifies libc paths\n" " --name [name] override output name\n" " --output-dir [dir] override output directory (defaults to cwd)\n" @@ -481,6 +482,7 @@ int main(int argc, char **argv) { bool timing_info = false; bool stack_report = false; bool enable_dump_analysis = false; + bool enable_doc_generation = false; const char *cache_dir = nullptr; CliPkg *cur_pkg = allocate(1); BuildMode build_mode = BuildModeDebug; @@ -666,6 +668,8 @@ int main(int argc, char **argv) { stack_report = true; } else if (strcmp(arg, "-fdump-analysis") == 0) { enable_dump_analysis = true; + } else if (strcmp(arg, "-fgenerate-docs") == 0) { + enable_doc_generation = true; } else if (strcmp(arg, "--enable-valgrind") == 0) { valgrind_support = ValgrindSupportEnabled; } else if (strcmp(arg, "--disable-valgrind") == 0) { @@ -1143,6 +1147,7 @@ int main(int argc, char **argv) { g->enable_time_report = timing_info; g->enable_stack_report = stack_report; g->enable_dump_analysis = enable_dump_analysis; + g->enable_doc_generation = enable_doc_generation; codegen_set_out_name(g, buf_out_name); codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); g->want_single_threaded = want_single_threaded;