authorgravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2022-03-01 20:16:32+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-19 19:10:11-07:00
log580e633777e55bcfad6f039f8acfa338d686368e
tree01ca024bde1af941cc412e3bbd6509d1c8cde057
parent253e7e112e06d899e7b51194a637f4fddc6c1d65

autodoc: added support for non-lazy decl paths


2 files changed, 139 insertions(+), 23 deletions(-)

lib/docs/main.js+13-6
......@@ -1315,16 +1315,23 @@
13151315 if (field.failure === true) {
13161316 html += '<span class="tok-kw" style="color:red;">#FAILURE#</span>';
13171317 } else if ("declPath" in field) {
1318 console.assert(field.declPath.lenght == 1);
1319 var decl = zigAnalysis.decls[field.declPath[0]];
1318 for (var j = field.declPath.length - 1; j >= 0; j--) {
1319 console.log("hello");
1320 var decl = zigAnalysis.decls[field.declPath[j]];
1321
1322 html += '<a href="'+navLinkDecl(decl.name)+'">';
1323 html += '<span class="tok-kw" style="color:lightblue;">' +
1324 escapeHtml(decl.name) + '</span>';
1325 html += '</a>';
1326 if (j != 0) html += ".";
1327 }
1328 // at the end of the for loop this is the value of `decl`
1329 //decl = zigAnalysis.decls[field.declPath[0]];
1330
13201331 var val = resolveValue(decl.value);
13211332 console.assert("type" in val);
13221333 var valType = zigAnalysis.types[val.type];
1323
13241334 var valTypeName = typeShorthandName(valType);
1325 html += '<a href="'+navLinkDecl(decl.name)+'">';
1326 html += '<span class="tok-kw" style="color:lightblue;">' + escapeHtml(decl.name) + '</span>';
1327 html += '</a>';
13281335 html += ' ('+ valTypeName +')';
13291336 } else if ("type" in field) {
13301337 var name = zigAnalysis.types[field.type].name;
src/Autodoc.zig+126-17
......@@ -9,6 +9,7 @@ const Ref = Zir.Inst.Ref;
99module: *Module,
1010doc_location: Compilation.EmitLoc,
1111arena: std.mem.Allocator,
12files: std.AutoHashMapUnmanaged(*File, DocData.AutodocFile) = .{},
1213types: std.ArrayListUnmanaged(DocData.Type) = .{},
1314decls: std.ArrayListUnmanaged(DocData.Decl) = .{},
1415ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{},
......@@ -126,12 +127,20 @@ pub fn generateZirData(self: *Autodoc) !void {
126127 }
127128 }
128129
129 var root_scope: Scope = .{ .parent = null };
130 var root_scope = Scope{ .parent = null };
130131 try self.ast_nodes.append(self.arena, .{ .name = "(root)" });
132 try self.files.put(self.arena, file, .{
133 .analyzed = false,
134 .root_struct = self.types.items.len,
135 });
131136 const main_type_index = try self.walkInstruction(file, &root_scope, Zir.main_struct_inst);
137 self.files.getPtr(file).?.analyzed = true;
138
139 // TODO: solve every single pending declpath whose analysis
140 // was delayed because of circular imports.
132141
133142 var data = DocData{
134 .files = &[1][]const u8{root_file_path},
143 .files = .{ .data = self.files },
135144 .types = self.types.items,
136145 .decls = self.decls.items,
137146 .astNodes = self.ast_nodes.items,
......@@ -221,11 +230,42 @@ const DocData = struct {
221230
222231 // non-hardcoded stuff
223232 astNodes: []AstNode,
224 files: []const []const u8,
233 files: struct {
234 // this struct is a temporary hack to support json serialization
235 data: std.AutoHashMapUnmanaged(*File, AutodocFile),
236 pub fn jsonStringify(
237 self: @This(),
238 opt: std.json.StringifyOptions,
239 w: anytype,
240 ) !void {
241 var idx: usize = 0;
242 var it = self.data.iterator();
243 try w.writeAll("{\n");
244
245 var options = opt;
246 if (options.whitespace) |*ws| ws.indent_level += 1;
247 while (it.next()) |kv| : (idx += 1) {
248 if (options.whitespace) |ws| try ws.outputIndent(w);
249 try w.print("\"{s}\": {d}", .{
250 kv.key_ptr.*.sub_file_path,
251 kv.value_ptr.root_struct,
252 });
253 if (idx != self.data.count() - 1) try w.writeByte(',');
254 try w.writeByte('\n');
255 }
256 if (opt.whitespace) |ws| try ws.outputIndent(w);
257 try w.writeAll("}");
258 }
259 },
225260 types: []Type,
226261 decls: []Decl,
227262 comptimeExprs: []ComptimeExpr,
228263
264 const AutodocFile = struct {
265 analyzed: bool, // omitted in json data
266 root_struct: usize, // index into `types`
267 };
268
229269 const DocTypeKinds = blk: {
230270 var info = @typeInfo(std.builtin.TypeId);
231271 info.Enum.fields = info.Enum.fields ++ [1]std.builtin.TypeInfo.EnumField{
......@@ -289,7 +329,7 @@ const DocData = struct {
289329 name: []const u8,
290330 src: ?usize = null, // index into astNodes
291331 privDecls: ?[]usize = null, // index into decls
292 pubDecls: ?[]usize = null, // index into decls
332 pubDecls: []usize, // index into decls
293333 fields: ?[]TypeRef = null, // (use src->fields to find names)
294334 },
295335 ComptimeExpr: struct { name: []const u8 },
......@@ -544,9 +584,27 @@ fn walkInstruction(
544584 // importFile cannot error out since all files
545585 // are already loaded at this point
546586 const new_file = self.module.importFile(file, path) catch unreachable;
547 // TODO: cycles not handled, add file info to outuput
587
588 const result = try self.files.getOrPut(self.arena, new_file.file);
589 if (result.found_existing) {
590 return DocData.WalkResult{ .type = result.value_ptr.root_struct };
591 }
592
593 result.value_ptr.* = .{
594 .analyzed = false,
595 .root_struct = self.types.items.len,
596 };
597
548598 var new_scope = Scope{ .parent = null };
549 return self.walkInstruction(new_file.file, &new_scope, Zir.main_struct_inst);
599 const new_file_walk_result = self.walkInstruction(
600 new_file.file,
601 &new_scope,
602 Zir.main_struct_inst,
603 );
604 // We re-access the hashmap in case it was modified
605 // by walkInstruction()
606 self.files.getPtr(new_file.file).?.analyzed = true;
607 return new_file_walk_result;
550608 },
551609 .block => {
552610 const res = DocData.WalkResult{ .comptimeExpr = self.comptimeExprs.items.len };
......@@ -641,18 +699,69 @@ fn walkInstruction(
641699 path[0] = decls_slot_index;
642700 return DocData.WalkResult{ .declPath = path };
643701 },
644 //.field_val => {
645 // const pl_node = data[inst_index].pl_node;
646 // const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index);
647 // // In case that we have a path (eg Foo.Bar.Baz.X.Y.Z),
648 // // then we want to deal with them in a while loop instead
649 // // of using `walkRef()`.
650 //
651 // var path = try self.arena.alloc(usize, 1);
652 // path[0] = decls_slot_index;
653 // return DocData.WalkResult{ .declPath = path };
702 .field_val => {
703 const pl_node = data[inst_index].pl_node;
704 const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index);
705
706 var path: std.ArrayListUnmanaged(usize) = .{};
707 var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs
708
709 try path.append(self.arena, extra.data.field_name_start);
710 // Put inside path the starting index of each decl name
711 // that we encounter as we navigate through all the field_vals
712 while (tags[lhs] == .field_val) {
713 const lhs_extra = file.zir.extraData(
714 Zir.Inst.Field,
715 data[lhs].pl_node.payload_index,
716 );
654717
655 //},
718 try path.append(self.arena, lhs_extra.data.field_name_start);
719 lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs
720 }
721
722 if (tags[lhs] != .decl_val) {
723 @panic("TODO: handle non-decl_val endings in walkInstruction.field_val");
724 }
725 const str_tok = data[lhs].str_tok;
726 const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
727 try path.append(self.arena, decls_slot_index);
728
729 // Righ now, every element of `path` is the first index of a
730 // decl name except for the final element, which instead points to
731 // the analyzed data corresponding to the top-most decl of this path.
732 // We are now going to reverse loop over `path` to resolve each name
733 // to its corresponding index in `decls`.
734
735 var i: usize = path.items.len;
736 while (i > 1) {
737 i -= 1;
738 const parent = self.decls.items[path.items[i]];
739 const child_decl_name = file.zir.nullTerminatedString(path.items[i - 1]);
740 switch (parent.value) {
741 else => {
742 std.debug.print(
743 "TODO: handle `{s}`in walkInstruction.field_val\n",
744 .{@tagName(parent.value)},
745 );
746 unreachable;
747 },
748 .type => |t_index| {
749 const t_struct = self.types.items[t_index].Struct; // todo: support more types
750 for (t_struct.pubDecls) |d| {
751 // TODO: this could be improved a lot
752 // by having our own string table!
753 const decl = self.decls.items[d];
754 if (std.mem.eql(u8, decl.name, child_decl_name)) {
755 path.items[i - 1] = d;
756 continue;
757 }
758 }
759 },
760 }
761 }
762
763 return DocData.WalkResult{ .declPath = path.items };
764 },
656765 .int_type => {
657766 const int_type = data[inst_index].int_type;
658767 const sign = if (int_type.signedness == .unsigned) "u" else "i";