authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-04 17:39:35-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-04 20:18:06-04:00
logdca6e74fec247f2d15de3cfad9e0e23bc7884212
treedc409bcd1ce27e92715dc859daf767f093457e72
parent2f4dad04e06005318ae3f37798e734a63edef6c0

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.

7 files changed, 212 insertions(+), 5 deletions(-)

lib/std/special/doc/index.html created+35
......@@ -0,0 +1,35 @@
1<!doctype html>
2<html>
3 <head>
4 <meta charset="utf-8">
5 <title>Documentation - Zig</title>
6 <link rel="icon" href="favicon.png">
7 <style type="text/css">
8 .hidden {
9 display: none;
10 }
11
12 @media (prefers-color-scheme: dark) {
13 body{
14 background-color: #111;
15 color: #bbb;
16 }
17 }
18 </style>
19 </head>
20 <body>
21 <p id="status">Loading...</p>
22 <div id="sectPkgs" class="hidden">
23 <h2>Packages</h2>
24 <ul id="listPkgs">
25 </ul>
26 </div>
27 <div id="sectTypes" class="hidden">
28 <h2>Types</h2>
29 <ul id="listTypes">
30 </ul>
31 </div>
32 <script src="data.js"></script>
33 <script src="main.js"></script>
34 </body>
35</html>
lib/std/special/doc/main.js created+126
......@@ -0,0 +1,126 @@
1(function() {
2 var domStatus = document.getElementById("status");
3 var domSectPkgs = document.getElementById("sectPkgs");
4 var domListPkgs = document.getElementById("listPkgs");
5 var domSectTypes = document.getElementById("sectTypes");
6 var domListTypes = document.getElementById("listTypes");
7
8 var curNav = {
9 kind: "pkg",
10 index: zigAnalysis.rootPkg,
11 };
12
13 var rootIsStd = detectRootIsStd();
14 var typeKindTypeId = findTypeKindType();
15 var typeTypeId = findTypeTypeId();
16 render();
17
18 function render() {
19 domStatus.classList.add("hidden");
20
21 if (curNav.kind === "pkg") {
22 var pkg = zigAnalysis.packages[curNav.index];
23 renderPkgList(pkg);
24 var pkgStruct = zigAnalysis.types[pkg.main];
25 renderContainer(pkgStruct);
26 } else {
27 throw new Error("TODO");
28 }
29 }
30
31 function renderPkgList(pkg) {
32 var list = [];
33 for (var key in pkg.table) {
34 if (key === "root" && rootIsStd) continue;
35 list.push({
36 name: key,
37 pkg: pkg.table[key],
38 });
39 }
40 list.sort(function(a, b) {
41 return operatorCompare(a.name.toLowerCase(), b.name.toLowerCase());
42 });
43
44 resizeDomList(domListPkgs, list.length, '<li></li>');
45 var domItems = domListPkgs.children;
46 for (var i = 0; i < list.length; i += 1) {
47 var domItem = domItems[i];
48 domItem.textContent = list[i].name;
49 }
50
51 domSectPkgs.classList.remove("hidden");
52 }
53
54 function resizeDomList(listDom, desiredLen, templateHtml) {
55 // add the missing dom entries
56 var i, ev;
57 for (i = listDom.childElementCount; i < desiredLen; i += 1) {
58 listDom.insertAdjacentHTML('beforeend', templateHtml);
59 }
60 // remove extra dom entries
61 while (desiredLen < listDom.childElementCount) {
62 listDom.removeChild(listDom.lastChild);
63 }
64 }
65
66 function renderContainer(container) {
67 // Find only the types of this package
68 var list = [];
69 for (var i = 0; i < container.decls.length; i += 1) {
70 var decl = zigAnalysis.decls[container.decls[i]];
71 if (decl.type == typeTypeId) {
72 list.push(decl);
73 }
74 }
75 list.sort(function(a, b) {
76 return operatorCompare(a.name.toLowerCase(), b.name.toLowerCase());
77 });
78
79 resizeDomList(domListTypes, list.length, '<li></li>');
80 for (var i = 0; i < list.length; i += 1) {
81 var domItem = domListTypes.children[i];
82 var decl = list[i];
83 domItem.textContent = decl.name;
84 }
85
86 domSectTypes.classList.remove("hidden");
87 }
88
89 function operatorCompare(a, b) {
90 if (a === b) {
91 return 0;
92 } else if (a < b) {
93 return -1;
94 } else {
95 return 1;
96 }
97 }
98
99 function detectRootIsStd() {
100 var rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg];
101 if (rootPkg.table["std"] == null) {
102 // no std mapped into the root package
103 return false;
104 }
105 var stdPkg = zigAnalysis.packages[rootPkg.table["std"]];
106 return rootPkg.file === stdPkg.file;
107 }
108
109 function findTypeKindType() {
110 for (var i = 0; i < zigAnalysis.typeKinds.length; i += 1) {
111 if (zigAnalysis.typeKinds[i] === "Type") {
112 return i;
113 }
114 }
115 throw new Error("No type kind 'Type' found");
116 }
117
118 function findTypeTypeId() {
119 for (var i = 0; i < zigAnalysis.types.length; i += 1) {
120 if (zigAnalysis.types[i].kind == typeKindTypeId) {
121 return i;
122 }
123 }
124 throw new Error("No type 'type' found");
125 }
126})();
src/all_types.hpp+1
......@@ -2063,6 +2063,7 @@ struct CodeGen {
20632063 bool have_stack_probing;
20642064 bool function_sections;
20652065 bool enable_dump_analysis;
2066 bool enable_doc_generation;
20662067
20672068 Buf *mmacosx_version_min;
20682069 Buf *mios_version_min;
src/codegen.cpp+38-1
......@@ -10158,6 +10158,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
1015810158 cache_bool(ch, g->is_dummy_so);
1015910159 cache_bool(ch, g->function_sections);
1016010160 cache_bool(ch, g->enable_dump_analysis);
10161 cache_bool(ch, g->enable_doc_generation);
1016110162 cache_buf_opt(ch, g->mmacosx_version_min);
1016210163 cache_buf_opt(ch, g->mios_version_min);
1016310164 cache_usize(ch, g->version_major);
......@@ -10347,12 +10348,48 @@ void codegen_build_and_link(CodeGen *g) {
1034710348 fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno));
1034810349 exit(1);
1034910350 }
10350 zig_print_analysis_dump(g, f);
10351 zig_print_analysis_dump(g, f, " ", "\n");
1035110352 if (fclose(f) != 0) {
1035210353 fprintf(stderr, "Unable to write '%s': %s\n", analysis_json_filename, strerror(errno));
1035310354 exit(1);
1035410355 }
10356 }
10357 if (g->enable_doc_generation) {
10358 Buf *doc_dir_path = buf_sprintf("%s" OS_SEP "doc", buf_ptr(g->output_dir));
10359 if ((err = os_make_path(doc_dir_path))) {
10360 fprintf(stderr, "Unable to create directory %s: %s\n", buf_ptr(doc_dir_path), err_str(err));
10361 exit(1);
10362 }
10363 Buf *index_html_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "doc" OS_SEP "index.html",
10364 buf_ptr(g->zig_std_dir));
10365 Buf *index_html_dest_path = buf_sprintf("%s" OS_SEP "index.html", buf_ptr(doc_dir_path));
10366 Buf *main_js_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "doc" OS_SEP "main.js",
10367 buf_ptr(g->zig_std_dir));
10368 Buf *main_js_dest_path = buf_sprintf("%s" OS_SEP "main.js", buf_ptr(doc_dir_path));
1035510369
10370 if ((err = os_copy_file(index_html_src_path, index_html_dest_path))) {
10371 fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(index_html_src_path),
10372 buf_ptr(index_html_dest_path), err_str(err));
10373 exit(1);
10374 }
10375 if ((err = os_copy_file(main_js_src_path, main_js_dest_path))) {
10376 fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(main_js_src_path),
10377 buf_ptr(main_js_dest_path), err_str(err));
10378 exit(1);
10379 }
10380 const char *data_js_filename = buf_ptr(buf_sprintf("%s" OS_SEP "data.js", buf_ptr(doc_dir_path)));
10381 FILE *f = fopen(data_js_filename, "wb");
10382 if (f == nullptr) {
10383 fprintf(stderr, "Unable to open '%s': %s\n", data_js_filename, strerror(errno));
10384 exit(1);
10385 }
10386 fprintf(f, "zigAnalysis=");
10387 zig_print_analysis_dump(g, f, "", "");
10388 fprintf(f, ";");
10389 if (fclose(f) != 0) {
10390 fprintf(stderr, "Unable to write '%s': %s\n", data_js_filename, strerror(errno));
10391 exit(1);
10392 }
1035610393 }
1035710394
1035810395 // If we're outputting assembly or llvm IR we skip linking.
src/dump_analysis.cpp+5-2
......@@ -688,12 +688,12 @@ static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) {
688688 jw_end_object(jw);
689689}
690690
691void zig_print_analysis_dump(CodeGen *g, FILE *f) {
691void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl) {
692692 Error err;
693693 AnalDumpCtx ctx = {};
694694 ctx.g = g;
695695 JsonWriter *jw = &ctx.jw;
696 jw_init(jw, f, " ", "\n");
696 jw_init(jw, f, one_indent, nl);
697697 ctx.type_map.init(16);
698698 ctx.pkg_map.init(16);
699699 ctx.file_map.init(16);
......@@ -728,6 +728,9 @@ void zig_print_analysis_dump(CodeGen *g, FILE *f) {
728728 Buf triple_buf = BUF_INIT;
729729 target_triple_zig(&triple_buf, g->zig_target);
730730 jw_string(jw, buf_ptr(&triple_buf));
731
732 jw_object_field(jw, "rootName");
733 jw_string(jw, buf_ptr(g->root_out_name));
731734 }
732735 jw_end_object(jw);
733736
src/dump_analysis.hpp+1-1
......@@ -12,6 +12,6 @@
1212#include <stdio.h>
1313
1414void zig_print_stack_report(CodeGen *g, FILE *f);
15void zig_print_analysis_dump(CodeGen *g, FILE *f);
15void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl);
1616
1717#endif
src/main.cpp+6-1
......@@ -64,7 +64,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
6464 " -fno-PIC disable Position Independent Code\n"
6565 " -ftime-report print timing diagnostics\n"
6666 " -fstack-report print stack size diagnostics\n"
67 " -fdump-analysis write analysis.json file for use with zig docs\n"
67 " -fdump-analysis write analysis.json file with type information\n"
68 " -fgenerate-docs create a doc/ dir with html documentation\n"
6869 " --libc [file] Provide a file which specifies libc paths\n"
6970 " --name [name] override output name\n"
7071 " --output-dir [dir] override output directory (defaults to cwd)\n"
......@@ -481,6 +482,7 @@ int main(int argc, char **argv) {
481482 bool timing_info = false;
482483 bool stack_report = false;
483484 bool enable_dump_analysis = false;
485 bool enable_doc_generation = false;
484486 const char *cache_dir = nullptr;
485487 CliPkg *cur_pkg = allocate<CliPkg>(1);
486488 BuildMode build_mode = BuildModeDebug;
......@@ -666,6 +668,8 @@ int main(int argc, char **argv) {
666668 stack_report = true;
667669 } else if (strcmp(arg, "-fdump-analysis") == 0) {
668670 enable_dump_analysis = true;
671 } else if (strcmp(arg, "-fgenerate-docs") == 0) {
672 enable_doc_generation = true;
669673 } else if (strcmp(arg, "--enable-valgrind") == 0) {
670674 valgrind_support = ValgrindSupportEnabled;
671675 } else if (strcmp(arg, "--disable-valgrind") == 0) {
......@@ -1143,6 +1147,7 @@ int main(int argc, char **argv) {
11431147 g->enable_time_report = timing_info;
11441148 g->enable_stack_report = stack_report;
11451149 g->enable_dump_analysis = enable_dump_analysis;
1150 g->enable_doc_generation = enable_doc_generation;
11461151 codegen_set_out_name(g, buf_out_name);
11471152 codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
11481153 g->want_single_threaded = want_single_threaded;