Skip to content

Commit dca6e74

Browse files
committed
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.
1 parent 2f4dad0 commit dca6e74

File tree

7 files changed

+212
-5
lines changed

7 files changed

+212
-5
lines changed

lib/std/special/doc/index.html

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -688,12 +688,12 @@ static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) {
688688
jw_end_object(jw);
689689
}
690690

691-
void zig_print_analysis_dump(CodeGen *g, FILE *f) {
691+
void 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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
#include <stdio.h>
1313

1414
void zig_print_stack_report(CodeGen *g, FILE *f);
15-
void zig_print_analysis_dump(CodeGen *g, FILE *f);
15+
void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl);
1616

1717
#endif

src/main.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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;

0 commit comments

Comments
 (0)