authorgravatar for der.teufel.mail@gmail.comKrzysztof Wolicki <der.teufel.mail@gmail.com> 2023-04-26 23:05:02+02:00
committergravatar for der.teufel.mail@gmail.comKrzysztof Wolicki <der.teufel.mail@gmail.com> 2023-04-29 00:54:23+02:00
logaa51a5c5571338657c1abe5aeeb34c0d4db8186a
tree28a633710ab60e0eeff50586fd15e470b66b3791
parentfd6200eda6d4fe19c34a59430a88a9ce38d6d7a4

autodoc: Gather and display decltests


2 files changed, 93 insertions(+), 5 deletions(-)

lib/docs/main.js+22-4
......@@ -563,12 +563,14 @@ const NAV_MODES = {
563563
564564 let currentType = getType(mod.main);
565565 curNav.declObjs = [currentType];
566 let lastDecl = mod.main;
566567 for (let i = 0; i < curNav.declNames.length; i += 1) {
567568 let childDecl = findSubDecl(currentType, curNav.declNames[i]);
568569 window.last_decl = childDecl;
569570 if (childDecl == null) {
570571 return render404();
571572 }
573 lastDecl = childDecl;
572574
573575 let childDeclValue = resolveValue(childDecl.value).expr;
574576 if ("type" in childDeclValue) {
......@@ -593,9 +595,7 @@ const NAV_MODES = {
593595 let lastIsType = isType(last);
594596 let lastIsContainerType = isContainerType(last);
595597
596 if (lastIsDecl) {
597 renderDocTest(last);
598 }
598 renderDocTest(lastDecl);
599599
600600 if (lastIsContainerType) {
601601 return renderContainer(last);
......@@ -642,7 +642,25 @@ const NAV_MODES = {
642642 if (!decl.decltest) return;
643643 const astNode = getAstNode(decl.decltest);
644644 domSectDocTests.classList.remove("hidden");
645 domDocTestsCode.innerHTML = astNode.code;
645 domDocTestsCode.innerHTML = renderZigSource(astNode.code);
646 }
647
648 function renderZigSource(code) {
649 let lines = code.split("\n");
650 let result = "";
651 let indent_level = 0;
652 for(let i = 0; i < lines.length; i += 1) {
653 let line = lines[i].trim();
654 if(line[0] == "}") indent_level -= 1;
655 for(let j = 0; j < indent_level; j += 1) {
656 result += " ";
657 }
658 if (line.startsWith("\\\\")) result += " "
659 result += line;
660 result += "\n";
661 if(line[line.length - 1] == "{") indent_level += 1;
662 }
663 return result;
646664 }
647665
648666 function renderUnknownDecl(decl) {
src/Autodoc.zig+71-1
......@@ -3104,7 +3104,9 @@ fn analyzeAllDecls(
31043104 while (it.next()) |d| {
31053105 const decl_name_index = file.zir.extra[d.sub_index + 5];
31063106 switch (decl_name_index) {
3107 0, 1, 2 => continue, // skip over usingnamespace decls
3107 0, 1 => continue, // skip over usingnamespace decls
3108 2 => continue, // skip decltests
3109
31083110 else => if (file.zir.string_bytes[decl_name_index] == 0) {
31093111 continue;
31103112 },
......@@ -3120,6 +3122,24 @@ fn analyzeAllDecls(
31203122 );
31213123 }
31223124
3125 // Fourth loop to analyze decltests
3126 it = original_it;
3127 while (it.next()) |d| {
3128 const decl_name_index = file.zir.extra[d.sub_index + 5];
3129 switch (decl_name_index) {
3130 0, 1 => continue, // skip over usingnamespace decls
3131 2 => {},
3132 else => continue, // skip tests and normal decls
3133 }
3134
3135 try self.analyzeDecltest(
3136 file,
3137 scope,
3138 parent_src,
3139 d,
3140 );
3141 }
3142
31233143 return it.extra_index;
31243144}
31253145
......@@ -3327,6 +3347,56 @@ fn analyzeUsingnamespaceDecl(
33273347 }
33283348}
33293349
3350fn analyzeDecltest(
3351 self: *Autodoc,
3352 file: *File,
3353 scope: *Scope,
3354 parent_src: SrcLocInfo,
3355 d: Zir.DeclIterator.Item,
3356) AutodocErrors!void {
3357 const data = file.zir.instructions.items(.data);
3358
3359 const value_index = file.zir.extra[d.sub_index + 6];
3360 const decl_name_index = file.zir.extra[d.sub_index + 7];
3361
3362 // This is known to work because decl values are always block_inlines
3363 const value_pl_node = data[value_index].pl_node;
3364 const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
3365
3366 const func_index = getBlockInlineBreak(file.zir, value_index).?;
3367 const pl_node = data[Zir.refToIndex(func_index).?].pl_node;
3368 const fn_src = try self.srcLocInfo(file, pl_node.src_node, decl_src);
3369 const tree = try file.getTree(self.comp_module.gpa);
3370 const test_source_code = tree.getNodeSource(fn_src.src_node);
3371
3372 const decl_name: ?[]const u8 = if (decl_name_index != 0)
3373 file.zir.nullTerminatedString(decl_name_index)
3374 else
3375 null;
3376
3377 // astnode
3378 const ast_node_index = idx: {
3379 const idx = self.ast_nodes.items.len;
3380 try self.ast_nodes.append(self.arena, .{
3381 .file = self.files.getIndex(file).?,
3382 .line = decl_src.line,
3383 .col = 0,
3384 .name = decl_name,
3385 .code = test_source_code,
3386 });
3387 break :idx idx;
3388 };
3389
3390 const decl_status = scope.resolveDeclName(decl_name_index, file, 0);
3391
3392 switch (decl_status.*) {
3393 .Analyzed => |idx| {
3394 self.decls.items[idx].decltest = ast_node_index;
3395 },
3396 else => unreachable, // we assume analyzeAllDecls analyzed other decls by this point
3397 }
3398}
3399
33303400/// An unresolved path has a non-string WalkResult at its beginnig, while every
33313401/// other element is a string WalkResult. Resolving means iteratively map each
33323402/// string to a Decl / Type / Call / etc.