| author | |
| committer | |
| log | b13a55db97b14fac0462d2a1cb83be8dcb2eb335 |
| tree | 393f86a0582e10931b19b7f68d2f06b0d9e19449 |
| parent | 6b8c7540a876b000e5c53fdbf9336c9fdc8d7556 |
upstream commit 1f921d540e1a8bb40839be30239019c820eb663d
after this branch is merged, ziglang/zig becomes the new repository for
this code.8 files changed, 1074 insertions(+), 330 deletions(-)
lib/docs/index.html+57-31| ... | @@ -12,6 +12,9 @@ | ... | @@ -12,6 +12,9 @@ |
| 12 | .hidden { | 12 | .hidden { |
| 13 | display: none; | 13 | display: none; |
| 14 | } | 14 | } |
| 15 | table { | ||
| 16 | width: 100%; | ||
| 17 | } | ||
| 15 | a { | 18 | a { |
| 16 | color: #2A6286; | 19 | color: #2A6286; |
| 17 | } | 20 | } |
| ... | @@ -25,25 +28,38 @@ | ... | @@ -25,25 +28,38 @@ |
| 25 | } | 28 | } |
| 26 | code { | 29 | code { |
| 27 | font-family:"Source Code Pro",monospace; | 30 | font-family:"Source Code Pro",monospace; |
| 28 | font-size:1em; | 31 | font-size: 0.9em; |
| 29 | } | 32 | } |
| 30 | code a { | 33 | code a { |
| 31 | color: #000000; | 34 | color: #000000; |
| 32 | } | 35 | } |
| 33 | #listFields > div { | 36 | #listFields > div, #listParams > div { |
| 34 | margin-bottom: 1em; | 37 | margin-bottom: 1em; |
| 35 | } | 38 | } |
| 39 | #hdrName a { | ||
| 40 | font-size: 0.7em; | ||
| 41 | padding-left: 1em; | ||
| 42 | } | ||
| 36 | .fieldDocs { | 43 | .fieldDocs { |
| 37 | border: 1px solid #F5F5F5; | 44 | border: 1px solid #F5F5F5; |
| 38 | border-top: 0px; | 45 | border-top: 0px; |
| 39 | padding: 1px 1em; | 46 | padding: 1px 1em; |
| 40 | } | 47 | } |
| 41 | 48 | ||
| 49 | #logo { | ||
| 50 | width: 8em; | ||
| 51 | padding: 0.5em 1em; | ||
| 52 | } | ||
| 42 | 53 | ||
| 43 | #navWrap { | 54 | #navWrap { |
| 44 | float: left; | 55 | width: -moz-available; |
| 45 | width: 47em; | 56 | width: -webkit-fill-available; |
| 46 | margin-left: 1em; | 57 | width: stretch; |
| 58 | margin-left: 11em; | ||
| 59 | } | ||
| 60 | |||
| 61 | #search { | ||
| 62 | width: 100%; | ||
| 47 | } | 63 | } |
| 48 | 64 | ||
| 49 | nav { | 65 | nav { |
| ... | @@ -102,15 +118,6 @@ | ... | @@ -102,15 +118,6 @@ |
| 102 | color: #000; | 118 | color: #000; |
| 103 | } | 119 | } |
| 104 | 120 | ||
| 105 | #logo { | ||
| 106 | width: 8em; | ||
| 107 | padding: 0.5em 1em; | ||
| 108 | } | ||
| 109 | |||
| 110 | #search { | ||
| 111 | width: 100%; | ||
| 112 | } | ||
| 113 | |||
| 114 | #helpDialog { | 121 | #helpDialog { |
| 115 | width: 21em; | 122 | width: 21em; |
| 116 | height: 21em; | 123 | height: 21em; |
| ... | @@ -154,6 +161,12 @@ | ... | @@ -154,6 +161,12 @@ |
| 154 | font-weight: bold; | 161 | font-weight: bold; |
| 155 | } | 162 | } |
| 156 | 163 | ||
| 164 | dl > div { | ||
| 165 | padding: 0.5em; | ||
| 166 | border: 1px solid #c0c0c0; | ||
| 167 | margin-top: 0.5em; | ||
| 168 | } | ||
| 169 | |||
| 157 | td { | 170 | td { |
| 158 | vertical-align: top; | 171 | vertical-align: top; |
| 159 | margin: 0; | 172 | margin: 0; |
| ... | @@ -163,6 +176,10 @@ | ... | @@ -163,6 +176,10 @@ |
| 163 | overflow-x: hidden; | 176 | overflow-x: hidden; |
| 164 | } | 177 | } |
| 165 | 178 | ||
| 179 | ul.columns { | ||
| 180 | column-width: 20em; | ||
| 181 | } | ||
| 182 | |||
| 166 | .tok-kw { | 183 | .tok-kw { |
| 167 | color: #333; | 184 | color: #333; |
| 168 | font-weight: bold; | 185 | font-weight: bold; |
| ... | @@ -193,18 +210,19 @@ | ... | @@ -193,18 +210,19 @@ |
| 193 | } | 210 | } |
| 194 | 211 | ||
| 195 | @media (prefers-color-scheme: dark) { | 212 | @media (prefers-color-scheme: dark) { |
| 196 | body{ | 213 | body { |
| 197 | background-color: #111; | 214 | background-color: #111; |
| 198 | color: #bbb; | 215 | color: #bbb; |
| 199 | } | 216 | } |
| 217 | pre { | ||
| 218 | background-color: #222; | ||
| 219 | color: #ccc; | ||
| 220 | } | ||
| 200 | a { | 221 | a { |
| 201 | color: #88f; | 222 | color: #88f; |
| 202 | } | 223 | } |
| 203 | code a { | 224 | code a { |
| 204 | color: #bbb; | 225 | color: #ccc; |
| 205 | } | ||
| 206 | pre{ | ||
| 207 | background-color:#2A2A2A; | ||
| 208 | } | 226 | } |
| 209 | .fieldDocs { | 227 | .fieldDocs { |
| 210 | border-color:#2A2A2A; | 228 | border-color:#2A2A2A; |
| ... | @@ -229,6 +247,9 @@ | ... | @@ -229,6 +247,9 @@ |
| 229 | #listSearchResults li.selected a { | 247 | #listSearchResults li.selected a { |
| 230 | color: #fff; | 248 | color: #fff; |
| 231 | } | 249 | } |
| 250 | dl > div { | ||
| 251 | border-color: #373737; | ||
| 252 | } | ||
| 232 | .tok-kw { | 253 | .tok-kw { |
| 233 | color: #eee; | 254 | color: #eee; |
| 234 | } | 255 | } |
| ... | @@ -242,7 +263,7 @@ | ... | @@ -242,7 +263,7 @@ |
| 242 | color: #aa7; | 263 | color: #aa7; |
| 243 | } | 264 | } |
| 244 | .tok-fn { | 265 | .tok-fn { |
| 245 | color: #e33; | 266 | color: #B1A0F8; |
| 246 | } | 267 | } |
| 247 | .tok-null { | 268 | .tok-null { |
| 248 | color: #ff8080; | 269 | color: #ff8080; |
| ... | @@ -258,7 +279,7 @@ | ... | @@ -258,7 +279,7 @@ |
| 258 | </head> | 279 | </head> |
| 259 | <body> | 280 | <body> |
| 260 | <nav> | 281 | <nav> |
| 261 | <div class="logo"> | 282 | <a class="logo" href="#"> |
| 262 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 140"> | 283 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 140"> |
| 263 | <g fill="#F7A41D"> | 284 | <g fill="#F7A41D"> |
| 264 | <g> | 285 | <g> |
| ... | @@ -297,7 +318,7 @@ | ... | @@ -297,7 +318,7 @@ |
| 297 | </g> | 318 | </g> |
| 298 | </g> | 319 | </g> |
| 299 | </svg> | 320 | </svg> |
| 300 | </div> | 321 | </a> |
| 301 | </nav> | 322 | </nav> |
| 302 | <div id="navWrap"> | 323 | <div id="navWrap"> |
| 303 | <input type="search" id="search" autocomplete="off" spellcheck="false" placeholder="`s` to search, `?` to see more options"> | 324 | <input type="search" id="search" autocomplete="off" spellcheck="false" placeholder="`s` to search, `?` to see more options"> |
| ... | @@ -305,11 +326,16 @@ | ... | @@ -305,11 +326,16 @@ |
| 305 | </div> | 326 | </div> |
| 306 | <section> | 327 | <section> |
| 307 | <p id="status">Loading...</p> | 328 | <p id="status">Loading...</p> |
| 329 | <h1 id="hdrName" class="hidden"><span></span><a href="#">[src]</a></h1> | ||
| 308 | <div id="fnProto" class="hidden"> | 330 | <div id="fnProto" class="hidden"> |
| 309 | <pre><code id="fnProtoCode"></code></pre> | 331 | <pre><code id="fnProtoCode"></code></pre> |
| 310 | </div> | 332 | </div> |
| 311 | <h1 id="hdrName" class="hidden"></h1> | ||
| 312 | <div id="tldDocs" class="hidden"></div> | 333 | <div id="tldDocs" class="hidden"></div> |
| 334 | <div id="sectParams" class="hidden"> | ||
| 335 | <h2>Parameters</h2> | ||
| 336 | <div id="listParams"> | ||
| 337 | </div> | ||
| 338 | </div> | ||
| 313 | <div id="sectFnErrors" class="hidden"> | 339 | <div id="sectFnErrors" class="hidden"> |
| 314 | <h2>Errors</h2> | 340 | <h2>Errors</h2> |
| 315 | <div id="fnErrorsAnyError"> | 341 | <div id="fnErrorsAnyError"> |
| ... | @@ -332,12 +358,12 @@ | ... | @@ -332,12 +358,12 @@ |
| 332 | </div> | 358 | </div> |
| 333 | <div id="sectTypes" class="hidden"> | 359 | <div id="sectTypes" class="hidden"> |
| 334 | <h2>Types</h2> | 360 | <h2>Types</h2> |
| 335 | <ul id="listTypes"> | 361 | <ul id="listTypes" class="columns"> |
| 336 | </ul> | 362 | </ul> |
| 337 | </div> | 363 | </div> |
| 338 | <div id="sectNamespaces" class="hidden"> | 364 | <div id="sectNamespaces" class="hidden"> |
| 339 | <h2>Namespaces</h2> | 365 | <h2>Namespaces</h2> |
| 340 | <ul id="listNamespaces"> | 366 | <ul id="listNamespaces" class="columns"> |
| 341 | </ul> | 367 | </ul> |
| 342 | </div> | 368 | </div> |
| 343 | <div id="sectGlobalVars" class="hidden"> | 369 | <div id="sectGlobalVars" class="hidden"> |
| ... | @@ -347,11 +373,6 @@ | ... | @@ -347,11 +373,6 @@ |
| 347 | </tbody> | 373 | </tbody> |
| 348 | </table> | 374 | </table> |
| 349 | </div> | 375 | </div> |
| 350 | <div id="sectFns" class="hidden"> | ||
| 351 | <h2>Functions</h2> | ||
| 352 | <dl id="listFns"> | ||
| 353 | </dl> | ||
| 354 | </div> | ||
| 355 | <div id="sectValues" class="hidden"> | 376 | <div id="sectValues" class="hidden"> |
| 356 | <h2>Values</h2> | 377 | <h2>Values</h2> |
| 357 | <table> | 378 | <table> |
| ... | @@ -359,9 +380,14 @@ | ... | @@ -359,9 +380,14 @@ |
| 359 | </tbody> | 380 | </tbody> |
| 360 | </table> | 381 | </table> |
| 361 | </div> | 382 | </div> |
| 383 | <div id="sectFns" class="hidden"> | ||
| 384 | <h2>Functions</h2> | ||
| 385 | <dl id="listFns"> | ||
| 386 | </dl> | ||
| 387 | </div> | ||
| 362 | <div id="sectErrSets" class="hidden"> | 388 | <div id="sectErrSets" class="hidden"> |
| 363 | <h2>Error Sets</h2> | 389 | <h2>Error Sets</h2> |
| 364 | <ul id="listErrSets"> | 390 | <ul id="listErrSets" class="columns"> |
| 365 | </ul> | 391 | </ul> |
| 366 | </div> | 392 | </div> |
| 367 | <div id="sectDocTests" class="hidden"> | 393 | <div id="sectDocTests" class="hidden"> |
lib/docs/main.js+344-208| ... | @@ -7,6 +7,8 @@ | ... | @@ -7,6 +7,8 @@ |
| 7 | const CAT_global_const = 5; | 7 | const CAT_global_const = 5; |
| 8 | const CAT_alias = 6; | 8 | const CAT_alias = 6; |
| 9 | const CAT_type = 7; | 9 | const CAT_type = 7; |
| 10 | const CAT_type_type = 8; | ||
| 11 | const CAT_type_function = 9; | ||
| 10 | 12 | ||
| 11 | const domDocTestsCode = document.getElementById("docTestsCode"); | 13 | const domDocTestsCode = document.getElementById("docTestsCode"); |
| 12 | const domFnErrorsAnyError = document.getElementById("fnErrorsAnyError"); | 14 | const domFnErrorsAnyError = document.getElementById("fnErrorsAnyError"); |
| ... | @@ -16,6 +18,7 @@ | ... | @@ -16,6 +18,7 @@ |
| 16 | const domHelpModal = document.getElementById("helpDialog"); | 18 | const domHelpModal = document.getElementById("helpDialog"); |
| 17 | const domListErrSets = document.getElementById("listErrSets"); | 19 | const domListErrSets = document.getElementById("listErrSets"); |
| 18 | const domListFields = document.getElementById("listFields"); | 20 | const domListFields = document.getElementById("listFields"); |
| 21 | const domListParams = document.getElementById("listParams"); | ||
| 19 | const domListFnErrors = document.getElementById("listFnErrors"); | 22 | const domListFnErrors = document.getElementById("listFnErrors"); |
| 20 | const domListFns = document.getElementById("listFns"); | 23 | const domListFns = document.getElementById("listFns"); |
| 21 | const domListGlobalVars = document.getElementById("listGlobalVars"); | 24 | const domListGlobalVars = document.getElementById("listGlobalVars"); |
| ... | @@ -29,6 +32,7 @@ | ... | @@ -29,6 +32,7 @@ |
| 29 | const domSectDocTests = document.getElementById("sectDocTests"); | 32 | const domSectDocTests = document.getElementById("sectDocTests"); |
| 30 | const domSectErrSets = document.getElementById("sectErrSets"); | 33 | const domSectErrSets = document.getElementById("sectErrSets"); |
| 31 | const domSectFields = document.getElementById("sectFields"); | 34 | const domSectFields = document.getElementById("sectFields"); |
| 35 | const domSectParams = document.getElementById("sectParams"); | ||
| 32 | const domSectFnErrors = document.getElementById("sectFnErrors"); | 36 | const domSectFnErrors = document.getElementById("sectFnErrors"); |
| 33 | const domSectFns = document.getElementById("sectFns"); | 37 | const domSectFns = document.getElementById("sectFns"); |
| 34 | const domSectGlobalVars = document.getElementById("sectGlobalVars"); | 38 | const domSectGlobalVars = document.getElementById("sectGlobalVars"); |
| ... | @@ -64,8 +68,8 @@ | ... | @@ -64,8 +68,8 @@ |
| 64 | var curSearchIndex = -1; | 68 | var curSearchIndex = -1; |
| 65 | var imFeelingLucky = false; | 69 | var imFeelingLucky = false; |
| 66 | 70 | ||
| 67 | // names of packages in the same order as wasm | 71 | // names of modules in the same order as wasm |
| 68 | const packageList = []; | 72 | const moduleList = []; |
| 69 | 73 | ||
| 70 | let wasm_promise = fetch("main.wasm"); | 74 | let wasm_promise = fetch("main.wasm"); |
| 71 | let sources_promise = fetch("sources.tar").then(function(response) { | 75 | let sources_promise = fetch("sources.tar").then(function(response) { |
| ... | @@ -99,13 +103,13 @@ | ... | @@ -99,13 +103,13 @@ |
| 99 | wasm_array.set(js_array); | 103 | wasm_array.set(js_array); |
| 100 | wasm_exports.unpack(ptr, js_array.length); | 104 | wasm_exports.unpack(ptr, js_array.length); |
| 101 | 105 | ||
| 102 | updatePackageList(); | 106 | updateModuleList(); |
| 103 | 107 | ||
| 104 | window.addEventListener('hashchange', onHashChange, false); | 108 | window.addEventListener('popstate', onPopState, false); |
| 105 | domSearch.addEventListener('keydown', onSearchKeyDown, false); | 109 | domSearch.addEventListener('keydown', onSearchKeyDown, false); |
| 106 | domSearch.addEventListener('input', onSearchChange, false); | 110 | domSearch.addEventListener('input', onSearchChange, false); |
| 107 | window.addEventListener('keydown', onWindowKeyDown, false); | 111 | window.addEventListener('keydown', onWindowKeyDown, false); |
| 108 | onHashChange(); | 112 | onHashChange(null); |
| 109 | }); | 113 | }); |
| 110 | }); | 114 | }); |
| 111 | 115 | ||
| ... | @@ -118,7 +122,7 @@ | ... | @@ -118,7 +122,7 @@ |
| 118 | } else if (curNav.path != null) { | 122 | } else if (curNav.path != null) { |
| 119 | document.title = curNav.path + suffix; | 123 | document.title = curNav.path + suffix; |
| 120 | } else { | 124 | } else { |
| 121 | document.title = packageList[0] + suffix; // Home | 125 | document.title = moduleList[0] + suffix; // Home |
| 122 | } | 126 | } |
| 123 | } | 127 | } |
| 124 | 128 | ||
| ... | @@ -130,6 +134,7 @@ | ... | @@ -130,6 +134,7 @@ |
| 130 | domSectErrSets.classList.add("hidden"); | 134 | domSectErrSets.classList.add("hidden"); |
| 131 | domSectDocTests.classList.add("hidden"); | 135 | domSectDocTests.classList.add("hidden"); |
| 132 | domSectFields.classList.add("hidden"); | 136 | domSectFields.classList.add("hidden"); |
| 137 | domSectParams.classList.add("hidden"); | ||
| 133 | domSectFnErrors.classList.add("hidden"); | 138 | domSectFnErrors.classList.add("hidden"); |
| 134 | domSectFns.classList.add("hidden"); | 139 | domSectFns.classList.add("hidden"); |
| 135 | domSectGlobalVars.classList.add("hidden"); | 140 | domSectGlobalVars.classList.add("hidden"); |
| ... | @@ -152,7 +157,7 @@ | ... | @@ -152,7 +157,7 @@ |
| 152 | case 0: return renderHome(); | 157 | case 0: return renderHome(); |
| 153 | case 1: | 158 | case 1: |
| 154 | if (curNav.decl == null) { | 159 | if (curNav.decl == null) { |
| 155 | return render404(); | 160 | return renderNotFound(); |
| 156 | } else { | 161 | } else { |
| 157 | return renderDecl(curNav.decl); | 162 | return renderDecl(curNav.decl); |
| 158 | } | 163 | } |
| ... | @@ -162,37 +167,51 @@ | ... | @@ -162,37 +167,51 @@ |
| 162 | } | 167 | } |
| 163 | 168 | ||
| 164 | function renderHome() { | 169 | function renderHome() { |
| 165 | if (packageList.length == 1) return renderPackage(0); | 170 | if (moduleList.length == 0) { |
| 166 | 171 | domStatus.textContent = "sources.tar contains no modules"; | |
| 167 | domStatus.textContent = "TODO implement renderHome for multiple packages"; | 172 | domStatus.classList.remove("hidden"); |
| 168 | domStatus.classList.remove("hidden"); | 173 | return; |
| 174 | } | ||
| 175 | return renderModule(0); | ||
| 169 | } | 176 | } |
| 170 | 177 | ||
| 171 | function renderPackage(pkg_index) { | 178 | function renderModule(pkg_index) { |
| 172 | const root_decl = wasm_exports.find_package_root(pkg_index); | 179 | const root_decl = wasm_exports.find_module_root(pkg_index); |
| 173 | return renderDecl(root_decl); | 180 | return renderDecl(root_decl); |
| 174 | } | 181 | } |
| 175 | 182 | ||
| 176 | function renderDecl(decl_index) { | 183 | function renderDecl(decl_index) { |
| 177 | const category = wasm_exports.categorize_decl(decl_index, 0); | 184 | const category = wasm_exports.categorize_decl(decl_index, 0); |
| 178 | switch (category) { | 185 | switch (category) { |
| 179 | case CAT_namespace: return renderNamespace(decl_index); | 186 | case CAT_namespace: |
| 180 | case CAT_global_variable: throw new Error("TODO: CAT_GLOBAL_VARIABLE"); | 187 | return renderNamespacePage(decl_index); |
| 181 | case CAT_function: return renderFunction(decl_index); | 188 | case CAT_global_variable: |
| 182 | case CAT_primitive: throw new Error("TODO CAT_primitive"); | 189 | case CAT_primitive: |
| 183 | case CAT_error_set: throw new Error("TODO CAT_error_set"); | 190 | case CAT_global_const: |
| 184 | case CAT_global_const: return renderGlobalConst(decl_index); | 191 | case CAT_type: |
| 185 | case CAT_alias: return renderDecl(wasm_exports.get_aliasee()); | 192 | case CAT_type_type: |
| 186 | case CAT_type: throw new Error("TODO CAT_type"); | 193 | return renderGlobal(decl_index); |
| 187 | default: throw new Error("unrecognized category " + category); | 194 | case CAT_function: |
| 195 | return renderFunction(decl_index); | ||
| 196 | case CAT_type_function: | ||
| 197 | return renderTypeFunction(decl_index); | ||
| 198 | case CAT_error_set: | ||
| 199 | return renderErrorSetPage(decl_index); | ||
| 200 | case CAT_alias: | ||
| 201 | return renderDecl(wasm_exports.get_aliasee()); | ||
| 202 | default: | ||
| 203 | throw new Error("unrecognized category " + category); | ||
| 188 | } | 204 | } |
| 189 | } | 205 | } |
| 190 | 206 | ||
| 191 | function renderSource(path) { | 207 | function renderSource(path) { |
| 192 | const decl_index = findFileRoot(path); | 208 | const decl_index = findFileRoot(path); |
| 193 | if (decl_index == null) return render404(); | 209 | if (decl_index == null) return renderNotFound(); |
| 194 | 210 | ||
| 195 | renderNav(decl_index); | 211 | renderNavFancy(decl_index, [{ |
| 212 | name: "[src]", | ||
| 213 | href: location.hash, | ||
| 214 | }]); | ||
| 196 | 215 | ||
| 197 | domSourceText.innerHTML = declSourceHtml(decl_index); | 216 | domSourceText.innerHTML = declSourceHtml(decl_index); |
| 198 | 217 | ||
| ... | @@ -200,7 +219,12 @@ | ... | @@ -200,7 +219,12 @@ |
| 200 | } | 219 | } |
| 201 | 220 | ||
| 202 | function renderDeclHeading(decl_index) { | 221 | function renderDeclHeading(decl_index) { |
| 203 | domHdrName.innerText = unwrapString(wasm_exports.decl_category_name(decl_index)); | 222 | curNav.viewSourceHash = "#src/" + unwrapString(wasm_exports.decl_file_path(decl_index)); |
| 223 | |||
| 224 | const hdrNameSpan = domHdrName.children[0]; | ||
| 225 | const srcLink = domHdrName.children[1]; | ||
| 226 | hdrNameSpan.innerText = unwrapString(wasm_exports.decl_category_name(decl_index)); | ||
| 227 | srcLink.setAttribute('href', curNav.viewSourceHash); | ||
| 204 | domHdrName.classList.remove("hidden"); | 228 | domHdrName.classList.remove("hidden"); |
| 205 | 229 | ||
| 206 | renderTopLevelDocs(decl_index); | 230 | renderTopLevelDocs(decl_index); |
| ... | @@ -214,8 +238,11 @@ | ... | @@ -214,8 +238,11 @@ |
| 214 | } | 238 | } |
| 215 | } | 239 | } |
| 216 | 240 | ||
| 217 | function renderNav(cur_nav_decl) { | 241 | function renderNav(cur_nav_decl, list) { |
| 218 | const list = []; | 242 | return renderNavFancy(cur_nav_decl, []); |
| 243 | } | ||
| 244 | |||
| 245 | function renderNavFancy(cur_nav_decl, list) { | ||
| 219 | { | 246 | { |
| 220 | // First, walk backwards the decl parents within a file. | 247 | // First, walk backwards the decl parents within a file. |
| 221 | let decl_it = cur_nav_decl; | 248 | let decl_it = cur_nav_decl; |
| ... | @@ -235,11 +262,12 @@ | ... | @@ -235,11 +262,12 @@ |
| 235 | const parts = file_path.split("."); | 262 | const parts = file_path.split("."); |
| 236 | parts.pop(); // skip last | 263 | parts.pop(); // skip last |
| 237 | for (;;) { | 264 | for (;;) { |
| 238 | let part = parts.pop(); | 265 | const href = navLinkFqn(parts.join(".")); |
| 266 | const part = parts.pop(); | ||
| 239 | if (!part) break; | 267 | if (!part) break; |
| 240 | list.push({ | 268 | list.push({ |
| 241 | name: part, | 269 | name: part, |
| 242 | href: navLinkFqn(parts.join(".")), | 270 | href: href, |
| 243 | }); | 271 | }); |
| 244 | } | 272 | } |
| 245 | } | 273 | } |
| ... | @@ -263,8 +291,8 @@ | ... | @@ -263,8 +291,8 @@ |
| 263 | domSectNav.classList.remove("hidden"); | 291 | domSectNav.classList.remove("hidden"); |
| 264 | } | 292 | } |
| 265 | 293 | ||
| 266 | function render404() { | 294 | function renderNotFound() { |
| 267 | domStatus.textContent = "404 Not Found"; | 295 | domStatus.textContent = "Declaration not found."; |
| 268 | domStatus.classList.remove("hidden"); | 296 | domStatus.classList.remove("hidden"); |
| 269 | } | 297 | } |
| 270 | 298 | ||
| ... | @@ -288,31 +316,91 @@ | ... | @@ -288,31 +316,91 @@ |
| 288 | } | 316 | } |
| 289 | } | 317 | } |
| 290 | 318 | ||
| 291 | function setViewSourceDecl(decl_index) { | 319 | function renderErrorSetPage(decl_index) { |
| 292 | curNav.viewSourceHash = "#src/" + unwrapString(wasm_exports.decl_file_path(decl_index)); | 320 | renderNav(decl_index); |
| 321 | renderDeclHeading(decl_index); | ||
| 322 | |||
| 323 | const errorSetList = declErrorSet(decl_index).slice(); | ||
| 324 | renderErrorSet(decl_index, errorSetList); | ||
| 293 | } | 325 | } |
| 294 | 326 | ||
| 295 | function renderFunction(decl_index) { | 327 | function renderErrorSet(base_decl, errorSetList) { |
| 296 | renderNav(decl_index); | 328 | if (errorSetList == null) { |
| 297 | setViewSourceDecl(decl_index); | 329 | domFnErrorsAnyError.classList.remove("hidden"); |
| 330 | } else { | ||
| 331 | resizeDomList(domListFnErrors, errorSetList.length, '<div></div>'); | ||
| 332 | for (let i = 0; i < errorSetList.length; i += 1) { | ||
| 333 | const divDom = domListFnErrors.children[i]; | ||
| 334 | const html = unwrapString(wasm_exports.error_html(base_decl, errorSetList[i])); | ||
| 335 | divDom.innerHTML = html; | ||
| 336 | } | ||
| 337 | domTableFnErrors.classList.remove("hidden"); | ||
| 338 | } | ||
| 339 | domSectFnErrors.classList.remove("hidden"); | ||
| 340 | } | ||
| 341 | |||
| 342 | function renderParams(decl_index) { | ||
| 343 | // Prevent params from being emptied next time wasm calls memory.grow. | ||
| 344 | const params = declParams(decl_index).slice(); | ||
| 345 | if (params.length !== 0) { | ||
| 346 | resizeDomList(domListParams, params.length, '<div></div>'); | ||
| 347 | for (let i = 0; i < params.length; i += 1) { | ||
| 348 | const divDom = domListParams.children[i]; | ||
| 349 | divDom.innerHTML = unwrapString(wasm_exports.decl_param_html(decl_index, params[i])); | ||
| 350 | } | ||
| 351 | domSectParams.classList.remove("hidden"); | ||
| 352 | } | ||
| 353 | } | ||
| 298 | 354 | ||
| 299 | domFnProtoCode.innerHTML = fnProtoHtml(decl_index); | 355 | function renderTypeFunction(decl_index) { |
| 356 | renderNav(decl_index); | ||
| 357 | renderDeclHeading(decl_index); | ||
| 300 | renderTopLevelDocs(decl_index); | 358 | renderTopLevelDocs(decl_index); |
| 301 | domSourceText.innerHTML = declSourceHtml(decl_index); | 359 | renderParams(decl_index); |
| 360 | renderDocTests(decl_index); | ||
| 361 | |||
| 362 | const members = unwrapSlice32(wasm_exports.type_fn_members(decl_index, false)).slice(); | ||
| 363 | const fields = unwrapSlice32(wasm_exports.type_fn_fields(decl_index)).slice(); | ||
| 364 | if (members.length !== 0 || fields.length !== 0) { | ||
| 365 | renderNamespace(decl_index, members, fields); | ||
| 366 | } else { | ||
| 367 | domSourceText.innerHTML = declSourceHtml(decl_index); | ||
| 368 | domSectSource.classList.remove("hidden"); | ||
| 369 | } | ||
| 370 | } | ||
| 302 | 371 | ||
| 372 | function renderDocTests(decl_index) { | ||
| 303 | const doctest_html = declDoctestHtml(decl_index); | 373 | const doctest_html = declDoctestHtml(decl_index); |
| 304 | if (doctest_html.length > 0) { | 374 | if (doctest_html.length > 0) { |
| 305 | domDocTestsCode.innerHTML = doctest_html; | 375 | domDocTestsCode.innerHTML = doctest_html; |
| 306 | domSectDocTests.classList.remove("hidden"); | 376 | domSectDocTests.classList.remove("hidden"); |
| 307 | } | 377 | } |
| 378 | } | ||
| 308 | 379 | ||
| 309 | domSectSource.classList.remove("hidden"); | 380 | function renderFunction(decl_index) { |
| 381 | renderNav(decl_index); | ||
| 382 | renderDeclHeading(decl_index); | ||
| 383 | renderTopLevelDocs(decl_index); | ||
| 384 | renderParams(decl_index); | ||
| 385 | renderDocTests(decl_index); | ||
| 386 | |||
| 387 | domFnProtoCode.innerHTML = fnProtoHtml(decl_index, false); | ||
| 310 | domFnProto.classList.remove("hidden"); | 388 | domFnProto.classList.remove("hidden"); |
| 389 | |||
| 390 | |||
| 391 | const errorSetNode = fnErrorSet(decl_index); | ||
| 392 | if (errorSetNode != null) { | ||
| 393 | const base_decl = wasm_exports.fn_error_set_decl(decl_index, errorSetNode); | ||
| 394 | renderErrorSet(base_decl, errorSetNodeList(decl_index, errorSetNode)); | ||
| 395 | } | ||
| 396 | |||
| 397 | domSourceText.innerHTML = declSourceHtml(decl_index); | ||
| 398 | domSectSource.classList.remove("hidden"); | ||
| 311 | } | 399 | } |
| 312 | 400 | ||
| 313 | function renderGlobalConst(decl_index) { | 401 | function renderGlobal(decl_index) { |
| 314 | renderNav(decl_index); | 402 | renderNav(decl_index); |
| 315 | setViewSourceDecl(decl_index); | 403 | renderDeclHeading(decl_index); |
| 316 | 404 | ||
| 317 | const docs_html = declDocsHtmlShort(decl_index); | 405 | const docs_html = declDocsHtmlShort(decl_index); |
| 318 | if (docs_html.length > 0) { | 406 | if (docs_html.length > 0) { |
| ... | @@ -324,171 +412,177 @@ | ... | @@ -324,171 +412,177 @@ |
| 324 | domSectSource.classList.remove("hidden"); | 412 | domSectSource.classList.remove("hidden"); |
| 325 | } | 413 | } |
| 326 | 414 | ||
| 327 | function renderNamespace(decl_index) { | 415 | function renderNamespace(base_decl, members, fields) { |
| 328 | renderNav(decl_index); | 416 | const typesList = []; |
| 329 | renderDeclHeading(decl_index); | 417 | const namespacesList = []; |
| 330 | setViewSourceDecl(decl_index); | 418 | const errSetsList = []; |
| 331 | 419 | const fnsList = []; | |
| 332 | const typesList = []; | 420 | const varsList = []; |
| 333 | const namespacesList = []; | 421 | const valsList = []; |
| 334 | const errSetsList = []; | 422 | |
| 335 | const fnsList = []; | 423 | member_loop: for (let i = 0; i < members.length; i += 1) { |
| 336 | const varsList = []; | 424 | let member = members[i]; |
| 337 | const valsList = []; | 425 | const original = member; |
| 338 | const members = namespaceMembers(decl_index, false); | 426 | while (true) { |
| 339 | 427 | const member_category = wasm_exports.categorize_decl(member, 0); | |
| 340 | member_loop: for (let i = 0; i < members.length; i += 1) { | 428 | switch (member_category) { |
| 341 | let member = members[i]; | 429 | case CAT_namespace: |
| 342 | while (true) { | 430 | if (wasm_exports.decl_field_count(member) > 0) { |
| 343 | const member_category = wasm_exports.categorize_decl(member, 0); | 431 | typesList.push({original: original, member: member}); |
| 344 | switch (member_category) { | 432 | } else { |
| 345 | case CAT_namespace: | 433 | namespacesList.push({original: original, member: member}); |
| 346 | namespacesList.push(member); | 434 | } |
| 347 | continue member_loop; | 435 | continue member_loop; |
| 348 | case CAT_global_variable: | 436 | case CAT_namespace: |
| 349 | varsList.push(member); | 437 | namespacesList.push({original: original, member: member}); |
| 350 | continue member_loop; | 438 | continue member_loop; |
| 351 | case CAT_function: | 439 | case CAT_global_variable: |
| 352 | fnsList.push(member); | 440 | varsList.push(member); |
| 353 | continue member_loop; | 441 | continue member_loop; |
| 354 | case CAT_type: | 442 | case CAT_function: |
| 355 | typesList.push(member); | 443 | fnsList.push(member); |
| 356 | continue member_loop; | 444 | continue member_loop; |
| 357 | case CAT_error_set: | 445 | case CAT_type: |
| 358 | errSetsList.push(member); | 446 | case CAT_type_type: |
| 359 | continue member_loop; | 447 | case CAT_type_function: |
| 360 | case CAT_global_const: | 448 | typesList.push({original: original, member: member}); |
| 361 | case CAT_primitive: | 449 | continue member_loop; |
| 362 | valsList.push(member); | 450 | case CAT_error_set: |
| 363 | continue member_loop; | 451 | errSetsList.push({original: original, member: member}); |
| 364 | case CAT_alias: | 452 | continue member_loop; |
| 365 | // TODO: handle aliasing loop | 453 | case CAT_global_const: |
| 366 | member = wasm_exports.get_aliasee(); | 454 | case CAT_primitive: |
| 367 | continue; | 455 | valsList.push({original: original, member: member}); |
| 368 | default: | 456 | continue member_loop; |
| 369 | throw new Error("uknown category: " + member_category); | 457 | case CAT_alias: |
| 370 | } | 458 | member = wasm_exports.get_aliasee(); |
| 459 | continue; | ||
| 460 | default: | ||
| 461 | throw new Error("uknown category: " + member_category); | ||
| 371 | } | 462 | } |
| 372 | } | 463 | } |
| 464 | } | ||
| 373 | 465 | ||
| 374 | typesList.sort(byDeclIndexName); | 466 | typesList.sort(byDeclIndexName2); |
| 375 | namespacesList.sort(byDeclIndexName); | 467 | namespacesList.sort(byDeclIndexName2); |
| 376 | errSetsList.sort(byDeclIndexName); | 468 | errSetsList.sort(byDeclIndexName2); |
| 377 | fnsList.sort(byDeclIndexName); | 469 | fnsList.sort(byDeclIndexName); |
| 378 | varsList.sort(byDeclIndexName); | 470 | varsList.sort(byDeclIndexName); |
| 379 | valsList.sort(byDeclIndexName); | 471 | valsList.sort(byDeclIndexName2); |
| 380 | 472 | ||
| 381 | if (typesList.length !== 0) { | 473 | if (typesList.length !== 0) { |
| 382 | resizeDomList(domListTypes, typesList.length, '<li><a href="#"></a></li>'); | 474 | resizeDomList(domListTypes, typesList.length, '<li><a href="#"></a></li>'); |
| 383 | for (let i = 0; i < typesList.length; i += 1) { | 475 | for (let i = 0; i < typesList.length; i += 1) { |
| 384 | const liDom = domListTypes.children[i]; | 476 | const liDom = domListTypes.children[i]; |
| 385 | const aDom = liDom.children[0]; | 477 | const aDom = liDom.children[0]; |
| 386 | const decl = typesList[i]; | 478 | const original_decl = typesList[i].original; |
| 387 | aDom.textContent = declIndexName(decl); | 479 | const decl = typesList[i].member; |
| 388 | aDom.setAttribute('href', navLinkDeclIndex(decl)); | 480 | aDom.textContent = declIndexName(original_decl); |
| 389 | } | 481 | aDom.setAttribute('href', navLinkDeclIndex(decl)); |
| 390 | domSectTypes.classList.remove("hidden"); | 482 | } |
| 391 | } | 483 | domSectTypes.classList.remove("hidden"); |
| 392 | if (namespacesList.length !== 0) { | 484 | } |
| 393 | resizeDomList(domListNamespaces, namespacesList.length, '<li><a href="#"></a></li>'); | 485 | if (namespacesList.length !== 0) { |
| 394 | for (let i = 0; i < namespacesList.length; i += 1) { | 486 | resizeDomList(domListNamespaces, namespacesList.length, '<li><a href="#"></a></li>'); |
| 395 | const liDom = domListNamespaces.children[i]; | 487 | for (let i = 0; i < namespacesList.length; i += 1) { |
| 396 | const aDom = liDom.children[0]; | 488 | const liDom = domListNamespaces.children[i]; |
| 397 | const decl = namespacesList[i]; | 489 | const aDom = liDom.children[0]; |
| 398 | aDom.textContent = declIndexName(decl); | 490 | const original_decl = namespacesList[i].original; |
| 399 | aDom.setAttribute('href', navLinkDeclIndex(decl)); | 491 | const decl = namespacesList[i].member; |
| 400 | } | 492 | aDom.textContent = declIndexName(original_decl); |
| 401 | domSectNamespaces.classList.remove("hidden"); | 493 | aDom.setAttribute('href', navLinkDeclIndex(decl)); |
| 402 | } | 494 | } |
| 403 | 495 | domSectNamespaces.classList.remove("hidden"); | |
| 404 | if (errSetsList.length !== 0) { | 496 | } |
| 405 | resizeDomList(domListErrSets, errSetsList.length, '<li><a href="#"></a></li>'); | ||
| 406 | for (let i = 0; i < errSetsList.length; i += 1) { | ||
| 407 | const liDom = domListErrSets.children[i]; | ||
| 408 | const aDom = liDom.children[0]; | ||
| 409 | const decl = errSetsList[i]; | ||
| 410 | aDom.textContent = declIndexName(decl); | ||
| 411 | aDom.setAttribute('href', navLinkDeclIndex(decl)); | ||
| 412 | } | ||
| 413 | domSectErrSets.classList.remove("hidden"); | ||
| 414 | } | ||
| 415 | |||
| 416 | if (fnsList.length !== 0) { | ||
| 417 | resizeDomList(domListFns, fnsList.length, | ||
| 418 | '<div><dt><a href="#"></a></dt><dd></dd><details><summary>source</summary><pre><code></code></pre></details></div>'); | ||
| 419 | for (let i = 0; i < fnsList.length; i += 1) { | ||
| 420 | const decl = fnsList[i]; | ||
| 421 | const divDom = domListFns.children[i]; | ||
| 422 | |||
| 423 | const dtName = divDom.children[0]; | ||
| 424 | const ddDocs = divDom.children[1]; | ||
| 425 | const codeDom = divDom.children[2].children[1].children[0]; | ||
| 426 | |||
| 427 | const nameLinkDom = dtName.children[0]; | ||
| 428 | const expandSourceDom = dtName.children[1]; | ||
| 429 | |||
| 430 | nameLinkDom.setAttribute('href', navLinkDeclIndex(decl)); | ||
| 431 | nameLinkDom.textContent = declIndexName(decl); | ||
| 432 | 497 | ||
| 433 | ddDocs.innerHTML = declDocsHtmlShort(decl); | 498 | if (errSetsList.length !== 0) { |
| 499 | resizeDomList(domListErrSets, errSetsList.length, '<li><a href="#"></a></li>'); | ||
| 500 | for (let i = 0; i < errSetsList.length; i += 1) { | ||
| 501 | const liDom = domListErrSets.children[i]; | ||
| 502 | const aDom = liDom.children[0]; | ||
| 503 | const original_decl = errSetsList[i].original; | ||
| 504 | const decl = errSetsList[i].member; | ||
| 505 | aDom.textContent = declIndexName(original_decl); | ||
| 506 | aDom.setAttribute('href', navLinkDeclIndex(decl)); | ||
| 507 | } | ||
| 508 | domSectErrSets.classList.remove("hidden"); | ||
| 509 | } | ||
| 434 | 510 | ||
| 435 | codeDom.innerHTML = declSourceHtml(decl); | 511 | if (fnsList.length !== 0) { |
| 436 | } | 512 | resizeDomList(domListFns, fnsList.length, |
| 437 | domSectFns.classList.remove("hidden"); | 513 | '<div><dt><code></code></dt><dd></dd></div>'); |
| 438 | } | 514 | for (let i = 0; i < fnsList.length; i += 1) { |
| 515 | const decl = fnsList[i]; | ||
| 516 | const divDom = domListFns.children[i]; | ||
| 439 | 517 | ||
| 440 | // Prevent fields from being emptied next time wasm calls memory.grow. | 518 | const dtDom = divDom.children[0]; |
| 441 | const fields = declFields(decl_index).slice(); | 519 | const ddDocs = divDom.children[1]; |
| 442 | if (fields.length !== 0) { | 520 | const protoCodeDom = dtDom.children[0]; |
| 443 | resizeDomList(domListFields, fields.length, '<div></div>'); | ||
| 444 | for (let i = 0; i < fields.length; i += 1) { | ||
| 445 | const divDom = domListFields.children[i]; | ||
| 446 | divDom.innerHTML = unwrapString(wasm_exports.decl_field_html(decl_index, fields[i])); | ||
| 447 | } | ||
| 448 | domSectFields.classList.remove("hidden"); | ||
| 449 | } | ||
| 450 | 521 | ||
| 451 | if (varsList.length !== 0) { | 522 | protoCodeDom.innerHTML = fnProtoHtml(decl, true); |
| 452 | resizeDomList(domListGlobalVars, varsList.length, | 523 | ddDocs.innerHTML = declDocsHtmlShort(decl); |
| 453 | '<tr><td><a href="#"></a></td><td></td><td></td></tr>'); | 524 | } |
| 454 | for (let i = 0; i < varsList.length; i += 1) { | 525 | domSectFns.classList.remove("hidden"); |
| 455 | const decl = varsList[i]; | 526 | } |
| 456 | const trDom = domListGlobalVars.children[i]; | ||
| 457 | 527 | ||
| 458 | const tdName = trDom.children[0]; | 528 | if (fields.length !== 0) { |
| 459 | const tdNameA = tdName.children[0]; | 529 | resizeDomList(domListFields, fields.length, '<div></div>'); |
| 460 | const tdType = trDom.children[1]; | 530 | for (let i = 0; i < fields.length; i += 1) { |
| 461 | const tdDesc = trDom.children[2]; | 531 | const divDom = domListFields.children[i]; |
| 532 | divDom.innerHTML = unwrapString(wasm_exports.decl_field_html(base_decl, fields[i])); | ||
| 533 | } | ||
| 534 | domSectFields.classList.remove("hidden"); | ||
| 535 | } | ||
| 462 | 536 | ||
| 463 | tdNameA.setAttribute('href', navLinkDeclIndex(decl)); | 537 | if (varsList.length !== 0) { |
| 464 | tdNameA.textContent = declIndexName(decl); | 538 | resizeDomList(domListGlobalVars, varsList.length, |
| 539 | '<tr><td><a href="#"></a></td><td></td><td></td></tr>'); | ||
| 540 | for (let i = 0; i < varsList.length; i += 1) { | ||
| 541 | const decl = varsList[i]; | ||
| 542 | const trDom = domListGlobalVars.children[i]; | ||
| 465 | 543 | ||
| 466 | tdType.innerHTML = declTypeHtml(decl); | 544 | const tdName = trDom.children[0]; |
| 467 | tdDesc.innerHTML = declDocsHtmlShort(decl); | 545 | const tdNameA = tdName.children[0]; |
| 468 | } | 546 | const tdType = trDom.children[1]; |
| 469 | domSectGlobalVars.classList.remove("hidden"); | 547 | const tdDesc = trDom.children[2]; |
| 470 | } | ||
| 471 | 548 | ||
| 472 | if (valsList.length !== 0) { | 549 | tdNameA.setAttribute('href', navLinkDeclIndex(decl)); |
| 473 | resizeDomList(domListValues, valsList.length, | 550 | tdNameA.textContent = declIndexName(decl); |
| 474 | '<tr><td><a href="#"></a></td><td></td><td></td></tr>'); | ||
| 475 | for (let i = 0; i < valsList.length; i += 1) { | ||
| 476 | const decl = valsList[i]; | ||
| 477 | const trDom = domListValues.children[i]; | ||
| 478 | 551 | ||
| 479 | const tdName = trDom.children[0]; | 552 | tdType.innerHTML = declTypeHtml(decl); |
| 480 | const tdNameA = tdName.children[0]; | 553 | tdDesc.innerHTML = declDocsHtmlShort(decl); |
| 481 | const tdType = trDom.children[1]; | 554 | } |
| 482 | const tdDesc = trDom.children[2]; | 555 | domSectGlobalVars.classList.remove("hidden"); |
| 556 | } | ||
| 483 | 557 | ||
| 484 | tdNameA.setAttribute('href', navLinkDeclIndex(decl)); | 558 | if (valsList.length !== 0) { |
| 485 | tdNameA.textContent = declIndexName(decl); | 559 | resizeDomList(domListValues, valsList.length, |
| 560 | '<tr><td><a href="#"></a></td><td></td><td></td></tr>'); | ||
| 561 | for (let i = 0; i < valsList.length; i += 1) { | ||
| 562 | const trDom = domListValues.children[i]; | ||
| 563 | const tdName = trDom.children[0]; | ||
| 564 | const tdNameA = tdName.children[0]; | ||
| 565 | const tdType = trDom.children[1]; | ||
| 566 | const tdDesc = trDom.children[2]; | ||
| 567 | |||
| 568 | const original_decl = valsList[i].original; | ||
| 569 | const decl = valsList[i].member; | ||
| 570 | tdNameA.setAttribute('href', navLinkDeclIndex(decl)); | ||
| 571 | tdNameA.textContent = declIndexName(original_decl); | ||
| 572 | |||
| 573 | tdType.innerHTML = declTypeHtml(decl); | ||
| 574 | tdDesc.innerHTML = declDocsHtmlShort(decl); | ||
| 575 | } | ||
| 576 | domSectValues.classList.remove("hidden"); | ||
| 577 | } | ||
| 578 | } | ||
| 486 | 579 | ||
| 487 | tdType.innerHTML = declTypeHtml(decl); | 580 | function renderNamespacePage(decl_index) { |
| 488 | tdDesc.innerHTML = declDocsHtmlShort(decl); | 581 | renderNav(decl_index); |
| 489 | } | 582 | renderDeclHeading(decl_index); |
| 490 | domSectValues.classList.remove("hidden"); | 583 | const members = namespaceMembers(decl_index, false).slice(); |
| 491 | } | 584 | const fields = declFields(decl_index).slice(); |
| 585 | renderNamespace(decl_index, members, fields); | ||
| 492 | } | 586 | } |
| 493 | 587 | ||
| 494 | function operatorCompare(a, b) { | 588 | function operatorCompare(a, b) { |
| ... | @@ -508,7 +602,7 @@ | ... | @@ -508,7 +602,7 @@ |
| 508 | curNav.viewSourceHash = null; | 602 | curNav.viewSourceHash = null; |
| 509 | curNavSearch = ""; | 603 | curNavSearch = ""; |
| 510 | 604 | ||
| 511 | if (location_hash[0] === '#' && location_hash.length > 1) { | 605 | if (location_hash.length > 1 && location_hash[0] === '#') { |
| 512 | const query = location_hash.substring(1); | 606 | const query = location_hash.substring(1); |
| 513 | const qpos = query.indexOf("?"); | 607 | const qpos = query.indexOf("?"); |
| 514 | let nonSearchPart; | 608 | let nonSearchPart; |
| ... | @@ -532,8 +626,14 @@ | ... | @@ -532,8 +626,14 @@ |
| 532 | } | 626 | } |
| 533 | } | 627 | } |
| 534 | 628 | ||
| 535 | function onHashChange() { | 629 | function onHashChange(state) { |
| 630 | history.replaceState({}, ""); | ||
| 536 | navigate(location.hash); | 631 | navigate(location.hash); |
| 632 | if (state == null) window.scrollTo({top: 0}); | ||
| 633 | } | ||
| 634 | |||
| 635 | function onPopState(ev) { | ||
| 636 | onHashChange(ev.state); | ||
| 537 | } | 637 | } |
| 538 | 638 | ||
| 539 | function navigate(location_hash) { | 639 | function navigate(location_hash) { |
| ... | @@ -686,13 +786,19 @@ | ... | @@ -686,13 +786,19 @@ |
| 686 | 786 | ||
| 687 | function startAsyncSearch() { | 787 | function startAsyncSearch() { |
| 688 | clearAsyncSearch(); | 788 | clearAsyncSearch(); |
| 689 | searchTimer = setTimeout(startSearch, 100); | 789 | searchTimer = setTimeout(startSearch, 10); |
| 690 | } | 790 | } |
| 691 | function computeSearchHash() { | 791 | function computeSearchHash() { |
| 692 | const oldHash = location.hash; | 792 | // How location.hash works: |
| 793 | // 1. http://example.com/ => "" | ||
| 794 | // 2. http://example.com/# => "" | ||
| 795 | // 3. http://example.com/#foo => "#foo" | ||
| 796 | // wat | ||
| 797 | const oldWatHash = location.hash; | ||
| 798 | const oldHash = oldWatHash.startsWith("#") ? oldWatHash : "#" + oldWatHash; | ||
| 693 | const parts = oldHash.split("?"); | 799 | const parts = oldHash.split("?"); |
| 694 | const newPart2 = (domSearch.value === "") ? "" : ("?" + domSearch.value); | 800 | const newPart2 = (domSearch.value === "") ? "" : ("?" + domSearch.value); |
| 695 | return (parts.length === 1) ? (oldHash + newPart2) : ("#" + parts[0] + newPart2); | 801 | return parts[0] + newPart2; |
| 696 | } | 802 | } |
| 697 | function startSearch() { | 803 | function startSearch() { |
| 698 | clearAsyncSearch(); | 804 | clearAsyncSearch(); |
| ... | @@ -734,12 +840,12 @@ | ... | @@ -734,12 +840,12 @@ |
| 734 | } | 840 | } |
| 735 | } | 841 | } |
| 736 | 842 | ||
| 737 | function updatePackageList() { | 843 | function updateModuleList() { |
| 738 | packageList.length = 0; | 844 | moduleList.length = 0; |
| 739 | for (let i = 0;; i += 1) { | 845 | for (let i = 0;; i += 1) { |
| 740 | const name = unwrapString(wasm_exports.package_name(i)); | 846 | const name = unwrapString(wasm_exports.module_name(i)); |
| 741 | if (name.length == 0) break; | 847 | if (name.length == 0) break; |
| 742 | packageList.push(name); | 848 | moduleList.push(name); |
| 743 | } | 849 | } |
| 744 | } | 850 | } |
| 745 | 851 | ||
| ... | @@ -749,6 +855,12 @@ | ... | @@ -749,6 +855,12 @@ |
| 749 | return operatorCompare(a_name, b_name); | 855 | return operatorCompare(a_name, b_name); |
| 750 | } | 856 | } |
| 751 | 857 | ||
| 858 | function byDeclIndexName2(a, b) { | ||
| 859 | const a_name = declIndexName(a.original); | ||
| 860 | const b_name = declIndexName(b.original); | ||
| 861 | return operatorCompare(a_name, b_name); | ||
| 862 | } | ||
| 863 | |||
| 752 | function decodeString(ptr, len) { | 864 | function decodeString(ptr, len) { |
| 753 | if (len === 0) return ""; | 865 | if (len === 0) return ""; |
| 754 | return text_decoder.decode(new Uint8Array(wasm_exports.memory.buffer, ptr, len)); | 866 | return text_decoder.decode(new Uint8Array(wasm_exports.memory.buffer, ptr, len)); |
| ... | @@ -784,8 +896,8 @@ | ... | @@ -784,8 +896,8 @@ |
| 784 | return unwrapString(wasm_exports.decl_doctest_html(decl_index)); | 896 | return unwrapString(wasm_exports.decl_doctest_html(decl_index)); |
| 785 | } | 897 | } |
| 786 | 898 | ||
| 787 | function fnProtoHtml(decl_index) { | 899 | function fnProtoHtml(decl_index, linkify_fn_name) { |
| 788 | return unwrapString(wasm_exports.decl_fn_proto_html(decl_index)); | 900 | return unwrapString(wasm_exports.decl_fn_proto_html(decl_index, linkify_fn_name)); |
| 789 | } | 901 | } |
| 790 | 902 | ||
| 791 | function setQueryString(s) { | 903 | function setQueryString(s) { |
| ... | @@ -805,19 +917,37 @@ | ... | @@ -805,19 +917,37 @@ |
| 805 | } | 917 | } |
| 806 | 918 | ||
| 807 | function namespaceMembers(decl_index, include_private) { | 919 | function namespaceMembers(decl_index, include_private) { |
| 808 | const bigint = wasm_exports.namespace_members(decl_index, include_private); | 920 | return unwrapSlice32(wasm_exports.namespace_members(decl_index, include_private)); |
| 921 | } | ||
| 922 | |||
| 923 | function declFields(decl_index) { | ||
| 924 | return unwrapSlice32(wasm_exports.decl_fields(decl_index)); | ||
| 925 | } | ||
| 926 | |||
| 927 | function declParams(decl_index) { | ||
| 928 | return unwrapSlice32(wasm_exports.decl_params(decl_index)); | ||
| 929 | } | ||
| 930 | |||
| 931 | function declErrorSet(decl_index) { | ||
| 932 | return unwrapSlice64(wasm_exports.decl_error_set(decl_index)); | ||
| 933 | } | ||
| 934 | |||
| 935 | function errorSetNodeList(base_decl, err_set_node) { | ||
| 936 | return unwrapSlice64(wasm_exports.error_set_node_list(base_decl, err_set_node)); | ||
| 937 | } | ||
| 938 | |||
| 939 | function unwrapSlice32(bigint) { | ||
| 809 | const ptr = Number(bigint & 0xffffffffn); | 940 | const ptr = Number(bigint & 0xffffffffn); |
| 810 | const len = Number(bigint >> 32n); | 941 | const len = Number(bigint >> 32n); |
| 811 | if (len == 0) return []; | 942 | if (len === 0) return []; |
| 812 | return new Uint32Array(wasm_exports.memory.buffer, ptr, len); | 943 | return new Uint32Array(wasm_exports.memory.buffer, ptr, len); |
| 813 | } | 944 | } |
| 814 | 945 | ||
| 815 | function declFields(decl_index) { | 946 | function unwrapSlice64(bigint) { |
| 816 | const bigint = wasm_exports.decl_fields(decl_index); | ||
| 817 | const ptr = Number(bigint & 0xffffffffn); | 947 | const ptr = Number(bigint & 0xffffffffn); |
| 818 | const len = Number(bigint >> 32n); | 948 | const len = Number(bigint >> 32n); |
| 819 | if (len === 0) return []; | 949 | if (len === 0) return []; |
| 820 | return new Uint32Array(wasm_exports.memory.buffer, ptr, len); | 950 | return new BigUint64Array(wasm_exports.memory.buffer, ptr, len); |
| 821 | } | 951 | } |
| 822 | 952 | ||
| 823 | function findDecl(fqn) { | 953 | function findDecl(fqn) { |
| ... | @@ -840,6 +970,12 @@ | ... | @@ -840,6 +970,12 @@ |
| 840 | return result; | 970 | return result; |
| 841 | } | 971 | } |
| 842 | 972 | ||
| 973 | function fnErrorSet(decl_index) { | ||
| 974 | const result = wasm_exports.fn_error_set(decl_index); | ||
| 975 | if (result === 0) return null; | ||
| 976 | return result; | ||
| 977 | } | ||
| 978 | |||
| 843 | function setInputString(s) { | 979 | function setInputString(s) { |
| 844 | const jsArray = text_encoder.encode(s); | 980 | const jsArray = text_encoder.encode(s); |
| 845 | const len = jsArray.length; | 981 | const len = jsArray.length; |
lib/docs/wasm/Decl.zig+50-4| ... | @@ -111,8 +111,35 @@ pub fn categorize(decl: *const Decl) Walk.Category { | ... | @@ -111,8 +111,35 @@ pub fn categorize(decl: *const Decl) Walk.Category { |
| 111 | return decl.file.categorize_decl(decl.ast_node); | 111 | return decl.file.categorize_decl(decl.ast_node); |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | /// Looks up a direct child of `decl` by name. | ||
| 115 | pub fn get_child(decl: *const Decl, name: []const u8) ?Decl.Index { | ||
| 116 | switch (decl.categorize()) { | ||
| 117 | .alias => |aliasee| return aliasee.get().get_child(name), | ||
| 118 | .namespace => |node| { | ||
| 119 | const file = decl.file.get(); | ||
| 120 | const scope = file.scopes.get(node) orelse return null; | ||
| 121 | const child_node = scope.get_child(name) orelse return null; | ||
| 122 | return file.node_decls.get(child_node); | ||
| 123 | }, | ||
| 124 | else => return null, | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Looks up a decl by name accessible in `decl`'s namespace. | ||
| 129 | pub fn lookup(decl: *const Decl, name: []const u8) ?Decl.Index { | ||
| 130 | const namespace_node = switch (decl.categorize()) { | ||
| 131 | .namespace => |node| node, | ||
| 132 | else => decl.parent.get().ast_node, | ||
| 133 | }; | ||
| 134 | const file = decl.file.get(); | ||
| 135 | const scope = file.scopes.get(namespace_node) orelse return null; | ||
| 136 | const resolved_node = scope.lookup(&file.ast, name) orelse return null; | ||
| 137 | return file.node_decls.get(resolved_node); | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Appends the fully qualified name to `out`. | ||
| 114 | pub fn fqn(decl: *const Decl, out: *std.ArrayListUnmanaged(u8)) Oom!void { | 141 | pub fn fqn(decl: *const Decl, out: *std.ArrayListUnmanaged(u8)) Oom!void { |
| 115 | try decl.reset_with_path(out); | 142 | try decl.append_path(out); |
| 116 | if (decl.parent != .none) { | 143 | if (decl.parent != .none) { |
| 117 | try append_parent_ns(out, decl.parent); | 144 | try append_parent_ns(out, decl.parent); |
| 118 | try out.appendSlice(gpa, decl.extra_info().name); | 145 | try out.appendSlice(gpa, decl.extra_info().name); |
| ... | @@ -123,9 +150,13 @@ pub fn fqn(decl: *const Decl, out: *std.ArrayListUnmanaged(u8)) Oom!void { | ... | @@ -123,9 +150,13 @@ pub fn fqn(decl: *const Decl, out: *std.ArrayListUnmanaged(u8)) Oom!void { |
| 123 | 150 | ||
| 124 | pub fn reset_with_path(decl: *const Decl, list: *std.ArrayListUnmanaged(u8)) Oom!void { | 151 | pub fn reset_with_path(decl: *const Decl, list: *std.ArrayListUnmanaged(u8)) Oom!void { |
| 125 | list.clearRetainingCapacity(); | 152 | list.clearRetainingCapacity(); |
| 153 | try append_path(decl, list); | ||
| 154 | } | ||
| 126 | 155 | ||
| 127 | // Prefer the package name alias. | 156 | pub fn append_path(decl: *const Decl, list: *std.ArrayListUnmanaged(u8)) Oom!void { |
| 128 | for (Walk.packages.keys(), Walk.packages.values()) |pkg_name, pkg_file| { | 157 | const start = list.items.len; |
| 158 | // Prefer the module name alias. | ||
| 159 | for (Walk.modules.keys(), Walk.modules.values()) |pkg_name, pkg_file| { | ||
| 129 | if (pkg_file == decl.file) { | 160 | if (pkg_file == decl.file) { |
| 130 | try list.ensureUnusedCapacity(gpa, pkg_name.len + 1); | 161 | try list.ensureUnusedCapacity(gpa, pkg_name.len + 1); |
| 131 | list.appendSliceAssumeCapacity(pkg_name); | 162 | list.appendSliceAssumeCapacity(pkg_name); |
| ... | @@ -137,7 +168,7 @@ pub fn reset_with_path(decl: *const Decl, list: *std.ArrayListUnmanaged(u8)) Oom | ... | @@ -137,7 +168,7 @@ pub fn reset_with_path(decl: *const Decl, list: *std.ArrayListUnmanaged(u8)) Oom |
| 137 | const file_path = decl.file.path(); | 168 | const file_path = decl.file.path(); |
| 138 | try list.ensureUnusedCapacity(gpa, file_path.len + 1); | 169 | try list.ensureUnusedCapacity(gpa, file_path.len + 1); |
| 139 | list.appendSliceAssumeCapacity(file_path); | 170 | list.appendSliceAssumeCapacity(file_path); |
| 140 | for (list.items) |*byte| switch (byte.*) { | 171 | for (list.items[start..]) |*byte| switch (byte.*) { |
| 141 | '/' => byte.* = '.', | 172 | '/' => byte.* = '.', |
| 142 | else => continue, | 173 | else => continue, |
| 143 | }; | 174 | }; |
| ... | @@ -170,6 +201,21 @@ pub fn findFirstDocComment(ast: *const Ast, token: Ast.TokenIndex) Ast.TokenInde | ... | @@ -170,6 +201,21 @@ pub fn findFirstDocComment(ast: *const Ast, token: Ast.TokenIndex) Ast.TokenInde |
| 170 | return it; | 201 | return it; |
| 171 | } | 202 | } |
| 172 | 203 | ||
| 204 | /// Successively looks up each component. | ||
| 205 | pub fn find(search_string: []const u8) Decl.Index { | ||
| 206 | var path_components = std.mem.splitScalar(u8, search_string, '.'); | ||
| 207 | const file = Walk.modules.get(path_components.first()) orelse return .none; | ||
| 208 | var current_decl_index = file.findRootDecl(); | ||
| 209 | while (path_components.next()) |component| { | ||
| 210 | while (true) switch (current_decl_index.get().categorize()) { | ||
| 211 | .alias => |aliasee| current_decl_index = aliasee, | ||
| 212 | else => break, | ||
| 213 | }; | ||
| 214 | current_decl_index = current_decl_index.get().get_child(component) orelse return .none; | ||
| 215 | } | ||
| 216 | return current_decl_index; | ||
| 217 | } | ||
| 218 | |||
| 173 | const Decl = @This(); | 219 | const Decl = @This(); |
| 174 | const std = @import("std"); | 220 | const std = @import("std"); |
| 175 | const Ast = std.zig.Ast; | 221 | const Ast = std.zig.Ast; |
lib/docs/wasm/Walk.zig+225-31| ... | @@ -1,21 +1,26 @@ | ... | @@ -1,21 +1,26 @@ |
| 1 | //! Find and annotate identifiers with links to their declarations. | 1 | //! Find and annotate identifiers with links to their declarations. |
| 2 | pub var files: std.StringArrayHashMapUnmanaged(File) = .{}; | 2 | pub var files: std.StringArrayHashMapUnmanaged(File) = .{}; |
| 3 | pub var decls: std.ArrayListUnmanaged(Decl) = .{}; | 3 | pub var decls: std.ArrayListUnmanaged(Decl) = .{}; |
| 4 | pub var packages: std.StringArrayHashMapUnmanaged(File.Index) = .{}; | 4 | pub var modules: std.StringArrayHashMapUnmanaged(File.Index) = .{}; |
| 5 | 5 | ||
| 6 | arena: std.mem.Allocator, | ||
| 7 | file: File.Index, | 6 | file: File.Index, |
| 8 | 7 | ||
| 9 | /// keep in sync with "CAT_" constants in main.js | 8 | /// keep in sync with "CAT_" constants in main.js |
| 10 | pub const Category = union(enum(u8)) { | 9 | pub const Category = union(enum(u8)) { |
| 11 | namespace: Ast.Node.Index, | 10 | namespace: Ast.Node.Index, |
| 12 | global_variable: Ast.Node.Index, | 11 | global_variable: Ast.Node.Index, |
| 12 | /// A function that has not been detected as returning a type. | ||
| 13 | function: Ast.Node.Index, | 13 | function: Ast.Node.Index, |
| 14 | primitive: Ast.Node.Index, | 14 | primitive: Ast.Node.Index, |
| 15 | error_set: Ast.Node.Index, | 15 | error_set: Ast.Node.Index, |
| 16 | global_const: Ast.Node.Index, | 16 | global_const: Ast.Node.Index, |
| 17 | alias: Decl.Index, | 17 | alias: Decl.Index, |
| 18 | /// A primitive identifier that is also a type. | ||
| 18 | type, | 19 | type, |
| 20 | /// Specifically it is the literal `type`. | ||
| 21 | type_type, | ||
| 22 | /// A function that returns a type. | ||
| 23 | type_function: Ast.Node.Index, | ||
| 19 | 24 | ||
| 20 | pub const Tag = @typeInfo(Category).Union.tag_type.?; | 25 | pub const Tag = @typeInfo(Category).Union.tag_type.?; |
| 21 | }; | 26 | }; |
| ... | @@ -30,12 +35,23 @@ pub const File = struct { | ... | @@ -30,12 +35,23 @@ pub const File = struct { |
| 30 | node_decls: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, Decl.Index) = .{}, | 35 | node_decls: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, Decl.Index) = .{}, |
| 31 | /// Maps function declarations to doctests. | 36 | /// Maps function declarations to doctests. |
| 32 | doctests: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, Ast.Node.Index) = .{}, | 37 | doctests: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, Ast.Node.Index) = .{}, |
| 38 | /// root node => its namespace scope | ||
| 39 | /// struct/union/enum/opaque decl node => its namespace scope | ||
| 40 | /// local var decl node => its local variable scope | ||
| 41 | scopes: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, *Scope) = .{}, | ||
| 33 | 42 | ||
| 34 | pub fn lookup_token(file: *File, token: Ast.TokenIndex) Decl.Index { | 43 | pub fn lookup_token(file: *File, token: Ast.TokenIndex) Decl.Index { |
| 35 | const decl_node = file.ident_decls.get(token) orelse return .none; | 44 | const decl_node = file.ident_decls.get(token) orelse return .none; |
| 36 | return file.node_decls.get(decl_node) orelse return .none; | 45 | return file.node_decls.get(decl_node) orelse return .none; |
| 37 | } | 46 | } |
| 38 | 47 | ||
| 48 | pub fn field_count(file: *const File, node: Ast.Node.Index) u32 { | ||
| 49 | const scope = file.scopes.get(node) orelse return 0; | ||
| 50 | if (scope.tag != .namespace) return 0; | ||
| 51 | const namespace = @fieldParentPtr(Scope.Namespace, "base", scope); | ||
| 52 | return namespace.field_count; | ||
| 53 | } | ||
| 54 | |||
| 39 | pub const Index = enum(u32) { | 55 | pub const Index = enum(u32) { |
| 40 | _, | 56 | _, |
| 41 | 57 | ||
| ... | @@ -90,16 +106,41 @@ pub const File = struct { | ... | @@ -90,16 +106,41 @@ pub const File = struct { |
| 90 | .fn_proto_one, | 106 | .fn_proto_one, |
| 91 | .fn_proto_simple, | 107 | .fn_proto_simple, |
| 92 | .fn_decl, | 108 | .fn_decl, |
| 93 | => return .{ .function = node }, | 109 | => { |
| 110 | var buf: [1]Ast.Node.Index = undefined; | ||
| 111 | const full = ast.fullFnProto(&buf, node).?; | ||
| 112 | return categorize_func(file_index, node, full); | ||
| 113 | }, | ||
| 94 | 114 | ||
| 95 | else => unreachable, | 115 | else => unreachable, |
| 96 | } | 116 | } |
| 97 | } | 117 | } |
| 98 | 118 | ||
| 119 | pub fn categorize_func( | ||
| 120 | file_index: File.Index, | ||
| 121 | node: Ast.Node.Index, | ||
| 122 | full: Ast.full.FnProto, | ||
| 123 | ) Category { | ||
| 124 | return switch (categorize_expr(file_index, full.ast.return_type)) { | ||
| 125 | .namespace, .error_set, .type_type => .{ .type_function = node }, | ||
| 126 | else => .{ .function = node }, | ||
| 127 | }; | ||
| 128 | } | ||
| 129 | |||
| 130 | pub fn categorize_expr_deep(file_index: File.Index, node: Ast.Node.Index) Category { | ||
| 131 | return switch (categorize_expr(file_index, node)) { | ||
| 132 | .alias => |aliasee| aliasee.get().categorize(), | ||
| 133 | else => |result| result, | ||
| 134 | }; | ||
| 135 | } | ||
| 136 | |||
| 99 | pub fn categorize_expr(file_index: File.Index, node: Ast.Node.Index) Category { | 137 | pub fn categorize_expr(file_index: File.Index, node: Ast.Node.Index) Category { |
| 138 | const file = file_index.get(); | ||
| 100 | const ast = file_index.get_ast(); | 139 | const ast = file_index.get_ast(); |
| 101 | const node_tags = ast.nodes.items(.tag); | 140 | const node_tags = ast.nodes.items(.tag); |
| 102 | const node_datas = ast.nodes.items(.data); | 141 | const node_datas = ast.nodes.items(.data); |
| 142 | const main_tokens = ast.nodes.items(.main_token); | ||
| 143 | //log.debug("categorize_expr tag {s}", .{@tagName(node_tags[node])}); | ||
| 103 | return switch (node_tags[node]) { | 144 | return switch (node_tags[node]) { |
| 104 | .container_decl, | 145 | .container_decl, |
| 105 | .container_decl_trailing, | 146 | .container_decl_trailing, |
| ... | @@ -116,24 +157,43 @@ pub const File = struct { | ... | @@ -116,24 +157,43 @@ pub const File = struct { |
| 116 | => .{ .namespace = node }, | 157 | => .{ .namespace = node }, |
| 117 | 158 | ||
| 118 | .error_set_decl, | 159 | .error_set_decl, |
| 160 | .merge_error_sets, | ||
| 119 | => .{ .error_set = node }, | 161 | => .{ .error_set = node }, |
| 120 | 162 | ||
| 121 | .identifier => { | 163 | .identifier => { |
| 122 | const name_token = ast.nodes.items(.main_token)[node]; | 164 | const name_token = ast.nodes.items(.main_token)[node]; |
| 123 | const ident_name = ast.tokenSlice(name_token); | 165 | const ident_name = ast.tokenSlice(name_token); |
| 124 | if (std.zig.primitives.isPrimitive(ident_name)) { | 166 | if (std.mem.eql(u8, ident_name, "type")) |
| 167 | return .type_type; | ||
| 168 | |||
| 169 | if (isPrimitiveNonType(ident_name)) | ||
| 125 | return .{ .primitive = node }; | 170 | return .{ .primitive = node }; |
| 126 | } | ||
| 127 | 171 | ||
| 128 | const decl_index = file_index.get().lookup_token(name_token); | 172 | if (std.zig.primitives.isPrimitive(ident_name)) |
| 129 | if (decl_index != .none) return .{ .alias = decl_index }; | 173 | return .type; |
| 174 | |||
| 175 | if (file.ident_decls.get(name_token)) |decl_node| { | ||
| 176 | const decl_index = file.node_decls.get(decl_node) orelse .none; | ||
| 177 | if (decl_index != .none) return .{ .alias = decl_index }; | ||
| 178 | return categorize_decl(file_index, decl_node); | ||
| 179 | } | ||
| 130 | 180 | ||
| 131 | return .{ .global_const = node }; | 181 | return .{ .global_const = node }; |
| 132 | }, | 182 | }, |
| 133 | 183 | ||
| 134 | .field_access => { | 184 | .field_access => { |
| 135 | // TODO: | 185 | const object_node = node_datas[node].lhs; |
| 136 | //return .alias; | 186 | const dot_token = main_tokens[node]; |
| 187 | const field_ident = dot_token + 1; | ||
| 188 | const field_name = ast.tokenSlice(field_ident); | ||
| 189 | |||
| 190 | switch (categorize_expr(file_index, object_node)) { | ||
| 191 | .alias => |aliasee| if (aliasee.get().get_child(field_name)) |decl_index| { | ||
| 192 | return .{ .alias = decl_index }; | ||
| 193 | }, | ||
| 194 | else => {}, | ||
| 195 | } | ||
| 196 | |||
| 137 | return .{ .global_const = node }; | 197 | return .{ .global_const = node }; |
| 138 | }, | 198 | }, |
| 139 | 199 | ||
| ... | @@ -154,10 +214,77 @@ pub const File = struct { | ... | @@ -154,10 +214,77 @@ pub const File = struct { |
| 154 | return categorize_builtin_call(file_index, node, params); | 214 | return categorize_builtin_call(file_index, node, params); |
| 155 | }, | 215 | }, |
| 156 | 216 | ||
| 217 | .call_one, | ||
| 218 | .call_one_comma, | ||
| 219 | .async_call_one, | ||
| 220 | .async_call_one_comma, | ||
| 221 | .call, | ||
| 222 | .call_comma, | ||
| 223 | .async_call, | ||
| 224 | .async_call_comma, | ||
| 225 | => { | ||
| 226 | var buf: [1]Ast.Node.Index = undefined; | ||
| 227 | return categorize_call(file_index, node, ast.fullCall(&buf, node).?); | ||
| 228 | }, | ||
| 229 | |||
| 230 | .if_simple, | ||
| 231 | .@"if", | ||
| 232 | => { | ||
| 233 | const if_full = ast.fullIf(node).?; | ||
| 234 | if (if_full.ast.else_expr != 0) { | ||
| 235 | const then_cat = categorize_expr_deep(file_index, if_full.ast.then_expr); | ||
| 236 | const else_cat = categorize_expr_deep(file_index, if_full.ast.else_expr); | ||
| 237 | if (then_cat == .type_type and else_cat == .type_type) { | ||
| 238 | return .type_type; | ||
| 239 | } else if (then_cat == .error_set and else_cat == .error_set) { | ||
| 240 | return .{ .error_set = node }; | ||
| 241 | } else if (then_cat == .type or else_cat == .type or | ||
| 242 | then_cat == .namespace or else_cat == .namespace or | ||
| 243 | then_cat == .error_set or else_cat == .error_set or | ||
| 244 | then_cat == .type_function or else_cat == .type_function) | ||
| 245 | { | ||
| 246 | return .type; | ||
| 247 | } | ||
| 248 | } | ||
| 249 | return .{ .global_const = node }; | ||
| 250 | }, | ||
| 251 | |||
| 252 | .@"switch", .switch_comma => return categorize_switch(file_index, node), | ||
| 253 | |||
| 254 | .optional_type, | ||
| 255 | .array_type, | ||
| 256 | .array_type_sentinel, | ||
| 257 | .ptr_type_aligned, | ||
| 258 | .ptr_type_sentinel, | ||
| 259 | .ptr_type, | ||
| 260 | .ptr_type_bit_range, | ||
| 261 | .anyframe_type, | ||
| 262 | => .type, | ||
| 263 | |||
| 157 | else => .{ .global_const = node }, | 264 | else => .{ .global_const = node }, |
| 158 | }; | 265 | }; |
| 159 | } | 266 | } |
| 160 | 267 | ||
| 268 | fn categorize_call( | ||
| 269 | file_index: File.Index, | ||
| 270 | node: Ast.Node.Index, | ||
| 271 | call: Ast.full.Call, | ||
| 272 | ) Category { | ||
| 273 | return switch (categorize_expr(file_index, call.ast.fn_expr)) { | ||
| 274 | .type_function => .type, | ||
| 275 | .alias => |aliasee| categorize_decl_as_callee(aliasee, node), | ||
| 276 | else => .{ .global_const = node }, | ||
| 277 | }; | ||
| 278 | } | ||
| 279 | |||
| 280 | fn categorize_decl_as_callee(decl_index: Decl.Index, call_node: Ast.Node.Index) Category { | ||
| 281 | return switch (decl_index.get().categorize()) { | ||
| 282 | .type_function => .type, | ||
| 283 | .alias => |aliasee| categorize_decl_as_callee(aliasee, call_node), | ||
| 284 | else => .{ .global_const = call_node }, | ||
| 285 | }; | ||
| 286 | } | ||
| 287 | |||
| 161 | fn categorize_builtin_call( | 288 | fn categorize_builtin_call( |
| 162 | file_index: File.Index, | 289 | file_index: File.Index, |
| 163 | node: Ast.Node.Index, | 290 | node: Ast.Node.Index, |
| ... | @@ -172,6 +299,9 @@ pub const File = struct { | ... | @@ -172,6 +299,9 @@ pub const File = struct { |
| 172 | const str_bytes = ast.tokenSlice(str_lit_token); | 299 | const str_bytes = ast.tokenSlice(str_lit_token); |
| 173 | const file_path = std.zig.string_literal.parseAlloc(gpa, str_bytes) catch @panic("OOM"); | 300 | const file_path = std.zig.string_literal.parseAlloc(gpa, str_bytes) catch @panic("OOM"); |
| 174 | defer gpa.free(file_path); | 301 | defer gpa.free(file_path); |
| 302 | if (modules.get(file_path)) |imported_file_index| { | ||
| 303 | return .{ .alias = File.Index.findRootDecl(imported_file_index) }; | ||
| 304 | } | ||
| 175 | const base_path = file_index.path(); | 305 | const base_path = file_index.path(); |
| 176 | const resolved_path = std.fs.path.resolvePosix(gpa, &.{ | 306 | const resolved_path = std.fs.path.resolvePosix(gpa, &.{ |
| 177 | base_path, "..", file_path, | 307 | base_path, "..", file_path, |
| ... | @@ -180,8 +310,8 @@ pub const File = struct { | ... | @@ -180,8 +310,8 @@ pub const File = struct { |
| 180 | log.debug("from '{s}' @import '{s}' resolved='{s}'", .{ | 310 | log.debug("from '{s}' @import '{s}' resolved='{s}'", .{ |
| 181 | base_path, file_path, resolved_path, | 311 | base_path, file_path, resolved_path, |
| 182 | }); | 312 | }); |
| 183 | if (Walk.files.getIndex(resolved_path)) |imported_file_index| { | 313 | if (files.getIndex(resolved_path)) |imported_file_index| { |
| 184 | return .{ .alias = Walk.File.Index.findRootDecl(@enumFromInt(imported_file_index)) }; | 314 | return .{ .alias = File.Index.findRootDecl(@enumFromInt(imported_file_index)) }; |
| 185 | } else { | 315 | } else { |
| 186 | log.warn("import target '{s}' did not resolve to any file", .{resolved_path}); | 316 | log.warn("import target '{s}' did not resolve to any file", .{resolved_path}); |
| 187 | } | 317 | } |
| ... | @@ -195,10 +325,47 @@ pub const File = struct { | ... | @@ -195,10 +325,47 @@ pub const File = struct { |
| 195 | 325 | ||
| 196 | return .{ .global_const = node }; | 326 | return .{ .global_const = node }; |
| 197 | } | 327 | } |
| 328 | |||
| 329 | fn categorize_switch(file_index: File.Index, node: Ast.Node.Index) Category { | ||
| 330 | const ast = file_index.get_ast(); | ||
| 331 | const node_datas = ast.nodes.items(.data); | ||
| 332 | const extra = ast.extraData(node_datas[node].rhs, Ast.Node.SubRange); | ||
| 333 | const case_nodes = ast.extra_data[extra.start..extra.end]; | ||
| 334 | var all_type_type = true; | ||
| 335 | var all_error_set = true; | ||
| 336 | var any_type = false; | ||
| 337 | if (case_nodes.len == 0) return .{ .global_const = node }; | ||
| 338 | for (case_nodes) |case_node| { | ||
| 339 | const case = ast.fullSwitchCase(case_node).?; | ||
| 340 | switch (categorize_expr_deep(file_index, case.ast.target_expr)) { | ||
| 341 | .type_type => { | ||
| 342 | any_type = true; | ||
| 343 | all_error_set = false; | ||
| 344 | }, | ||
| 345 | .error_set => { | ||
| 346 | any_type = true; | ||
| 347 | all_type_type = false; | ||
| 348 | }, | ||
| 349 | .type, .namespace, .type_function => { | ||
| 350 | any_type = true; | ||
| 351 | all_error_set = false; | ||
| 352 | all_type_type = false; | ||
| 353 | }, | ||
| 354 | else => { | ||
| 355 | all_error_set = false; | ||
| 356 | all_type_type = false; | ||
| 357 | }, | ||
| 358 | } | ||
| 359 | } | ||
| 360 | if (all_type_type) return .type_type; | ||
| 361 | if (all_error_set) return .{ .error_set = node }; | ||
| 362 | if (any_type) return .type; | ||
| 363 | return .{ .global_const = node }; | ||
| 364 | } | ||
| 198 | }; | 365 | }; |
| 199 | }; | 366 | }; |
| 200 | 367 | ||
| 201 | pub const PackageIndex = enum(u32) { | 368 | pub const ModuleIndex = enum(u32) { |
| 202 | _, | 369 | _, |
| 203 | }; | 370 | }; |
| 204 | 371 | ||
| ... | @@ -208,28 +375,25 @@ pub fn add_file(file_name: []const u8, bytes: []u8) !File.Index { | ... | @@ -208,28 +375,25 @@ pub fn add_file(file_name: []const u8, bytes: []u8) !File.Index { |
| 208 | try files.put(gpa, file_name, .{ .ast = ast }); | 375 | try files.put(gpa, file_name, .{ .ast = ast }); |
| 209 | 376 | ||
| 210 | if (ast.errors.len > 0) { | 377 | if (ast.errors.len > 0) { |
| 211 | // TODO: expose this in the UI | ||
| 212 | log.err("can't index '{s}' because it has syntax errors", .{file_index.path()}); | 378 | log.err("can't index '{s}' because it has syntax errors", .{file_index.path()}); |
| 213 | return file_index; | 379 | return file_index; |
| 214 | } | 380 | } |
| 215 | 381 | ||
| 216 | var arena_instance = std.heap.ArenaAllocator.init(gpa); | ||
| 217 | defer arena_instance.deinit(); | ||
| 218 | |||
| 219 | var w: Walk = .{ | 382 | var w: Walk = .{ |
| 220 | .arena = arena_instance.allocator(), | ||
| 221 | .file = file_index, | 383 | .file = file_index, |
| 222 | }; | 384 | }; |
| 223 | var scope: Scope = .{ .tag = .top }; | 385 | const scope = try gpa.create(Scope); |
| 386 | scope.* = .{ .tag = .top }; | ||
| 224 | 387 | ||
| 225 | const decl_index = try file_index.add_decl(0, .none); | 388 | const decl_index = try file_index.add_decl(0, .none); |
| 226 | try struct_decl(&w, &scope, decl_index, ast.containerDeclRoot()); | 389 | try struct_decl(&w, scope, decl_index, 0, ast.containerDeclRoot()); |
| 227 | 390 | ||
| 228 | const file = file_index.get(); | 391 | const file = file_index.get(); |
| 229 | shrinkToFit(&file.ident_decls); | 392 | shrinkToFit(&file.ident_decls); |
| 230 | shrinkToFit(&file.token_parents); | 393 | shrinkToFit(&file.token_parents); |
| 231 | shrinkToFit(&file.node_decls); | 394 | shrinkToFit(&file.node_decls); |
| 232 | shrinkToFit(&file.doctests); | 395 | shrinkToFit(&file.doctests); |
| 396 | shrinkToFit(&file.scopes); | ||
| 233 | 397 | ||
| 234 | return file_index; | 398 | return file_index; |
| 235 | } | 399 | } |
| ... | @@ -250,7 +414,7 @@ fn parse(source: []u8) Oom!Ast { | ... | @@ -250,7 +414,7 @@ fn parse(source: []u8) Oom!Ast { |
| 250 | return Ast.parse(gpa, adjusted_source, .zig); | 414 | return Ast.parse(gpa, adjusted_source, .zig); |
| 251 | } | 415 | } |
| 252 | 416 | ||
| 253 | const Scope = struct { | 417 | pub const Scope = struct { |
| 254 | tag: Tag, | 418 | tag: Tag, |
| 255 | 419 | ||
| 256 | const Tag = enum { top, local, namespace }; | 420 | const Tag = enum { top, local, namespace }; |
| ... | @@ -267,6 +431,7 @@ const Scope = struct { | ... | @@ -267,6 +431,7 @@ const Scope = struct { |
| 267 | names: std.StringArrayHashMapUnmanaged(Ast.Node.Index) = .{}, | 431 | names: std.StringArrayHashMapUnmanaged(Ast.Node.Index) = .{}, |
| 268 | doctests: std.StringArrayHashMapUnmanaged(Ast.Node.Index) = .{}, | 432 | doctests: std.StringArrayHashMapUnmanaged(Ast.Node.Index) = .{}, |
| 269 | decl_index: Decl.Index, | 433 | decl_index: Decl.Index, |
| 434 | field_count: u32, | ||
| 270 | }; | 435 | }; |
| 271 | 436 | ||
| 272 | fn getNamespaceDecl(start_scope: *Scope) Decl.Index { | 437 | fn getNamespaceDecl(start_scope: *Scope) Decl.Index { |
| ... | @@ -284,7 +449,17 @@ const Scope = struct { | ... | @@ -284,7 +449,17 @@ const Scope = struct { |
| 284 | }; | 449 | }; |
| 285 | } | 450 | } |
| 286 | 451 | ||
| 287 | fn lookup(start_scope: *Scope, ast: *const Ast, name: []const u8) ?Ast.Node.Index { | 452 | pub fn get_child(scope: *Scope, name: []const u8) ?Ast.Node.Index { |
| 453 | switch (scope.tag) { | ||
| 454 | .top, .local => return null, | ||
| 455 | .namespace => { | ||
| 456 | const namespace = @fieldParentPtr(Namespace, "base", scope); | ||
| 457 | return namespace.names.get(name); | ||
| 458 | }, | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | pub fn lookup(start_scope: *Scope, ast: *const Ast, name: []const u8) ?Ast.Node.Index { | ||
| 288 | const main_tokens = ast.nodes.items(.main_token); | 463 | const main_tokens = ast.nodes.items(.main_token); |
| 289 | var it: *Scope = start_scope; | 464 | var it: *Scope = start_scope; |
| 290 | while (true) switch (it.tag) { | 465 | while (true) switch (it.tag) { |
| ... | @@ -314,17 +489,21 @@ fn struct_decl( | ... | @@ -314,17 +489,21 @@ fn struct_decl( |
| 314 | w: *Walk, | 489 | w: *Walk, |
| 315 | scope: *Scope, | 490 | scope: *Scope, |
| 316 | parent_decl: Decl.Index, | 491 | parent_decl: Decl.Index, |
| 492 | node: Ast.Node.Index, | ||
| 317 | container_decl: Ast.full.ContainerDecl, | 493 | container_decl: Ast.full.ContainerDecl, |
| 318 | ) Oom!void { | 494 | ) Oom!void { |
| 319 | const ast = w.file.get_ast(); | 495 | const ast = w.file.get_ast(); |
| 320 | const node_tags = ast.nodes.items(.tag); | 496 | const node_tags = ast.nodes.items(.tag); |
| 321 | const node_datas = ast.nodes.items(.data); | 497 | const node_datas = ast.nodes.items(.data); |
| 322 | 498 | ||
| 323 | var namespace: Scope.Namespace = .{ | 499 | const namespace = try gpa.create(Scope.Namespace); |
| 500 | namespace.* = .{ | ||
| 324 | .parent = scope, | 501 | .parent = scope, |
| 325 | .decl_index = parent_decl, | 502 | .decl_index = parent_decl, |
| 503 | .field_count = 0, | ||
| 326 | }; | 504 | }; |
| 327 | try w.scanDecls(&namespace, container_decl.ast.members); | 505 | try w.file.get().scopes.putNoClobber(gpa, node, &namespace.base); |
| 506 | try w.scanDecls(namespace, container_decl.ast.members); | ||
| 328 | 507 | ||
| 329 | for (container_decl.ast.members) |member| switch (node_tags[member]) { | 508 | for (container_decl.ast.members) |member| switch (node_tags[member]) { |
| 330 | .container_field_init, | 509 | .container_field_init, |
| ... | @@ -584,7 +763,8 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index) | ... | @@ -584,7 +763,8 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index) |
| 584 | => { | 763 | => { |
| 585 | const full = ast.fullAsm(node).?; | 764 | const full = ast.fullAsm(node).?; |
| 586 | for (full.ast.items) |n| { | 765 | for (full.ast.items) |n| { |
| 587 | // TODO handle .asm_input, .asm_output | 766 | // There is a missing call here to expr() for .asm_input and |
| 767 | // .asm_output nodes. | ||
| 588 | _ = n; | 768 | _ = n; |
| 589 | } | 769 | } |
| 590 | try expr(w, scope, parent_decl, full.ast.template); | 770 | 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) | ... | @@ -701,7 +881,7 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index) |
| 701 | .tagged_union_two_trailing, | 881 | .tagged_union_two_trailing, |
| 702 | => { | 882 | => { |
| 703 | var buf: [2]Ast.Node.Index = undefined; | 883 | var buf: [2]Ast.Node.Index = undefined; |
| 704 | return struct_decl(w, scope, parent_decl, ast.fullContainerDecl(&buf, node).?); | 884 | return struct_decl(w, scope, parent_decl, node, ast.fullContainerDecl(&buf, node).?); |
| 705 | }, | 885 | }, |
| 706 | 886 | ||
| 707 | .array_type_sentinel => { | 887 | .array_type_sentinel => { |
| ... | @@ -803,7 +983,6 @@ fn block( | ... | @@ -803,7 +983,6 @@ fn block( |
| 803 | statements: []const Ast.Node.Index, | 983 | statements: []const Ast.Node.Index, |
| 804 | ) Oom!void { | 984 | ) Oom!void { |
| 805 | const ast = w.file.get_ast(); | 985 | const ast = w.file.get_ast(); |
| 806 | const arena = w.arena; | ||
| 807 | const node_tags = ast.nodes.items(.tag); | 986 | const node_tags = ast.nodes.items(.tag); |
| 808 | const node_datas = ast.nodes.items(.data); | 987 | const node_datas = ast.nodes.items(.data); |
| 809 | 988 | ||
| ... | @@ -818,16 +997,17 @@ fn block( | ... | @@ -818,16 +997,17 @@ fn block( |
| 818 | => { | 997 | => { |
| 819 | const full = ast.fullVarDecl(node).?; | 998 | const full = ast.fullVarDecl(node).?; |
| 820 | try global_var_decl(w, scope, parent_decl, full); | 999 | try global_var_decl(w, scope, parent_decl, full); |
| 821 | const local = try arena.create(Scope.Local); | 1000 | const local = try gpa.create(Scope.Local); |
| 822 | local.* = .{ | 1001 | local.* = .{ |
| 823 | .parent = scope, | 1002 | .parent = scope, |
| 824 | .var_node = node, | 1003 | .var_node = node, |
| 825 | }; | 1004 | }; |
| 1005 | try w.file.get().scopes.putNoClobber(gpa, node, &local.base); | ||
| 826 | scope = &local.base; | 1006 | scope = &local.base; |
| 827 | }, | 1007 | }, |
| 828 | 1008 | ||
| 829 | .assign_destructure => { | 1009 | .assign_destructure => { |
| 830 | // TODO | 1010 | log.debug("walk assign_destructure not implemented yet", .{}); |
| 831 | }, | 1011 | }, |
| 832 | 1012 | ||
| 833 | .grouped_expression => try expr(w, scope, parent_decl, node_datas[node].lhs), | 1013 | .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 | ... | @@ -849,7 +1029,6 @@ fn while_expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, full: Ast.full.W |
| 849 | } | 1029 | } |
| 850 | 1030 | ||
| 851 | fn scanDecls(w: *Walk, namespace: *Scope.Namespace, members: []const Ast.Node.Index) Oom!void { | 1031 | fn scanDecls(w: *Walk, namespace: *Scope.Namespace, members: []const Ast.Node.Index) Oom!void { |
| 852 | const arena = w.arena; | ||
| 853 | const ast = w.file.get_ast(); | 1032 | const ast = w.file.get_ast(); |
| 854 | const node_tags = ast.nodes.items(.tag); | 1033 | const node_tags = ast.nodes.items(.tag); |
| 855 | const main_tokens = ast.nodes.items(.main_token); | 1034 | const main_tokens = ast.nodes.items(.main_token); |
| ... | @@ -880,19 +1059,34 @@ fn scanDecls(w: *Walk, namespace: *Scope.Namespace, members: []const Ast.Node.In | ... | @@ -880,19 +1059,34 @@ fn scanDecls(w: *Walk, namespace: *Scope.Namespace, members: []const Ast.Node.In |
| 880 | const is_doctest = token_tags[ident_token] == .identifier; | 1059 | const is_doctest = token_tags[ident_token] == .identifier; |
| 881 | if (is_doctest) { | 1060 | if (is_doctest) { |
| 882 | const token_bytes = ast.tokenSlice(ident_token); | 1061 | const token_bytes = ast.tokenSlice(ident_token); |
| 883 | try namespace.doctests.put(arena, token_bytes, member_node); | 1062 | try namespace.doctests.put(gpa, token_bytes, member_node); |
| 884 | } | 1063 | } |
| 885 | continue; | 1064 | continue; |
| 886 | }, | 1065 | }, |
| 887 | 1066 | ||
| 1067 | .container_field_init, | ||
| 1068 | .container_field_align, | ||
| 1069 | .container_field, | ||
| 1070 | => { | ||
| 1071 | namespace.field_count += 1; | ||
| 1072 | continue; | ||
| 1073 | }, | ||
| 1074 | |||
| 888 | else => continue, | 1075 | else => continue, |
| 889 | }; | 1076 | }; |
| 890 | 1077 | ||
| 891 | const token_bytes = ast.tokenSlice(name_token); | 1078 | const token_bytes = ast.tokenSlice(name_token); |
| 892 | try namespace.names.put(arena, token_bytes, member_node); | 1079 | try namespace.names.put(gpa, token_bytes, member_node); |
| 893 | } | 1080 | } |
| 894 | } | 1081 | } |
| 895 | 1082 | ||
| 1083 | pub fn isPrimitiveNonType(name: []const u8) bool { | ||
| 1084 | return std.mem.eql(u8, name, "undefined") or | ||
| 1085 | std.mem.eql(u8, name, "null") or | ||
| 1086 | std.mem.eql(u8, name, "true") or | ||
| 1087 | std.mem.eql(u8, name, "false"); | ||
| 1088 | } | ||
| 1089 | |||
| 896 | //test { | 1090 | //test { |
| 897 | // const gpa = std.testing.allocator; | 1091 | // const gpa = std.testing.allocator; |
| 898 | // | 1092 | // |
lib/docs/wasm/main.zig+387-48| ... | @@ -1,3 +1,6 @@ | ... | @@ -1,3 +1,6 @@ |
| 1 | /// Delete this to find out where URL escaping needs to be added. | ||
| 2 | const missing_feature_url_escape = true; | ||
| 3 | |||
| 1 | const gpa = std.heap.wasm_allocator; | 4 | const gpa = std.heap.wasm_allocator; |
| 2 | 5 | ||
| 3 | const std = @import("std"); | 6 | const std = @import("std"); |
| ... | @@ -80,7 +83,7 @@ export fn query_exec(ignore_case: bool) [*]Decl.Index { | ... | @@ -80,7 +83,7 @@ export fn query_exec(ignore_case: bool) [*]Decl.Index { |
| 80 | return query_results.items.ptr; | 83 | return query_results.items.ptr; |
| 81 | } | 84 | } |
| 82 | 85 | ||
| 83 | const max_matched_items = 2000; | 86 | const max_matched_items = 1000; |
| 84 | 87 | ||
| 85 | fn query_exec_fallible(query: []const u8, ignore_case: bool) !void { | 88 | fn query_exec_fallible(query: []const u8, ignore_case: bool) !void { |
| 86 | const Score = packed struct(u32) { | 89 | const Score = packed struct(u32) { |
| ... | @@ -181,7 +184,7 @@ fn query_exec_fallible(query: []const u8, ignore_case: bool) !void { | ... | @@ -181,7 +184,7 @@ fn query_exec_fallible(query: []const u8, ignore_case: bool) !void { |
| 181 | const b_decl = query_results.items[b_index]; | 184 | const b_decl = query_results.items[b_index]; |
| 182 | const a_file_path = a_decl.get().file.path(); | 185 | const a_file_path = a_decl.get().file.path(); |
| 183 | const b_file_path = b_decl.get().file.path(); | 186 | const b_file_path = b_decl.get().file.path(); |
| 184 | // TODO Also check the local namespace inside the file | 187 | // This neglects to check the local namespace inside the file. |
| 185 | return std.mem.lessThan(u8, b_file_path, a_file_path); | 188 | return std.mem.lessThan(u8, b_file_path, a_file_path); |
| 186 | } | 189 | } |
| 187 | } | 190 | } |
| ... | @@ -209,12 +212,178 @@ fn Slice(T: type) type { | ... | @@ -209,12 +212,178 @@ fn Slice(T: type) type { |
| 209 | }; | 212 | }; |
| 210 | } | 213 | } |
| 211 | 214 | ||
| 215 | const ErrorIdentifier = packed struct(u64) { | ||
| 216 | token_index: Ast.TokenIndex, | ||
| 217 | decl_index: Decl.Index, | ||
| 218 | |||
| 219 | fn hasDocs(ei: ErrorIdentifier) bool { | ||
| 220 | const decl_index = ei.decl_index; | ||
| 221 | const ast = decl_index.get().file.get_ast(); | ||
| 222 | const token_tags = ast.tokens.items(.tag); | ||
| 223 | const token_index = ei.token_index; | ||
| 224 | if (token_index == 0) return false; | ||
| 225 | return token_tags[token_index - 1] == .doc_comment; | ||
| 226 | } | ||
| 227 | |||
| 228 | fn html(ei: ErrorIdentifier, base_decl: Decl.Index, out: *std.ArrayListUnmanaged(u8)) Oom!void { | ||
| 229 | const decl_index = ei.decl_index; | ||
| 230 | const ast = decl_index.get().file.get_ast(); | ||
| 231 | const name = ast.tokenSlice(ei.token_index); | ||
| 232 | const first_doc_comment = Decl.findFirstDocComment(ast, ei.token_index); | ||
| 233 | const has_docs = ast.tokens.items(.tag)[first_doc_comment] == .doc_comment; | ||
| 234 | const has_link = base_decl != decl_index; | ||
| 235 | |||
| 236 | try out.appendSlice(gpa, "<dt>"); | ||
| 237 | try out.appendSlice(gpa, name); | ||
| 238 | if (has_link) { | ||
| 239 | try out.appendSlice(gpa, " <a href=\"#"); | ||
| 240 | _ = missing_feature_url_escape; | ||
| 241 | try decl_index.get().fqn(out); | ||
| 242 | try out.appendSlice(gpa, "\">"); | ||
| 243 | try out.appendSlice(gpa, decl_index.get().extra_info().name); | ||
| 244 | try out.appendSlice(gpa, "</a>"); | ||
| 245 | } | ||
| 246 | try out.appendSlice(gpa, "</dt>"); | ||
| 247 | |||
| 248 | if (has_docs) { | ||
| 249 | try out.appendSlice(gpa, "<dd>"); | ||
| 250 | try render_docs(out, decl_index, first_doc_comment, false); | ||
| 251 | try out.appendSlice(gpa, "</dd>"); | ||
| 252 | } | ||
| 253 | } | ||
| 254 | }; | ||
| 255 | |||
| 212 | var string_result: std.ArrayListUnmanaged(u8) = .{}; | 256 | var string_result: std.ArrayListUnmanaged(u8) = .{}; |
| 257 | var error_set_result: std.StringArrayHashMapUnmanaged(ErrorIdentifier) = .{}; | ||
| 258 | |||
| 259 | export fn decl_error_set(decl_index: Decl.Index) Slice(ErrorIdentifier) { | ||
| 260 | return Slice(ErrorIdentifier).init(decl_error_set_fallible(decl_index) catch @panic("OOM")); | ||
| 261 | } | ||
| 262 | |||
| 263 | export fn error_set_node_list(base_decl: Decl.Index, node: Ast.Node.Index) Slice(ErrorIdentifier) { | ||
| 264 | error_set_result.clearRetainingCapacity(); | ||
| 265 | addErrorsFromExpr(base_decl, &error_set_result, node) catch @panic("OOM"); | ||
| 266 | sort_error_set_result(); | ||
| 267 | return Slice(ErrorIdentifier).init(error_set_result.values()); | ||
| 268 | } | ||
| 269 | |||
| 270 | export fn fn_error_set_decl(decl_index: Decl.Index, node: Ast.Node.Index) Decl.Index { | ||
| 271 | return switch (decl_index.get().file.categorize_expr(node)) { | ||
| 272 | .alias => |aliasee| fn_error_set_decl(aliasee, aliasee.get().ast_node), | ||
| 273 | else => decl_index, | ||
| 274 | }; | ||
| 275 | } | ||
| 276 | |||
| 277 | export fn decl_field_count(decl_index: Decl.Index) u32 { | ||
| 278 | switch (decl_index.get().categorize()) { | ||
| 279 | .namespace => |node| return decl_index.get().file.get().field_count(node), | ||
| 280 | else => return 0, | ||
| 281 | } | ||
| 282 | } | ||
| 283 | |||
| 284 | fn decl_error_set_fallible(decl_index: Decl.Index) Oom![]ErrorIdentifier { | ||
| 285 | error_set_result.clearRetainingCapacity(); | ||
| 286 | try addErrorsFromDecl(decl_index, &error_set_result); | ||
| 287 | sort_error_set_result(); | ||
| 288 | return error_set_result.values(); | ||
| 289 | } | ||
| 290 | |||
| 291 | fn sort_error_set_result() void { | ||
| 292 | const sort_context: struct { | ||
| 293 | pub fn lessThan(sc: @This(), a_index: usize, b_index: usize) bool { | ||
| 294 | _ = sc; | ||
| 295 | const a_name = error_set_result.keys()[a_index]; | ||
| 296 | const b_name = error_set_result.keys()[b_index]; | ||
| 297 | return std.mem.lessThan(u8, a_name, b_name); | ||
| 298 | } | ||
| 299 | } = .{}; | ||
| 300 | error_set_result.sortUnstable(sort_context); | ||
| 301 | } | ||
| 302 | |||
| 303 | fn addErrorsFromDecl( | ||
| 304 | decl_index: Decl.Index, | ||
| 305 | out: *std.StringArrayHashMapUnmanaged(ErrorIdentifier), | ||
| 306 | ) Oom!void { | ||
| 307 | switch (decl_index.get().categorize()) { | ||
| 308 | .error_set => |node| try addErrorsFromExpr(decl_index, out, node), | ||
| 309 | .alias => |aliasee| try addErrorsFromDecl(aliasee, out), | ||
| 310 | else => |cat| log.debug("unable to addErrorsFromDecl: {any}", .{cat}), | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | fn addErrorsFromExpr( | ||
| 315 | decl_index: Decl.Index, | ||
| 316 | out: *std.StringArrayHashMapUnmanaged(ErrorIdentifier), | ||
| 317 | node: Ast.Node.Index, | ||
| 318 | ) Oom!void { | ||
| 319 | const decl = decl_index.get(); | ||
| 320 | const ast = decl.file.get_ast(); | ||
| 321 | const node_tags = ast.nodes.items(.tag); | ||
| 322 | const node_datas = ast.nodes.items(.data); | ||
| 323 | |||
| 324 | switch (decl.file.categorize_expr(node)) { | ||
| 325 | .error_set => |n| switch (node_tags[n]) { | ||
| 326 | .error_set_decl => { | ||
| 327 | try addErrorsFromNode(decl_index, out, node); | ||
| 328 | }, | ||
| 329 | .merge_error_sets => { | ||
| 330 | try addErrorsFromExpr(decl_index, out, node_datas[node].lhs); | ||
| 331 | try addErrorsFromExpr(decl_index, out, node_datas[node].rhs); | ||
| 332 | }, | ||
| 333 | else => unreachable, | ||
| 334 | }, | ||
| 335 | .alias => |aliasee| { | ||
| 336 | try addErrorsFromDecl(aliasee, out); | ||
| 337 | }, | ||
| 338 | else => return, | ||
| 339 | } | ||
| 340 | } | ||
| 341 | |||
| 342 | fn addErrorsFromNode( | ||
| 343 | decl_index: Decl.Index, | ||
| 344 | out: *std.StringArrayHashMapUnmanaged(ErrorIdentifier), | ||
| 345 | node: Ast.Node.Index, | ||
| 346 | ) Oom!void { | ||
| 347 | const decl = decl_index.get(); | ||
| 348 | const ast = decl.file.get_ast(); | ||
| 349 | const main_tokens = ast.nodes.items(.main_token); | ||
| 350 | const token_tags = ast.tokens.items(.tag); | ||
| 351 | const error_token = main_tokens[node]; | ||
| 352 | var tok_i = error_token + 2; | ||
| 353 | while (true) : (tok_i += 1) switch (token_tags[tok_i]) { | ||
| 354 | .doc_comment, .comma => {}, | ||
| 355 | .identifier => { | ||
| 356 | const name = ast.tokenSlice(tok_i); | ||
| 357 | const gop = try out.getOrPut(gpa, name); | ||
| 358 | // If there are more than one, take the one with doc comments. | ||
| 359 | // If they both have doc comments, prefer the existing one. | ||
| 360 | const new: ErrorIdentifier = .{ | ||
| 361 | .token_index = tok_i, | ||
| 362 | .decl_index = decl_index, | ||
| 363 | }; | ||
| 364 | if (!gop.found_existing or | ||
| 365 | (!gop.value_ptr.hasDocs() and new.hasDocs())) | ||
| 366 | { | ||
| 367 | gop.value_ptr.* = new; | ||
| 368 | } | ||
| 369 | }, | ||
| 370 | .r_brace => break, | ||
| 371 | else => unreachable, | ||
| 372 | }; | ||
| 373 | } | ||
| 374 | |||
| 375 | export fn type_fn_fields(decl_index: Decl.Index) Slice(Ast.Node.Index) { | ||
| 376 | return decl_fields(decl_index); | ||
| 377 | } | ||
| 213 | 378 | ||
| 214 | export fn decl_fields(decl_index: Decl.Index) Slice(Ast.Node.Index) { | 379 | export fn decl_fields(decl_index: Decl.Index) Slice(Ast.Node.Index) { |
| 215 | return Slice(Ast.Node.Index).init(decl_fields_fallible(decl_index) catch @panic("OOM")); | 380 | return Slice(Ast.Node.Index).init(decl_fields_fallible(decl_index) catch @panic("OOM")); |
| 216 | } | 381 | } |
| 217 | 382 | ||
| 383 | export fn decl_params(decl_index: Decl.Index) Slice(Ast.Node.Index) { | ||
| 384 | return Slice(Ast.Node.Index).init(decl_params_fallible(decl_index) catch @panic("OOM")); | ||
| 385 | } | ||
| 386 | |||
| 218 | fn decl_fields_fallible(decl_index: Decl.Index) ![]Ast.Node.Index { | 387 | fn decl_fields_fallible(decl_index: Decl.Index) ![]Ast.Node.Index { |
| 219 | const g = struct { | 388 | const g = struct { |
| 220 | var result: std.ArrayListUnmanaged(Ast.Node.Index) = .{}; | 389 | var result: std.ArrayListUnmanaged(Ast.Node.Index) = .{}; |
| ... | @@ -237,12 +406,38 @@ fn decl_fields_fallible(decl_index: Decl.Index) ![]Ast.Node.Index { | ... | @@ -237,12 +406,38 @@ fn decl_fields_fallible(decl_index: Decl.Index) ![]Ast.Node.Index { |
| 237 | return g.result.items; | 406 | return g.result.items; |
| 238 | } | 407 | } |
| 239 | 408 | ||
| 409 | fn decl_params_fallible(decl_index: Decl.Index) ![]Ast.Node.Index { | ||
| 410 | const g = struct { | ||
| 411 | var result: std.ArrayListUnmanaged(Ast.Node.Index) = .{}; | ||
| 412 | }; | ||
| 413 | g.result.clearRetainingCapacity(); | ||
| 414 | const decl = decl_index.get(); | ||
| 415 | const ast = decl.file.get_ast(); | ||
| 416 | const value_node = decl.value_node() orelse return &.{}; | ||
| 417 | var buf: [1]Ast.Node.Index = undefined; | ||
| 418 | const fn_proto = ast.fullFnProto(&buf, value_node) orelse return &.{}; | ||
| 419 | try g.result.appendSlice(gpa, fn_proto.ast.params); | ||
| 420 | return g.result.items; | ||
| 421 | } | ||
| 422 | |||
| 423 | export fn error_html(base_decl: Decl.Index, error_identifier: ErrorIdentifier) String { | ||
| 424 | string_result.clearRetainingCapacity(); | ||
| 425 | error_identifier.html(base_decl, &string_result) catch @panic("OOM"); | ||
| 426 | return String.init(string_result.items); | ||
| 427 | } | ||
| 428 | |||
| 240 | export fn decl_field_html(decl_index: Decl.Index, field_node: Ast.Node.Index) String { | 429 | export fn decl_field_html(decl_index: Decl.Index, field_node: Ast.Node.Index) String { |
| 241 | string_result.clearRetainingCapacity(); | 430 | string_result.clearRetainingCapacity(); |
| 242 | decl_field_html_fallible(&string_result, decl_index, field_node) catch @panic("OOM"); | 431 | decl_field_html_fallible(&string_result, decl_index, field_node) catch @panic("OOM"); |
| 243 | return String.init(string_result.items); | 432 | return String.init(string_result.items); |
| 244 | } | 433 | } |
| 245 | 434 | ||
| 435 | export fn decl_param_html(decl_index: Decl.Index, param_node: Ast.Node.Index) String { | ||
| 436 | string_result.clearRetainingCapacity(); | ||
| 437 | decl_param_html_fallible(&string_result, decl_index, param_node) catch @panic("OOM"); | ||
| 438 | return String.init(string_result.items); | ||
| 439 | } | ||
| 440 | |||
| 246 | fn decl_field_html_fallible( | 441 | fn decl_field_html_fallible( |
| 247 | out: *std.ArrayListUnmanaged(u8), | 442 | out: *std.ArrayListUnmanaged(u8), |
| 248 | decl_index: Decl.Index, | 443 | decl_index: Decl.Index, |
| ... | @@ -251,7 +446,7 @@ fn decl_field_html_fallible( | ... | @@ -251,7 +446,7 @@ fn decl_field_html_fallible( |
| 251 | const decl = decl_index.get(); | 446 | const decl = decl_index.get(); |
| 252 | const ast = decl.file.get_ast(); | 447 | const ast = decl.file.get_ast(); |
| 253 | try out.appendSlice(gpa, "<pre><code>"); | 448 | try out.appendSlice(gpa, "<pre><code>"); |
| 254 | try file_source_html(decl.file, out, field_node); | 449 | try file_source_html(decl.file, out, field_node, .{}); |
| 255 | try out.appendSlice(gpa, "</code></pre>"); | 450 | try out.appendSlice(gpa, "</code></pre>"); |
| 256 | 451 | ||
| 257 | const field = ast.fullContainerField(field_node).?; | 452 | const field = ast.fullContainerField(field_node).?; |
| ... | @@ -259,12 +454,48 @@ fn decl_field_html_fallible( | ... | @@ -259,12 +454,48 @@ fn decl_field_html_fallible( |
| 259 | 454 | ||
| 260 | if (ast.tokens.items(.tag)[first_doc_comment] == .doc_comment) { | 455 | if (ast.tokens.items(.tag)[first_doc_comment] == .doc_comment) { |
| 261 | try out.appendSlice(gpa, "<div class=\"fieldDocs\">"); | 456 | try out.appendSlice(gpa, "<div class=\"fieldDocs\">"); |
| 262 | try render_docs(out, ast, first_doc_comment, false); | 457 | try render_docs(out, decl_index, first_doc_comment, false); |
| 458 | try out.appendSlice(gpa, "</div>"); | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | fn decl_param_html_fallible( | ||
| 463 | out: *std.ArrayListUnmanaged(u8), | ||
| 464 | decl_index: Decl.Index, | ||
| 465 | param_node: Ast.Node.Index, | ||
| 466 | ) !void { | ||
| 467 | const decl = decl_index.get(); | ||
| 468 | const ast = decl.file.get_ast(); | ||
| 469 | const token_tags = ast.tokens.items(.tag); | ||
| 470 | const colon = ast.firstToken(param_node) - 1; | ||
| 471 | const name_token = colon - 1; | ||
| 472 | const first_doc_comment = f: { | ||
| 473 | var it = ast.firstToken(param_node); | ||
| 474 | while (it > 0) { | ||
| 475 | it -= 1; | ||
| 476 | switch (token_tags[it]) { | ||
| 477 | .doc_comment, .colon, .identifier, .keyword_comptime, .keyword_noalias => {}, | ||
| 478 | else => break, | ||
| 479 | } | ||
| 480 | } | ||
| 481 | break :f it + 1; | ||
| 482 | }; | ||
| 483 | const name = ast.tokenSlice(name_token); | ||
| 484 | |||
| 485 | try out.appendSlice(gpa, "<pre><code>"); | ||
| 486 | try appendEscaped(out, name); | ||
| 487 | try out.appendSlice(gpa, ": "); | ||
| 488 | try file_source_html(decl.file, out, param_node, .{}); | ||
| 489 | try out.appendSlice(gpa, "</code></pre>"); | ||
| 490 | |||
| 491 | if (ast.tokens.items(.tag)[first_doc_comment] == .doc_comment) { | ||
| 492 | try out.appendSlice(gpa, "<div class=\"fieldDocs\">"); | ||
| 493 | try render_docs(out, decl_index, first_doc_comment, false); | ||
| 263 | try out.appendSlice(gpa, "</div>"); | 494 | try out.appendSlice(gpa, "</div>"); |
| 264 | } | 495 | } |
| 265 | } | 496 | } |
| 266 | 497 | ||
| 267 | export fn decl_fn_proto_html(decl_index: Decl.Index) String { | 498 | export fn decl_fn_proto_html(decl_index: Decl.Index, linkify_fn_name: bool) String { |
| 268 | const decl = decl_index.get(); | 499 | const decl = decl_index.get(); |
| 269 | const ast = decl.file.get_ast(); | 500 | const ast = decl.file.get_ast(); |
| 270 | const node_tags = ast.nodes.items(.tag); | 501 | const node_tags = ast.nodes.items(.tag); |
| ... | @@ -282,7 +513,12 @@ export fn decl_fn_proto_html(decl_index: Decl.Index) String { | ... | @@ -282,7 +513,12 @@ export fn decl_fn_proto_html(decl_index: Decl.Index) String { |
| 282 | }; | 513 | }; |
| 283 | 514 | ||
| 284 | string_result.clearRetainingCapacity(); | 515 | string_result.clearRetainingCapacity(); |
| 285 | file_source_html(decl.file, &string_result, proto_node) catch |err| { | 516 | file_source_html(decl.file, &string_result, proto_node, .{ |
| 517 | .skip_doc_comments = true, | ||
| 518 | .skip_comments = true, | ||
| 519 | .collapse_whitespace = true, | ||
| 520 | .fn_link = if (linkify_fn_name) decl_index else .none, | ||
| 521 | }) catch |err| { | ||
| 286 | fatal("unable to render source: {s}", .{@errorName(err)}); | 522 | fatal("unable to render source: {s}", .{@errorName(err)}); |
| 287 | }; | 523 | }; |
| 288 | return String.init(string_result.items); | 524 | return String.init(string_result.items); |
| ... | @@ -292,7 +528,7 @@ export fn decl_source_html(decl_index: Decl.Index) String { | ... | @@ -292,7 +528,7 @@ export fn decl_source_html(decl_index: Decl.Index) String { |
| 292 | const decl = decl_index.get(); | 528 | const decl = decl_index.get(); |
| 293 | 529 | ||
| 294 | string_result.clearRetainingCapacity(); | 530 | string_result.clearRetainingCapacity(); |
| 295 | file_source_html(decl.file, &string_result, decl.ast_node) catch |err| { | 531 | file_source_html(decl.file, &string_result, decl.ast_node, .{}) catch |err| { |
| 296 | fatal("unable to render source: {s}", .{@errorName(err)}); | 532 | fatal("unable to render source: {s}", .{@errorName(err)}); |
| 297 | }; | 533 | }; |
| 298 | return String.init(string_result.items); | 534 | return String.init(string_result.items); |
| ... | @@ -304,7 +540,7 @@ export fn decl_doctest_html(decl_index: Decl.Index) String { | ... | @@ -304,7 +540,7 @@ export fn decl_doctest_html(decl_index: Decl.Index) String { |
| 304 | return String.init(""); | 540 | return String.init(""); |
| 305 | 541 | ||
| 306 | string_result.clearRetainingCapacity(); | 542 | string_result.clearRetainingCapacity(); |
| 307 | file_source_html(decl.file, &string_result, doctest_ast_node) catch |err| { | 543 | file_source_html(decl.file, &string_result, doctest_ast_node, .{}) catch |err| { |
| 308 | fatal("unable to render source: {s}", .{@errorName(err)}); | 544 | fatal("unable to render source: {s}", .{@errorName(err)}); |
| 309 | }; | 545 | }; |
| 310 | return String.init(string_result.items); | 546 | return String.init(string_result.items); |
| ... | @@ -312,6 +548,7 @@ export fn decl_doctest_html(decl_index: Decl.Index) String { | ... | @@ -312,6 +548,7 @@ export fn decl_doctest_html(decl_index: Decl.Index) String { |
| 312 | 548 | ||
| 313 | export fn decl_fqn(decl_index: Decl.Index) String { | 549 | export fn decl_fqn(decl_index: Decl.Index) String { |
| 314 | const decl = decl_index.get(); | 550 | const decl = decl_index.get(); |
| 551 | string_result.clearRetainingCapacity(); | ||
| 315 | decl.fqn(&string_result) catch @panic("OOM"); | 552 | decl.fqn(&string_result) catch @panic("OOM"); |
| 316 | return String.init(string_result.items); | 553 | return String.init(string_result.items); |
| 317 | } | 554 | } |
| ... | @@ -321,6 +558,20 @@ export fn decl_parent(decl_index: Decl.Index) Decl.Index { | ... | @@ -321,6 +558,20 @@ export fn decl_parent(decl_index: Decl.Index) Decl.Index { |
| 321 | return decl.parent; | 558 | return decl.parent; |
| 322 | } | 559 | } |
| 323 | 560 | ||
| 561 | export fn fn_error_set(decl_index: Decl.Index) Ast.Node.Index { | ||
| 562 | const decl = decl_index.get(); | ||
| 563 | const ast = decl.file.get_ast(); | ||
| 564 | var buf: [1]Ast.Node.Index = undefined; | ||
| 565 | const full = ast.fullFnProto(&buf, decl.ast_node).?; | ||
| 566 | const node_tags = ast.nodes.items(.tag); | ||
| 567 | const node_datas = ast.nodes.items(.data); | ||
| 568 | return switch (node_tags[full.ast.return_type]) { | ||
| 569 | .error_set_decl => full.ast.return_type, | ||
| 570 | .error_union => node_datas[full.ast.return_type].lhs, | ||
| 571 | else => 0, | ||
| 572 | }; | ||
| 573 | } | ||
| 574 | |||
| 324 | export fn decl_file_path(decl_index: Decl.Index) String { | 575 | export fn decl_file_path(decl_index: Decl.Index) String { |
| 325 | string_result.clearRetainingCapacity(); | 576 | string_result.clearRetainingCapacity(); |
| 326 | string_result.appendSlice(gpa, decl_index.get().file.path()) catch @panic("OOM"); | 577 | 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 { | ... | @@ -350,7 +601,8 @@ export fn decl_category_name(decl_index: Decl.Index) String { |
| 350 | }, | 601 | }, |
| 351 | .global_variable => "Global Variable", | 602 | .global_variable => "Global Variable", |
| 352 | .function => "Function", | 603 | .function => "Function", |
| 353 | .type => "Type", | 604 | .type_function => "Type Function", |
| 605 | .type, .type_type => "Type", | ||
| 354 | .error_set => "Error Set", | 606 | .error_set => "Error Set", |
| 355 | .global_const => "Constant", | 607 | .global_const => "Constant", |
| 356 | .primitive => "Primitive Value", | 608 | .primitive => "Primitive Value", |
| ... | @@ -375,9 +627,8 @@ export fn decl_name(decl_index: Decl.Index) String { | ... | @@ -375,9 +627,8 @@ export fn decl_name(decl_index: Decl.Index) String { |
| 375 | 627 | ||
| 376 | export fn decl_docs_html(decl_index: Decl.Index, short: bool) String { | 628 | export fn decl_docs_html(decl_index: Decl.Index, short: bool) String { |
| 377 | const decl = decl_index.get(); | 629 | const decl = decl_index.get(); |
| 378 | const ast = decl.file.get_ast(); | ||
| 379 | string_result.clearRetainingCapacity(); | 630 | string_result.clearRetainingCapacity(); |
| 380 | render_docs(&string_result, ast, decl.extra_info().first_doc_comment, short) catch @panic("OOM"); | 631 | render_docs(&string_result, decl_index, decl.extra_info().first_doc_comment, short) catch @panic("OOM"); |
| 381 | return String.init(string_result.items); | 632 | return String.init(string_result.items); |
| 382 | } | 633 | } |
| 383 | 634 | ||
| ... | @@ -402,10 +653,12 @@ fn collect_docs( | ... | @@ -402,10 +653,12 @@ fn collect_docs( |
| 402 | 653 | ||
| 403 | fn render_docs( | 654 | fn render_docs( |
| 404 | out: *std.ArrayListUnmanaged(u8), | 655 | out: *std.ArrayListUnmanaged(u8), |
| 405 | ast: *const Ast, | 656 | decl_index: Decl.Index, |
| 406 | first_doc_comment: Ast.TokenIndex, | 657 | first_doc_comment: Ast.TokenIndex, |
| 407 | short: bool, | 658 | short: bool, |
| 408 | ) Oom!void { | 659 | ) Oom!void { |
| 660 | const decl = decl_index.get(); | ||
| 661 | const ast = decl.file.get_ast(); | ||
| 409 | const token_tags = ast.tokens.items(.tag); | 662 | const token_tags = ast.tokens.items(.tag); |
| 410 | 663 | ||
| 411 | var parser = try markdown.Parser.init(gpa); | 664 | var parser = try markdown.Parser.init(gpa); |
| ... | @@ -423,10 +676,14 @@ fn render_docs( | ... | @@ -423,10 +676,14 @@ fn render_docs( |
| 423 | var parsed_doc = try parser.endInput(); | 676 | var parsed_doc = try parser.endInput(); |
| 424 | defer parsed_doc.deinit(gpa); | 677 | defer parsed_doc.deinit(gpa); |
| 425 | 678 | ||
| 679 | const g = struct { | ||
| 680 | var link_buffer: std.ArrayListUnmanaged(u8) = .{}; | ||
| 681 | }; | ||
| 682 | |||
| 426 | const Writer = std.ArrayListUnmanaged(u8).Writer; | 683 | const Writer = std.ArrayListUnmanaged(u8).Writer; |
| 427 | const Renderer = markdown.Renderer(Writer, void); | 684 | const Renderer = markdown.Renderer(Writer, Decl.Index); |
| 428 | const renderer: Renderer = .{ | 685 | const renderer: Renderer = .{ |
| 429 | .context = {}, | 686 | .context = decl_index, |
| 430 | .renderFn = struct { | 687 | .renderFn = struct { |
| 431 | fn render( | 688 | fn render( |
| 432 | r: Renderer, | 689 | r: Renderer, |
| ... | @@ -436,23 +693,22 @@ fn render_docs( | ... | @@ -436,23 +693,22 @@ fn render_docs( |
| 436 | ) !void { | 693 | ) !void { |
| 437 | const data = doc.nodes.items(.data)[@intFromEnum(node)]; | 694 | const data = doc.nodes.items(.data)[@intFromEnum(node)]; |
| 438 | switch (doc.nodes.items(.tag)[@intFromEnum(node)]) { | 695 | switch (doc.nodes.items(.tag)[@intFromEnum(node)]) { |
| 439 | // TODO: detect identifier references (dotted paths) in | ||
| 440 | // these three node types and render them appropriately. | ||
| 441 | // Also, syntax highlighting can be applied in code blocks | ||
| 442 | // unless the tag says otherwise. | ||
| 443 | .code_block => { | ||
| 444 | const tag = doc.string(data.code_block.tag); | ||
| 445 | _ = tag; | ||
| 446 | const content = doc.string(data.code_block.content); | ||
| 447 | try writer.print("<pre><code>{}</code></pre>\n", .{markdown.fmtHtml(content)}); | ||
| 448 | }, | ||
| 449 | .code_span => { | 696 | .code_span => { |
| 697 | try writer.writeAll("<code>"); | ||
| 450 | const content = doc.string(data.text.content); | 698 | const content = doc.string(data.text.content); |
| 451 | try writer.print("<code>{}</code>", .{markdown.fmtHtml(content)}); | 699 | if (resolve_decl_path(r.context, content)) |resolved_decl_index| { |
| 452 | }, | 700 | g.link_buffer.clearRetainingCapacity(); |
| 453 | .text => { | 701 | try resolve_decl_link(resolved_decl_index, &g.link_buffer); |
| 454 | const content = doc.string(data.text.content); | 702 | |
| 455 | try writer.print("{}", .{markdown.fmtHtml(content)}); | 703 | try writer.writeAll("<a href=\"#"); |
| 704 | _ = missing_feature_url_escape; | ||
| 705 | try writer.writeAll(g.link_buffer.items); | ||
| 706 | try writer.print("\">{}</a>", .{markdown.fmtHtml(content)}); | ||
| 707 | } else { | ||
| 708 | try writer.print("{}", .{markdown.fmtHtml(content)}); | ||
| 709 | } | ||
| 710 | |||
| 711 | try writer.writeAll("</code>"); | ||
| 456 | }, | 712 | }, |
| 457 | 713 | ||
| 458 | else => try Renderer.renderDefault(r, doc, node, writer), | 714 | else => try Renderer.renderDefault(r, doc, node, writer), |
| ... | @@ -463,12 +719,39 @@ fn render_docs( | ... | @@ -463,12 +719,39 @@ fn render_docs( |
| 463 | try renderer.render(parsed_doc, out.writer(gpa)); | 719 | try renderer.render(parsed_doc, out.writer(gpa)); |
| 464 | } | 720 | } |
| 465 | 721 | ||
| 722 | fn resolve_decl_path(decl_index: Decl.Index, path: []const u8) ?Decl.Index { | ||
| 723 | var path_components = std.mem.splitScalar(u8, path, '.'); | ||
| 724 | var current_decl_index = decl_index.get().lookup(path_components.first()) orelse return null; | ||
| 725 | while (path_components.next()) |component| { | ||
| 726 | switch (current_decl_index.get().categorize()) { | ||
| 727 | .alias => |aliasee| current_decl_index = aliasee, | ||
| 728 | else => {}, | ||
| 729 | } | ||
| 730 | current_decl_index = current_decl_index.get().get_child(component) orelse return null; | ||
| 731 | } | ||
| 732 | return current_decl_index; | ||
| 733 | } | ||
| 734 | |||
| 466 | export fn decl_type_html(decl_index: Decl.Index) String { | 735 | export fn decl_type_html(decl_index: Decl.Index) String { |
| 467 | const decl = decl_index.get(); | 736 | const decl = decl_index.get(); |
| 468 | const ast = decl.file.get_ast(); | 737 | const ast = decl.file.get_ast(); |
| 469 | string_result.clearRetainingCapacity(); | 738 | string_result.clearRetainingCapacity(); |
| 470 | _ = ast; // TODO | 739 | t: { |
| 471 | string_result.appendSlice(gpa, "TODO_type_here") catch @panic("OOM"); | 740 | // If there is an explicit type, use it. |
| 741 | if (ast.fullVarDecl(decl.ast_node)) |var_decl| { | ||
| 742 | if (var_decl.ast.type_node != 0) { | ||
| 743 | string_result.appendSlice(gpa, "<code>") catch @panic("OOM"); | ||
| 744 | file_source_html(decl.file, &string_result, var_decl.ast.type_node, .{ | ||
| 745 | .skip_comments = true, | ||
| 746 | .collapse_whitespace = true, | ||
| 747 | }) catch |e| { | ||
| 748 | fatal("unable to render html: {s}", .{@errorName(e)}); | ||
| 749 | }; | ||
| 750 | string_result.appendSlice(gpa, "</code>") catch @panic("OOM"); | ||
| 751 | break :t; | ||
| 752 | } | ||
| 753 | } | ||
| 754 | } | ||
| 472 | return String.init(string_result.items); | 755 | return String.init(string_result.items); |
| 473 | } | 756 | } |
| 474 | 757 | ||
| ... | @@ -491,7 +774,7 @@ fn unpack_inner(tar_bytes: []u8) !void { | ... | @@ -491,7 +774,7 @@ fn unpack_inner(tar_bytes: []u8) !void { |
| 491 | const file_name = try gpa.dupe(u8, tar_file.name); | 774 | const file_name = try gpa.dupe(u8, tar_file.name); |
| 492 | if (std.mem.indexOfScalar(u8, file_name, '/')) |pkg_name_end| { | 775 | if (std.mem.indexOfScalar(u8, file_name, '/')) |pkg_name_end| { |
| 493 | const pkg_name = file_name[0..pkg_name_end]; | 776 | const pkg_name = file_name[0..pkg_name_end]; |
| 494 | const gop = try Walk.packages.getOrPut(gpa, pkg_name); | 777 | const gop = try Walk.modules.getOrPut(gpa, pkg_name); |
| 495 | const file: Walk.File.Index = @enumFromInt(Walk.files.entries.len); | 778 | const file: Walk.File.Index = @enumFromInt(Walk.files.entries.len); |
| 496 | if (!gop.found_existing or | 779 | if (!gop.found_existing or |
| 497 | std.mem.eql(u8, file_name[pkg_name_end..], "/root.zig") or | 780 | std.mem.eql(u8, file_name[pkg_name_end..], "/root.zig") or |
| ... | @@ -527,13 +810,13 @@ fn ascii_lower(bytes: []u8) void { | ... | @@ -527,13 +810,13 @@ fn ascii_lower(bytes: []u8) void { |
| 527 | for (bytes) |*b| b.* = std.ascii.toLower(b.*); | 810 | for (bytes) |*b| b.* = std.ascii.toLower(b.*); |
| 528 | } | 811 | } |
| 529 | 812 | ||
| 530 | export fn package_name(index: u32) String { | 813 | export fn module_name(index: u32) String { |
| 531 | const names = Walk.packages.keys(); | 814 | const names = Walk.modules.keys(); |
| 532 | return String.init(if (index >= names.len) "" else names[index]); | 815 | return String.init(if (index >= names.len) "" else names[index]); |
| 533 | } | 816 | } |
| 534 | 817 | ||
| 535 | export fn find_package_root(pkg: Walk.PackageIndex) Decl.Index { | 818 | export fn find_module_root(pkg: Walk.ModuleIndex) Decl.Index { |
| 536 | const root_file = Walk.packages.values()[@intFromEnum(pkg)]; | 819 | const root_file = Walk.modules.values()[@intFromEnum(pkg)]; |
| 537 | const result = root_file.findRootDecl(); | 820 | const result = root_file.findRootDecl(); |
| 538 | assert(result != .none); | 821 | assert(result != .none); |
| 539 | return result; | 822 | return result; |
| ... | @@ -555,11 +838,17 @@ export fn find_file_root() Decl.Index { | ... | @@ -555,11 +838,17 @@ export fn find_file_root() Decl.Index { |
| 555 | } | 838 | } |
| 556 | 839 | ||
| 557 | /// Uses `input_string`. | 840 | /// Uses `input_string`. |
| 841 | /// Tries to look up the Decl component-wise but then falls back to a file path | ||
| 842 | /// based scan. | ||
| 558 | export fn find_decl() Decl.Index { | 843 | export fn find_decl() Decl.Index { |
| 844 | const result = Decl.find(input_string.items); | ||
| 845 | if (result != .none) return result; | ||
| 846 | |||
| 559 | const g = struct { | 847 | const g = struct { |
| 560 | var match_fqn: std.ArrayListUnmanaged(u8) = .{}; | 848 | var match_fqn: std.ArrayListUnmanaged(u8) = .{}; |
| 561 | }; | 849 | }; |
| 562 | for (Walk.decls.items, 0..) |*decl, decl_index| { | 850 | for (Walk.decls.items, 0..) |*decl, decl_index| { |
| 851 | g.match_fqn.clearRetainingCapacity(); | ||
| 563 | decl.fqn(&g.match_fqn) catch @panic("OOM"); | 852 | decl.fqn(&g.match_fqn) catch @panic("OOM"); |
| 564 | if (std.mem.eql(u8, g.match_fqn.items, input_string.items)) { | 853 | if (std.mem.eql(u8, g.match_fqn.items, input_string.items)) { |
| 565 | //const path = @as(Decl.Index, @enumFromInt(decl_index)).get().file.path(); | 854 | //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 | ... | @@ -583,7 +872,7 @@ export fn categorize_decl(decl_index: Decl.Index, resolve_alias_count: usize) Wa |
| 583 | var decl = decl_index.get(); | 872 | var decl = decl_index.get(); |
| 584 | while (true) { | 873 | while (true) { |
| 585 | const result = decl.categorize(); | 874 | const result = decl.categorize(); |
| 586 | switch (decl.categorize()) { | 875 | switch (result) { |
| 587 | .alias => |new_index| { | 876 | .alias => |new_index| { |
| 588 | assert(new_index != .none); | 877 | assert(new_index != .none); |
| 589 | global_aliasee = new_index; | 878 | global_aliasee = new_index; |
| ... | @@ -599,6 +888,10 @@ export fn categorize_decl(decl_index: Decl.Index, resolve_alias_count: usize) Wa | ... | @@ -599,6 +888,10 @@ export fn categorize_decl(decl_index: Decl.Index, resolve_alias_count: usize) Wa |
| 599 | } | 888 | } |
| 600 | } | 889 | } |
| 601 | 890 | ||
| 891 | export fn type_fn_members(parent: Decl.Index, include_private: bool) Slice(Decl.Index) { | ||
| 892 | return namespace_members(parent, include_private); | ||
| 893 | } | ||
| 894 | |||
| 602 | export fn namespace_members(parent: Decl.Index, include_private: bool) Slice(Decl.Index) { | 895 | export fn namespace_members(parent: Decl.Index, include_private: bool) Slice(Decl.Index) { |
| 603 | const g = struct { | 896 | const g = struct { |
| 604 | var members: std.ArrayListUnmanaged(Decl.Index) = .{}; | 897 | var members: std.ArrayListUnmanaged(Decl.Index) = .{}; |
| ... | @@ -617,10 +910,18 @@ export fn namespace_members(parent: Decl.Index, include_private: bool) Slice(Dec | ... | @@ -617,10 +910,18 @@ export fn namespace_members(parent: Decl.Index, include_private: bool) Slice(Dec |
| 617 | return Slice(Decl.Index).init(g.members.items); | 910 | return Slice(Decl.Index).init(g.members.items); |
| 618 | } | 911 | } |
| 619 | 912 | ||
| 913 | const RenderSourceOptions = struct { | ||
| 914 | skip_doc_comments: bool = false, | ||
| 915 | skip_comments: bool = false, | ||
| 916 | collapse_whitespace: bool = false, | ||
| 917 | fn_link: Decl.Index = .none, | ||
| 918 | }; | ||
| 919 | |||
| 620 | fn file_source_html( | 920 | fn file_source_html( |
| 621 | file_index: Walk.File.Index, | 921 | file_index: Walk.File.Index, |
| 622 | out: *std.ArrayListUnmanaged(u8), | 922 | out: *std.ArrayListUnmanaged(u8), |
| 623 | root_node: Ast.Node.Index, | 923 | root_node: Ast.Node.Index, |
| 924 | options: RenderSourceOptions, | ||
| 624 | ) !void { | 925 | ) !void { |
| 625 | const ast = file_index.get_ast(); | 926 | const ast = file_index.get_ast(); |
| 626 | const file = file_index.get(); | 927 | const file = file_index.get(); |
| ... | @@ -631,6 +932,7 @@ fn file_source_html( | ... | @@ -631,6 +932,7 @@ fn file_source_html( |
| 631 | 932 | ||
| 632 | const token_tags = ast.tokens.items(.tag); | 933 | const token_tags = ast.tokens.items(.tag); |
| 633 | const token_starts = ast.tokens.items(.start); | 934 | const token_starts = ast.tokens.items(.start); |
| 935 | const main_tokens = ast.nodes.items(.main_token); | ||
| 634 | 936 | ||
| 635 | const start_token = ast.firstToken(root_node); | 937 | const start_token = ast.firstToken(root_node); |
| 636 | const end_token = ast.lastToken(root_node) + 1; | 938 | const end_token = ast.lastToken(root_node) + 1; |
| ... | @@ -643,7 +945,20 @@ fn file_source_html( | ... | @@ -643,7 +945,20 @@ fn file_source_html( |
| 643 | start_token.., | 945 | start_token.., |
| 644 | ) |tag, start, token_index| { | 946 | ) |tag, start, token_index| { |
| 645 | const between = ast.source[cursor..start]; | 947 | const between = ast.source[cursor..start]; |
| 646 | try appendEscaped(out, between); | 948 | if (std.mem.trim(u8, between, " \t\r\n").len > 0) { |
| 949 | if (!options.skip_comments) { | ||
| 950 | try out.appendSlice(gpa, "<span class=\"tok-comment\">"); | ||
| 951 | try appendEscaped(out, between); | ||
| 952 | try out.appendSlice(gpa, "</span>"); | ||
| 953 | } | ||
| 954 | } else if (between.len > 0) { | ||
| 955 | if (options.collapse_whitespace) { | ||
| 956 | if (out.items.len > 0 and out.items[out.items.len - 1] != ' ') | ||
| 957 | try out.append(gpa, ' '); | ||
| 958 | } else { | ||
| 959 | try out.appendSlice(gpa, between); | ||
| 960 | } | ||
| 961 | } | ||
| 647 | if (tag == .eof) break; | 962 | if (tag == .eof) break; |
| 648 | const slice = ast.tokenSlice(token_index); | 963 | const slice = ast.tokenSlice(token_index); |
| 649 | cursor = start + slice.len; | 964 | cursor = start + slice.len; |
| ... | @@ -723,17 +1038,36 @@ fn file_source_html( | ... | @@ -723,17 +1038,36 @@ fn file_source_html( |
| 723 | .doc_comment, | 1038 | .doc_comment, |
| 724 | .container_doc_comment, | 1039 | .container_doc_comment, |
| 725 | => { | 1040 | => { |
| 726 | try out.appendSlice(gpa, "<span class=\"tok-comment\">"); | 1041 | if (!options.skip_doc_comments) { |
| 727 | try appendEscaped(out, slice); | 1042 | try out.appendSlice(gpa, "<span class=\"tok-comment\">"); |
| 728 | try out.appendSlice(gpa, "</span>"); | 1043 | try appendEscaped(out, slice); |
| 1044 | try out.appendSlice(gpa, "</span>"); | ||
| 1045 | } | ||
| 729 | }, | 1046 | }, |
| 730 | 1047 | ||
| 731 | .identifier => i: { | 1048 | .identifier => i: { |
| 732 | if (std.mem.eql(u8, slice, "undefined") or | 1049 | if (options.fn_link != .none) { |
| 733 | std.mem.eql(u8, slice, "null") or | 1050 | const fn_link = options.fn_link.get(); |
| 734 | std.mem.eql(u8, slice, "true") or | 1051 | const fn_token = main_tokens[fn_link.ast_node]; |
| 735 | std.mem.eql(u8, slice, "false")) | 1052 | if (token_index == fn_token + 1) { |
| 736 | { | 1053 | try out.appendSlice(gpa, "<a class=\"tok-fn\" href=\"#"); |
| 1054 | _ = missing_feature_url_escape; | ||
| 1055 | try fn_link.fqn(out); | ||
| 1056 | try out.appendSlice(gpa, "\">"); | ||
| 1057 | try appendEscaped(out, slice); | ||
| 1058 | try out.appendSlice(gpa, "</a>"); | ||
| 1059 | break :i; | ||
| 1060 | } | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | if (token_index > 0 and token_tags[token_index - 1] == .keyword_fn) { | ||
| 1064 | try out.appendSlice(gpa, "<span class=\"tok-fn\">"); | ||
| 1065 | try appendEscaped(out, slice); | ||
| 1066 | try out.appendSlice(gpa, "</span>"); | ||
| 1067 | break :i; | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | if (Walk.isPrimitiveNonType(slice)) { | ||
| 737 | try out.appendSlice(gpa, "<span class=\"tok-null\">"); | 1071 | try out.appendSlice(gpa, "<span class=\"tok-null\">"); |
| 738 | try appendEscaped(out, slice); | 1072 | try appendEscaped(out, slice); |
| 739 | try out.appendSlice(gpa, "</span>"); | 1073 | try out.appendSlice(gpa, "</span>"); |
| ... | @@ -752,7 +1086,8 @@ fn file_source_html( | ... | @@ -752,7 +1086,8 @@ fn file_source_html( |
| 752 | try walk_field_accesses(file_index, &g.field_access_buffer, field_access_node); | 1086 | try walk_field_accesses(file_index, &g.field_access_buffer, field_access_node); |
| 753 | if (g.field_access_buffer.items.len > 0) { | 1087 | if (g.field_access_buffer.items.len > 0) { |
| 754 | try out.appendSlice(gpa, "<a href=\"#"); | 1088 | try out.appendSlice(gpa, "<a href=\"#"); |
| 755 | try out.appendSlice(gpa, g.field_access_buffer.items); // TODO url escape | 1089 | _ = missing_feature_url_escape; |
| 1090 | try out.appendSlice(gpa, g.field_access_buffer.items); | ||
| 756 | try out.appendSlice(gpa, "\">"); | 1091 | try out.appendSlice(gpa, "\">"); |
| 757 | try appendEscaped(out, slice); | 1092 | try appendEscaped(out, slice); |
| 758 | try out.appendSlice(gpa, "</a>"); | 1093 | try out.appendSlice(gpa, "</a>"); |
| ... | @@ -767,7 +1102,8 @@ fn file_source_html( | ... | @@ -767,7 +1102,8 @@ fn file_source_html( |
| 767 | try resolve_ident_link(file_index, &g.field_access_buffer, token_index); | 1102 | try resolve_ident_link(file_index, &g.field_access_buffer, token_index); |
| 768 | if (g.field_access_buffer.items.len > 0) { | 1103 | if (g.field_access_buffer.items.len > 0) { |
| 769 | try out.appendSlice(gpa, "<a href=\"#"); | 1104 | try out.appendSlice(gpa, "<a href=\"#"); |
| 770 | try out.appendSlice(gpa, g.field_access_buffer.items); // TODO url escape | 1105 | _ = missing_feature_url_escape; |
| 1106 | try out.appendSlice(gpa, g.field_access_buffer.items); | ||
| 771 | try out.appendSlice(gpa, "\">"); | 1107 | try out.appendSlice(gpa, "\">"); |
| 772 | try appendEscaped(out, slice); | 1108 | try appendEscaped(out, slice); |
| 773 | try out.appendSlice(gpa, "</a>"); | 1109 | try out.appendSlice(gpa, "</a>"); |
| ... | @@ -860,7 +1196,10 @@ fn resolve_ident_link( | ... | @@ -860,7 +1196,10 @@ fn resolve_ident_link( |
| 860 | ) Oom!void { | 1196 | ) Oom!void { |
| 861 | const decl_index = file_index.get().lookup_token(ident_token); | 1197 | const decl_index = file_index.get().lookup_token(ident_token); |
| 862 | if (decl_index == .none) return; | 1198 | if (decl_index == .none) return; |
| 1199 | try resolve_decl_link(decl_index, out); | ||
| 1200 | } | ||
| 863 | 1201 | ||
| 1202 | fn resolve_decl_link(decl_index: Decl.Index, out: *std.ArrayListUnmanaged(u8)) Oom!void { | ||
| 864 | const decl = decl_index.get(); | 1203 | const decl = decl_index.get(); |
| 865 | switch (decl.categorize()) { | 1204 | switch (decl.categorize()) { |
| 866 | .alias => |alias_decl| try alias_decl.get().fqn(out), | 1205 | .alias => |alias_decl| try alias_decl.get().fqn(out), |
lib/docs/wasm/markdown.zig+8-1| ... | @@ -517,6 +517,10 @@ test "tables require leading and trailing pipes" { | ... | @@ -517,6 +517,10 @@ test "tables require leading and trailing pipes" { |
| 517 | \\ | 517 | \\ |
| 518 | \\| But | this | is | | 518 | \\| But | this | is | |
| 519 | \\ | 519 | \\ |
| 520 | \\Also not a table: | ||
| 521 | \\| | ||
| 522 | \\ | | ||
| 523 | \\ | ||
| 520 | , | 524 | , |
| 521 | \\<p>Not | a | table</p> | 525 | \\<p>Not | a | table</p> |
| 522 | \\<table> | 526 | \\<table> |
| ... | @@ -526,6 +530,9 @@ test "tables require leading and trailing pipes" { | ... | @@ -526,6 +530,9 @@ test "tables require leading and trailing pipes" { |
| 526 | \\<td>is</td> | 530 | \\<td>is</td> |
| 527 | \\</tr> | 531 | \\</tr> |
| 528 | \\</table> | 532 | \\</table> |
| 533 | \\<p>Also not a table: | ||
| 534 | \\| | ||
| 535 | \\|</p> | ||
| 529 | \\ | 536 | \\ |
| 530 | ); | 537 | ); |
| 531 | } | 538 | } |
| ... | @@ -584,7 +591,7 @@ test "code blocks" { | ... | @@ -584,7 +591,7 @@ test "code blocks" { |
| 584 | \\<pre><code>Hello, world! | 591 | \\<pre><code>Hello, world! |
| 585 | \\This is some code. | 592 | \\This is some code. |
| 586 | \\</code></pre> | 593 | \\</code></pre> |
| 587 | \\<pre><code class="zig test">const std = @import(&quot;std&quot;); | 594 | \\<pre><code>const std = @import(&quot;std&quot;); |
| 588 | \\ | 595 | \\ |
| 589 | \\test { | 596 | \\test { |
| 590 | \\ try std.testing.expect(2 + 2 == 4); | 597 | \\ try std.testing.expect(2 + 2 == 4); |
lib/docs/wasm/markdown/Parser.zig+2-1| ... | @@ -610,7 +610,8 @@ const TableRowStart = struct { | ... | @@ -610,7 +610,8 @@ const TableRowStart = struct { |
| 610 | }; | 610 | }; |
| 611 | 611 | ||
| 612 | fn startTableRow(unindented_line: []const u8) ?TableRowStart { | 612 | fn startTableRow(unindented_line: []const u8) ?TableRowStart { |
| 613 | if (!mem.startsWith(u8, unindented_line, "|") or | 613 | if (unindented_line.len < 2 or |
| 614 | !mem.startsWith(u8, unindented_line, "|") or | ||
| 614 | mem.endsWith(u8, unindented_line, "\\|") or | 615 | mem.endsWith(u8, unindented_line, "\\|") or |
| 615 | !mem.endsWith(u8, unindented_line, "|")) return null; | 616 | !mem.endsWith(u8, unindented_line, "|")) return null; |
| 616 | 617 |
lib/docs/wasm/markdown/renderer.zig+1-6| ... | @@ -112,13 +112,8 @@ pub fn Renderer(comptime Writer: type, comptime Context: type) type { | ... | @@ -112,13 +112,8 @@ pub fn Renderer(comptime Writer: type, comptime Context: type) type { |
| 112 | try writer.print("</h{}>\n", .{data.heading.level}); | 112 | try writer.print("</h{}>\n", .{data.heading.level}); |
| 113 | }, | 113 | }, |
| 114 | .code_block => { | 114 | .code_block => { |
| 115 | const tag = doc.string(data.code_block.tag); | ||
| 116 | const content = doc.string(data.code_block.content); | 115 | const content = doc.string(data.code_block.content); |
| 117 | if (tag.len > 0) { | 116 | try writer.print("<pre><code>{}</code></pre>\n", .{fmtHtml(content)}); |
| 118 | try writer.print("<pre><code class=\"{}\">{}</code></pre>\n", .{ fmtHtml(tag), fmtHtml(content) }); | ||
| 119 | } else { | ||
| 120 | try writer.print("<pre><code>{}</code></pre>\n", .{fmtHtml(content)}); | ||
| 121 | } | ||
| 122 | }, | 117 | }, |
| 123 | .blockquote => { | 118 | .blockquote => { |
| 124 | try writer.writeAll("<blockquote>\n"); | 119 | try writer.writeAll("<blockquote>\n"); |