authorgravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2023-04-15 16:36:35+02:00
committergravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2023-04-15 18:26:53+02:00
logaa765c1d70443a0495aae9239956683d8a29f823
treedb269140fa27d8b6868a720ef0ce1a7e239b9838
parent61c08d3c7e75a8d8df50bb836bb8fa2691d49cd8

autodoc: add support for defining guide sections

For example: //!zig-autodoc-section: Advanced Topics

3 files changed, 107 insertions(+), 38 deletions(-)

lib/docs/index.html+1-2
......@@ -700,8 +700,7 @@
700700 </ul>
701701 </div>
702702 <div id="guidesMenu" class="hidden">
703 <h2><span>Guide List</span></h2>
704 <ul id="guidesList" class="packages"></ul>
703 <div id="guidesList"></div>
705704 </div>
706705 <div id="apiMenu" class="hidden">
707706 <div id="sectMainPkg" class="hidden">
lib/docs/main.js+39-19
......@@ -405,26 +405,45 @@ const NAV_MODES = {
405405 domApiMenu.classList.add("hidden");
406406
407407 // sidebar guides list
408 const list = Object.keys(zigAnalysis.guides);
409 resizeDomList(domGuidesList, list.length, '<li><a href="#"></a></li>');
410 for (let i = 0; i < list.length; i += 1) {
411 let liDom = domGuidesList.children[i];
412 let aDom = liDom.children[0];
413 aDom.textContent = list[i];
414 aDom.setAttribute("href", NAV_MODES.GUIDES + list[i]);
415 if (list[i] === curNav.activeGuide) {
416 aDom.classList.add("active");
417 } else {
418 aDom.classList.remove("active");
419 }
408 const section_list = zigAnalysis.guide_sections;
409 resizeDomList(domGuidesList, section_list.length, '<div><h2><span></span></h2><ul class="packages"></ul></div>');
410 for (let j = 0; j < section_list.length; j += 1) {
411 const section = section_list[j];
412 const domSectionName = domGuidesList.children[j].children[0].children[0];
413 const domGuides = domGuidesList.children[j].children[1];
414 domSectionName.textContent = section.name;
415 resizeDomList(domGuides, section.guides.length, '<li><a href="#"></a></li>');
416 for (let i = 0; i < section.guides.length; i += 1) {
417 const guide = section.guides[i];
418 let liDom = domGuides.children[i];
419 let aDom = liDom.children[0];
420 aDom.textContent = guide.name;
421 aDom.setAttribute("href", NAV_MODES.GUIDES + guide.name);
422 if (guide.name === curNav.activeGuide) {
423 aDom.classList.add("active");
424 } else {
425 aDom.classList.remove("active");
426 }
427 }
420428 }
421
422 if (list.length > 0) {
429
430 if (section_list.length > 0) {
423431 domGuidesMenu.classList.remove("hidden");
424432 }
425433
426434 // main content
427 const activeGuide = zigAnalysis.guides[curNav.activeGuide];
435 let activeGuide = undefined;
436 outer: for (let i = 0; i < zigAnalysis.guide_sections.length; i += 1) {
437 const section = zigAnalysis.guide_sections[i];
438 for (let j = 0; j < section.guides.length; j += 1) {
439 const guide = section.guides[j];
440 if (guide.name == curNav.activeGuide) {
441 activeGuide = guide;
442 break outer;
443 }
444 }
445 }
446
428447 if (activeGuide == undefined) {
429448 const root_file_idx = zigAnalysis.packages[zigAnalysis.rootPkg].file;
430449 const root_file_name = zigAnalysis.files[root_file_idx];
......@@ -446,6 +465,7 @@ const NAV_MODES = {
446465 \`\`\`
447466 //!zig-autodoc-guide: intro.md
448467 //!zig-autodoc-guide: quickstart.md
468 //!zig-autodoc-section: Advanced topics
449469 //!zig-autodoc-guide: ../advanced-docs/advanced-stuff.md
450470 \`\`\`
451471
......@@ -455,7 +475,7 @@ const NAV_MODES = {
455475 Happy writing!
456476 `);
457477 } else {
458 domGuides.innerHTML = markdown(activeGuide);
478 domGuides.innerHTML = markdown(activeGuide.body);
459479 }
460480 }
461481
......@@ -3104,9 +3124,9 @@ const NAV_MODES = {
31043124
31053125 return;
31063126 case NAV_MODES.GUIDES:
3107 const guides = Object.keys(zigAnalysis.guides);
3108 if (guides.length != 0 && query == "") {
3109 location.hash = NAV_MODES.GUIDES + guides[0];
3127 const sections = zigAnalysis.guide_sections;
3128 if (sections.length != 0 && sections[0].guides.length != 0 && query == "") {
3129 location.hash = NAV_MODES.GUIDES + sections[0].guides[0].name;
31103130 return;
31113131 }
31123132
src/Autodoc.zig+67-17
......@@ -28,7 +28,7 @@ decls: std.ArrayListUnmanaged(DocData.Decl) = .{},
2828exprs: std.ArrayListUnmanaged(DocData.Expr) = .{},
2929ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{},
3030comptime_exprs: std.ArrayListUnmanaged(DocData.ComptimeExpr) = .{},
31guides: std.StringHashMapUnmanaged([]const u8) = .{},
31guide_sections: std.ArrayListUnmanaged(Section) = .{},
3232
3333// These fields hold temporary state of the analysis process
3434// and are mainly used by the decl path resolving algorithm.
......@@ -63,6 +63,16 @@ const SrcLocInfo = struct {
6363 src_node: u32 = 0,
6464};
6565
66const Section = struct {
67 name: []const u8 = "", // empty string is the default section
68 guides: std.ArrayListUnmanaged(Guide) = .{},
69
70 const Guide = struct {
71 name: []const u8,
72 body: []const u8,
73 };
74};
75
6676var arena_allocator: std.heap.ArenaAllocator = undefined;
6777pub fn init(m: *Module, doc_location: Compilation.EmitLoc) Autodoc {
6878 arena_allocator = std.heap.ArenaAllocator.init(m.gpa);
......@@ -253,7 +263,7 @@ pub fn generateZirData(self: *Autodoc) !void {
253263 .exprs = self.exprs.items,
254264 .astNodes = self.ast_nodes.items,
255265 .comptimeExprs = self.comptime_exprs.items,
256 .guides = self.guides,
266 .guide_sections = self.guide_sections,
257267 };
258268
259269 const base_dir = self.doc_location.directory orelse
......@@ -419,7 +429,7 @@ const DocData = struct {
419429 exprs: []Expr,
420430 comptimeExprs: []ComptimeExpr,
421431
422 guides: std.StringHashMapUnmanaged([]const u8),
432 guide_sections: std.ArrayListUnmanaged(Section),
423433
424434 const Call = struct {
425435 func: Expr,
......@@ -440,7 +450,7 @@ const DocData = struct {
440450 try jsw.objectField(f_name);
441451 switch (f) {
442452 .files => try writeFileTableToJson(self.files, &jsw),
443 .guides => try writeGuidesToJson(self.guides, &jsw),
453 .guide_sections => try writeGuidesToJson(self.guide_sections, &jsw),
444454 else => {
445455 try std.json.stringify(@field(self, f_name), opts, w);
446456 jsw.state_index -= 1;
......@@ -4613,14 +4623,39 @@ fn writeFileTableToJson(map: std.AutoArrayHashMapUnmanaged(*File, usize), jsw: a
46134623 try jsw.endArray();
46144624}
46154625
4616fn writeGuidesToJson(map: std.StringHashMapUnmanaged([]const u8), jsw: anytype) !void {
4617 try jsw.beginObject();
4618 var it = map.iterator();
4619 while (it.next()) |entry| {
4620 try jsw.objectField(entry.key_ptr.*);
4621 try jsw.emitString(entry.value_ptr.*);
4626/// Writes the data like so:
4627/// ```
4628/// {
4629/// "<section name>": [{name: "<guide name>", text: "<guide contents>"},],
4630/// }
4631/// ```
4632fn writeGuidesToJson(sections: std.ArrayListUnmanaged(Section), jsw: anytype) !void {
4633 try jsw.beginArray();
4634
4635 for (sections.items) |s| {
4636 // section name
4637 try jsw.arrayElem();
4638 try jsw.beginObject();
4639 try jsw.objectField("name");
4640 try jsw.emitString(s.name);
4641 try jsw.objectField("guides");
4642
4643 // section value
4644 try jsw.beginArray();
4645 for (s.guides.items) |g| {
4646 try jsw.arrayElem();
4647 try jsw.beginObject();
4648 try jsw.objectField("name");
4649 try jsw.emitString(g.name);
4650 try jsw.objectField("body");
4651 try jsw.emitString(g.body);
4652 try jsw.endObject();
4653 }
4654 try jsw.endArray();
4655 try jsw.endObject();
46224656 }
4623 try jsw.endObject();
4657
4658 try jsw.endArray();
46244659}
46254660
46264661fn writePackageTableToJson(
......@@ -4688,19 +4723,31 @@ fn getTLDocComment(self: *Autodoc, file: *File) ![]const u8 {
46884723}
46894724
46904725fn findGuidePaths(self: *Autodoc, file: *File, str: []const u8) !void {
4691 const prefix = "zig-autodoc-guide:";
4726 const guide_prefix = "zig-autodoc-guide:";
4727 const section_prefix = "zig-autodoc-section:";
4728
4729 try self.guide_sections.append(self.arena, .{}); // add a default section
4730 var current_section = &self.guide_sections.items[self.guide_sections.items.len - 1];
4731
46924732 var it = std.mem.tokenize(u8, str, "\n");
46934733 while (it.next()) |line| {
46944734 const trimmed_line = std.mem.trim(u8, line, " ");
4695 if (std.mem.startsWith(u8, trimmed_line, prefix)) {
4696 const path = trimmed_line[prefix.len..];
4735 if (std.mem.startsWith(u8, trimmed_line, guide_prefix)) {
4736 const path = trimmed_line[guide_prefix.len..];
46974737 const trimmed_path = std.mem.trim(u8, path, " ");
4698 try self.addGuide(file, trimmed_path);
4738 try self.addGuide(file, trimmed_path, current_section);
4739 } else if (std.mem.startsWith(u8, trimmed_line, section_prefix)) {
4740 const section_name = trimmed_line[section_prefix.len..];
4741 const trimmed_section_name = std.mem.trim(u8, section_name, " ");
4742 try self.guide_sections.append(self.arena, .{
4743 .name = trimmed_section_name,
4744 });
4745 current_section = &self.guide_sections.items[self.guide_sections.items.len - 1];
46994746 }
47004747 }
47014748}
47024749
4703fn addGuide(self: *Autodoc, file: *File, guide_path: []const u8) !void {
4750fn addGuide(self: *Autodoc, file: *File, guide_path: []const u8, section: *Section) !void {
47044751 if (guide_path.len == 0) return error.MissingAutodocGuideName;
47054752
47064753 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 {
47164763 else => |e| return e,
47174764 };
47184765
4719 try self.guides.put(self.arena, resolved_path, guide);
4766 try section.guides.append(self.arena, .{
4767 .name = resolved_path,
4768 .body = guide,
4769 });
47204770}