zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit aa765c1d70443a0495aae9239956683d8a29f823 (tree)
parent 61c08d3c7e75a8d8df50bb836bb8fa2691d49cd8
Author: Loris Cro <kappaloris@gmail.com>
Date:   Sat, 15 Apr 2023 16:36:35 +0200

autodoc: add support for defining guide sections

For example:

//!zig-autodoc-section: Advanced Topics

Diffstat:
Mlib/docs/index.html | 3+--
Mlib/docs/main.js | 58+++++++++++++++++++++++++++++++++++++++-------------------
Msrc/Autodoc.zig | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
3 files changed, 107 insertions(+), 38 deletions(-)

diff --git a/lib/docs/index.html b/lib/docs/index.html @@ -700,8 +700,7 @@ </ul> </div> <div id="guidesMenu" class="hidden"> - <h2><span>Guide List</span></h2> - <ul id="guidesList" class="packages"></ul> + <div id="guidesList"></div> </div> <div id="apiMenu" class="hidden"> <div id="sectMainPkg" class="hidden"> diff --git a/lib/docs/main.js b/lib/docs/main.js @@ -405,26 +405,45 @@ const NAV_MODES = { domApiMenu.classList.add("hidden"); // sidebar guides list - const list = Object.keys(zigAnalysis.guides); - resizeDomList(domGuidesList, list.length, '<li><a href="#"></a></li>'); - for (let i = 0; i < list.length; i += 1) { - let liDom = domGuidesList.children[i]; - let aDom = liDom.children[0]; - aDom.textContent = list[i]; - aDom.setAttribute("href", NAV_MODES.GUIDES + list[i]); - if (list[i] === curNav.activeGuide) { - aDom.classList.add("active"); - } else { - aDom.classList.remove("active"); - } + const section_list = zigAnalysis.guide_sections; + resizeDomList(domGuidesList, section_list.length, '<div><h2><span></span></h2><ul class="packages"></ul></div>'); + for (let j = 0; j < section_list.length; j += 1) { + const section = section_list[j]; + const domSectionName = domGuidesList.children[j].children[0].children[0]; + const domGuides = domGuidesList.children[j].children[1]; + domSectionName.textContent = section.name; + resizeDomList(domGuides, section.guides.length, '<li><a href="#"></a></li>'); + for (let i = 0; i < section.guides.length; i += 1) { + const guide = section.guides[i]; + let liDom = domGuides.children[i]; + let aDom = liDom.children[0]; + aDom.textContent = guide.name; + aDom.setAttribute("href", NAV_MODES.GUIDES + guide.name); + if (guide.name === curNav.activeGuide) { + aDom.classList.add("active"); + } else { + aDom.classList.remove("active"); + } + } } - - if (list.length > 0) { + + if (section_list.length > 0) { domGuidesMenu.classList.remove("hidden"); } // main content - const activeGuide = zigAnalysis.guides[curNav.activeGuide]; + let activeGuide = undefined; + outer: for (let i = 0; i < zigAnalysis.guide_sections.length; i += 1) { + const section = zigAnalysis.guide_sections[i]; + for (let j = 0; j < section.guides.length; j += 1) { + const guide = section.guides[j]; + if (guide.name == curNav.activeGuide) { + activeGuide = guide; + break outer; + } + } + } + if (activeGuide == undefined) { const root_file_idx = zigAnalysis.packages[zigAnalysis.rootPkg].file; const root_file_name = zigAnalysis.files[root_file_idx]; @@ -446,6 +465,7 @@ const NAV_MODES = { \`\`\` //!zig-autodoc-guide: intro.md //!zig-autodoc-guide: quickstart.md + //!zig-autodoc-section: Advanced topics //!zig-autodoc-guide: ../advanced-docs/advanced-stuff.md \`\`\` @@ -455,7 +475,7 @@ const NAV_MODES = { Happy writing! `); } else { - domGuides.innerHTML = markdown(activeGuide); + domGuides.innerHTML = markdown(activeGuide.body); } } @@ -3104,9 +3124,9 @@ const NAV_MODES = { return; case NAV_MODES.GUIDES: - const guides = Object.keys(zigAnalysis.guides); - if (guides.length != 0 && query == "") { - location.hash = NAV_MODES.GUIDES + guides[0]; + const sections = zigAnalysis.guide_sections; + if (sections.length != 0 && sections[0].guides.length != 0 && query == "") { + location.hash = NAV_MODES.GUIDES + sections[0].guides[0].name; return; } diff --git a/src/Autodoc.zig b/src/Autodoc.zig @@ -28,7 +28,7 @@ decls: std.ArrayListUnmanaged(DocData.Decl) = .{}, exprs: std.ArrayListUnmanaged(DocData.Expr) = .{}, ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{}, comptime_exprs: std.ArrayListUnmanaged(DocData.ComptimeExpr) = .{}, -guides: std.StringHashMapUnmanaged([]const u8) = .{}, +guide_sections: std.ArrayListUnmanaged(Section) = .{}, // These fields hold temporary state of the analysis process // and are mainly used by the decl path resolving algorithm. @@ -63,6 +63,16 @@ const SrcLocInfo = struct { src_node: u32 = 0, }; +const Section = struct { + name: []const u8 = "", // empty string is the default section + guides: std.ArrayListUnmanaged(Guide) = .{}, + + const Guide = struct { + name: []const u8, + body: []const u8, + }; +}; + var arena_allocator: std.heap.ArenaAllocator = undefined; pub fn init(m: *Module, doc_location: Compilation.EmitLoc) Autodoc { arena_allocator = std.heap.ArenaAllocator.init(m.gpa); @@ -253,7 +263,7 @@ pub fn generateZirData(self: *Autodoc) !void { .exprs = self.exprs.items, .astNodes = self.ast_nodes.items, .comptimeExprs = self.comptime_exprs.items, - .guides = self.guides, + .guide_sections = self.guide_sections, }; const base_dir = self.doc_location.directory orelse @@ -419,7 +429,7 @@ const DocData = struct { exprs: []Expr, comptimeExprs: []ComptimeExpr, - guides: std.StringHashMapUnmanaged([]const u8), + guide_sections: std.ArrayListUnmanaged(Section), const Call = struct { func: Expr, @@ -440,7 +450,7 @@ const DocData = struct { try jsw.objectField(f_name); switch (f) { .files => try writeFileTableToJson(self.files, &jsw), - .guides => try writeGuidesToJson(self.guides, &jsw), + .guide_sections => try writeGuidesToJson(self.guide_sections, &jsw), else => { try std.json.stringify(@field(self, f_name), opts, w); jsw.state_index -= 1; @@ -4613,14 +4623,39 @@ fn writeFileTableToJson(map: std.AutoArrayHashMapUnmanaged(*File, usize), jsw: a try jsw.endArray(); } -fn writeGuidesToJson(map: std.StringHashMapUnmanaged([]const u8), jsw: anytype) !void { - try jsw.beginObject(); - var it = map.iterator(); - while (it.next()) |entry| { - try jsw.objectField(entry.key_ptr.*); - try jsw.emitString(entry.value_ptr.*); +/// Writes the data like so: +/// ``` +/// { +/// "<section name>": [{name: "<guide name>", text: "<guide contents>"},], +/// } +/// ``` +fn writeGuidesToJson(sections: std.ArrayListUnmanaged(Section), jsw: anytype) !void { + try jsw.beginArray(); + + for (sections.items) |s| { + // section name + try jsw.arrayElem(); + try jsw.beginObject(); + try jsw.objectField("name"); + try jsw.emitString(s.name); + try jsw.objectField("guides"); + + // section value + try jsw.beginArray(); + for (s.guides.items) |g| { + try jsw.arrayElem(); + try jsw.beginObject(); + try jsw.objectField("name"); + try jsw.emitString(g.name); + try jsw.objectField("body"); + try jsw.emitString(g.body); + try jsw.endObject(); + } + try jsw.endArray(); + try jsw.endObject(); } - try jsw.endObject(); + + try jsw.endArray(); } fn writePackageTableToJson( @@ -4688,19 +4723,31 @@ fn getTLDocComment(self: *Autodoc, file: *File) ![]const u8 { } fn findGuidePaths(self: *Autodoc, file: *File, str: []const u8) !void { - const prefix = "zig-autodoc-guide:"; + const guide_prefix = "zig-autodoc-guide:"; + const section_prefix = "zig-autodoc-section:"; + + try self.guide_sections.append(self.arena, .{}); // add a default section + var current_section = &self.guide_sections.items[self.guide_sections.items.len - 1]; + var it = std.mem.tokenize(u8, str, "\n"); while (it.next()) |line| { const trimmed_line = std.mem.trim(u8, line, " "); - if (std.mem.startsWith(u8, trimmed_line, prefix)) { - const path = trimmed_line[prefix.len..]; + if (std.mem.startsWith(u8, trimmed_line, guide_prefix)) { + const path = trimmed_line[guide_prefix.len..]; const trimmed_path = std.mem.trim(u8, path, " "); - try self.addGuide(file, trimmed_path); + try self.addGuide(file, trimmed_path, current_section); + } else if (std.mem.startsWith(u8, trimmed_line, section_prefix)) { + const section_name = trimmed_line[section_prefix.len..]; + const trimmed_section_name = std.mem.trim(u8, section_name, " "); + try self.guide_sections.append(self.arena, .{ + .name = trimmed_section_name, + }); + current_section = &self.guide_sections.items[self.guide_sections.items.len - 1]; } } } -fn addGuide(self: *Autodoc, file: *File, guide_path: []const u8) !void { +fn addGuide(self: *Autodoc, file: *File, guide_path: []const u8, section: *Section) !void { if (guide_path.len == 0) return error.MissingAutodocGuideName; const cur_pkg_dir_path = file.pkg.root_src_directory.path orelse "."; @@ -4716,5 +4763,8 @@ fn addGuide(self: *Autodoc, file: *File, guide_path: []const u8) !void { else => |e| return e, }; - try self.guides.put(self.arena, resolved_path, guide); + try section.guides.append(self.arena, .{ + .name = resolved_path, + .body = guide, + }); }