mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 22:35:24 +00:00
autodoc: use commonmark.js for Markdown rendering
This commit is contained in:
parent
ba6e5e65a0
commit
d3eaa75c07
386
lib/docs/main.js
386
lib/docs/main.js
@ -482,40 +482,40 @@ const NAV_MODES = {
|
||||
const root_file_idx = zigAnalysis.modules[zigAnalysis.rootMod].file;
|
||||
const root_file_name = getFile(root_file_idx).name;
|
||||
domActiveGuide.innerHTML = markdown(`
|
||||
# Zig Guides
|
||||
These autodocs don't contain any guide.
|
||||
# Zig Guides
|
||||
These autodocs don't contain any guide.
|
||||
|
||||
While the API section is a reference guide autogenerated from Zig source code,
|
||||
guides are meant to be handwritten explanations that provide for example:
|
||||
While the API section is a reference guide autogenerated from Zig source code,
|
||||
guides are meant to be handwritten explanations that provide for example:
|
||||
|
||||
- how-to explanations for common use-cases
|
||||
- technical documentation
|
||||
- information about advanced usage patterns
|
||||
|
||||
You can add guides by specifying which markdown files to include
|
||||
in the top level doc comment of your root file, like so:
|
||||
- how-to explanations for common use-cases
|
||||
- technical documentation
|
||||
- information about advanced usage patterns
|
||||
|
||||
(At the top of *${root_file_name}*)
|
||||
\`\`\`
|
||||
//!zig-autodoc-guide: intro.md
|
||||
//!zig-autodoc-guide: quickstart.md
|
||||
//!zig-autodoc-guide: advanced-docs/advanced-stuff.md
|
||||
\`\`\`
|
||||
You can add guides by specifying which markdown files to include
|
||||
in the top level doc comment of your root file, like so:
|
||||
|
||||
You can also create sections to group guides together:
|
||||
(At the top of *${root_file_name}*)
|
||||
\`\`\`
|
||||
//!zig-autodoc-guide: intro.md
|
||||
//!zig-autodoc-guide: quickstart.md
|
||||
//!zig-autodoc-guide: advanced-docs/advanced-stuff.md
|
||||
\`\`\`
|
||||
|
||||
\`\`\`
|
||||
//!zig-autodoc-section: CLI Usage
|
||||
//!zig-autodoc-guide: cli-basics.md
|
||||
//!zig-autodoc-guide: cli-advanced.md
|
||||
\`\`\`
|
||||
|
||||
|
||||
**Note that this feature is still under heavy development so expect bugs**
|
||||
**and missing features!**
|
||||
You can also create sections to group guides together:
|
||||
|
||||
Happy writing!
|
||||
`);
|
||||
\`\`\`
|
||||
//!zig-autodoc-section: CLI Usage
|
||||
//!zig-autodoc-guide: cli-basics.md
|
||||
//!zig-autodoc-guide: cli-advanced.md
|
||||
\`\`\`
|
||||
|
||||
|
||||
**Note that this feature is still under heavy development so expect bugs**
|
||||
**and missing features!**
|
||||
|
||||
Happy writing!
|
||||
`);
|
||||
} else {
|
||||
domActiveGuide.innerHTML = markdown(activeGuide.body);
|
||||
}
|
||||
@ -3586,231 +3586,25 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
|
||||
|
||||
|
||||
function markdown(input, contextType) {
|
||||
const raw_lines = input.split("\n"); // zig allows no '\r', so we don't need to split on CR
|
||||
const parsed = new commonmark.Parser({ smart: true }).parse(input);
|
||||
|
||||
const lines = [];
|
||||
|
||||
// PHASE 1:
|
||||
// Dissect lines and determine the type for each line.
|
||||
// Also computes indentation level and removes unnecessary whitespace
|
||||
|
||||
let is_reading_code = false;
|
||||
let code_indent = 0;
|
||||
for (let line_no = 0; line_no < raw_lines.length; line_no++) {
|
||||
const raw_line = raw_lines[line_no];
|
||||
|
||||
const line = {
|
||||
indent: 0,
|
||||
raw_text: raw_line,
|
||||
text: raw_line.trim(),
|
||||
type: "p", // p, h1 … h6, code, ul, ol, blockquote, skip, empty
|
||||
ordered_number: -1, // NOTE: hack to make the type checker happy
|
||||
};
|
||||
|
||||
if (!is_reading_code) {
|
||||
while (
|
||||
line.indent < line.raw_text.length &&
|
||||
line.raw_text[line.indent] == " "
|
||||
) {
|
||||
line.indent += 1;
|
||||
// Look for decl references in inline code (`ref`)
|
||||
const walker = parsed.walker();
|
||||
let event;
|
||||
while ((event = walker.next())) {
|
||||
const node = event.node;
|
||||
if (node.type === "code") {
|
||||
const declHash = detectDeclPath(node.literal, contextType);
|
||||
if (declHash) {
|
||||
const link = new commonmark.Node("link");
|
||||
link.destination = declHash;
|
||||
node.insertBefore(link);
|
||||
link.appendChild(node);
|
||||
}
|
||||
|
||||
if (line.text.startsWith("######")) {
|
||||
line.type = "h6";
|
||||
line.text = line.text.substr(6);
|
||||
} else if (line.text.startsWith("#####")) {
|
||||
line.type = "h5";
|
||||
line.text = line.text.substr(5);
|
||||
} else if (line.text.startsWith("####")) {
|
||||
line.type = "h4";
|
||||
line.text = line.text.substr(4);
|
||||
} else if (line.text.startsWith("###")) {
|
||||
line.type = "h3";
|
||||
line.text = line.text.substr(3);
|
||||
} else if (line.text.startsWith("##")) {
|
||||
line.type = "h2";
|
||||
line.text = line.text.substr(2);
|
||||
} else if (line.text.startsWith("#")) {
|
||||
line.type = "h1";
|
||||
line.text = line.text.substr(1);
|
||||
} else if (line.text.match(/^-[ \t]+.*$/)) {
|
||||
// line starts with a hyphen, followed by spaces or tabs
|
||||
const match = line.text.match(/^-[ \t]+/);
|
||||
line.type = "ul";
|
||||
line.text = line.text.substr(match[0].length);
|
||||
} else if (line.text.match(/^\d+\.[ \t]+.*$/)) {
|
||||
// line starts with {number}{dot}{spaces or tabs}
|
||||
const match = line.text.match(/(\d+)\.[ \t]+/);
|
||||
line.type = "ol";
|
||||
line.text = line.text.substr(match[0].length);
|
||||
line.ordered_number = Number(match[1].length);
|
||||
} else if (line.text == "```") {
|
||||
line.type = "skip";
|
||||
is_reading_code = true;
|
||||
code_indent = line.indent;
|
||||
} else if (line.text == "") {
|
||||
line.type = "empty";
|
||||
}
|
||||
} else {
|
||||
if (line.text == "```") {
|
||||
is_reading_code = false;
|
||||
line.type = "skip";
|
||||
} else {
|
||||
line.type = "code";
|
||||
line.text = line.raw_text.substr(code_indent); // remove the indent of the ``` from all the code block
|
||||
}
|
||||
}
|
||||
|
||||
if (line.type != "skip") {
|
||||
lines.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
// PHASE 2:
|
||||
// Render HTML from markdown lines.
|
||||
// Look at each line and emit fitting HTML code
|
||||
|
||||
function markdownInlines(innerText, contextType) {
|
||||
// inline types:
|
||||
// **{INLINE}** : <strong>
|
||||
// __{INLINE}__ : <u>
|
||||
// ~~{INLINE}~~ : <s>
|
||||
// *{INLINE}* : <emph>
|
||||
// _{INLINE}_ : <emph>
|
||||
// `{TEXT}` : <code>
|
||||
// [{INLINE}]({URL}) : <a>
|
||||
//  : <img>
|
||||
// [[std;format.fmt]] : <a> (inner link)
|
||||
|
||||
const formats = [
|
||||
{
|
||||
marker: "**",
|
||||
tag: "strong",
|
||||
},
|
||||
{
|
||||
marker: "~~",
|
||||
tag: "s",
|
||||
},
|
||||
{
|
||||
marker: "__",
|
||||
tag: "u",
|
||||
},
|
||||
{
|
||||
marker: "*",
|
||||
tag: "em",
|
||||
},
|
||||
];
|
||||
|
||||
const stack = [];
|
||||
|
||||
let innerHTML = "";
|
||||
let currentRun = "";
|
||||
|
||||
function flushRun() {
|
||||
if (currentRun != "") {
|
||||
innerHTML += escapeHtml(currentRun);
|
||||
}
|
||||
currentRun = "";
|
||||
}
|
||||
|
||||
let parsing_code = false;
|
||||
let codetag = "";
|
||||
let in_code = false;
|
||||
|
||||
// state used to link decl references
|
||||
let quote_start = undefined;
|
||||
let quote_start_html = undefined;
|
||||
|
||||
for (let i = 0; i < innerText.length; i++) {
|
||||
if (parsing_code && in_code) {
|
||||
if (innerText.substr(i, codetag.length) == codetag) {
|
||||
// remove leading and trailing whitespace if string both starts and ends with one.
|
||||
if (
|
||||
currentRun[0] == " " &&
|
||||
currentRun[currentRun.length - 1] == " "
|
||||
) {
|
||||
currentRun = currentRun.substr(1, currentRun.length - 2);
|
||||
}
|
||||
flushRun();
|
||||
i += codetag.length - 1;
|
||||
in_code = false;
|
||||
parsing_code = false;
|
||||
innerHTML += "</code>";
|
||||
codetag = "";
|
||||
|
||||
// find out if this is a decl that should be linked
|
||||
const maybe_decl_path = innerText.substr(quote_start, i-quote_start);
|
||||
const decl_hash = detectDeclPath(maybe_decl_path, contextType);
|
||||
if (decl_hash) {
|
||||
const anchor_opening_tag = "<a href='"+ decl_hash +"'>";
|
||||
innerHTML = innerHTML.slice(0, quote_start_html)
|
||||
+ anchor_opening_tag
|
||||
+ innerHTML.slice(quote_start_html) + "</a>";
|
||||
}
|
||||
} else {
|
||||
currentRun += innerText[i];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (innerText[i] == "`") {
|
||||
flushRun();
|
||||
if (!parsing_code) {
|
||||
quote_start = i + 1;
|
||||
quote_start_html = innerHTML.length;
|
||||
innerHTML += "<code>";
|
||||
}
|
||||
parsing_code = true;
|
||||
codetag += "`";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parsing_code) {
|
||||
currentRun += innerText[i];
|
||||
in_code = true;
|
||||
} else {
|
||||
let any = false;
|
||||
for (
|
||||
let idx = stack.length > 0 ? -1 : 0;
|
||||
idx < formats.length;
|
||||
idx++
|
||||
) {
|
||||
const fmt = idx >= 0 ? formats[idx] : stack[stack.length - 1];
|
||||
if (innerText.substr(i, fmt.marker.length) == fmt.marker) {
|
||||
flushRun();
|
||||
if (stack[stack.length - 1] == fmt) {
|
||||
stack.pop();
|
||||
innerHTML += "</" + fmt.tag + ">";
|
||||
} else {
|
||||
stack.push(fmt);
|
||||
innerHTML += "<" + fmt.tag + ">";
|
||||
}
|
||||
i += fmt.marker.length - 1;
|
||||
any = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!any) {
|
||||
currentRun += innerText[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
flushRun();
|
||||
|
||||
if (in_code) {
|
||||
in_code = false;
|
||||
parsing_code = false;
|
||||
innerHTML += "</code>";
|
||||
codetag = "";
|
||||
}
|
||||
|
||||
while (stack.length > 0) {
|
||||
const fmt = stack.pop();
|
||||
innerHTML += "</" + fmt.tag + ">";
|
||||
}
|
||||
|
||||
return innerHTML;
|
||||
}
|
||||
return new commonmark.HtmlRenderer({ safe: true }).render(parsed);
|
||||
|
||||
function detectDeclPath(text, context) {
|
||||
let result = "";
|
||||
@ -3876,102 +3670,6 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) {
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
function previousLineIs(type, line_no) {
|
||||
if (line_no > 0) {
|
||||
return lines[line_no - 1].type == type;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function nextLineIs(type, line_no) {
|
||||
if (line_no < lines.length - 1) {
|
||||
return lines[line_no + 1].type == type;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getPreviousLineIndent(line_no) {
|
||||
if (line_no > 0) {
|
||||
return lines[line_no - 1].indent;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function getNextLineIndent(line_no) {
|
||||
if (line_no < lines.length - 1) {
|
||||
return lines[line_no + 1].indent;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
let html = "";
|
||||
for (let line_no = 0; line_no < lines.length; line_no++) {
|
||||
const line = lines[line_no];
|
||||
|
||||
switch (line.type) {
|
||||
case "h1":
|
||||
case "h2":
|
||||
case "h3":
|
||||
case "h4":
|
||||
case "h5":
|
||||
case "h6":
|
||||
html +=
|
||||
"<" +
|
||||
line.type +
|
||||
">" +
|
||||
markdownInlines(line.text, contextType) +
|
||||
"</" +
|
||||
line.type +
|
||||
">\n";
|
||||
break;
|
||||
|
||||
case "ul":
|
||||
case "ol":
|
||||
if (
|
||||
!previousLineIs(line.type, line_no) ||
|
||||
getPreviousLineIndent(line_no) < line.indent
|
||||
) {
|
||||
html += "<" + line.type + ">\n";
|
||||
}
|
||||
|
||||
html += "<li>" + markdownInlines(line.text, contextType) + "</li>\n";
|
||||
|
||||
if (
|
||||
!nextLineIs(line.type, line_no) ||
|
||||
getNextLineIndent(line_no) < line.indent
|
||||
) {
|
||||
html += "</" + line.type + ">\n";
|
||||
}
|
||||
break;
|
||||
|
||||
case "p":
|
||||
if (!previousLineIs("p", line_no)) {
|
||||
html += "<p>\n";
|
||||
}
|
||||
html += markdownInlines(line.text, contextType) + "\n";
|
||||
if (!nextLineIs("p", line_no)) {
|
||||
html += "</p>\n";
|
||||
}
|
||||
break;
|
||||
|
||||
case "code":
|
||||
if (!previousLineIs("code", line_no)) {
|
||||
html += "<pre><code>";
|
||||
}
|
||||
html += escapeHtml(line.text) + "\n";
|
||||
if (!nextLineIs("code", line_no)) {
|
||||
html += "</code></pre>\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function activateSelectedResult() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user