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 {...@@ -2063,6 +2063,7 @@ struct CodeGen {
2063 bool have_stack_probing;2063 bool have_stack_probing;
2064 bool function_sections;2064 bool function_sections;
2065 bool enable_dump_analysis;2065 bool enable_dump_analysis;
2066 bool enable_doc_generation;
20662067
2067 Buf *mmacosx_version_min;2068 Buf *mmacosx_version_min;
2068 Buf *mios_version_min;2069 Buf *mios_version_min;
src/codegen.cpp+38-1
...@@ -10158,6 +10158,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {...@@ -10158,6 +10158,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
10158 cache_bool(ch, g->is_dummy_so);10158 cache_bool(ch, g->is_dummy_so);
10159 cache_bool(ch, g->function_sections);10159 cache_bool(ch, g->function_sections);
10160 cache_bool(ch, g->enable_dump_analysis);10160 cache_bool(ch, g->enable_dump_analysis);
10161 cache_bool(ch, g->enable_doc_generation);
10161 cache_buf_opt(ch, g->mmacosx_version_min);10162 cache_buf_opt(ch, g->mmacosx_version_min);
10162 cache_buf_opt(ch, g->mios_version_min);10163 cache_buf_opt(ch, g->mios_version_min);
10163 cache_usize(ch, g->version_major);10164 cache_usize(ch, g->version_major);
...@@ -10347,12 +10348,48 @@ void codegen_build_and_link(CodeGen *g) {...@@ -10347,12 +10348,48 @@ void codegen_build_and_link(CodeGen *g) {
10347 fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno));10348 fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno));
10348 exit(1);10349 exit(1);
10349 }10350 }
10350 zig_print_analysis_dump(g, f);10351 zig_print_analysis_dump(g, f, " ", "\n");
10351 if (fclose(f) != 0) {10352 if (fclose(f) != 0) {
10352 fprintf(stderr, "Unable to write '%s': %s\n", analysis_json_filename, strerror(errno));10353 fprintf(stderr, "Unable to write '%s': %s\n", analysis_json_filename, strerror(errno));
10353 exit(1);10354 exit(1);
10354 }10355 }
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 }
10356 }10393 }
1035710394
10358 // If we're outputting assembly or llvm IR we skip linking.10395 // 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) {...@@ -688,12 +688,12 @@ static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) {
688 jw_end_object(jw);688 jw_end_object(jw);
689}689}
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) {
692 Error err;692 Error err;
693 AnalDumpCtx ctx = {};693 AnalDumpCtx ctx = {};
694 ctx.g = g;694 ctx.g = g;
695 JsonWriter *jw = &ctx.jw;695 JsonWriter *jw = &ctx.jw;
696 jw_init(jw, f, " ", "\n");696 jw_init(jw, f, one_indent, nl);
697 ctx.type_map.init(16);697 ctx.type_map.init(16);
698 ctx.pkg_map.init(16);698 ctx.pkg_map.init(16);
699 ctx.file_map.init(16);699 ctx.file_map.init(16);
...@@ -728,6 +728,9 @@ void zig_print_analysis_dump(CodeGen *g, FILE *f) {...@@ -728,6 +728,9 @@ void zig_print_analysis_dump(CodeGen *g, FILE *f) {
728 Buf triple_buf = BUF_INIT;728 Buf triple_buf = BUF_INIT;
729 target_triple_zig(&triple_buf, g->zig_target);729 target_triple_zig(&triple_buf, g->zig_target);
730 jw_string(jw, buf_ptr(&triple_buf));730 jw_string(jw, buf_ptr(&triple_buf));
731
732 jw_object_field(jw, "rootName");
733 jw_string(jw, buf_ptr(g->root_out_name));
731 }734 }
732 jw_end_object(jw);735 jw_end_object(jw);
733736
src/dump_analysis.hpp+1-1
...@@ -12,6 +12,6 @@...@@ -12,6 +12,6 @@
12#include <stdio.h>12#include <stdio.h>
1313
14void zig_print_stack_report(CodeGen *g, FILE *f);14void 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
17#endif17#endif
src/main.cpp+6-1
...@@ -64,7 +64,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {...@@ -64,7 +64,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
64 " -fno-PIC disable Position Independent Code\n"64 " -fno-PIC disable Position Independent Code\n"
65 " -ftime-report print timing diagnostics\n"65 " -ftime-report print timing diagnostics\n"
66 " -fstack-report print stack size diagnostics\n"66 " -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"
68 " --libc [file] Provide a file which specifies libc paths\n"69 " --libc [file] Provide a file which specifies libc paths\n"
69 " --name [name] override output name\n"70 " --name [name] override output name\n"
70 " --output-dir [dir] override output directory (defaults to cwd)\n"71 " --output-dir [dir] override output directory (defaults to cwd)\n"
...@@ -481,6 +482,7 @@ int main(int argc, char **argv) {...@@ -481,6 +482,7 @@ int main(int argc, char **argv) {
481 bool timing_info = false;482 bool timing_info = false;
482 bool stack_report = false;483 bool stack_report = false;
483 bool enable_dump_analysis = false;484 bool enable_dump_analysis = false;
485 bool enable_doc_generation = false;
484 const char *cache_dir = nullptr;486 const char *cache_dir = nullptr;
485 CliPkg *cur_pkg = allocate<CliPkg>(1);487 CliPkg *cur_pkg = allocate<CliPkg>(1);
486 BuildMode build_mode = BuildModeDebug;488 BuildMode build_mode = BuildModeDebug;
...@@ -666,6 +668,8 @@ int main(int argc, char **argv) {...@@ -666,6 +668,8 @@ int main(int argc, char **argv) {
666 stack_report = true;668 stack_report = true;
667 } else if (strcmp(arg, "-fdump-analysis") == 0) {669 } else if (strcmp(arg, "-fdump-analysis") == 0) {
668 enable_dump_analysis = true;670 enable_dump_analysis = true;
671 } else if (strcmp(arg, "-fgenerate-docs") == 0) {
672 enable_doc_generation = true;
669 } else if (strcmp(arg, "--enable-valgrind") == 0) {673 } else if (strcmp(arg, "--enable-valgrind") == 0) {
670 valgrind_support = ValgrindSupportEnabled;674 valgrind_support = ValgrindSupportEnabled;
671 } else if (strcmp(arg, "--disable-valgrind") == 0) {675 } else if (strcmp(arg, "--disable-valgrind") == 0) {
...@@ -1143,6 +1147,7 @@ int main(int argc, char **argv) {...@@ -1143,6 +1147,7 @@ int main(int argc, char **argv) {
1143 g->enable_time_report = timing_info;1147 g->enable_time_report = timing_info;
1144 g->enable_stack_report = stack_report;1148 g->enable_stack_report = stack_report;
1145 g->enable_dump_analysis = enable_dump_analysis;1149 g->enable_dump_analysis = enable_dump_analysis;
1150 g->enable_doc_generation = enable_doc_generation;
1146 codegen_set_out_name(g, buf_out_name);1151 codegen_set_out_name(g, buf_out_name);
1147 codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);1152 codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
1148 g->want_single_threaded = want_single_threaded;1153 g->want_single_threaded = want_single_threaded;