diff --git a/lib/docs/main.js b/lib/docs/main.js index b25323af98..3249ac0f13 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -66,7 +66,7 @@ const NAV_MODES = { const domHdrName = document.getElementById("hdrName"); const domHelpModal = document.getElementById("helpModal"); const domSearchPlaceholder = document.getElementById("searchPlaceholder"); - const sourceFileUrlTemplate = "src/{{file}}.html#L{{line}}" + const sourceFileUrlTemplate = "src/{{pkg}}/{{file}}.html#L{{line}}" const domLangRefLink = document.getElementById("langRefLink"); let searchTimer = null; @@ -446,7 +446,7 @@ const NAV_MODES = { if (activeGuide == undefined) { const root_file_idx = zigAnalysis.packages[zigAnalysis.rootPkg].file; - const root_file_name = zigAnalysis.files[root_file_idx]; + const root_file_name = getFile(root_file_idx).name; domGuides.innerHTML = markdown(` # Zig Guides These autodocs don't contain any guide. @@ -2685,8 +2685,10 @@ const NAV_MODES = { function sourceFileLink(decl) { const srcNode = getAstNode(decl.src); + const srcFile = getFile(srcNode.file); return sourceFileUrlTemplate. - replace("{{file}}", zigAnalysis.files[srcNode.file]). + replace("{{pkg}}", zigAnalysis.packages[srcFile.pkgIndex].name). + replace("{{file}}", srcFile.name). replace("{{line}}", srcNode.line + 1); } @@ -4135,6 +4137,14 @@ function addDeclToSearchResults(decl, declIndex, pkgNames, item, list, stack) { }; } + function getFile(idx) { + const file = zigAnalysis.files[idx]; + return { + name: file[0], + pkgIndex: file[1], + }; + } + function getType(idx) { const ty = zigAnalysis.types[idx]; switch (ty[0]) { diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 86973a9a7f..aecaef1b3f 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -255,7 +255,7 @@ pub fn generateZirData(self: *Autodoc) !void { var data = DocData{ .params = .{}, - .packages = self.packages.values(), + .packages = self.packages, .files = self.files, .calls = self.calls.items, .types = self.types.items, @@ -313,9 +313,26 @@ pub fn generateZirData(self: *Autodoc) !void { var files_iterator = self.files.iterator(); while (files_iterator.next()) |entry| { - const new_html_path = try std.mem.concat(self.arena, u8, &.{ entry.key_ptr.*.sub_file_path, ".html" }); + const sub_file_path = entry.key_ptr.*.sub_file_path; + const file_package = entry.key_ptr.*.pkg; + const package_name = (self.packages.get(file_package) orelse continue).name; - const html_file = try createFromPath(html_dir, new_html_path); + const file_path = std.fs.path.dirname(sub_file_path) orelse ""; + const file_name = if (file_path.len > 0) sub_file_path[file_path.len + 1 ..] else sub_file_path; + + const html_file_name = try std.mem.concat(self.arena, u8, &.{ file_name, ".html" }); + defer self.arena.free(html_file_name); + + const dir_name = try std.fs.path.join(self.arena, &.{ package_name, file_path }); + defer self.arena.free(dir_name); + + var dir = try html_dir.makeOpenPath(dir_name, .{}); + defer dir.close(); + + const html_file = dir.createFile(html_file_name, .{}) catch |err| switch (err) { + error.PathAlreadyExists => try dir.openFile(html_file_name, .{}), + else => return err, + }; defer html_file.close(); var buffer = std.io.bufferedWriter(html_file.writer()); @@ -333,26 +350,6 @@ pub fn generateZirData(self: *Autodoc) !void { try docs_dir.copyFile("index.html", output_dir, "index.html", .{}); } -fn createFromPath(base_dir: std.fs.Dir, path: []const u8) !std.fs.File { - var path_tokens = std.mem.tokenize(u8, path, std.fs.path.sep_str); - var dir = base_dir; - while (path_tokens.next()) |toc| { - if (path_tokens.peek() != null) { - dir.makeDir(toc) catch |e| switch (e) { - error.PathAlreadyExists => {}, - else => |err| return err, - }; - dir = try dir.openDir(toc, .{}); - } else { - return dir.createFile(toc, .{}) catch |e| switch (e) { - error.PathAlreadyExists => try dir.openFile(toc, .{}), - else => |err| return err, - }; - } - } - return error.EmptyPath; -} - /// Represents a chain of scopes, used to resolve decl references to the /// corresponding entry in `self.decls`. It also keeps track of whether /// a given decl has been analyzed or not. @@ -417,7 +414,7 @@ const DocData = struct { .{ .target = "arst" }, }, }, - packages: []const DocPackage, + packages: std.AutoArrayHashMapUnmanaged(*Package, DocPackage), errors: []struct {} = &.{}, // non-hardcoded stuff @@ -449,8 +446,12 @@ const DocData = struct { const f_name = @tagName(f); try jsw.objectField(f_name); switch (f) { - .files => try writeFileTableToJson(self.files, &jsw), + .files => try writeFileTableToJson(self.files, self.packages, &jsw), .guide_sections => try writeGuidesToJson(self.guide_sections, &jsw), + .packages => { + try std.json.stringify(self.packages.values(), opts, w); + jsw.state_index -= 1; + }, else => { try std.json.stringify(@field(self, f_name), opts, w); jsw.state_index -= 1; @@ -950,7 +951,7 @@ fn walkInstruction( .table = .{}, }; - // TODO: Add this package as a dependency to the current pakcage + // TODO: Add this package as a dependency to the current package // TODO: this seems something that could be done in bulk // at the beginning or the end, or something. const root_src_dir = other_package.root_src_directory; @@ -4627,12 +4628,21 @@ fn cteTodo(self: *Autodoc, msg: []const u8) error{OutOfMemory}!DocData.WalkResul return DocData.WalkResult{ .expr = .{ .comptimeExpr = cte_slot_index } }; } -fn writeFileTableToJson(map: std.AutoArrayHashMapUnmanaged(*File, usize), jsw: anytype) !void { +fn writeFileTableToJson( + map: std.AutoArrayHashMapUnmanaged(*File, usize), + pkgs: std.AutoArrayHashMapUnmanaged(*Package, DocData.DocPackage), + jsw: anytype, +) !void { try jsw.beginArray(); var it = map.iterator(); while (it.next()) |entry| { + try jsw.arrayElem(); + try jsw.beginArray(); try jsw.arrayElem(); try jsw.emitString(entry.key_ptr.*.sub_file_path); + try jsw.arrayElem(); + try jsw.emitNumber(pkgs.getIndex(entry.key_ptr.*.pkg) orelse 0); + try jsw.endArray(); } try jsw.endArray(); }