mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
update autodocs web application to latest
upstream commit 1f921d540e1a8bb40839be30239019c820eb663d after this branch is merged, ziglang/zig becomes the new repository for this code.
This commit is contained in:
parent
6b8c7540a8
commit
b13a55db97
@ -12,6 +12,9 @@
|
|||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
a {
|
a {
|
||||||
color: #2A6286;
|
color: #2A6286;
|
||||||
}
|
}
|
||||||
@ -25,25 +28,38 @@
|
|||||||
}
|
}
|
||||||
code {
|
code {
|
||||||
font-family:"Source Code Pro",monospace;
|
font-family:"Source Code Pro",monospace;
|
||||||
font-size:1em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
code a {
|
code a {
|
||||||
color: #000000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
#listFields > div {
|
#listFields > div, #listParams > div {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
#hdrName a {
|
||||||
|
font-size: 0.7em;
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
.fieldDocs {
|
.fieldDocs {
|
||||||
border: 1px solid #F5F5F5;
|
border: 1px solid #F5F5F5;
|
||||||
border-top: 0px;
|
border-top: 0px;
|
||||||
padding: 1px 1em;
|
padding: 1px 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
width: 8em;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
#navWrap {
|
#navWrap {
|
||||||
float: left;
|
width: -moz-available;
|
||||||
width: 47em;
|
width: -webkit-fill-available;
|
||||||
margin-left: 1em;
|
width: stretch;
|
||||||
|
margin-left: 11em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
@ -102,15 +118,6 @@
|
|||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logo {
|
|
||||||
width: 8em;
|
|
||||||
padding: 0.5em 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#search {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#helpDialog {
|
#helpDialog {
|
||||||
width: 21em;
|
width: 21em;
|
||||||
height: 21em;
|
height: 21em;
|
||||||
@ -154,6 +161,12 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dl > div {
|
||||||
|
padding: 0.5em;
|
||||||
|
border: 1px solid #c0c0c0;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -163,6 +176,10 @@
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul.columns {
|
||||||
|
column-width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
.tok-kw {
|
.tok-kw {
|
||||||
color: #333;
|
color: #333;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@ -197,14 +214,15 @@
|
|||||||
background-color: #111;
|
background-color: #111;
|
||||||
color: #bbb;
|
color: #bbb;
|
||||||
}
|
}
|
||||||
|
pre {
|
||||||
|
background-color: #222;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
a {
|
a {
|
||||||
color: #88f;
|
color: #88f;
|
||||||
}
|
}
|
||||||
code a {
|
code a {
|
||||||
color: #bbb;
|
color: #ccc;
|
||||||
}
|
|
||||||
pre{
|
|
||||||
background-color:#2A2A2A;
|
|
||||||
}
|
}
|
||||||
.fieldDocs {
|
.fieldDocs {
|
||||||
border-color:#2A2A2A;
|
border-color:#2A2A2A;
|
||||||
@ -229,6 +247,9 @@
|
|||||||
#listSearchResults li.selected a {
|
#listSearchResults li.selected a {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
dl > div {
|
||||||
|
border-color: #373737;
|
||||||
|
}
|
||||||
.tok-kw {
|
.tok-kw {
|
||||||
color: #eee;
|
color: #eee;
|
||||||
}
|
}
|
||||||
@ -242,7 +263,7 @@
|
|||||||
color: #aa7;
|
color: #aa7;
|
||||||
}
|
}
|
||||||
.tok-fn {
|
.tok-fn {
|
||||||
color: #e33;
|
color: #B1A0F8;
|
||||||
}
|
}
|
||||||
.tok-null {
|
.tok-null {
|
||||||
color: #ff8080;
|
color: #ff8080;
|
||||||
@ -258,7 +279,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav>
|
<nav>
|
||||||
<div class="logo">
|
<a class="logo" href="#">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 140">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 140">
|
||||||
<g fill="#F7A41D">
|
<g fill="#F7A41D">
|
||||||
<g>
|
<g>
|
||||||
@ -297,7 +318,7 @@
|
|||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
<div id="navWrap">
|
<div id="navWrap">
|
||||||
<input type="search" id="search" autocomplete="off" spellcheck="false" placeholder="`s` to search, `?` to see more options">
|
<input type="search" id="search" autocomplete="off" spellcheck="false" placeholder="`s` to search, `?` to see more options">
|
||||||
@ -305,11 +326,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<section>
|
<section>
|
||||||
<p id="status">Loading...</p>
|
<p id="status">Loading...</p>
|
||||||
|
<h1 id="hdrName" class="hidden"><span></span><a href="#">[src]</a></h1>
|
||||||
<div id="fnProto" class="hidden">
|
<div id="fnProto" class="hidden">
|
||||||
<pre><code id="fnProtoCode"></code></pre>
|
<pre><code id="fnProtoCode"></code></pre>
|
||||||
</div>
|
</div>
|
||||||
<h1 id="hdrName" class="hidden"></h1>
|
|
||||||
<div id="tldDocs" class="hidden"></div>
|
<div id="tldDocs" class="hidden"></div>
|
||||||
|
<div id="sectParams" class="hidden">
|
||||||
|
<h2>Parameters</h2>
|
||||||
|
<div id="listParams">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="sectFnErrors" class="hidden">
|
<div id="sectFnErrors" class="hidden">
|
||||||
<h2>Errors</h2>
|
<h2>Errors</h2>
|
||||||
<div id="fnErrorsAnyError">
|
<div id="fnErrorsAnyError">
|
||||||
@ -332,12 +358,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="sectTypes" class="hidden">
|
<div id="sectTypes" class="hidden">
|
||||||
<h2>Types</h2>
|
<h2>Types</h2>
|
||||||
<ul id="listTypes">
|
<ul id="listTypes" class="columns">
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="sectNamespaces" class="hidden">
|
<div id="sectNamespaces" class="hidden">
|
||||||
<h2>Namespaces</h2>
|
<h2>Namespaces</h2>
|
||||||
<ul id="listNamespaces">
|
<ul id="listNamespaces" class="columns">
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="sectGlobalVars" class="hidden">
|
<div id="sectGlobalVars" class="hidden">
|
||||||
@ -347,11 +373,6 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div id="sectFns" class="hidden">
|
|
||||||
<h2>Functions</h2>
|
|
||||||
<dl id="listFns">
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
<div id="sectValues" class="hidden">
|
<div id="sectValues" class="hidden">
|
||||||
<h2>Values</h2>
|
<h2>Values</h2>
|
||||||
<table>
|
<table>
|
||||||
@ -359,9 +380,14 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="sectFns" class="hidden">
|
||||||
|
<h2>Functions</h2>
|
||||||
|
<dl id="listFns">
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
<div id="sectErrSets" class="hidden">
|
<div id="sectErrSets" class="hidden">
|
||||||
<h2>Error Sets</h2>
|
<h2>Error Sets</h2>
|
||||||
<ul id="listErrSets">
|
<ul id="listErrSets" class="columns">
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="sectDocTests" class="hidden">
|
<div id="sectDocTests" class="hidden">
|
||||||
|
|||||||
330
lib/docs/main.js
330
lib/docs/main.js
@ -7,6 +7,8 @@
|
|||||||
const CAT_global_const = 5;
|
const CAT_global_const = 5;
|
||||||
const CAT_alias = 6;
|
const CAT_alias = 6;
|
||||||
const CAT_type = 7;
|
const CAT_type = 7;
|
||||||
|
const CAT_type_type = 8;
|
||||||
|
const CAT_type_function = 9;
|
||||||
|
|
||||||
const domDocTestsCode = document.getElementById("docTestsCode");
|
const domDocTestsCode = document.getElementById("docTestsCode");
|
||||||
const domFnErrorsAnyError = document.getElementById("fnErrorsAnyError");
|
const domFnErrorsAnyError = document.getElementById("fnErrorsAnyError");
|
||||||
@ -16,6 +18,7 @@
|
|||||||
const domHelpModal = document.getElementById("helpDialog");
|
const domHelpModal = document.getElementById("helpDialog");
|
||||||
const domListErrSets = document.getElementById("listErrSets");
|
const domListErrSets = document.getElementById("listErrSets");
|
||||||
const domListFields = document.getElementById("listFields");
|
const domListFields = document.getElementById("listFields");
|
||||||
|
const domListParams = document.getElementById("listParams");
|
||||||
const domListFnErrors = document.getElementById("listFnErrors");
|
const domListFnErrors = document.getElementById("listFnErrors");
|
||||||
const domListFns = document.getElementById("listFns");
|
const domListFns = document.getElementById("listFns");
|
||||||
const domListGlobalVars = document.getElementById("listGlobalVars");
|
const domListGlobalVars = document.getElementById("listGlobalVars");
|
||||||
@ -29,6 +32,7 @@
|
|||||||
const domSectDocTests = document.getElementById("sectDocTests");
|
const domSectDocTests = document.getElementById("sectDocTests");
|
||||||
const domSectErrSets = document.getElementById("sectErrSets");
|
const domSectErrSets = document.getElementById("sectErrSets");
|
||||||
const domSectFields = document.getElementById("sectFields");
|
const domSectFields = document.getElementById("sectFields");
|
||||||
|
const domSectParams = document.getElementById("sectParams");
|
||||||
const domSectFnErrors = document.getElementById("sectFnErrors");
|
const domSectFnErrors = document.getElementById("sectFnErrors");
|
||||||
const domSectFns = document.getElementById("sectFns");
|
const domSectFns = document.getElementById("sectFns");
|
||||||
const domSectGlobalVars = document.getElementById("sectGlobalVars");
|
const domSectGlobalVars = document.getElementById("sectGlobalVars");
|
||||||
@ -64,8 +68,8 @@
|
|||||||
var curSearchIndex = -1;
|
var curSearchIndex = -1;
|
||||||
var imFeelingLucky = false;
|
var imFeelingLucky = false;
|
||||||
|
|
||||||
// names of packages in the same order as wasm
|
// names of modules in the same order as wasm
|
||||||
const packageList = [];
|
const moduleList = [];
|
||||||
|
|
||||||
let wasm_promise = fetch("main.wasm");
|
let wasm_promise = fetch("main.wasm");
|
||||||
let sources_promise = fetch("sources.tar").then(function(response) {
|
let sources_promise = fetch("sources.tar").then(function(response) {
|
||||||
@ -99,13 +103,13 @@
|
|||||||
wasm_array.set(js_array);
|
wasm_array.set(js_array);
|
||||||
wasm_exports.unpack(ptr, js_array.length);
|
wasm_exports.unpack(ptr, js_array.length);
|
||||||
|
|
||||||
updatePackageList();
|
updateModuleList();
|
||||||
|
|
||||||
window.addEventListener('hashchange', onHashChange, false);
|
window.addEventListener('popstate', onPopState, false);
|
||||||
domSearch.addEventListener('keydown', onSearchKeyDown, false);
|
domSearch.addEventListener('keydown', onSearchKeyDown, false);
|
||||||
domSearch.addEventListener('input', onSearchChange, false);
|
domSearch.addEventListener('input', onSearchChange, false);
|
||||||
window.addEventListener('keydown', onWindowKeyDown, false);
|
window.addEventListener('keydown', onWindowKeyDown, false);
|
||||||
onHashChange();
|
onHashChange(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -118,7 +122,7 @@
|
|||||||
} else if (curNav.path != null) {
|
} else if (curNav.path != null) {
|
||||||
document.title = curNav.path + suffix;
|
document.title = curNav.path + suffix;
|
||||||
} else {
|
} else {
|
||||||
document.title = packageList[0] + suffix; // Home
|
document.title = moduleList[0] + suffix; // Home
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +134,7 @@
|
|||||||
domSectErrSets.classList.add("hidden");
|
domSectErrSets.classList.add("hidden");
|
||||||
domSectDocTests.classList.add("hidden");
|
domSectDocTests.classList.add("hidden");
|
||||||
domSectFields.classList.add("hidden");
|
domSectFields.classList.add("hidden");
|
||||||
|
domSectParams.classList.add("hidden");
|
||||||
domSectFnErrors.classList.add("hidden");
|
domSectFnErrors.classList.add("hidden");
|
||||||
domSectFns.classList.add("hidden");
|
domSectFns.classList.add("hidden");
|
||||||
domSectGlobalVars.classList.add("hidden");
|
domSectGlobalVars.classList.add("hidden");
|
||||||
@ -152,7 +157,7 @@
|
|||||||
case 0: return renderHome();
|
case 0: return renderHome();
|
||||||
case 1:
|
case 1:
|
||||||
if (curNav.decl == null) {
|
if (curNav.decl == null) {
|
||||||
return render404();
|
return renderNotFound();
|
||||||
} else {
|
} else {
|
||||||
return renderDecl(curNav.decl);
|
return renderDecl(curNav.decl);
|
||||||
}
|
}
|
||||||
@ -162,37 +167,51 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderHome() {
|
function renderHome() {
|
||||||
if (packageList.length == 1) return renderPackage(0);
|
if (moduleList.length == 0) {
|
||||||
|
domStatus.textContent = "sources.tar contains no modules";
|
||||||
domStatus.textContent = "TODO implement renderHome for multiple packages";
|
|
||||||
domStatus.classList.remove("hidden");
|
domStatus.classList.remove("hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return renderModule(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPackage(pkg_index) {
|
function renderModule(pkg_index) {
|
||||||
const root_decl = wasm_exports.find_package_root(pkg_index);
|
const root_decl = wasm_exports.find_module_root(pkg_index);
|
||||||
return renderDecl(root_decl);
|
return renderDecl(root_decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDecl(decl_index) {
|
function renderDecl(decl_index) {
|
||||||
const category = wasm_exports.categorize_decl(decl_index, 0);
|
const category = wasm_exports.categorize_decl(decl_index, 0);
|
||||||
switch (category) {
|
switch (category) {
|
||||||
case CAT_namespace: return renderNamespace(decl_index);
|
case CAT_namespace:
|
||||||
case CAT_global_variable: throw new Error("TODO: CAT_GLOBAL_VARIABLE");
|
return renderNamespacePage(decl_index);
|
||||||
case CAT_function: return renderFunction(decl_index);
|
case CAT_global_variable:
|
||||||
case CAT_primitive: throw new Error("TODO CAT_primitive");
|
case CAT_primitive:
|
||||||
case CAT_error_set: throw new Error("TODO CAT_error_set");
|
case CAT_global_const:
|
||||||
case CAT_global_const: return renderGlobalConst(decl_index);
|
case CAT_type:
|
||||||
case CAT_alias: return renderDecl(wasm_exports.get_aliasee());
|
case CAT_type_type:
|
||||||
case CAT_type: throw new Error("TODO CAT_type");
|
return renderGlobal(decl_index);
|
||||||
default: throw new Error("unrecognized category " + category);
|
case CAT_function:
|
||||||
|
return renderFunction(decl_index);
|
||||||
|
case CAT_type_function:
|
||||||
|
return renderTypeFunction(decl_index);
|
||||||
|
case CAT_error_set:
|
||||||
|
return renderErrorSetPage(decl_index);
|
||||||
|
case CAT_alias:
|
||||||
|
return renderDecl(wasm_exports.get_aliasee());
|
||||||
|
default:
|
||||||
|
throw new Error("unrecognized category " + category);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSource(path) {
|
function renderSource(path) {
|
||||||
const decl_index = findFileRoot(path);
|
const decl_index = findFileRoot(path);
|
||||||
if (decl_index == null) return render404();
|
if (decl_index == null) return renderNotFound();
|
||||||
|
|
||||||
renderNav(decl_index);
|
renderNavFancy(decl_index, [{
|
||||||
|
name: "[src]",
|
||||||
|
href: location.hash,
|
||||||
|
}]);
|
||||||
|
|
||||||
domSourceText.innerHTML = declSourceHtml(decl_index);
|
domSourceText.innerHTML = declSourceHtml(decl_index);
|
||||||
|
|
||||||
@ -200,7 +219,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderDeclHeading(decl_index) {
|
function renderDeclHeading(decl_index) {
|
||||||
domHdrName.innerText = unwrapString(wasm_exports.decl_category_name(decl_index));
|
curNav.viewSourceHash = "#src/" + unwrapString(wasm_exports.decl_file_path(decl_index));
|
||||||
|
|
||||||
|
const hdrNameSpan = domHdrName.children[0];
|
||||||
|
const srcLink = domHdrName.children[1];
|
||||||
|
hdrNameSpan.innerText = unwrapString(wasm_exports.decl_category_name(decl_index));
|
||||||
|
srcLink.setAttribute('href', curNav.viewSourceHash);
|
||||||
domHdrName.classList.remove("hidden");
|
domHdrName.classList.remove("hidden");
|
||||||
|
|
||||||
renderTopLevelDocs(decl_index);
|
renderTopLevelDocs(decl_index);
|
||||||
@ -214,8 +238,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNav(cur_nav_decl) {
|
function renderNav(cur_nav_decl, list) {
|
||||||
const list = [];
|
return renderNavFancy(cur_nav_decl, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNavFancy(cur_nav_decl, list) {
|
||||||
{
|
{
|
||||||
// First, walk backwards the decl parents within a file.
|
// First, walk backwards the decl parents within a file.
|
||||||
let decl_it = cur_nav_decl;
|
let decl_it = cur_nav_decl;
|
||||||
@ -235,11 +262,12 @@
|
|||||||
const parts = file_path.split(".");
|
const parts = file_path.split(".");
|
||||||
parts.pop(); // skip last
|
parts.pop(); // skip last
|
||||||
for (;;) {
|
for (;;) {
|
||||||
let part = parts.pop();
|
const href = navLinkFqn(parts.join("."));
|
||||||
|
const part = parts.pop();
|
||||||
if (!part) break;
|
if (!part) break;
|
||||||
list.push({
|
list.push({
|
||||||
name: part,
|
name: part,
|
||||||
href: navLinkFqn(parts.join(".")),
|
href: href,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,8 +291,8 @@
|
|||||||
domSectNav.classList.remove("hidden");
|
domSectNav.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
function render404() {
|
function renderNotFound() {
|
||||||
domStatus.textContent = "404 Not Found";
|
domStatus.textContent = "Declaration not found.";
|
||||||
domStatus.classList.remove("hidden");
|
domStatus.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,31 +316,91 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setViewSourceDecl(decl_index) {
|
function renderErrorSetPage(decl_index) {
|
||||||
curNav.viewSourceHash = "#src/" + unwrapString(wasm_exports.decl_file_path(decl_index));
|
renderNav(decl_index);
|
||||||
|
renderDeclHeading(decl_index);
|
||||||
|
|
||||||
|
const errorSetList = declErrorSet(decl_index).slice();
|
||||||
|
renderErrorSet(decl_index, errorSetList);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderFunction(decl_index) {
|
function renderErrorSet(base_decl, errorSetList) {
|
||||||
|
if (errorSetList == null) {
|
||||||
|
domFnErrorsAnyError.classList.remove("hidden");
|
||||||
|
} else {
|
||||||
|
resizeDomList(domListFnErrors, errorSetList.length, '<div></div>');
|
||||||
|
for (let i = 0; i < errorSetList.length; i += 1) {
|
||||||
|
const divDom = domListFnErrors.children[i];
|
||||||
|
const html = unwrapString(wasm_exports.error_html(base_decl, errorSetList[i]));
|
||||||
|
divDom.innerHTML = html;
|
||||||
|
}
|
||||||
|
domTableFnErrors.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
domSectFnErrors.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderParams(decl_index) {
|
||||||
|
// Prevent params from being emptied next time wasm calls memory.grow.
|
||||||
|
const params = declParams(decl_index).slice();
|
||||||
|
if (params.length !== 0) {
|
||||||
|
resizeDomList(domListParams, params.length, '<div></div>');
|
||||||
|
for (let i = 0; i < params.length; i += 1) {
|
||||||
|
const divDom = domListParams.children[i];
|
||||||
|
divDom.innerHTML = unwrapString(wasm_exports.decl_param_html(decl_index, params[i]));
|
||||||
|
}
|
||||||
|
domSectParams.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTypeFunction(decl_index) {
|
||||||
renderNav(decl_index);
|
renderNav(decl_index);
|
||||||
setViewSourceDecl(decl_index);
|
renderDeclHeading(decl_index);
|
||||||
|
|
||||||
domFnProtoCode.innerHTML = fnProtoHtml(decl_index);
|
|
||||||
renderTopLevelDocs(decl_index);
|
renderTopLevelDocs(decl_index);
|
||||||
domSourceText.innerHTML = declSourceHtml(decl_index);
|
renderParams(decl_index);
|
||||||
|
renderDocTests(decl_index);
|
||||||
|
|
||||||
|
const members = unwrapSlice32(wasm_exports.type_fn_members(decl_index, false)).slice();
|
||||||
|
const fields = unwrapSlice32(wasm_exports.type_fn_fields(decl_index)).slice();
|
||||||
|
if (members.length !== 0 || fields.length !== 0) {
|
||||||
|
renderNamespace(decl_index, members, fields);
|
||||||
|
} else {
|
||||||
|
domSourceText.innerHTML = declSourceHtml(decl_index);
|
||||||
|
domSectSource.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDocTests(decl_index) {
|
||||||
const doctest_html = declDoctestHtml(decl_index);
|
const doctest_html = declDoctestHtml(decl_index);
|
||||||
if (doctest_html.length > 0) {
|
if (doctest_html.length > 0) {
|
||||||
domDocTestsCode.innerHTML = doctest_html;
|
domDocTestsCode.innerHTML = doctest_html;
|
||||||
domSectDocTests.classList.remove("hidden");
|
domSectDocTests.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
domSectSource.classList.remove("hidden");
|
|
||||||
domFnProto.classList.remove("hidden");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderGlobalConst(decl_index) {
|
function renderFunction(decl_index) {
|
||||||
renderNav(decl_index);
|
renderNav(decl_index);
|
||||||
setViewSourceDecl(decl_index);
|
renderDeclHeading(decl_index);
|
||||||
|
renderTopLevelDocs(decl_index);
|
||||||
|
renderParams(decl_index);
|
||||||
|
renderDocTests(decl_index);
|
||||||
|
|
||||||
|
domFnProtoCode.innerHTML = fnProtoHtml(decl_index, false);
|
||||||
|
domFnProto.classList.remove("hidden");
|
||||||
|
|
||||||
|
|
||||||
|
const errorSetNode = fnErrorSet(decl_index);
|
||||||
|
if (errorSetNode != null) {
|
||||||
|
const base_decl = wasm_exports.fn_error_set_decl(decl_index, errorSetNode);
|
||||||
|
renderErrorSet(base_decl, errorSetNodeList(decl_index, errorSetNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
domSourceText.innerHTML = declSourceHtml(decl_index);
|
||||||
|
domSectSource.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGlobal(decl_index) {
|
||||||
|
renderNav(decl_index);
|
||||||
|
renderDeclHeading(decl_index);
|
||||||
|
|
||||||
const docs_html = declDocsHtmlShort(decl_index);
|
const docs_html = declDocsHtmlShort(decl_index);
|
||||||
if (docs_html.length > 0) {
|
if (docs_html.length > 0) {
|
||||||
@ -324,26 +412,29 @@
|
|||||||
domSectSource.classList.remove("hidden");
|
domSectSource.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNamespace(decl_index) {
|
function renderNamespace(base_decl, members, fields) {
|
||||||
renderNav(decl_index);
|
|
||||||
renderDeclHeading(decl_index);
|
|
||||||
setViewSourceDecl(decl_index);
|
|
||||||
|
|
||||||
const typesList = [];
|
const typesList = [];
|
||||||
const namespacesList = [];
|
const namespacesList = [];
|
||||||
const errSetsList = [];
|
const errSetsList = [];
|
||||||
const fnsList = [];
|
const fnsList = [];
|
||||||
const varsList = [];
|
const varsList = [];
|
||||||
const valsList = [];
|
const valsList = [];
|
||||||
const members = namespaceMembers(decl_index, false);
|
|
||||||
|
|
||||||
member_loop: for (let i = 0; i < members.length; i += 1) {
|
member_loop: for (let i = 0; i < members.length; i += 1) {
|
||||||
let member = members[i];
|
let member = members[i];
|
||||||
|
const original = member;
|
||||||
while (true) {
|
while (true) {
|
||||||
const member_category = wasm_exports.categorize_decl(member, 0);
|
const member_category = wasm_exports.categorize_decl(member, 0);
|
||||||
switch (member_category) {
|
switch (member_category) {
|
||||||
case CAT_namespace:
|
case CAT_namespace:
|
||||||
namespacesList.push(member);
|
if (wasm_exports.decl_field_count(member) > 0) {
|
||||||
|
typesList.push({original: original, member: member});
|
||||||
|
} else {
|
||||||
|
namespacesList.push({original: original, member: member});
|
||||||
|
}
|
||||||
|
continue member_loop;
|
||||||
|
case CAT_namespace:
|
||||||
|
namespacesList.push({original: original, member: member});
|
||||||
continue member_loop;
|
continue member_loop;
|
||||||
case CAT_global_variable:
|
case CAT_global_variable:
|
||||||
varsList.push(member);
|
varsList.push(member);
|
||||||
@ -352,17 +443,18 @@
|
|||||||
fnsList.push(member);
|
fnsList.push(member);
|
||||||
continue member_loop;
|
continue member_loop;
|
||||||
case CAT_type:
|
case CAT_type:
|
||||||
typesList.push(member);
|
case CAT_type_type:
|
||||||
|
case CAT_type_function:
|
||||||
|
typesList.push({original: original, member: member});
|
||||||
continue member_loop;
|
continue member_loop;
|
||||||
case CAT_error_set:
|
case CAT_error_set:
|
||||||
errSetsList.push(member);
|
errSetsList.push({original: original, member: member});
|
||||||
continue member_loop;
|
continue member_loop;
|
||||||
case CAT_global_const:
|
case CAT_global_const:
|
||||||
case CAT_primitive:
|
case CAT_primitive:
|
||||||
valsList.push(member);
|
valsList.push({original: original, member: member});
|
||||||
continue member_loop;
|
continue member_loop;
|
||||||
case CAT_alias:
|
case CAT_alias:
|
||||||
// TODO: handle aliasing loop
|
|
||||||
member = wasm_exports.get_aliasee();
|
member = wasm_exports.get_aliasee();
|
||||||
continue;
|
continue;
|
||||||
default:
|
default:
|
||||||
@ -371,20 +463,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typesList.sort(byDeclIndexName);
|
typesList.sort(byDeclIndexName2);
|
||||||
namespacesList.sort(byDeclIndexName);
|
namespacesList.sort(byDeclIndexName2);
|
||||||
errSetsList.sort(byDeclIndexName);
|
errSetsList.sort(byDeclIndexName2);
|
||||||
fnsList.sort(byDeclIndexName);
|
fnsList.sort(byDeclIndexName);
|
||||||
varsList.sort(byDeclIndexName);
|
varsList.sort(byDeclIndexName);
|
||||||
valsList.sort(byDeclIndexName);
|
valsList.sort(byDeclIndexName2);
|
||||||
|
|
||||||
if (typesList.length !== 0) {
|
if (typesList.length !== 0) {
|
||||||
resizeDomList(domListTypes, typesList.length, '<li><a href="#"></a></li>');
|
resizeDomList(domListTypes, typesList.length, '<li><a href="#"></a></li>');
|
||||||
for (let i = 0; i < typesList.length; i += 1) {
|
for (let i = 0; i < typesList.length; i += 1) {
|
||||||
const liDom = domListTypes.children[i];
|
const liDom = domListTypes.children[i];
|
||||||
const aDom = liDom.children[0];
|
const aDom = liDom.children[0];
|
||||||
const decl = typesList[i];
|
const original_decl = typesList[i].original;
|
||||||
aDom.textContent = declIndexName(decl);
|
const decl = typesList[i].member;
|
||||||
|
aDom.textContent = declIndexName(original_decl);
|
||||||
aDom.setAttribute('href', navLinkDeclIndex(decl));
|
aDom.setAttribute('href', navLinkDeclIndex(decl));
|
||||||
}
|
}
|
||||||
domSectTypes.classList.remove("hidden");
|
domSectTypes.classList.remove("hidden");
|
||||||
@ -394,8 +487,9 @@
|
|||||||
for (let i = 0; i < namespacesList.length; i += 1) {
|
for (let i = 0; i < namespacesList.length; i += 1) {
|
||||||
const liDom = domListNamespaces.children[i];
|
const liDom = domListNamespaces.children[i];
|
||||||
const aDom = liDom.children[0];
|
const aDom = liDom.children[0];
|
||||||
const decl = namespacesList[i];
|
const original_decl = namespacesList[i].original;
|
||||||
aDom.textContent = declIndexName(decl);
|
const decl = namespacesList[i].member;
|
||||||
|
aDom.textContent = declIndexName(original_decl);
|
||||||
aDom.setAttribute('href', navLinkDeclIndex(decl));
|
aDom.setAttribute('href', navLinkDeclIndex(decl));
|
||||||
}
|
}
|
||||||
domSectNamespaces.classList.remove("hidden");
|
domSectNamespaces.classList.remove("hidden");
|
||||||
@ -406,8 +500,9 @@
|
|||||||
for (let i = 0; i < errSetsList.length; i += 1) {
|
for (let i = 0; i < errSetsList.length; i += 1) {
|
||||||
const liDom = domListErrSets.children[i];
|
const liDom = domListErrSets.children[i];
|
||||||
const aDom = liDom.children[0];
|
const aDom = liDom.children[0];
|
||||||
const decl = errSetsList[i];
|
const original_decl = errSetsList[i].original;
|
||||||
aDom.textContent = declIndexName(decl);
|
const decl = errSetsList[i].member;
|
||||||
|
aDom.textContent = declIndexName(original_decl);
|
||||||
aDom.setAttribute('href', navLinkDeclIndex(decl));
|
aDom.setAttribute('href', navLinkDeclIndex(decl));
|
||||||
}
|
}
|
||||||
domSectErrSets.classList.remove("hidden");
|
domSectErrSets.classList.remove("hidden");
|
||||||
@ -415,35 +510,26 @@
|
|||||||
|
|
||||||
if (fnsList.length !== 0) {
|
if (fnsList.length !== 0) {
|
||||||
resizeDomList(domListFns, fnsList.length,
|
resizeDomList(domListFns, fnsList.length,
|
||||||
'<div><dt><a href="#"></a></dt><dd></dd><details><summary>source</summary><pre><code></code></pre></details></div>');
|
'<div><dt><code></code></dt><dd></dd></div>');
|
||||||
for (let i = 0; i < fnsList.length; i += 1) {
|
for (let i = 0; i < fnsList.length; i += 1) {
|
||||||
const decl = fnsList[i];
|
const decl = fnsList[i];
|
||||||
const divDom = domListFns.children[i];
|
const divDom = domListFns.children[i];
|
||||||
|
|
||||||
const dtName = divDom.children[0];
|
const dtDom = divDom.children[0];
|
||||||
const ddDocs = divDom.children[1];
|
const ddDocs = divDom.children[1];
|
||||||
const codeDom = divDom.children[2].children[1].children[0];
|
const protoCodeDom = dtDom.children[0];
|
||||||
|
|
||||||
const nameLinkDom = dtName.children[0];
|
|
||||||
const expandSourceDom = dtName.children[1];
|
|
||||||
|
|
||||||
nameLinkDom.setAttribute('href', navLinkDeclIndex(decl));
|
|
||||||
nameLinkDom.textContent = declIndexName(decl);
|
|
||||||
|
|
||||||
|
protoCodeDom.innerHTML = fnProtoHtml(decl, true);
|
||||||
ddDocs.innerHTML = declDocsHtmlShort(decl);
|
ddDocs.innerHTML = declDocsHtmlShort(decl);
|
||||||
|
|
||||||
codeDom.innerHTML = declSourceHtml(decl);
|
|
||||||
}
|
}
|
||||||
domSectFns.classList.remove("hidden");
|
domSectFns.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent fields from being emptied next time wasm calls memory.grow.
|
|
||||||
const fields = declFields(decl_index).slice();
|
|
||||||
if (fields.length !== 0) {
|
if (fields.length !== 0) {
|
||||||
resizeDomList(domListFields, fields.length, '<div></div>');
|
resizeDomList(domListFields, fields.length, '<div></div>');
|
||||||
for (let i = 0; i < fields.length; i += 1) {
|
for (let i = 0; i < fields.length; i += 1) {
|
||||||
const divDom = domListFields.children[i];
|
const divDom = domListFields.children[i];
|
||||||
divDom.innerHTML = unwrapString(wasm_exports.decl_field_html(decl_index, fields[i]));
|
divDom.innerHTML = unwrapString(wasm_exports.decl_field_html(base_decl, fields[i]));
|
||||||
}
|
}
|
||||||
domSectFields.classList.remove("hidden");
|
domSectFields.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
@ -473,16 +559,16 @@
|
|||||||
resizeDomList(domListValues, valsList.length,
|
resizeDomList(domListValues, valsList.length,
|
||||||
'<tr><td><a href="#"></a></td><td></td><td></td></tr>');
|
'<tr><td><a href="#"></a></td><td></td><td></td></tr>');
|
||||||
for (let i = 0; i < valsList.length; i += 1) {
|
for (let i = 0; i < valsList.length; i += 1) {
|
||||||
const decl = valsList[i];
|
|
||||||
const trDom = domListValues.children[i];
|
const trDom = domListValues.children[i];
|
||||||
|
|
||||||
const tdName = trDom.children[0];
|
const tdName = trDom.children[0];
|
||||||
const tdNameA = tdName.children[0];
|
const tdNameA = tdName.children[0];
|
||||||
const tdType = trDom.children[1];
|
const tdType = trDom.children[1];
|
||||||
const tdDesc = trDom.children[2];
|
const tdDesc = trDom.children[2];
|
||||||
|
|
||||||
|
const original_decl = valsList[i].original;
|
||||||
|
const decl = valsList[i].member;
|
||||||
tdNameA.setAttribute('href', navLinkDeclIndex(decl));
|
tdNameA.setAttribute('href', navLinkDeclIndex(decl));
|
||||||
tdNameA.textContent = declIndexName(decl);
|
tdNameA.textContent = declIndexName(original_decl);
|
||||||
|
|
||||||
tdType.innerHTML = declTypeHtml(decl);
|
tdType.innerHTML = declTypeHtml(decl);
|
||||||
tdDesc.innerHTML = declDocsHtmlShort(decl);
|
tdDesc.innerHTML = declDocsHtmlShort(decl);
|
||||||
@ -491,6 +577,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderNamespacePage(decl_index) {
|
||||||
|
renderNav(decl_index);
|
||||||
|
renderDeclHeading(decl_index);
|
||||||
|
const members = namespaceMembers(decl_index, false).slice();
|
||||||
|
const fields = declFields(decl_index).slice();
|
||||||
|
renderNamespace(decl_index, members, fields);
|
||||||
|
}
|
||||||
|
|
||||||
function operatorCompare(a, b) {
|
function operatorCompare(a, b) {
|
||||||
if (a === b) {
|
if (a === b) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -508,7 +602,7 @@
|
|||||||
curNav.viewSourceHash = null;
|
curNav.viewSourceHash = null;
|
||||||
curNavSearch = "";
|
curNavSearch = "";
|
||||||
|
|
||||||
if (location_hash[0] === '#' && location_hash.length > 1) {
|
if (location_hash.length > 1 && location_hash[0] === '#') {
|
||||||
const query = location_hash.substring(1);
|
const query = location_hash.substring(1);
|
||||||
const qpos = query.indexOf("?");
|
const qpos = query.indexOf("?");
|
||||||
let nonSearchPart;
|
let nonSearchPart;
|
||||||
@ -532,8 +626,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onHashChange() {
|
function onHashChange(state) {
|
||||||
|
history.replaceState({}, "");
|
||||||
navigate(location.hash);
|
navigate(location.hash);
|
||||||
|
if (state == null) window.scrollTo({top: 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPopState(ev) {
|
||||||
|
onHashChange(ev.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigate(location_hash) {
|
function navigate(location_hash) {
|
||||||
@ -686,13 +786,19 @@
|
|||||||
|
|
||||||
function startAsyncSearch() {
|
function startAsyncSearch() {
|
||||||
clearAsyncSearch();
|
clearAsyncSearch();
|
||||||
searchTimer = setTimeout(startSearch, 100);
|
searchTimer = setTimeout(startSearch, 10);
|
||||||
}
|
}
|
||||||
function computeSearchHash() {
|
function computeSearchHash() {
|
||||||
const oldHash = location.hash;
|
// How location.hash works:
|
||||||
|
// 1. http://example.com/ => ""
|
||||||
|
// 2. http://example.com/# => ""
|
||||||
|
// 3. http://example.com/#foo => "#foo"
|
||||||
|
// wat
|
||||||
|
const oldWatHash = location.hash;
|
||||||
|
const oldHash = oldWatHash.startsWith("#") ? oldWatHash : "#" + oldWatHash;
|
||||||
const parts = oldHash.split("?");
|
const parts = oldHash.split("?");
|
||||||
const newPart2 = (domSearch.value === "") ? "" : ("?" + domSearch.value);
|
const newPart2 = (domSearch.value === "") ? "" : ("?" + domSearch.value);
|
||||||
return (parts.length === 1) ? (oldHash + newPart2) : ("#" + parts[0] + newPart2);
|
return parts[0] + newPart2;
|
||||||
}
|
}
|
||||||
function startSearch() {
|
function startSearch() {
|
||||||
clearAsyncSearch();
|
clearAsyncSearch();
|
||||||
@ -734,12 +840,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePackageList() {
|
function updateModuleList() {
|
||||||
packageList.length = 0;
|
moduleList.length = 0;
|
||||||
for (let i = 0;; i += 1) {
|
for (let i = 0;; i += 1) {
|
||||||
const name = unwrapString(wasm_exports.package_name(i));
|
const name = unwrapString(wasm_exports.module_name(i));
|
||||||
if (name.length == 0) break;
|
if (name.length == 0) break;
|
||||||
packageList.push(name);
|
moduleList.push(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,6 +855,12 @@
|
|||||||
return operatorCompare(a_name, b_name);
|
return operatorCompare(a_name, b_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function byDeclIndexName2(a, b) {
|
||||||
|
const a_name = declIndexName(a.original);
|
||||||
|
const b_name = declIndexName(b.original);
|
||||||
|
return operatorCompare(a_name, b_name);
|
||||||
|
}
|
||||||
|
|
||||||
function decodeString(ptr, len) {
|
function decodeString(ptr, len) {
|
||||||
if (len === 0) return "";
|
if (len === 0) return "";
|
||||||
return text_decoder.decode(new Uint8Array(wasm_exports.memory.buffer, ptr, len));
|
return text_decoder.decode(new Uint8Array(wasm_exports.memory.buffer, ptr, len));
|
||||||
@ -784,8 +896,8 @@
|
|||||||
return unwrapString(wasm_exports.decl_doctest_html(decl_index));
|
return unwrapString(wasm_exports.decl_doctest_html(decl_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
function fnProtoHtml(decl_index) {
|
function fnProtoHtml(decl_index, linkify_fn_name) {
|
||||||
return unwrapString(wasm_exports.decl_fn_proto_html(decl_index));
|
return unwrapString(wasm_exports.decl_fn_proto_html(decl_index, linkify_fn_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setQueryString(s) {
|
function setQueryString(s) {
|
||||||
@ -805,21 +917,39 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function namespaceMembers(decl_index, include_private) {
|
function namespaceMembers(decl_index, include_private) {
|
||||||
const bigint = wasm_exports.namespace_members(decl_index, include_private);
|
return unwrapSlice32(wasm_exports.namespace_members(decl_index, include_private));
|
||||||
const ptr = Number(bigint & 0xffffffffn);
|
|
||||||
const len = Number(bigint >> 32n);
|
|
||||||
if (len == 0) return [];
|
|
||||||
return new Uint32Array(wasm_exports.memory.buffer, ptr, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function declFields(decl_index) {
|
function declFields(decl_index) {
|
||||||
const bigint = wasm_exports.decl_fields(decl_index);
|
return unwrapSlice32(wasm_exports.decl_fields(decl_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
function declParams(decl_index) {
|
||||||
|
return unwrapSlice32(wasm_exports.decl_params(decl_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
function declErrorSet(decl_index) {
|
||||||
|
return unwrapSlice64(wasm_exports.decl_error_set(decl_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorSetNodeList(base_decl, err_set_node) {
|
||||||
|
return unwrapSlice64(wasm_exports.error_set_node_list(base_decl, err_set_node));
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrapSlice32(bigint) {
|
||||||
const ptr = Number(bigint & 0xffffffffn);
|
const ptr = Number(bigint & 0xffffffffn);
|
||||||
const len = Number(bigint >> 32n);
|
const len = Number(bigint >> 32n);
|
||||||
if (len === 0) return [];
|
if (len === 0) return [];
|
||||||
return new Uint32Array(wasm_exports.memory.buffer, ptr, len);
|
return new Uint32Array(wasm_exports.memory.buffer, ptr, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unwrapSlice64(bigint) {
|
||||||
|
const ptr = Number(bigint & 0xffffffffn);
|
||||||
|
const len = Number(bigint >> 32n);
|
||||||
|
if (len === 0) return [];
|
||||||
|
return new BigUint64Array(wasm_exports.memory.buffer, ptr, len);
|
||||||
|
}
|
||||||
|
|
||||||
function findDecl(fqn) {
|
function findDecl(fqn) {
|
||||||
setInputString(fqn);
|
setInputString(fqn);
|
||||||
const result = wasm_exports.find_decl();
|
const result = wasm_exports.find_decl();
|
||||||
@ -840,6 +970,12 @@
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fnErrorSet(decl_index) {
|
||||||
|
const result = wasm_exports.fn_error_set(decl_index);
|
||||||
|
if (result === 0) return null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function setInputString(s) {
|
function setInputString(s) {
|
||||||
const jsArray = text_encoder.encode(s);
|
const jsArray = text_encoder.encode(s);
|
||||||
const len = jsArray.length;
|
const len = jsArray.length;
|
||||||
|
|||||||
@ -111,8 +111,35 @@ pub fn categorize(decl: *const Decl) Walk.Category {
|
|||||||
return decl.file.categorize_decl(decl.ast_node);
|
return decl.file.categorize_decl(decl.ast_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Looks up a direct child of `decl` by name.
|
||||||
|
pub fn get_child(decl: *const Decl, name: []const u8) ?Decl.Index {
|
||||||
|
switch (decl.categorize()) {
|
||||||
|
.alias => |aliasee| return aliasee.get().get_child(name),
|
||||||
|
.namespace => |node| {
|
||||||
|
const file = decl.file.get();
|
||||||
|
const scope = file.scopes.get(node) orelse return null;
|
||||||
|
const child_node = scope.get_child(name) orelse return null;
|
||||||
|
return file.node_decls.get(child_node);
|
||||||
|
},
|
||||||
|
else => return null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Looks up a decl by name accessible in `decl`'s namespace.
|
||||||
|
pub fn lookup(decl: *const Decl, name: []const u8) ?Decl.Index {
|
||||||
|
const namespace_node = switch (decl.categorize()) {
|
||||||
|
.namespace => |node| node,
|
||||||
|
else => decl.parent.get().ast_node,
|
||||||
|
};
|
||||||
|
const file = decl.file.get();
|
||||||
|
const scope = file.scopes.get(namespace_node) orelse return null;
|
||||||
|
const resolved_node = scope.lookup(&file.ast, name) orelse return null;
|
||||||
|
return file.node_decls.get(resolved_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Appends the fully qualified name to `out`.
|
||||||
pub fn fqn(decl: *const Decl, out: *std.ArrayListUnmanaged(u8)) Oom!void {
|
pub fn fqn(decl: *const Decl, out: *std.ArrayListUnmanaged(u8)) Oom!void {
|
||||||
try decl.reset_with_path(out);
|
try decl.append_path(out);
|
||||||
if (decl.parent != .none) {
|
if (decl.parent != .none) {
|
||||||
try append_parent_ns(out, decl.parent);
|
try append_parent_ns(out, decl.parent);
|
||||||
try out.appendSlice(gpa, decl.extra_info().name);
|
try out.appendSlice(gpa, decl.extra_info().name);
|
||||||
@ -123,9 +150,13 @@ pub fn fqn(decl: *const Decl, out: *std.ArrayListUnmanaged(u8)) Oom!void {
|
|||||||
|
|
||||||
pub fn reset_with_path(decl: *const Decl, list: *std.ArrayListUnmanaged(u8)) Oom!void {
|
pub fn reset_with_path(decl: *const Decl, list: *std.ArrayListUnmanaged(u8)) Oom!void {
|
||||||
list.clearRetainingCapacity();
|
list.clearRetainingCapacity();
|
||||||
|
try append_path(decl, list);
|
||||||
|
}
|
||||||
|
|
||||||
// Prefer the package name alias.
|
pub fn append_path(decl: *const Decl, list: *std.ArrayListUnmanaged(u8)) Oom!void {
|
||||||
for (Walk.packages.keys(), Walk.packages.values()) |pkg_name, pkg_file| {
|
const start = list.items.len;
|
||||||
|
// Prefer the module name alias.
|
||||||
|
for (Walk.modules.keys(), Walk.modules.values()) |pkg_name, pkg_file| {
|
||||||
if (pkg_file == decl.file) {
|
if (pkg_file == decl.file) {
|
||||||
try list.ensureUnusedCapacity(gpa, pkg_name.len + 1);
|
try list.ensureUnusedCapacity(gpa, pkg_name.len + 1);
|
||||||
list.appendSliceAssumeCapacity(pkg_name);
|
list.appendSliceAssumeCapacity(pkg_name);
|
||||||
@ -137,7 +168,7 @@ pub fn reset_with_path(decl: *const Decl, list: *std.ArrayListUnmanaged(u8)) Oom
|
|||||||
const file_path = decl.file.path();
|
const file_path = decl.file.path();
|
||||||
try list.ensureUnusedCapacity(gpa, file_path.len + 1);
|
try list.ensureUnusedCapacity(gpa, file_path.len + 1);
|
||||||
list.appendSliceAssumeCapacity(file_path);
|
list.appendSliceAssumeCapacity(file_path);
|
||||||
for (list.items) |*byte| switch (byte.*) {
|
for (list.items[start..]) |*byte| switch (byte.*) {
|
||||||
'/' => byte.* = '.',
|
'/' => byte.* = '.',
|
||||||
else => continue,
|
else => continue,
|
||||||
};
|
};
|
||||||
@ -170,6 +201,21 @@ pub fn findFirstDocComment(ast: *const Ast, token: Ast.TokenIndex) Ast.TokenInde
|
|||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Successively looks up each component.
|
||||||
|
pub fn find(search_string: []const u8) Decl.Index {
|
||||||
|
var path_components = std.mem.splitScalar(u8, search_string, '.');
|
||||||
|
const file = Walk.modules.get(path_components.first()) orelse return .none;
|
||||||
|
var current_decl_index = file.findRootDecl();
|
||||||
|
while (path_components.next()) |component| {
|
||||||
|
while (true) switch (current_decl_index.get().categorize()) {
|
||||||
|
.alias => |aliasee| current_decl_index = aliasee,
|
||||||
|
else => break,
|
||||||
|
};
|
||||||
|
current_decl_index = current_decl_index.get().get_child(component) orelse return .none;
|
||||||
|
}
|
||||||
|
return current_decl_index;
|
||||||
|
}
|
||||||
|
|
||||||
const Decl = @This();
|
const Decl = @This();
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Ast = std.zig.Ast;
|
const Ast = std.zig.Ast;
|
||||||
|
|||||||
@ -1,21 +1,26 @@
|
|||||||
//! Find and annotate identifiers with links to their declarations.
|
//! Find and annotate identifiers with links to their declarations.
|
||||||
pub var files: std.StringArrayHashMapUnmanaged(File) = .{};
|
pub var files: std.StringArrayHashMapUnmanaged(File) = .{};
|
||||||
pub var decls: std.ArrayListUnmanaged(Decl) = .{};
|
pub var decls: std.ArrayListUnmanaged(Decl) = .{};
|
||||||
pub var packages: std.StringArrayHashMapUnmanaged(File.Index) = .{};
|
pub var modules: std.StringArrayHashMapUnmanaged(File.Index) = .{};
|
||||||
|
|
||||||
arena: std.mem.Allocator,
|
|
||||||
file: File.Index,
|
file: File.Index,
|
||||||
|
|
||||||
/// keep in sync with "CAT_" constants in main.js
|
/// keep in sync with "CAT_" constants in main.js
|
||||||
pub const Category = union(enum(u8)) {
|
pub const Category = union(enum(u8)) {
|
||||||
namespace: Ast.Node.Index,
|
namespace: Ast.Node.Index,
|
||||||
global_variable: Ast.Node.Index,
|
global_variable: Ast.Node.Index,
|
||||||
|
/// A function that has not been detected as returning a type.
|
||||||
function: Ast.Node.Index,
|
function: Ast.Node.Index,
|
||||||
primitive: Ast.Node.Index,
|
primitive: Ast.Node.Index,
|
||||||
error_set: Ast.Node.Index,
|
error_set: Ast.Node.Index,
|
||||||
global_const: Ast.Node.Index,
|
global_const: Ast.Node.Index,
|
||||||
alias: Decl.Index,
|
alias: Decl.Index,
|
||||||
|
/// A primitive identifier that is also a type.
|
||||||
type,
|
type,
|
||||||
|
/// Specifically it is the literal `type`.
|
||||||
|
type_type,
|
||||||
|
/// A function that returns a type.
|
||||||
|
type_function: Ast.Node.Index,
|
||||||
|
|
||||||
pub const Tag = @typeInfo(Category).Union.tag_type.?;
|
pub const Tag = @typeInfo(Category).Union.tag_type.?;
|
||||||
};
|
};
|
||||||
@ -30,12 +35,23 @@ pub const File = struct {
|
|||||||
node_decls: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, Decl.Index) = .{},
|
node_decls: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, Decl.Index) = .{},
|
||||||
/// Maps function declarations to doctests.
|
/// Maps function declarations to doctests.
|
||||||
doctests: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, Ast.Node.Index) = .{},
|
doctests: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, Ast.Node.Index) = .{},
|
||||||
|
/// root node => its namespace scope
|
||||||
|
/// struct/union/enum/opaque decl node => its namespace scope
|
||||||
|
/// local var decl node => its local variable scope
|
||||||
|
scopes: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, *Scope) = .{},
|
||||||
|
|
||||||
pub fn lookup_token(file: *File, token: Ast.TokenIndex) Decl.Index {
|
pub fn lookup_token(file: *File, token: Ast.TokenIndex) Decl.Index {
|
||||||
const decl_node = file.ident_decls.get(token) orelse return .none;
|
const decl_node = file.ident_decls.get(token) orelse return .none;
|
||||||
return file.node_decls.get(decl_node) orelse return .none;
|
return file.node_decls.get(decl_node) orelse return .none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn field_count(file: *const File, node: Ast.Node.Index) u32 {
|
||||||
|
const scope = file.scopes.get(node) orelse return 0;
|
||||||
|
if (scope.tag != .namespace) return 0;
|
||||||
|
const namespace = @fieldParentPtr(Scope.Namespace, "base", scope);
|
||||||
|
return namespace.field_count;
|
||||||
|
}
|
||||||
|
|
||||||
pub const Index = enum(u32) {
|
pub const Index = enum(u32) {
|
||||||
_,
|
_,
|
||||||
|
|
||||||
@ -90,16 +106,41 @@ pub const File = struct {
|
|||||||
.fn_proto_one,
|
.fn_proto_one,
|
||||||
.fn_proto_simple,
|
.fn_proto_simple,
|
||||||
.fn_decl,
|
.fn_decl,
|
||||||
=> return .{ .function = node },
|
=> {
|
||||||
|
var buf: [1]Ast.Node.Index = undefined;
|
||||||
|
const full = ast.fullFnProto(&buf, node).?;
|
||||||
|
return categorize_func(file_index, node, full);
|
||||||
|
},
|
||||||
|
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn categorize_func(
|
||||||
|
file_index: File.Index,
|
||||||
|
node: Ast.Node.Index,
|
||||||
|
full: Ast.full.FnProto,
|
||||||
|
) Category {
|
||||||
|
return switch (categorize_expr(file_index, full.ast.return_type)) {
|
||||||
|
.namespace, .error_set, .type_type => .{ .type_function = node },
|
||||||
|
else => .{ .function = node },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn categorize_expr_deep(file_index: File.Index, node: Ast.Node.Index) Category {
|
||||||
|
return switch (categorize_expr(file_index, node)) {
|
||||||
|
.alias => |aliasee| aliasee.get().categorize(),
|
||||||
|
else => |result| result,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn categorize_expr(file_index: File.Index, node: Ast.Node.Index) Category {
|
pub fn categorize_expr(file_index: File.Index, node: Ast.Node.Index) Category {
|
||||||
|
const file = file_index.get();
|
||||||
const ast = file_index.get_ast();
|
const ast = file_index.get_ast();
|
||||||
const node_tags = ast.nodes.items(.tag);
|
const node_tags = ast.nodes.items(.tag);
|
||||||
const node_datas = ast.nodes.items(.data);
|
const node_datas = ast.nodes.items(.data);
|
||||||
|
const main_tokens = ast.nodes.items(.main_token);
|
||||||
|
//log.debug("categorize_expr tag {s}", .{@tagName(node_tags[node])});
|
||||||
return switch (node_tags[node]) {
|
return switch (node_tags[node]) {
|
||||||
.container_decl,
|
.container_decl,
|
||||||
.container_decl_trailing,
|
.container_decl_trailing,
|
||||||
@ -116,24 +157,43 @@ pub const File = struct {
|
|||||||
=> .{ .namespace = node },
|
=> .{ .namespace = node },
|
||||||
|
|
||||||
.error_set_decl,
|
.error_set_decl,
|
||||||
|
.merge_error_sets,
|
||||||
=> .{ .error_set = node },
|
=> .{ .error_set = node },
|
||||||
|
|
||||||
.identifier => {
|
.identifier => {
|
||||||
const name_token = ast.nodes.items(.main_token)[node];
|
const name_token = ast.nodes.items(.main_token)[node];
|
||||||
const ident_name = ast.tokenSlice(name_token);
|
const ident_name = ast.tokenSlice(name_token);
|
||||||
if (std.zig.primitives.isPrimitive(ident_name)) {
|
if (std.mem.eql(u8, ident_name, "type"))
|
||||||
return .{ .primitive = node };
|
return .type_type;
|
||||||
}
|
|
||||||
|
|
||||||
const decl_index = file_index.get().lookup_token(name_token);
|
if (isPrimitiveNonType(ident_name))
|
||||||
|
return .{ .primitive = node };
|
||||||
|
|
||||||
|
if (std.zig.primitives.isPrimitive(ident_name))
|
||||||
|
return .type;
|
||||||
|
|
||||||
|
if (file.ident_decls.get(name_token)) |decl_node| {
|
||||||
|
const decl_index = file.node_decls.get(decl_node) orelse .none;
|
||||||
if (decl_index != .none) return .{ .alias = decl_index };
|
if (decl_index != .none) return .{ .alias = decl_index };
|
||||||
|
return categorize_decl(file_index, decl_node);
|
||||||
|
}
|
||||||
|
|
||||||
return .{ .global_const = node };
|
return .{ .global_const = node };
|
||||||
},
|
},
|
||||||
|
|
||||||
.field_access => {
|
.field_access => {
|
||||||
// TODO:
|
const object_node = node_datas[node].lhs;
|
||||||
//return .alias;
|
const dot_token = main_tokens[node];
|
||||||
|
const field_ident = dot_token + 1;
|
||||||
|
const field_name = ast.tokenSlice(field_ident);
|
||||||
|
|
||||||
|
switch (categorize_expr(file_index, object_node)) {
|
||||||
|
.alias => |aliasee| if (aliasee.get().get_child(field_name)) |decl_index| {
|
||||||
|
return .{ .alias = decl_index };
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
|
||||||
return .{ .global_const = node };
|
return .{ .global_const = node };
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -154,10 +214,77 @@ pub const File = struct {
|
|||||||
return categorize_builtin_call(file_index, node, params);
|
return categorize_builtin_call(file_index, node, params);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.call_one,
|
||||||
|
.call_one_comma,
|
||||||
|
.async_call_one,
|
||||||
|
.async_call_one_comma,
|
||||||
|
.call,
|
||||||
|
.call_comma,
|
||||||
|
.async_call,
|
||||||
|
.async_call_comma,
|
||||||
|
=> {
|
||||||
|
var buf: [1]Ast.Node.Index = undefined;
|
||||||
|
return categorize_call(file_index, node, ast.fullCall(&buf, node).?);
|
||||||
|
},
|
||||||
|
|
||||||
|
.if_simple,
|
||||||
|
.@"if",
|
||||||
|
=> {
|
||||||
|
const if_full = ast.fullIf(node).?;
|
||||||
|
if (if_full.ast.else_expr != 0) {
|
||||||
|
const then_cat = categorize_expr_deep(file_index, if_full.ast.then_expr);
|
||||||
|
const else_cat = categorize_expr_deep(file_index, if_full.ast.else_expr);
|
||||||
|
if (then_cat == .type_type and else_cat == .type_type) {
|
||||||
|
return .type_type;
|
||||||
|
} else if (then_cat == .error_set and else_cat == .error_set) {
|
||||||
|
return .{ .error_set = node };
|
||||||
|
} else if (then_cat == .type or else_cat == .type or
|
||||||
|
then_cat == .namespace or else_cat == .namespace or
|
||||||
|
then_cat == .error_set or else_cat == .error_set or
|
||||||
|
then_cat == .type_function or else_cat == .type_function)
|
||||||
|
{
|
||||||
|
return .type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .{ .global_const = node };
|
||||||
|
},
|
||||||
|
|
||||||
|
.@"switch", .switch_comma => return categorize_switch(file_index, node),
|
||||||
|
|
||||||
|
.optional_type,
|
||||||
|
.array_type,
|
||||||
|
.array_type_sentinel,
|
||||||
|
.ptr_type_aligned,
|
||||||
|
.ptr_type_sentinel,
|
||||||
|
.ptr_type,
|
||||||
|
.ptr_type_bit_range,
|
||||||
|
.anyframe_type,
|
||||||
|
=> .type,
|
||||||
|
|
||||||
else => .{ .global_const = node },
|
else => .{ .global_const = node },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn categorize_call(
|
||||||
|
file_index: File.Index,
|
||||||
|
node: Ast.Node.Index,
|
||||||
|
call: Ast.full.Call,
|
||||||
|
) Category {
|
||||||
|
return switch (categorize_expr(file_index, call.ast.fn_expr)) {
|
||||||
|
.type_function => .type,
|
||||||
|
.alias => |aliasee| categorize_decl_as_callee(aliasee, node),
|
||||||
|
else => .{ .global_const = node },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn categorize_decl_as_callee(decl_index: Decl.Index, call_node: Ast.Node.Index) Category {
|
||||||
|
return switch (decl_index.get().categorize()) {
|
||||||
|
.type_function => .type,
|
||||||
|
.alias => |aliasee| categorize_decl_as_callee(aliasee, call_node),
|
||||||
|
else => .{ .global_const = call_node },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn categorize_builtin_call(
|
fn categorize_builtin_call(
|
||||||
file_index: File.Index,
|
file_index: File.Index,
|
||||||
node: Ast.Node.Index,
|
node: Ast.Node.Index,
|
||||||
@ -172,6 +299,9 @@ pub const File = struct {
|
|||||||
const str_bytes = ast.tokenSlice(str_lit_token);
|
const str_bytes = ast.tokenSlice(str_lit_token);
|
||||||
const file_path = std.zig.string_literal.parseAlloc(gpa, str_bytes) catch @panic("OOM");
|
const file_path = std.zig.string_literal.parseAlloc(gpa, str_bytes) catch @panic("OOM");
|
||||||
defer gpa.free(file_path);
|
defer gpa.free(file_path);
|
||||||
|
if (modules.get(file_path)) |imported_file_index| {
|
||||||
|
return .{ .alias = File.Index.findRootDecl(imported_file_index) };
|
||||||
|
}
|
||||||
const base_path = file_index.path();
|
const base_path = file_index.path();
|
||||||
const resolved_path = std.fs.path.resolvePosix(gpa, &.{
|
const resolved_path = std.fs.path.resolvePosix(gpa, &.{
|
||||||
base_path, "..", file_path,
|
base_path, "..", file_path,
|
||||||
@ -180,8 +310,8 @@ pub const File = struct {
|
|||||||
log.debug("from '{s}' @import '{s}' resolved='{s}'", .{
|
log.debug("from '{s}' @import '{s}' resolved='{s}'", .{
|
||||||
base_path, file_path, resolved_path,
|
base_path, file_path, resolved_path,
|
||||||
});
|
});
|
||||||
if (Walk.files.getIndex(resolved_path)) |imported_file_index| {
|
if (files.getIndex(resolved_path)) |imported_file_index| {
|
||||||
return .{ .alias = Walk.File.Index.findRootDecl(@enumFromInt(imported_file_index)) };
|
return .{ .alias = File.Index.findRootDecl(@enumFromInt(imported_file_index)) };
|
||||||
} else {
|
} else {
|
||||||
log.warn("import target '{s}' did not resolve to any file", .{resolved_path});
|
log.warn("import target '{s}' did not resolve to any file", .{resolved_path});
|
||||||
}
|
}
|
||||||
@ -195,10 +325,47 @@ pub const File = struct {
|
|||||||
|
|
||||||
return .{ .global_const = node };
|
return .{ .global_const = node };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn categorize_switch(file_index: File.Index, node: Ast.Node.Index) Category {
|
||||||
|
const ast = file_index.get_ast();
|
||||||
|
const node_datas = ast.nodes.items(.data);
|
||||||
|
const extra = ast.extraData(node_datas[node].rhs, Ast.Node.SubRange);
|
||||||
|
const case_nodes = ast.extra_data[extra.start..extra.end];
|
||||||
|
var all_type_type = true;
|
||||||
|
var all_error_set = true;
|
||||||
|
var any_type = false;
|
||||||
|
if (case_nodes.len == 0) return .{ .global_const = node };
|
||||||
|
for (case_nodes) |case_node| {
|
||||||
|
const case = ast.fullSwitchCase(case_node).?;
|
||||||
|
switch (categorize_expr_deep(file_index, case.ast.target_expr)) {
|
||||||
|
.type_type => {
|
||||||
|
any_type = true;
|
||||||
|
all_error_set = false;
|
||||||
|
},
|
||||||
|
.error_set => {
|
||||||
|
any_type = true;
|
||||||
|
all_type_type = false;
|
||||||
|
},
|
||||||
|
.type, .namespace, .type_function => {
|
||||||
|
any_type = true;
|
||||||
|
all_error_set = false;
|
||||||
|
all_type_type = false;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
all_error_set = false;
|
||||||
|
all_type_type = false;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (all_type_type) return .type_type;
|
||||||
|
if (all_error_set) return .{ .error_set = node };
|
||||||
|
if (any_type) return .type;
|
||||||
|
return .{ .global_const = node };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const PackageIndex = enum(u32) {
|
pub const ModuleIndex = enum(u32) {
|
||||||
_,
|
_,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -208,28 +375,25 @@ pub fn add_file(file_name: []const u8, bytes: []u8) !File.Index {
|
|||||||
try files.put(gpa, file_name, .{ .ast = ast });
|
try files.put(gpa, file_name, .{ .ast = ast });
|
||||||
|
|
||||||
if (ast.errors.len > 0) {
|
if (ast.errors.len > 0) {
|
||||||
// TODO: expose this in the UI
|
|
||||||
log.err("can't index '{s}' because it has syntax errors", .{file_index.path()});
|
log.err("can't index '{s}' because it has syntax errors", .{file_index.path()});
|
||||||
return file_index;
|
return file_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
var arena_instance = std.heap.ArenaAllocator.init(gpa);
|
|
||||||
defer arena_instance.deinit();
|
|
||||||
|
|
||||||
var w: Walk = .{
|
var w: Walk = .{
|
||||||
.arena = arena_instance.allocator(),
|
|
||||||
.file = file_index,
|
.file = file_index,
|
||||||
};
|
};
|
||||||
var scope: Scope = .{ .tag = .top };
|
const scope = try gpa.create(Scope);
|
||||||
|
scope.* = .{ .tag = .top };
|
||||||
|
|
||||||
const decl_index = try file_index.add_decl(0, .none);
|
const decl_index = try file_index.add_decl(0, .none);
|
||||||
try struct_decl(&w, &scope, decl_index, ast.containerDeclRoot());
|
try struct_decl(&w, scope, decl_index, 0, ast.containerDeclRoot());
|
||||||
|
|
||||||
const file = file_index.get();
|
const file = file_index.get();
|
||||||
shrinkToFit(&file.ident_decls);
|
shrinkToFit(&file.ident_decls);
|
||||||
shrinkToFit(&file.token_parents);
|
shrinkToFit(&file.token_parents);
|
||||||
shrinkToFit(&file.node_decls);
|
shrinkToFit(&file.node_decls);
|
||||||
shrinkToFit(&file.doctests);
|
shrinkToFit(&file.doctests);
|
||||||
|
shrinkToFit(&file.scopes);
|
||||||
|
|
||||||
return file_index;
|
return file_index;
|
||||||
}
|
}
|
||||||
@ -250,7 +414,7 @@ fn parse(source: []u8) Oom!Ast {
|
|||||||
return Ast.parse(gpa, adjusted_source, .zig);
|
return Ast.parse(gpa, adjusted_source, .zig);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Scope = struct {
|
pub const Scope = struct {
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
|
|
||||||
const Tag = enum { top, local, namespace };
|
const Tag = enum { top, local, namespace };
|
||||||
@ -267,6 +431,7 @@ const Scope = struct {
|
|||||||
names: std.StringArrayHashMapUnmanaged(Ast.Node.Index) = .{},
|
names: std.StringArrayHashMapUnmanaged(Ast.Node.Index) = .{},
|
||||||
doctests: std.StringArrayHashMapUnmanaged(Ast.Node.Index) = .{},
|
doctests: std.StringArrayHashMapUnmanaged(Ast.Node.Index) = .{},
|
||||||
decl_index: Decl.Index,
|
decl_index: Decl.Index,
|
||||||
|
field_count: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn getNamespaceDecl(start_scope: *Scope) Decl.Index {
|
fn getNamespaceDecl(start_scope: *Scope) Decl.Index {
|
||||||
@ -284,7 +449,17 @@ const Scope = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup(start_scope: *Scope, ast: *const Ast, name: []const u8) ?Ast.Node.Index {
|
pub fn get_child(scope: *Scope, name: []const u8) ?Ast.Node.Index {
|
||||||
|
switch (scope.tag) {
|
||||||
|
.top, .local => return null,
|
||||||
|
.namespace => {
|
||||||
|
const namespace = @fieldParentPtr(Namespace, "base", scope);
|
||||||
|
return namespace.names.get(name);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup(start_scope: *Scope, ast: *const Ast, name: []const u8) ?Ast.Node.Index {
|
||||||
const main_tokens = ast.nodes.items(.main_token);
|
const main_tokens = ast.nodes.items(.main_token);
|
||||||
var it: *Scope = start_scope;
|
var it: *Scope = start_scope;
|
||||||
while (true) switch (it.tag) {
|
while (true) switch (it.tag) {
|
||||||
@ -314,17 +489,21 @@ fn struct_decl(
|
|||||||
w: *Walk,
|
w: *Walk,
|
||||||
scope: *Scope,
|
scope: *Scope,
|
||||||
parent_decl: Decl.Index,
|
parent_decl: Decl.Index,
|
||||||
|
node: Ast.Node.Index,
|
||||||
container_decl: Ast.full.ContainerDecl,
|
container_decl: Ast.full.ContainerDecl,
|
||||||
) Oom!void {
|
) Oom!void {
|
||||||
const ast = w.file.get_ast();
|
const ast = w.file.get_ast();
|
||||||
const node_tags = ast.nodes.items(.tag);
|
const node_tags = ast.nodes.items(.tag);
|
||||||
const node_datas = ast.nodes.items(.data);
|
const node_datas = ast.nodes.items(.data);
|
||||||
|
|
||||||
var namespace: Scope.Namespace = .{
|
const namespace = try gpa.create(Scope.Namespace);
|
||||||
|
namespace.* = .{
|
||||||
.parent = scope,
|
.parent = scope,
|
||||||
.decl_index = parent_decl,
|
.decl_index = parent_decl,
|
||||||
|
.field_count = 0,
|
||||||
};
|
};
|
||||||
try w.scanDecls(&namespace, container_decl.ast.members);
|
try w.file.get().scopes.putNoClobber(gpa, node, &namespace.base);
|
||||||
|
try w.scanDecls(namespace, container_decl.ast.members);
|
||||||
|
|
||||||
for (container_decl.ast.members) |member| switch (node_tags[member]) {
|
for (container_decl.ast.members) |member| switch (node_tags[member]) {
|
||||||
.container_field_init,
|
.container_field_init,
|
||||||
@ -584,7 +763,8 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
|
|||||||
=> {
|
=> {
|
||||||
const full = ast.fullAsm(node).?;
|
const full = ast.fullAsm(node).?;
|
||||||
for (full.ast.items) |n| {
|
for (full.ast.items) |n| {
|
||||||
// TODO handle .asm_input, .asm_output
|
// There is a missing call here to expr() for .asm_input and
|
||||||
|
// .asm_output nodes.
|
||||||
_ = n;
|
_ = n;
|
||||||
}
|
}
|
||||||
try expr(w, scope, parent_decl, full.ast.template);
|
try expr(w, scope, parent_decl, full.ast.template);
|
||||||
@ -701,7 +881,7 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
|
|||||||
.tagged_union_two_trailing,
|
.tagged_union_two_trailing,
|
||||||
=> {
|
=> {
|
||||||
var buf: [2]Ast.Node.Index = undefined;
|
var buf: [2]Ast.Node.Index = undefined;
|
||||||
return struct_decl(w, scope, parent_decl, ast.fullContainerDecl(&buf, node).?);
|
return struct_decl(w, scope, parent_decl, node, ast.fullContainerDecl(&buf, node).?);
|
||||||
},
|
},
|
||||||
|
|
||||||
.array_type_sentinel => {
|
.array_type_sentinel => {
|
||||||
@ -803,7 +983,6 @@ fn block(
|
|||||||
statements: []const Ast.Node.Index,
|
statements: []const Ast.Node.Index,
|
||||||
) Oom!void {
|
) Oom!void {
|
||||||
const ast = w.file.get_ast();
|
const ast = w.file.get_ast();
|
||||||
const arena = w.arena;
|
|
||||||
const node_tags = ast.nodes.items(.tag);
|
const node_tags = ast.nodes.items(.tag);
|
||||||
const node_datas = ast.nodes.items(.data);
|
const node_datas = ast.nodes.items(.data);
|
||||||
|
|
||||||
@ -818,16 +997,17 @@ fn block(
|
|||||||
=> {
|
=> {
|
||||||
const full = ast.fullVarDecl(node).?;
|
const full = ast.fullVarDecl(node).?;
|
||||||
try global_var_decl(w, scope, parent_decl, full);
|
try global_var_decl(w, scope, parent_decl, full);
|
||||||
const local = try arena.create(Scope.Local);
|
const local = try gpa.create(Scope.Local);
|
||||||
local.* = .{
|
local.* = .{
|
||||||
.parent = scope,
|
.parent = scope,
|
||||||
.var_node = node,
|
.var_node = node,
|
||||||
};
|
};
|
||||||
|
try w.file.get().scopes.putNoClobber(gpa, node, &local.base);
|
||||||
scope = &local.base;
|
scope = &local.base;
|
||||||
},
|
},
|
||||||
|
|
||||||
.assign_destructure => {
|
.assign_destructure => {
|
||||||
// TODO
|
log.debug("walk assign_destructure not implemented yet", .{});
|
||||||
},
|
},
|
||||||
|
|
||||||
.grouped_expression => try expr(w, scope, parent_decl, node_datas[node].lhs),
|
.grouped_expression => try expr(w, scope, parent_decl, node_datas[node].lhs),
|
||||||
@ -849,7 +1029,6 @@ fn while_expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, full: Ast.full.W
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scanDecls(w: *Walk, namespace: *Scope.Namespace, members: []const Ast.Node.Index) Oom!void {
|
fn scanDecls(w: *Walk, namespace: *Scope.Namespace, members: []const Ast.Node.Index) Oom!void {
|
||||||
const arena = w.arena;
|
|
||||||
const ast = w.file.get_ast();
|
const ast = w.file.get_ast();
|
||||||
const node_tags = ast.nodes.items(.tag);
|
const node_tags = ast.nodes.items(.tag);
|
||||||
const main_tokens = ast.nodes.items(.main_token);
|
const main_tokens = ast.nodes.items(.main_token);
|
||||||
@ -880,19 +1059,34 @@ fn scanDecls(w: *Walk, namespace: *Scope.Namespace, members: []const Ast.Node.In
|
|||||||
const is_doctest = token_tags[ident_token] == .identifier;
|
const is_doctest = token_tags[ident_token] == .identifier;
|
||||||
if (is_doctest) {
|
if (is_doctest) {
|
||||||
const token_bytes = ast.tokenSlice(ident_token);
|
const token_bytes = ast.tokenSlice(ident_token);
|
||||||
try namespace.doctests.put(arena, token_bytes, member_node);
|
try namespace.doctests.put(gpa, token_bytes, member_node);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.container_field_init,
|
||||||
|
.container_field_align,
|
||||||
|
.container_field,
|
||||||
|
=> {
|
||||||
|
namespace.field_count += 1;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
|
||||||
else => continue,
|
else => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
const token_bytes = ast.tokenSlice(name_token);
|
const token_bytes = ast.tokenSlice(name_token);
|
||||||
try namespace.names.put(arena, token_bytes, member_node);
|
try namespace.names.put(gpa, token_bytes, member_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn isPrimitiveNonType(name: []const u8) bool {
|
||||||
|
return std.mem.eql(u8, name, "undefined") or
|
||||||
|
std.mem.eql(u8, name, "null") or
|
||||||
|
std.mem.eql(u8, name, "true") or
|
||||||
|
std.mem.eql(u8, name, "false");
|
||||||
|
}
|
||||||
|
|
||||||
//test {
|
//test {
|
||||||
// const gpa = std.testing.allocator;
|
// const gpa = std.testing.allocator;
|
||||||
//
|
//
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
/// Delete this to find out where URL escaping needs to be added.
|
||||||
|
const missing_feature_url_escape = true;
|
||||||
|
|
||||||
const gpa = std.heap.wasm_allocator;
|
const gpa = std.heap.wasm_allocator;
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
@ -80,7 +83,7 @@ export fn query_exec(ignore_case: bool) [*]Decl.Index {
|
|||||||
return query_results.items.ptr;
|
return query_results.items.ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const max_matched_items = 2000;
|
const max_matched_items = 1000;
|
||||||
|
|
||||||
fn query_exec_fallible(query: []const u8, ignore_case: bool) !void {
|
fn query_exec_fallible(query: []const u8, ignore_case: bool) !void {
|
||||||
const Score = packed struct(u32) {
|
const Score = packed struct(u32) {
|
||||||
@ -181,7 +184,7 @@ fn query_exec_fallible(query: []const u8, ignore_case: bool) !void {
|
|||||||
const b_decl = query_results.items[b_index];
|
const b_decl = query_results.items[b_index];
|
||||||
const a_file_path = a_decl.get().file.path();
|
const a_file_path = a_decl.get().file.path();
|
||||||
const b_file_path = b_decl.get().file.path();
|
const b_file_path = b_decl.get().file.path();
|
||||||
// TODO Also check the local namespace inside the file
|
// This neglects to check the local namespace inside the file.
|
||||||
return std.mem.lessThan(u8, b_file_path, a_file_path);
|
return std.mem.lessThan(u8, b_file_path, a_file_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,12 +212,178 @@ fn Slice(T: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ErrorIdentifier = packed struct(u64) {
|
||||||
|
token_index: Ast.TokenIndex,
|
||||||
|
decl_index: Decl.Index,
|
||||||
|
|
||||||
|
fn hasDocs(ei: ErrorIdentifier) bool {
|
||||||
|
const decl_index = ei.decl_index;
|
||||||
|
const ast = decl_index.get().file.get_ast();
|
||||||
|
const token_tags = ast.tokens.items(.tag);
|
||||||
|
const token_index = ei.token_index;
|
||||||
|
if (token_index == 0) return false;
|
||||||
|
return token_tags[token_index - 1] == .doc_comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn html(ei: ErrorIdentifier, base_decl: Decl.Index, out: *std.ArrayListUnmanaged(u8)) Oom!void {
|
||||||
|
const decl_index = ei.decl_index;
|
||||||
|
const ast = decl_index.get().file.get_ast();
|
||||||
|
const name = ast.tokenSlice(ei.token_index);
|
||||||
|
const first_doc_comment = Decl.findFirstDocComment(ast, ei.token_index);
|
||||||
|
const has_docs = ast.tokens.items(.tag)[first_doc_comment] == .doc_comment;
|
||||||
|
const has_link = base_decl != decl_index;
|
||||||
|
|
||||||
|
try out.appendSlice(gpa, "<dt>");
|
||||||
|
try out.appendSlice(gpa, name);
|
||||||
|
if (has_link) {
|
||||||
|
try out.appendSlice(gpa, " <a href=\"#");
|
||||||
|
_ = missing_feature_url_escape;
|
||||||
|
try decl_index.get().fqn(out);
|
||||||
|
try out.appendSlice(gpa, "\">");
|
||||||
|
try out.appendSlice(gpa, decl_index.get().extra_info().name);
|
||||||
|
try out.appendSlice(gpa, "</a>");
|
||||||
|
}
|
||||||
|
try out.appendSlice(gpa, "</dt>");
|
||||||
|
|
||||||
|
if (has_docs) {
|
||||||
|
try out.appendSlice(gpa, "<dd>");
|
||||||
|
try render_docs(out, decl_index, first_doc_comment, false);
|
||||||
|
try out.appendSlice(gpa, "</dd>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var string_result: std.ArrayListUnmanaged(u8) = .{};
|
var string_result: std.ArrayListUnmanaged(u8) = .{};
|
||||||
|
var error_set_result: std.StringArrayHashMapUnmanaged(ErrorIdentifier) = .{};
|
||||||
|
|
||||||
|
export fn decl_error_set(decl_index: Decl.Index) Slice(ErrorIdentifier) {
|
||||||
|
return Slice(ErrorIdentifier).init(decl_error_set_fallible(decl_index) catch @panic("OOM"));
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn error_set_node_list(base_decl: Decl.Index, node: Ast.Node.Index) Slice(ErrorIdentifier) {
|
||||||
|
error_set_result.clearRetainingCapacity();
|
||||||
|
addErrorsFromExpr(base_decl, &error_set_result, node) catch @panic("OOM");
|
||||||
|
sort_error_set_result();
|
||||||
|
return Slice(ErrorIdentifier).init(error_set_result.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn fn_error_set_decl(decl_index: Decl.Index, node: Ast.Node.Index) Decl.Index {
|
||||||
|
return switch (decl_index.get().file.categorize_expr(node)) {
|
||||||
|
.alias => |aliasee| fn_error_set_decl(aliasee, aliasee.get().ast_node),
|
||||||
|
else => decl_index,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn decl_field_count(decl_index: Decl.Index) u32 {
|
||||||
|
switch (decl_index.get().categorize()) {
|
||||||
|
.namespace => |node| return decl_index.get().file.get().field_count(node),
|
||||||
|
else => return 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decl_error_set_fallible(decl_index: Decl.Index) Oom![]ErrorIdentifier {
|
||||||
|
error_set_result.clearRetainingCapacity();
|
||||||
|
try addErrorsFromDecl(decl_index, &error_set_result);
|
||||||
|
sort_error_set_result();
|
||||||
|
return error_set_result.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sort_error_set_result() void {
|
||||||
|
const sort_context: struct {
|
||||||
|
pub fn lessThan(sc: @This(), a_index: usize, b_index: usize) bool {
|
||||||
|
_ = sc;
|
||||||
|
const a_name = error_set_result.keys()[a_index];
|
||||||
|
const b_name = error_set_result.keys()[b_index];
|
||||||
|
return std.mem.lessThan(u8, a_name, b_name);
|
||||||
|
}
|
||||||
|
} = .{};
|
||||||
|
error_set_result.sortUnstable(sort_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addErrorsFromDecl(
|
||||||
|
decl_index: Decl.Index,
|
||||||
|
out: *std.StringArrayHashMapUnmanaged(ErrorIdentifier),
|
||||||
|
) Oom!void {
|
||||||
|
switch (decl_index.get().categorize()) {
|
||||||
|
.error_set => |node| try addErrorsFromExpr(decl_index, out, node),
|
||||||
|
.alias => |aliasee| try addErrorsFromDecl(aliasee, out),
|
||||||
|
else => |cat| log.debug("unable to addErrorsFromDecl: {any}", .{cat}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addErrorsFromExpr(
|
||||||
|
decl_index: Decl.Index,
|
||||||
|
out: *std.StringArrayHashMapUnmanaged(ErrorIdentifier),
|
||||||
|
node: Ast.Node.Index,
|
||||||
|
) Oom!void {
|
||||||
|
const decl = decl_index.get();
|
||||||
|
const ast = decl.file.get_ast();
|
||||||
|
const node_tags = ast.nodes.items(.tag);
|
||||||
|
const node_datas = ast.nodes.items(.data);
|
||||||
|
|
||||||
|
switch (decl.file.categorize_expr(node)) {
|
||||||
|
.error_set => |n| switch (node_tags[n]) {
|
||||||
|
.error_set_decl => {
|
||||||
|
try addErrorsFromNode(decl_index, out, node);
|
||||||
|
},
|
||||||
|
.merge_error_sets => {
|
||||||
|
try addErrorsFromExpr(decl_index, out, node_datas[node].lhs);
|
||||||
|
try addErrorsFromExpr(decl_index, out, node_datas[node].rhs);
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
.alias => |aliasee| {
|
||||||
|
try addErrorsFromDecl(aliasee, out);
|
||||||
|
},
|
||||||
|
else => return,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addErrorsFromNode(
|
||||||
|
decl_index: Decl.Index,
|
||||||
|
out: *std.StringArrayHashMapUnmanaged(ErrorIdentifier),
|
||||||
|
node: Ast.Node.Index,
|
||||||
|
) Oom!void {
|
||||||
|
const decl = decl_index.get();
|
||||||
|
const ast = decl.file.get_ast();
|
||||||
|
const main_tokens = ast.nodes.items(.main_token);
|
||||||
|
const token_tags = ast.tokens.items(.tag);
|
||||||
|
const error_token = main_tokens[node];
|
||||||
|
var tok_i = error_token + 2;
|
||||||
|
while (true) : (tok_i += 1) switch (token_tags[tok_i]) {
|
||||||
|
.doc_comment, .comma => {},
|
||||||
|
.identifier => {
|
||||||
|
const name = ast.tokenSlice(tok_i);
|
||||||
|
const gop = try out.getOrPut(gpa, name);
|
||||||
|
// If there are more than one, take the one with doc comments.
|
||||||
|
// If they both have doc comments, prefer the existing one.
|
||||||
|
const new: ErrorIdentifier = .{
|
||||||
|
.token_index = tok_i,
|
||||||
|
.decl_index = decl_index,
|
||||||
|
};
|
||||||
|
if (!gop.found_existing or
|
||||||
|
(!gop.value_ptr.hasDocs() and new.hasDocs()))
|
||||||
|
{
|
||||||
|
gop.value_ptr.* = new;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.r_brace => break,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn type_fn_fields(decl_index: Decl.Index) Slice(Ast.Node.Index) {
|
||||||
|
return decl_fields(decl_index);
|
||||||
|
}
|
||||||
|
|
||||||
export fn decl_fields(decl_index: Decl.Index) Slice(Ast.Node.Index) {
|
export fn decl_fields(decl_index: Decl.Index) Slice(Ast.Node.Index) {
|
||||||
return Slice(Ast.Node.Index).init(decl_fields_fallible(decl_index) catch @panic("OOM"));
|
return Slice(Ast.Node.Index).init(decl_fields_fallible(decl_index) catch @panic("OOM"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn decl_params(decl_index: Decl.Index) Slice(Ast.Node.Index) {
|
||||||
|
return Slice(Ast.Node.Index).init(decl_params_fallible(decl_index) catch @panic("OOM"));
|
||||||
|
}
|
||||||
|
|
||||||
fn decl_fields_fallible(decl_index: Decl.Index) ![]Ast.Node.Index {
|
fn decl_fields_fallible(decl_index: Decl.Index) ![]Ast.Node.Index {
|
||||||
const g = struct {
|
const g = struct {
|
||||||
var result: std.ArrayListUnmanaged(Ast.Node.Index) = .{};
|
var result: std.ArrayListUnmanaged(Ast.Node.Index) = .{};
|
||||||
@ -237,12 +406,38 @@ fn decl_fields_fallible(decl_index: Decl.Index) ![]Ast.Node.Index {
|
|||||||
return g.result.items;
|
return g.result.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decl_params_fallible(decl_index: Decl.Index) ![]Ast.Node.Index {
|
||||||
|
const g = struct {
|
||||||
|
var result: std.ArrayListUnmanaged(Ast.Node.Index) = .{};
|
||||||
|
};
|
||||||
|
g.result.clearRetainingCapacity();
|
||||||
|
const decl = decl_index.get();
|
||||||
|
const ast = decl.file.get_ast();
|
||||||
|
const value_node = decl.value_node() orelse return &.{};
|
||||||
|
var buf: [1]Ast.Node.Index = undefined;
|
||||||
|
const fn_proto = ast.fullFnProto(&buf, value_node) orelse return &.{};
|
||||||
|
try g.result.appendSlice(gpa, fn_proto.ast.params);
|
||||||
|
return g.result.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn error_html(base_decl: Decl.Index, error_identifier: ErrorIdentifier) String {
|
||||||
|
string_result.clearRetainingCapacity();
|
||||||
|
error_identifier.html(base_decl, &string_result) catch @panic("OOM");
|
||||||
|
return String.init(string_result.items);
|
||||||
|
}
|
||||||
|
|
||||||
export fn decl_field_html(decl_index: Decl.Index, field_node: Ast.Node.Index) String {
|
export fn decl_field_html(decl_index: Decl.Index, field_node: Ast.Node.Index) String {
|
||||||
string_result.clearRetainingCapacity();
|
string_result.clearRetainingCapacity();
|
||||||
decl_field_html_fallible(&string_result, decl_index, field_node) catch @panic("OOM");
|
decl_field_html_fallible(&string_result, decl_index, field_node) catch @panic("OOM");
|
||||||
return String.init(string_result.items);
|
return String.init(string_result.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn decl_param_html(decl_index: Decl.Index, param_node: Ast.Node.Index) String {
|
||||||
|
string_result.clearRetainingCapacity();
|
||||||
|
decl_param_html_fallible(&string_result, decl_index, param_node) catch @panic("OOM");
|
||||||
|
return String.init(string_result.items);
|
||||||
|
}
|
||||||
|
|
||||||
fn decl_field_html_fallible(
|
fn decl_field_html_fallible(
|
||||||
out: *std.ArrayListUnmanaged(u8),
|
out: *std.ArrayListUnmanaged(u8),
|
||||||
decl_index: Decl.Index,
|
decl_index: Decl.Index,
|
||||||
@ -251,7 +446,7 @@ fn decl_field_html_fallible(
|
|||||||
const decl = decl_index.get();
|
const decl = decl_index.get();
|
||||||
const ast = decl.file.get_ast();
|
const ast = decl.file.get_ast();
|
||||||
try out.appendSlice(gpa, "<pre><code>");
|
try out.appendSlice(gpa, "<pre><code>");
|
||||||
try file_source_html(decl.file, out, field_node);
|
try file_source_html(decl.file, out, field_node, .{});
|
||||||
try out.appendSlice(gpa, "</code></pre>");
|
try out.appendSlice(gpa, "</code></pre>");
|
||||||
|
|
||||||
const field = ast.fullContainerField(field_node).?;
|
const field = ast.fullContainerField(field_node).?;
|
||||||
@ -259,12 +454,48 @@ fn decl_field_html_fallible(
|
|||||||
|
|
||||||
if (ast.tokens.items(.tag)[first_doc_comment] == .doc_comment) {
|
if (ast.tokens.items(.tag)[first_doc_comment] == .doc_comment) {
|
||||||
try out.appendSlice(gpa, "<div class=\"fieldDocs\">");
|
try out.appendSlice(gpa, "<div class=\"fieldDocs\">");
|
||||||
try render_docs(out, ast, first_doc_comment, false);
|
try render_docs(out, decl_index, first_doc_comment, false);
|
||||||
try out.appendSlice(gpa, "</div>");
|
try out.appendSlice(gpa, "</div>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn decl_fn_proto_html(decl_index: Decl.Index) String {
|
fn decl_param_html_fallible(
|
||||||
|
out: *std.ArrayListUnmanaged(u8),
|
||||||
|
decl_index: Decl.Index,
|
||||||
|
param_node: Ast.Node.Index,
|
||||||
|
) !void {
|
||||||
|
const decl = decl_index.get();
|
||||||
|
const ast = decl.file.get_ast();
|
||||||
|
const token_tags = ast.tokens.items(.tag);
|
||||||
|
const colon = ast.firstToken(param_node) - 1;
|
||||||
|
const name_token = colon - 1;
|
||||||
|
const first_doc_comment = f: {
|
||||||
|
var it = ast.firstToken(param_node);
|
||||||
|
while (it > 0) {
|
||||||
|
it -= 1;
|
||||||
|
switch (token_tags[it]) {
|
||||||
|
.doc_comment, .colon, .identifier, .keyword_comptime, .keyword_noalias => {},
|
||||||
|
else => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break :f it + 1;
|
||||||
|
};
|
||||||
|
const name = ast.tokenSlice(name_token);
|
||||||
|
|
||||||
|
try out.appendSlice(gpa, "<pre><code>");
|
||||||
|
try appendEscaped(out, name);
|
||||||
|
try out.appendSlice(gpa, ": ");
|
||||||
|
try file_source_html(decl.file, out, param_node, .{});
|
||||||
|
try out.appendSlice(gpa, "</code></pre>");
|
||||||
|
|
||||||
|
if (ast.tokens.items(.tag)[first_doc_comment] == .doc_comment) {
|
||||||
|
try out.appendSlice(gpa, "<div class=\"fieldDocs\">");
|
||||||
|
try render_docs(out, decl_index, first_doc_comment, false);
|
||||||
|
try out.appendSlice(gpa, "</div>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn decl_fn_proto_html(decl_index: Decl.Index, linkify_fn_name: bool) String {
|
||||||
const decl = decl_index.get();
|
const decl = decl_index.get();
|
||||||
const ast = decl.file.get_ast();
|
const ast = decl.file.get_ast();
|
||||||
const node_tags = ast.nodes.items(.tag);
|
const node_tags = ast.nodes.items(.tag);
|
||||||
@ -282,7 +513,12 @@ export fn decl_fn_proto_html(decl_index: Decl.Index) String {
|
|||||||
};
|
};
|
||||||
|
|
||||||
string_result.clearRetainingCapacity();
|
string_result.clearRetainingCapacity();
|
||||||
file_source_html(decl.file, &string_result, proto_node) catch |err| {
|
file_source_html(decl.file, &string_result, proto_node, .{
|
||||||
|
.skip_doc_comments = true,
|
||||||
|
.skip_comments = true,
|
||||||
|
.collapse_whitespace = true,
|
||||||
|
.fn_link = if (linkify_fn_name) decl_index else .none,
|
||||||
|
}) catch |err| {
|
||||||
fatal("unable to render source: {s}", .{@errorName(err)});
|
fatal("unable to render source: {s}", .{@errorName(err)});
|
||||||
};
|
};
|
||||||
return String.init(string_result.items);
|
return String.init(string_result.items);
|
||||||
@ -292,7 +528,7 @@ export fn decl_source_html(decl_index: Decl.Index) String {
|
|||||||
const decl = decl_index.get();
|
const decl = decl_index.get();
|
||||||
|
|
||||||
string_result.clearRetainingCapacity();
|
string_result.clearRetainingCapacity();
|
||||||
file_source_html(decl.file, &string_result, decl.ast_node) catch |err| {
|
file_source_html(decl.file, &string_result, decl.ast_node, .{}) catch |err| {
|
||||||
fatal("unable to render source: {s}", .{@errorName(err)});
|
fatal("unable to render source: {s}", .{@errorName(err)});
|
||||||
};
|
};
|
||||||
return String.init(string_result.items);
|
return String.init(string_result.items);
|
||||||
@ -304,7 +540,7 @@ export fn decl_doctest_html(decl_index: Decl.Index) String {
|
|||||||
return String.init("");
|
return String.init("");
|
||||||
|
|
||||||
string_result.clearRetainingCapacity();
|
string_result.clearRetainingCapacity();
|
||||||
file_source_html(decl.file, &string_result, doctest_ast_node) catch |err| {
|
file_source_html(decl.file, &string_result, doctest_ast_node, .{}) catch |err| {
|
||||||
fatal("unable to render source: {s}", .{@errorName(err)});
|
fatal("unable to render source: {s}", .{@errorName(err)});
|
||||||
};
|
};
|
||||||
return String.init(string_result.items);
|
return String.init(string_result.items);
|
||||||
@ -312,6 +548,7 @@ export fn decl_doctest_html(decl_index: Decl.Index) String {
|
|||||||
|
|
||||||
export fn decl_fqn(decl_index: Decl.Index) String {
|
export fn decl_fqn(decl_index: Decl.Index) String {
|
||||||
const decl = decl_index.get();
|
const decl = decl_index.get();
|
||||||
|
string_result.clearRetainingCapacity();
|
||||||
decl.fqn(&string_result) catch @panic("OOM");
|
decl.fqn(&string_result) catch @panic("OOM");
|
||||||
return String.init(string_result.items);
|
return String.init(string_result.items);
|
||||||
}
|
}
|
||||||
@ -321,6 +558,20 @@ export fn decl_parent(decl_index: Decl.Index) Decl.Index {
|
|||||||
return decl.parent;
|
return decl.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn fn_error_set(decl_index: Decl.Index) Ast.Node.Index {
|
||||||
|
const decl = decl_index.get();
|
||||||
|
const ast = decl.file.get_ast();
|
||||||
|
var buf: [1]Ast.Node.Index = undefined;
|
||||||
|
const full = ast.fullFnProto(&buf, decl.ast_node).?;
|
||||||
|
const node_tags = ast.nodes.items(.tag);
|
||||||
|
const node_datas = ast.nodes.items(.data);
|
||||||
|
return switch (node_tags[full.ast.return_type]) {
|
||||||
|
.error_set_decl => full.ast.return_type,
|
||||||
|
.error_union => node_datas[full.ast.return_type].lhs,
|
||||||
|
else => 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export fn decl_file_path(decl_index: Decl.Index) String {
|
export fn decl_file_path(decl_index: Decl.Index) String {
|
||||||
string_result.clearRetainingCapacity();
|
string_result.clearRetainingCapacity();
|
||||||
string_result.appendSlice(gpa, decl_index.get().file.path()) catch @panic("OOM");
|
string_result.appendSlice(gpa, decl_index.get().file.path()) catch @panic("OOM");
|
||||||
@ -350,7 +601,8 @@ export fn decl_category_name(decl_index: Decl.Index) String {
|
|||||||
},
|
},
|
||||||
.global_variable => "Global Variable",
|
.global_variable => "Global Variable",
|
||||||
.function => "Function",
|
.function => "Function",
|
||||||
.type => "Type",
|
.type_function => "Type Function",
|
||||||
|
.type, .type_type => "Type",
|
||||||
.error_set => "Error Set",
|
.error_set => "Error Set",
|
||||||
.global_const => "Constant",
|
.global_const => "Constant",
|
||||||
.primitive => "Primitive Value",
|
.primitive => "Primitive Value",
|
||||||
@ -375,9 +627,8 @@ export fn decl_name(decl_index: Decl.Index) String {
|
|||||||
|
|
||||||
export fn decl_docs_html(decl_index: Decl.Index, short: bool) String {
|
export fn decl_docs_html(decl_index: Decl.Index, short: bool) String {
|
||||||
const decl = decl_index.get();
|
const decl = decl_index.get();
|
||||||
const ast = decl.file.get_ast();
|
|
||||||
string_result.clearRetainingCapacity();
|
string_result.clearRetainingCapacity();
|
||||||
render_docs(&string_result, ast, decl.extra_info().first_doc_comment, short) catch @panic("OOM");
|
render_docs(&string_result, decl_index, decl.extra_info().first_doc_comment, short) catch @panic("OOM");
|
||||||
return String.init(string_result.items);
|
return String.init(string_result.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,10 +653,12 @@ fn collect_docs(
|
|||||||
|
|
||||||
fn render_docs(
|
fn render_docs(
|
||||||
out: *std.ArrayListUnmanaged(u8),
|
out: *std.ArrayListUnmanaged(u8),
|
||||||
ast: *const Ast,
|
decl_index: Decl.Index,
|
||||||
first_doc_comment: Ast.TokenIndex,
|
first_doc_comment: Ast.TokenIndex,
|
||||||
short: bool,
|
short: bool,
|
||||||
) Oom!void {
|
) Oom!void {
|
||||||
|
const decl = decl_index.get();
|
||||||
|
const ast = decl.file.get_ast();
|
||||||
const token_tags = ast.tokens.items(.tag);
|
const token_tags = ast.tokens.items(.tag);
|
||||||
|
|
||||||
var parser = try markdown.Parser.init(gpa);
|
var parser = try markdown.Parser.init(gpa);
|
||||||
@ -423,10 +676,14 @@ fn render_docs(
|
|||||||
var parsed_doc = try parser.endInput();
|
var parsed_doc = try parser.endInput();
|
||||||
defer parsed_doc.deinit(gpa);
|
defer parsed_doc.deinit(gpa);
|
||||||
|
|
||||||
|
const g = struct {
|
||||||
|
var link_buffer: std.ArrayListUnmanaged(u8) = .{};
|
||||||
|
};
|
||||||
|
|
||||||
const Writer = std.ArrayListUnmanaged(u8).Writer;
|
const Writer = std.ArrayListUnmanaged(u8).Writer;
|
||||||
const Renderer = markdown.Renderer(Writer, void);
|
const Renderer = markdown.Renderer(Writer, Decl.Index);
|
||||||
const renderer: Renderer = .{
|
const renderer: Renderer = .{
|
||||||
.context = {},
|
.context = decl_index,
|
||||||
.renderFn = struct {
|
.renderFn = struct {
|
||||||
fn render(
|
fn render(
|
||||||
r: Renderer,
|
r: Renderer,
|
||||||
@ -436,23 +693,22 @@ fn render_docs(
|
|||||||
) !void {
|
) !void {
|
||||||
const data = doc.nodes.items(.data)[@intFromEnum(node)];
|
const data = doc.nodes.items(.data)[@intFromEnum(node)];
|
||||||
switch (doc.nodes.items(.tag)[@intFromEnum(node)]) {
|
switch (doc.nodes.items(.tag)[@intFromEnum(node)]) {
|
||||||
// TODO: detect identifier references (dotted paths) in
|
|
||||||
// these three node types and render them appropriately.
|
|
||||||
// Also, syntax highlighting can be applied in code blocks
|
|
||||||
// unless the tag says otherwise.
|
|
||||||
.code_block => {
|
|
||||||
const tag = doc.string(data.code_block.tag);
|
|
||||||
_ = tag;
|
|
||||||
const content = doc.string(data.code_block.content);
|
|
||||||
try writer.print("<pre><code>{}</code></pre>\n", .{markdown.fmtHtml(content)});
|
|
||||||
},
|
|
||||||
.code_span => {
|
.code_span => {
|
||||||
|
try writer.writeAll("<code>");
|
||||||
const content = doc.string(data.text.content);
|
const content = doc.string(data.text.content);
|
||||||
try writer.print("<code>{}</code>", .{markdown.fmtHtml(content)});
|
if (resolve_decl_path(r.context, content)) |resolved_decl_index| {
|
||||||
},
|
g.link_buffer.clearRetainingCapacity();
|
||||||
.text => {
|
try resolve_decl_link(resolved_decl_index, &g.link_buffer);
|
||||||
const content = doc.string(data.text.content);
|
|
||||||
|
try writer.writeAll("<a href=\"#");
|
||||||
|
_ = missing_feature_url_escape;
|
||||||
|
try writer.writeAll(g.link_buffer.items);
|
||||||
|
try writer.print("\">{}</a>", .{markdown.fmtHtml(content)});
|
||||||
|
} else {
|
||||||
try writer.print("{}", .{markdown.fmtHtml(content)});
|
try writer.print("{}", .{markdown.fmtHtml(content)});
|
||||||
|
}
|
||||||
|
|
||||||
|
try writer.writeAll("</code>");
|
||||||
},
|
},
|
||||||
|
|
||||||
else => try Renderer.renderDefault(r, doc, node, writer),
|
else => try Renderer.renderDefault(r, doc, node, writer),
|
||||||
@ -463,12 +719,39 @@ fn render_docs(
|
|||||||
try renderer.render(parsed_doc, out.writer(gpa));
|
try renderer.render(parsed_doc, out.writer(gpa));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_decl_path(decl_index: Decl.Index, path: []const u8) ?Decl.Index {
|
||||||
|
var path_components = std.mem.splitScalar(u8, path, '.');
|
||||||
|
var current_decl_index = decl_index.get().lookup(path_components.first()) orelse return null;
|
||||||
|
while (path_components.next()) |component| {
|
||||||
|
switch (current_decl_index.get().categorize()) {
|
||||||
|
.alias => |aliasee| current_decl_index = aliasee,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
current_decl_index = current_decl_index.get().get_child(component) orelse return null;
|
||||||
|
}
|
||||||
|
return current_decl_index;
|
||||||
|
}
|
||||||
|
|
||||||
export fn decl_type_html(decl_index: Decl.Index) String {
|
export fn decl_type_html(decl_index: Decl.Index) String {
|
||||||
const decl = decl_index.get();
|
const decl = decl_index.get();
|
||||||
const ast = decl.file.get_ast();
|
const ast = decl.file.get_ast();
|
||||||
string_result.clearRetainingCapacity();
|
string_result.clearRetainingCapacity();
|
||||||
_ = ast; // TODO
|
t: {
|
||||||
string_result.appendSlice(gpa, "TODO_type_here") catch @panic("OOM");
|
// If there is an explicit type, use it.
|
||||||
|
if (ast.fullVarDecl(decl.ast_node)) |var_decl| {
|
||||||
|
if (var_decl.ast.type_node != 0) {
|
||||||
|
string_result.appendSlice(gpa, "<code>") catch @panic("OOM");
|
||||||
|
file_source_html(decl.file, &string_result, var_decl.ast.type_node, .{
|
||||||
|
.skip_comments = true,
|
||||||
|
.collapse_whitespace = true,
|
||||||
|
}) catch |e| {
|
||||||
|
fatal("unable to render html: {s}", .{@errorName(e)});
|
||||||
|
};
|
||||||
|
string_result.appendSlice(gpa, "</code>") catch @panic("OOM");
|
||||||
|
break :t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return String.init(string_result.items);
|
return String.init(string_result.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,7 +774,7 @@ fn unpack_inner(tar_bytes: []u8) !void {
|
|||||||
const file_name = try gpa.dupe(u8, tar_file.name);
|
const file_name = try gpa.dupe(u8, tar_file.name);
|
||||||
if (std.mem.indexOfScalar(u8, file_name, '/')) |pkg_name_end| {
|
if (std.mem.indexOfScalar(u8, file_name, '/')) |pkg_name_end| {
|
||||||
const pkg_name = file_name[0..pkg_name_end];
|
const pkg_name = file_name[0..pkg_name_end];
|
||||||
const gop = try Walk.packages.getOrPut(gpa, pkg_name);
|
const gop = try Walk.modules.getOrPut(gpa, pkg_name);
|
||||||
const file: Walk.File.Index = @enumFromInt(Walk.files.entries.len);
|
const file: Walk.File.Index = @enumFromInt(Walk.files.entries.len);
|
||||||
if (!gop.found_existing or
|
if (!gop.found_existing or
|
||||||
std.mem.eql(u8, file_name[pkg_name_end..], "/root.zig") or
|
std.mem.eql(u8, file_name[pkg_name_end..], "/root.zig") or
|
||||||
@ -527,13 +810,13 @@ fn ascii_lower(bytes: []u8) void {
|
|||||||
for (bytes) |*b| b.* = std.ascii.toLower(b.*);
|
for (bytes) |*b| b.* = std.ascii.toLower(b.*);
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn package_name(index: u32) String {
|
export fn module_name(index: u32) String {
|
||||||
const names = Walk.packages.keys();
|
const names = Walk.modules.keys();
|
||||||
return String.init(if (index >= names.len) "" else names[index]);
|
return String.init(if (index >= names.len) "" else names[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn find_package_root(pkg: Walk.PackageIndex) Decl.Index {
|
export fn find_module_root(pkg: Walk.ModuleIndex) Decl.Index {
|
||||||
const root_file = Walk.packages.values()[@intFromEnum(pkg)];
|
const root_file = Walk.modules.values()[@intFromEnum(pkg)];
|
||||||
const result = root_file.findRootDecl();
|
const result = root_file.findRootDecl();
|
||||||
assert(result != .none);
|
assert(result != .none);
|
||||||
return result;
|
return result;
|
||||||
@ -555,11 +838,17 @@ export fn find_file_root() Decl.Index {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Uses `input_string`.
|
/// Uses `input_string`.
|
||||||
|
/// Tries to look up the Decl component-wise but then falls back to a file path
|
||||||
|
/// based scan.
|
||||||
export fn find_decl() Decl.Index {
|
export fn find_decl() Decl.Index {
|
||||||
|
const result = Decl.find(input_string.items);
|
||||||
|
if (result != .none) return result;
|
||||||
|
|
||||||
const g = struct {
|
const g = struct {
|
||||||
var match_fqn: std.ArrayListUnmanaged(u8) = .{};
|
var match_fqn: std.ArrayListUnmanaged(u8) = .{};
|
||||||
};
|
};
|
||||||
for (Walk.decls.items, 0..) |*decl, decl_index| {
|
for (Walk.decls.items, 0..) |*decl, decl_index| {
|
||||||
|
g.match_fqn.clearRetainingCapacity();
|
||||||
decl.fqn(&g.match_fqn) catch @panic("OOM");
|
decl.fqn(&g.match_fqn) catch @panic("OOM");
|
||||||
if (std.mem.eql(u8, g.match_fqn.items, input_string.items)) {
|
if (std.mem.eql(u8, g.match_fqn.items, input_string.items)) {
|
||||||
//const path = @as(Decl.Index, @enumFromInt(decl_index)).get().file.path();
|
//const path = @as(Decl.Index, @enumFromInt(decl_index)).get().file.path();
|
||||||
@ -583,7 +872,7 @@ export fn categorize_decl(decl_index: Decl.Index, resolve_alias_count: usize) Wa
|
|||||||
var decl = decl_index.get();
|
var decl = decl_index.get();
|
||||||
while (true) {
|
while (true) {
|
||||||
const result = decl.categorize();
|
const result = decl.categorize();
|
||||||
switch (decl.categorize()) {
|
switch (result) {
|
||||||
.alias => |new_index| {
|
.alias => |new_index| {
|
||||||
assert(new_index != .none);
|
assert(new_index != .none);
|
||||||
global_aliasee = new_index;
|
global_aliasee = new_index;
|
||||||
@ -599,6 +888,10 @@ export fn categorize_decl(decl_index: Decl.Index, resolve_alias_count: usize) Wa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn type_fn_members(parent: Decl.Index, include_private: bool) Slice(Decl.Index) {
|
||||||
|
return namespace_members(parent, include_private);
|
||||||
|
}
|
||||||
|
|
||||||
export fn namespace_members(parent: Decl.Index, include_private: bool) Slice(Decl.Index) {
|
export fn namespace_members(parent: Decl.Index, include_private: bool) Slice(Decl.Index) {
|
||||||
const g = struct {
|
const g = struct {
|
||||||
var members: std.ArrayListUnmanaged(Decl.Index) = .{};
|
var members: std.ArrayListUnmanaged(Decl.Index) = .{};
|
||||||
@ -617,10 +910,18 @@ export fn namespace_members(parent: Decl.Index, include_private: bool) Slice(Dec
|
|||||||
return Slice(Decl.Index).init(g.members.items);
|
return Slice(Decl.Index).init(g.members.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RenderSourceOptions = struct {
|
||||||
|
skip_doc_comments: bool = false,
|
||||||
|
skip_comments: bool = false,
|
||||||
|
collapse_whitespace: bool = false,
|
||||||
|
fn_link: Decl.Index = .none,
|
||||||
|
};
|
||||||
|
|
||||||
fn file_source_html(
|
fn file_source_html(
|
||||||
file_index: Walk.File.Index,
|
file_index: Walk.File.Index,
|
||||||
out: *std.ArrayListUnmanaged(u8),
|
out: *std.ArrayListUnmanaged(u8),
|
||||||
root_node: Ast.Node.Index,
|
root_node: Ast.Node.Index,
|
||||||
|
options: RenderSourceOptions,
|
||||||
) !void {
|
) !void {
|
||||||
const ast = file_index.get_ast();
|
const ast = file_index.get_ast();
|
||||||
const file = file_index.get();
|
const file = file_index.get();
|
||||||
@ -631,6 +932,7 @@ fn file_source_html(
|
|||||||
|
|
||||||
const token_tags = ast.tokens.items(.tag);
|
const token_tags = ast.tokens.items(.tag);
|
||||||
const token_starts = ast.tokens.items(.start);
|
const token_starts = ast.tokens.items(.start);
|
||||||
|
const main_tokens = ast.nodes.items(.main_token);
|
||||||
|
|
||||||
const start_token = ast.firstToken(root_node);
|
const start_token = ast.firstToken(root_node);
|
||||||
const end_token = ast.lastToken(root_node) + 1;
|
const end_token = ast.lastToken(root_node) + 1;
|
||||||
@ -643,7 +945,20 @@ fn file_source_html(
|
|||||||
start_token..,
|
start_token..,
|
||||||
) |tag, start, token_index| {
|
) |tag, start, token_index| {
|
||||||
const between = ast.source[cursor..start];
|
const between = ast.source[cursor..start];
|
||||||
|
if (std.mem.trim(u8, between, " \t\r\n").len > 0) {
|
||||||
|
if (!options.skip_comments) {
|
||||||
|
try out.appendSlice(gpa, "<span class=\"tok-comment\">");
|
||||||
try appendEscaped(out, between);
|
try appendEscaped(out, between);
|
||||||
|
try out.appendSlice(gpa, "</span>");
|
||||||
|
}
|
||||||
|
} else if (between.len > 0) {
|
||||||
|
if (options.collapse_whitespace) {
|
||||||
|
if (out.items.len > 0 and out.items[out.items.len - 1] != ' ')
|
||||||
|
try out.append(gpa, ' ');
|
||||||
|
} else {
|
||||||
|
try out.appendSlice(gpa, between);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (tag == .eof) break;
|
if (tag == .eof) break;
|
||||||
const slice = ast.tokenSlice(token_index);
|
const slice = ast.tokenSlice(token_index);
|
||||||
cursor = start + slice.len;
|
cursor = start + slice.len;
|
||||||
@ -723,17 +1038,36 @@ fn file_source_html(
|
|||||||
.doc_comment,
|
.doc_comment,
|
||||||
.container_doc_comment,
|
.container_doc_comment,
|
||||||
=> {
|
=> {
|
||||||
|
if (!options.skip_doc_comments) {
|
||||||
try out.appendSlice(gpa, "<span class=\"tok-comment\">");
|
try out.appendSlice(gpa, "<span class=\"tok-comment\">");
|
||||||
try appendEscaped(out, slice);
|
try appendEscaped(out, slice);
|
||||||
try out.appendSlice(gpa, "</span>");
|
try out.appendSlice(gpa, "</span>");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.identifier => i: {
|
.identifier => i: {
|
||||||
if (std.mem.eql(u8, slice, "undefined") or
|
if (options.fn_link != .none) {
|
||||||
std.mem.eql(u8, slice, "null") or
|
const fn_link = options.fn_link.get();
|
||||||
std.mem.eql(u8, slice, "true") or
|
const fn_token = main_tokens[fn_link.ast_node];
|
||||||
std.mem.eql(u8, slice, "false"))
|
if (token_index == fn_token + 1) {
|
||||||
{
|
try out.appendSlice(gpa, "<a class=\"tok-fn\" href=\"#");
|
||||||
|
_ = missing_feature_url_escape;
|
||||||
|
try fn_link.fqn(out);
|
||||||
|
try out.appendSlice(gpa, "\">");
|
||||||
|
try appendEscaped(out, slice);
|
||||||
|
try out.appendSlice(gpa, "</a>");
|
||||||
|
break :i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token_index > 0 and token_tags[token_index - 1] == .keyword_fn) {
|
||||||
|
try out.appendSlice(gpa, "<span class=\"tok-fn\">");
|
||||||
|
try appendEscaped(out, slice);
|
||||||
|
try out.appendSlice(gpa, "</span>");
|
||||||
|
break :i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Walk.isPrimitiveNonType(slice)) {
|
||||||
try out.appendSlice(gpa, "<span class=\"tok-null\">");
|
try out.appendSlice(gpa, "<span class=\"tok-null\">");
|
||||||
try appendEscaped(out, slice);
|
try appendEscaped(out, slice);
|
||||||
try out.appendSlice(gpa, "</span>");
|
try out.appendSlice(gpa, "</span>");
|
||||||
@ -752,7 +1086,8 @@ fn file_source_html(
|
|||||||
try walk_field_accesses(file_index, &g.field_access_buffer, field_access_node);
|
try walk_field_accesses(file_index, &g.field_access_buffer, field_access_node);
|
||||||
if (g.field_access_buffer.items.len > 0) {
|
if (g.field_access_buffer.items.len > 0) {
|
||||||
try out.appendSlice(gpa, "<a href=\"#");
|
try out.appendSlice(gpa, "<a href=\"#");
|
||||||
try out.appendSlice(gpa, g.field_access_buffer.items); // TODO url escape
|
_ = missing_feature_url_escape;
|
||||||
|
try out.appendSlice(gpa, g.field_access_buffer.items);
|
||||||
try out.appendSlice(gpa, "\">");
|
try out.appendSlice(gpa, "\">");
|
||||||
try appendEscaped(out, slice);
|
try appendEscaped(out, slice);
|
||||||
try out.appendSlice(gpa, "</a>");
|
try out.appendSlice(gpa, "</a>");
|
||||||
@ -767,7 +1102,8 @@ fn file_source_html(
|
|||||||
try resolve_ident_link(file_index, &g.field_access_buffer, token_index);
|
try resolve_ident_link(file_index, &g.field_access_buffer, token_index);
|
||||||
if (g.field_access_buffer.items.len > 0) {
|
if (g.field_access_buffer.items.len > 0) {
|
||||||
try out.appendSlice(gpa, "<a href=\"#");
|
try out.appendSlice(gpa, "<a href=\"#");
|
||||||
try out.appendSlice(gpa, g.field_access_buffer.items); // TODO url escape
|
_ = missing_feature_url_escape;
|
||||||
|
try out.appendSlice(gpa, g.field_access_buffer.items);
|
||||||
try out.appendSlice(gpa, "\">");
|
try out.appendSlice(gpa, "\">");
|
||||||
try appendEscaped(out, slice);
|
try appendEscaped(out, slice);
|
||||||
try out.appendSlice(gpa, "</a>");
|
try out.appendSlice(gpa, "</a>");
|
||||||
@ -860,7 +1196,10 @@ fn resolve_ident_link(
|
|||||||
) Oom!void {
|
) Oom!void {
|
||||||
const decl_index = file_index.get().lookup_token(ident_token);
|
const decl_index = file_index.get().lookup_token(ident_token);
|
||||||
if (decl_index == .none) return;
|
if (decl_index == .none) return;
|
||||||
|
try resolve_decl_link(decl_index, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_decl_link(decl_index: Decl.Index, out: *std.ArrayListUnmanaged(u8)) Oom!void {
|
||||||
const decl = decl_index.get();
|
const decl = decl_index.get();
|
||||||
switch (decl.categorize()) {
|
switch (decl.categorize()) {
|
||||||
.alias => |alias_decl| try alias_decl.get().fqn(out),
|
.alias => |alias_decl| try alias_decl.get().fqn(out),
|
||||||
|
|||||||
@ -517,6 +517,10 @@ test "tables require leading and trailing pipes" {
|
|||||||
\\
|
\\
|
||||||
\\| But | this | is |
|
\\| But | this | is |
|
||||||
\\
|
\\
|
||||||
|
\\Also not a table:
|
||||||
|
\\|
|
||||||
|
\\ |
|
||||||
|
\\
|
||||||
,
|
,
|
||||||
\\<p>Not | a | table</p>
|
\\<p>Not | a | table</p>
|
||||||
\\<table>
|
\\<table>
|
||||||
@ -526,6 +530,9 @@ test "tables require leading and trailing pipes" {
|
|||||||
\\<td>is</td>
|
\\<td>is</td>
|
||||||
\\</tr>
|
\\</tr>
|
||||||
\\</table>
|
\\</table>
|
||||||
|
\\<p>Also not a table:
|
||||||
|
\\|
|
||||||
|
\\|</p>
|
||||||
\\
|
\\
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -584,7 +591,7 @@ test "code blocks" {
|
|||||||
\\<pre><code>Hello, world!
|
\\<pre><code>Hello, world!
|
||||||
\\This is some code.
|
\\This is some code.
|
||||||
\\</code></pre>
|
\\</code></pre>
|
||||||
\\<pre><code class="zig test">const std = @import("std");
|
\\<pre><code>const std = @import("std");
|
||||||
\\
|
\\
|
||||||
\\test {
|
\\test {
|
||||||
\\ try std.testing.expect(2 + 2 == 4);
|
\\ try std.testing.expect(2 + 2 == 4);
|
||||||
|
|||||||
@ -610,7 +610,8 @@ const TableRowStart = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn startTableRow(unindented_line: []const u8) ?TableRowStart {
|
fn startTableRow(unindented_line: []const u8) ?TableRowStart {
|
||||||
if (!mem.startsWith(u8, unindented_line, "|") or
|
if (unindented_line.len < 2 or
|
||||||
|
!mem.startsWith(u8, unindented_line, "|") or
|
||||||
mem.endsWith(u8, unindented_line, "\\|") or
|
mem.endsWith(u8, unindented_line, "\\|") or
|
||||||
!mem.endsWith(u8, unindented_line, "|")) return null;
|
!mem.endsWith(u8, unindented_line, "|")) return null;
|
||||||
|
|
||||||
|
|||||||
@ -112,13 +112,8 @@ pub fn Renderer(comptime Writer: type, comptime Context: type) type {
|
|||||||
try writer.print("</h{}>\n", .{data.heading.level});
|
try writer.print("</h{}>\n", .{data.heading.level});
|
||||||
},
|
},
|
||||||
.code_block => {
|
.code_block => {
|
||||||
const tag = doc.string(data.code_block.tag);
|
|
||||||
const content = doc.string(data.code_block.content);
|
const content = doc.string(data.code_block.content);
|
||||||
if (tag.len > 0) {
|
|
||||||
try writer.print("<pre><code class=\"{}\">{}</code></pre>\n", .{ fmtHtml(tag), fmtHtml(content) });
|
|
||||||
} else {
|
|
||||||
try writer.print("<pre><code>{}</code></pre>\n", .{fmtHtml(content)});
|
try writer.print("<pre><code>{}</code></pre>\n", .{fmtHtml(content)});
|
||||||
}
|
|
||||||
},
|
},
|
||||||
.blockquote => {
|
.blockquote => {
|
||||||
try writer.writeAll("<blockquote>\n");
|
try writer.writeAll("<blockquote>\n");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user