Skip to content

Commit

Permalink
proof of concept of stage1 doc generation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
andrewrk committed Oct 4, 2019
1 parent 2f4dad0 commit 1c1e1c7
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 5 deletions.
35 changes: 35 additions & 0 deletions lib/std/special/doc/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Documentation - Zig</title>
<link rel="icon" href="favicon.png">
<style type="text/css">
.hidden {
display: none;
}

@media (prefers-color-scheme: dark) {
body{
background-color: #111;
color: #bbb;
}
}
</style>
</head>
<body>
<p id="status">Loading...</p>
<div id="sectPkgs" class="hidden">
<h2>Packages</h2>
<ul id="listPkgs">
</ul>
</div>
<div id="sectTypes" class="hidden">
<h2>Types</h2>
<ul id="listTypes">
</ul>
</div>
<script src="data.js"></script>
<script src="main.js"></script>
</body>
</html>
126 changes: 126 additions & 0 deletions lib/std/special/doc/main.js
Original file line number Diff line number Diff line change
@@ -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, '<li></li>');
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, '<li></li>');
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");
}
})();
1 change: 1 addition & 0 deletions src/all_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
39 changes: 38 additions & 1 deletion src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 5 additions & 2 deletions src/dump_analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion src/dump_analysis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
#include <stdio.h>

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
7 changes: 6 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<CliPkg>(1);
BuildMode build_mode = BuildModeDebug;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 1c1e1c7

Please sign in to comment.