| ... | @@ -482,40 +482,40 @@ const NAV_MODES = { | ... | @@ -482,40 +482,40 @@ const NAV_MODES = { |
| 482 | const root_file_idx = zigAnalysis.modules[zigAnalysis.rootMod].file; | 482 | const root_file_idx = zigAnalysis.modules[zigAnalysis.rootMod].file; |
| 483 | const root_file_name = getFile(root_file_idx).name; | 483 | const root_file_name = getFile(root_file_idx).name; |
| 484 | domActiveGuide.innerHTML = markdown(` | 484 | domActiveGuide.innerHTML = markdown(` |
| 485 | # Zig Guides | 485 | # Zig Guides |
| 486 | These autodocs don't contain any guide. | 486 | These autodocs don't contain any guide. |
| 487 | | 487 | |
| 488 | While the API section is a reference guide autogenerated from Zig source code, | 488 | While the API section is a reference guide autogenerated from Zig source code, |
| 489 | guides are meant to be handwritten explanations that provide for example: | 489 | guides are meant to be handwritten explanations that provide for example: |
| 490 | | 490 | |
| 491 | - how-to explanations for common use-cases | 491 | - how-to explanations for common use-cases |
| 492 | - technical documentation | 492 | - technical documentation |
| 493 | - information about advanced usage patterns | 493 | - information about advanced usage patterns |
| 494 | | 494 | |
| 495 | You can add guides by specifying which markdown files to include | 495 | You can add guides by specifying which markdown files to include |
| 496 | in the top level doc comment of your root file, like so: | 496 | in the top level doc comment of your root file, like so: |
| 497 | | 497 | |
| 498 | (At the top of *${root_file_name}*) | 498 | (At the top of *${root_file_name}*) |
| 499 | \`\`\` | 499 | \`\`\` |
| 500 | //!zig-autodoc-guide: intro.md | 500 | //!zig-autodoc-guide: intro.md |
| 501 | //!zig-autodoc-guide: quickstart.md | 501 | //!zig-autodoc-guide: quickstart.md |
| 502 | //!zig-autodoc-guide: advanced-docs/advanced-stuff.md | 502 | //!zig-autodoc-guide: advanced-docs/advanced-stuff.md |
| 503 | \`\`\` | 503 | \`\`\` |
| 504 | | 504 | |
| 505 | You can also create sections to group guides together: | 505 | You can also create sections to group guides together: |
| 506 | | | |
| 507 | \`\`\` | | |
| 508 | //!zig-autodoc-section: CLI Usage | | |
| 509 | //!zig-autodoc-guide: cli-basics.md | | |
| 510 | //!zig-autodoc-guide: cli-advanced.md | | |
| 511 | \`\`\` | | |
| 512 | | | |
| 513 | | | |
| 514 | **Note that this feature is still under heavy development so expect bugs** | | |
| 515 | **and missing features!** | | |
| 516 | | 506 | |
| 517 | Happy writing! | 507 | \`\`\` |
| 518 | `); | 508 | //!zig-autodoc-section: CLI Usage |
| | 509 | //!zig-autodoc-guide: cli-basics.md |
| | 510 | //!zig-autodoc-guide: cli-advanced.md |
| | 511 | \`\`\` |
| | 512 | |
| | 513 | |
| | 514 | **Note that this feature is still under heavy development so expect bugs** |
| | 515 | **and missing features!** |
| | 516 | |
| | 517 | Happy writing! |
| | 518 | `); |
| 519 | } else { | 519 | } else { |
| 520 | domActiveGuide.innerHTML = markdown(activeGuide.body); | 520 | domActiveGuide.innerHTML = markdown(activeGuide.body); |
| 521 | } | 521 | } |
| ... | @@ -3586,231 +3586,25 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) { | ... | @@ -3586,231 +3586,25 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) { |
| 3586 | | 3586 | |
| 3587 | | 3587 | |
| 3588 | function markdown(input, contextType) { | 3588 | function markdown(input, contextType) { |
| 3589 | const raw_lines = input.split("\n"); // zig allows no '\r', so we don't need to split on CR | 3589 | const parsed = new commonmark.Parser({ smart: true }).parse(input); |
| 3590 | | 3590 | |
| 3591 | const lines = []; | 3591 | // Look for decl references in inline code (`ref`) |
| 3592 | | 3592 | const walker = parsed.walker(); |
| 3593 | // PHASE 1: | 3593 | let event; |
| 3594 | // Dissect lines and determine the type for each line. | 3594 | while ((event = walker.next())) { |
| 3595 | // Also computes indentation level and removes unnecessary whitespace | 3595 | const node = event.node; |
| 3596 | | 3596 | if (node.type === "code") { |
| 3597 | let is_reading_code = false; | 3597 | const declHash = detectDeclPath(node.literal, contextType); |
| 3598 | let code_indent = 0; | 3598 | if (declHash) { |
| 3599 | for (let line_no = 0; line_no < raw_lines.length; line_no++) { | 3599 | const link = new commonmark.Node("link"); |
| 3600 | const raw_line = raw_lines[line_no]; | 3600 | link.destination = declHash; |
| 3601 | | 3601 | node.insertBefore(link); |
| 3602 | const line = { | 3602 | link.appendChild(node); |
| 3603 | indent: 0, | | |
| 3604 | raw_text: raw_line, | | |
| 3605 | text: raw_line.trim(), | | |
| 3606 | type: "p", // p, h1 … h6, code, ul, ol, blockquote, skip, empty | | |
| 3607 | ordered_number: -1, // NOTE: hack to make the type checker happy | | |
| 3608 | }; | | |
| 3609 | | | |
| 3610 | if (!is_reading_code) { | | |
| 3611 | while ( | | |
| 3612 | line.indent < line.raw_text.length && | | |
| 3613 | line.raw_text[line.indent] == " " | | |
| 3614 | ) { | | |
| 3615 | line.indent += 1; | | |
| 3616 | } | | |
| 3617 | | | |
| 3618 | if (line.text.startsWith("######")) { | | |
| 3619 | line.type = "h6"; | | |
| 3620 | line.text = line.text.substr(6); | | |
| 3621 | } else if (line.text.startsWith("#####")) { | | |
| 3622 | line.type = "h5"; | | |
| 3623 | line.text = line.text.substr(5); | | |
| 3624 | } else if (line.text.startsWith("####")) { | | |
| 3625 | line.type = "h4"; | | |
| 3626 | line.text = line.text.substr(4); | | |
| 3627 | } else if (line.text.startsWith("###")) { | | |
| 3628 | line.type = "h3"; | | |
| 3629 | line.text = line.text.substr(3); | | |
| 3630 | } else if (line.text.startsWith("##")) { | | |
| 3631 | line.type = "h2"; | | |
| 3632 | line.text = line.text.substr(2); | | |
| 3633 | } else if (line.text.startsWith("#")) { | | |
| 3634 | line.type = "h1"; | | |
| 3635 | line.text = line.text.substr(1); | | |
| 3636 | } else if (line.text.match(/^-[ \t]+.*$/)) { | | |
| 3637 | // line starts with a hyphen, followed by spaces or tabs | | |
| 3638 | const match = line.text.match(/^-[ \t]+/); | | |
| 3639 | line.type = "ul"; | | |
| 3640 | line.text = line.text.substr(match[0].length); | | |
| 3641 | } else if (line.text.match(/^\d+\.[ \t]+.*$/)) { | | |
| 3642 | // line starts with {number}{dot}{spaces or tabs} | | |
| 3643 | const match = line.text.match(/(\d+)\.[ \t]+/); | | |
| 3644 | line.type = "ol"; | | |
| 3645 | line.text = line.text.substr(match[0].length); | | |
| 3646 | line.ordered_number = Number(match[1].length); | | |
| 3647 | } else if (line.text == "```") { | | |
| 3648 | line.type = "skip"; | | |
| 3649 | is_reading_code = true; | | |
| 3650 | code_indent = line.indent; | | |
| 3651 | } else if (line.text == "") { | | |
| 3652 | line.type = "empty"; | | |
| 3653 | } | 3603 | } |
| 3654 | } else { | | |
| 3655 | if (line.text == "```") { | | |
| 3656 | is_reading_code = false; | | |
| 3657 | line.type = "skip"; | | |
| 3658 | } else { | | |
| 3659 | line.type = "code"; | | |
| 3660 | line.text = line.raw_text.substr(code_indent); // remove the indent of the ``` from all the code block | | |
| 3661 | } | | |
| 3662 | } | | |
| 3663 | | | |
| 3664 | if (line.type != "skip") { | | |
| 3665 | lines.push(line); | | |
| 3666 | } | 3604 | } |
| 3667 | } | 3605 | } |
| 3668 | | 3606 | |
| 3669 | // PHASE 2: | 3607 | return new commonmark.HtmlRenderer({ safe: true }).render(parsed); |
| 3670 | // Render HTML from markdown lines. | | |
| 3671 | // Look at each line and emit fitting HTML code | | |
| 3672 | | | |
| 3673 | function markdownInlines(innerText, contextType) { | | |
| 3674 | // inline types: | | |
| 3675 | // **{INLINE}** : <strong> | | |
| 3676 | // __{INLINE}__ : <u> | | |
| 3677 | // ~~{INLINE}~~ : <s> | | |
| 3678 | // *{INLINE}* : <emph> | | |
| 3679 | // _{INLINE}_ : <emph> | | |
| 3680 | // `{TEXT}` : <code> | | |
| 3681 | // [{INLINE}]({URL}) : <a> | | |
| 3682 | //  : <img> | | |
| 3683 | // [[std;format.fmt]] : <a> (inner link) | | |
| 3684 | | | |
| 3685 | const formats = [ | | |
| 3686 | { | | |
| 3687 | marker: "**", | | |
| 3688 | tag: "strong", | | |
| 3689 | }, | | |
| 3690 | { | | |
| 3691 | marker: "~~", | | |
| 3692 | tag: "s", | | |
| 3693 | }, | | |
| 3694 | { | | |
| 3695 | marker: "__", | | |
| 3696 | tag: "u", | | |
| 3697 | }, | | |
| 3698 | { | | |
| 3699 | marker: "*", | | |
| 3700 | tag: "em", | | |
| 3701 | }, | | |
| 3702 | ]; | | |
| 3703 | | | |
| 3704 | const stack = []; | | |
| 3705 | | | |
| 3706 | let innerHTML = ""; | | |
| 3707 | let currentRun = ""; | | |
| 3708 | | | |
| 3709 | function flushRun() { | | |
| 3710 | if (currentRun != "") { | | |
| 3711 | innerHTML += escapeHtml(currentRun); | | |
| 3712 | } | | |
| 3713 | currentRun = ""; | | |
| 3714 | } | | |
| 3715 | | | |
| 3716 | let parsing_code = false; | | |
| 3717 | let codetag = ""; | | |
| 3718 | let in_code = false; | | |
| 3719 | | | |
| 3720 | // state used to link decl references | | |
| 3721 | let quote_start = undefined; | | |
| 3722 | let quote_start_html = undefined; | | |
| 3723 | | | |
| 3724 | for (let i = 0; i < innerText.length; i++) { | | |
| 3725 | if (parsing_code && in_code) { | | |
| 3726 | if (innerText.substr(i, codetag.length) == codetag) { | | |
| 3727 | // remove leading and trailing whitespace if string both starts and ends with one. | | |
| 3728 | if ( | | |
| 3729 | currentRun[0] == " " && | | |
| 3730 | currentRun[currentRun.length - 1] == " " | | |
| 3731 | ) { | | |
| 3732 | currentRun = currentRun.substr(1, currentRun.length - 2); | | |
| 3733 | } | | |
| 3734 | flushRun(); | | |
| 3735 | i += codetag.length - 1; | | |
| 3736 | in_code = false; | | |
| 3737 | parsing_code = false; | | |
| 3738 | innerHTML += "</code>"; | | |
| 3739 | codetag = ""; | | |
| 3740 | | | |
| 3741 | // find out if this is a decl that should be linked | | |
| 3742 | const maybe_decl_path = innerText.substr(quote_start, i-quote_start); | | |
| 3743 | const decl_hash = detectDeclPath(maybe_decl_path, contextType); | | |
| 3744 | if (decl_hash) { | | |
| 3745 | const anchor_opening_tag = "<a href='"+ decl_hash +"'>"; | | |
| 3746 | innerHTML = innerHTML.slice(0, quote_start_html) | | |
| 3747 | + anchor_opening_tag | | |
| 3748 | + innerHTML.slice(quote_start_html) + "</a>"; | | |
| 3749 | } | | |
| 3750 | } else { | | |
| 3751 | currentRun += innerText[i]; | | |
| 3752 | } | | |
| 3753 | continue; | | |
| 3754 | } | | |
| 3755 | | | |
| 3756 | if (innerText[i] == "`") { | | |
| 3757 | flushRun(); | | |
| 3758 | if (!parsing_code) { | | |
| 3759 | quote_start = i + 1; | | |
| 3760 | quote_start_html = innerHTML.length; | | |
| 3761 | innerHTML += "<code>"; | | |
| 3762 | } | | |
| 3763 | parsing_code = true; | | |
| 3764 | codetag += "`"; | | |
| 3765 | continue; | | |
| 3766 | } | | |
| 3767 | | | |
| 3768 | if (parsing_code) { | | |
| 3769 | currentRun += innerText[i]; | | |
| 3770 | in_code = true; | | |
| 3771 | } else { | | |
| 3772 | let any = false; | | |
| 3773 | for ( | | |
| 3774 | let idx = stack.length > 0 ? -1 : 0; | | |
| 3775 | idx < formats.length; | | |
| 3776 | idx++ | | |
| 3777 | ) { | | |
| 3778 | const fmt = idx >= 0 ? formats[idx] : stack[stack.length - 1]; | | |
| 3779 | if (innerText.substr(i, fmt.marker.length) == fmt.marker) { | | |
| 3780 | flushRun(); | | |
| 3781 | if (stack[stack.length - 1] == fmt) { | | |
| 3782 | stack.pop(); | | |
| 3783 | innerHTML += "</" + fmt.tag + ">"; | | |
| 3784 | } else { | | |
| 3785 | stack.push(fmt); | | |
| 3786 | innerHTML += "<" + fmt.tag + ">"; | | |
| 3787 | } | | |
| 3788 | i += fmt.marker.length - 1; | | |
| 3789 | any = true; | | |
| 3790 | break; | | |
| 3791 | } | | |
| 3792 | } | | |
| 3793 | if (!any) { | | |
| 3794 | currentRun += innerText[i]; | | |
| 3795 | } | | |
| 3796 | } | | |
| 3797 | } | | |
| 3798 | flushRun(); | | |
| 3799 | | | |
| 3800 | if (in_code) { | | |
| 3801 | in_code = false; | | |
| 3802 | parsing_code = false; | | |
| 3803 | innerHTML += "</code>"; | | |
| 3804 | codetag = ""; | | |
| 3805 | } | | |
| 3806 | | | |
| 3807 | while (stack.length > 0) { | | |
| 3808 | const fmt = stack.pop(); | | |
| 3809 | innerHTML += "</" + fmt.tag + ">"; | | |
| 3810 | } | | |
| 3811 | | | |
| 3812 | return innerHTML; | | |
| 3813 | } | | |
| 3814 | | 3608 | |
| 3815 | function detectDeclPath(text, context) { | 3609 | function detectDeclPath(text, context) { |
| 3816 | let result = ""; | 3610 | let result = ""; |
| ... | @@ -3876,102 +3670,6 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) { | ... | @@ -3876,102 +3670,6 @@ function addDeclToSearchResults(decl, declIndex, modNames, item, list, stack) { |
| 3876 | return result; | 3670 | return result; |
| 3877 | | 3671 | |
| 3878 | } | 3672 | } |
| 3879 | | | |
| 3880 | function previousLineIs(type, line_no) { | | |
| 3881 | if (line_no > 0) { | | |
| 3882 | return lines[line_no - 1].type == type; | | |
| 3883 | } else { | | |
| 3884 | return false; | | |
| 3885 | } | | |
| 3886 | } | | |
| 3887 | | | |
| 3888 | function nextLineIs(type, line_no) { | | |
| 3889 | if (line_no < lines.length - 1) { | | |
| 3890 | return lines[line_no + 1].type == type; | | |
| 3891 | } else { | | |
| 3892 | return false; | | |
| 3893 | } | | |
| 3894 | } | | |
| 3895 | | | |
| 3896 | function getPreviousLineIndent(line_no) { | | |
| 3897 | if (line_no > 0) { | | |
| 3898 | return lines[line_no - 1].indent; | | |
| 3899 | } else { | | |
| 3900 | return 0; | | |
| 3901 | } | | |
| 3902 | } | | |
| 3903 | | | |
| 3904 | function getNextLineIndent(line_no) { | | |
| 3905 | if (line_no < lines.length - 1) { | | |
| 3906 | return lines[line_no + 1].indent; | | |
| 3907 | } else { | | |
| 3908 | return 0; | | |
| 3909 | } | | |
| 3910 | } | | |
| 3911 | | | |
| 3912 | let html = ""; | | |
| 3913 | for (let line_no = 0; line_no < lines.length; line_no++) { | | |
| 3914 | const line = lines[line_no]; | | |
| 3915 | | | |
| 3916 | switch (line.type) { | | |
| 3917 | case "h1": | | |
| 3918 | case "h2": | | |
| 3919 | case "h3": | | |
| 3920 | case "h4": | | |
| 3921 | case "h5": | | |
| 3922 | case "h6": | | |
| 3923 | html += | | |
| 3924 | "<" + | | |
| 3925 | line.type + | | |
| 3926 | ">" + | | |
| 3927 | markdownInlines(line.text, contextType) + | | |
| 3928 | "</" + | | |
| 3929 | line.type + | | |
| 3930 | ">\n"; | | |
| 3931 | break; | | |
| 3932 | | | |
| 3933 | case "ul": | | |
| 3934 | case "ol": | | |
| 3935 | if ( | | |
| 3936 | !previousLineIs(line.type, line_no) || | | |
| 3937 | getPreviousLineIndent(line_no) < line.indent | | |
| 3938 | ) { | | |
| 3939 | html += "<" + line.type + ">\n"; | | |
| 3940 | } | | |
| 3941 | | | |
| 3942 | html += "<li>" + markdownInlines(line.text, contextType) + "</li>\n"; | | |
| 3943 | | | |
| 3944 | if ( | | |
| 3945 | !nextLineIs(line.type, line_no) || | | |
| 3946 | getNextLineIndent(line_no) < line.indent | | |
| 3947 | ) { | | |
| 3948 | html += "</" + line.type + ">\n"; | | |
| 3949 | } | | |
| 3950 | break; | | |
| 3951 | | | |
| 3952 | case "p": | | |
| 3953 | if (!previousLineIs("p", line_no)) { | | |
| 3954 | html += "<p>\n"; | | |
| 3955 | } | | |
| 3956 | html += markdownInlines(line.text, contextType) + "\n"; | | |
| 3957 | if (!nextLineIs("p", line_no)) { | | |
| 3958 | html += "</p>\n"; | | |
| 3959 | } | | |
| 3960 | break; | | |
| 3961 | | | |
| 3962 | case "code": | | |
| 3963 | if (!previousLineIs("code", line_no)) { | | |
| 3964 | html += "<pre><code>"; | | |
| 3965 | } | | |
| 3966 | html += escapeHtml(line.text) + "\n"; | | |
| 3967 | if (!nextLineIs("code", line_no)) { | | |
| 3968 | html += "</code></pre>\n"; | | |
| 3969 | } | | |
| 3970 | break; | | |
| 3971 | } | | |
| 3972 | } | | |
| 3973 | | | |
| 3974 | return html; | | |
| 3975 | } | 3673 | } |
| 3976 | | 3674 | |
| 3977 | function activateSelectedResult() { | 3675 | function activateSelectedResult() { |