zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 0bbca2b3 (103897B) - Raw


      1 'use strict';
      2 
      3 /**
      4  * @typedef {
      5     | "Type"
      6     | "Void"
      7     | "Bool"
      8     | "NoReturn"
      9     | "Int"
     10     | "Float"
     11     | "Pointer"
     12     | "Array"
     13     | "Struct"
     14     | "ComptimeFloat"
     15     | "ComptimeInt"
     16     | "Undefined"
     17     | "Null"
     18     | "Optional"
     19     | "ErrorUnion"
     20     | "ErrorSet"
     21     | "Enum"
     22     | "Union"
     23     | "Fn"
     24     | "BoundFn"
     25     | "Opaque"
     26     | "Frame"
     27     | "AnyFrame"
     28     | "Vector"
     29     | "EnumLiteral"
     30     | "ComptimeExpr"
     31     | "Unanalyzed"
     32    } TypeKind
     33 */
     34 
     35 /**
     36  * @typedef {{
     37      typeRef: Expr?,
     38      expr: Expr,
     39    }} WalkResult
     40 */
     41 
     42 /**
     43  * @typedef {{
     44      void: {},
     45      unreachable: {},
     46      anytype: {},
     47      type: number,
     48      comptimeExpr: number,
     49      call: number,
     50      int: number,
     51      float: number,
     52      bool: boolean,
     53      undefined: WalkResult,
     54      null: WalkResult,
     55      typeOf: WalkResult,
     56      compileError: string
     57      string: string,
     58      struct: Expr[],
     59      refPath: Expr[],
     60      declRef: number,
     61      array: ZigArray,
     62      enumLiteral: string,
     63    }} Expr
     64 */
     65 
     66 /**
     67  * @typedef {{
     68       kind: number,
     69       name: string,
     70       src: number,
     71       privDecls: number[],
     72       pubDecls: number[],
     73       fields: WalkResult[]
     74 }} ContainerType
     75 */
     76 
     77 /**
     78  * @typedef {{
     79       kind: number,
     80       name: string,
     81       src: number,
     82       ret: WalkResult,
     83       params: WalkResult[],
     84       generic: boolean,
     85  }} Fn
     86 */
     87 
     88 /**
     89 * @typedef {{
     90      kind: number,
     91      name: string,
     92      fields: { name: string, docs: string }[]
     93      fn: number | undefined,
     94 }} ErrSetType
     95 */
     96 
     97 /**
     98 * @typedef {{
     99      kind: number,
    100      err: WalkResult,
    101      payload: WalkResult,
    102 }} ErrUnionType
    103 */
    104 
    105 // Type, Void, Bool, NoReturn, Int, Float, ComptimeExpr, ComptimeFloat, ComptimeInt, Undefined, Null, ErrorUnion, BoundFn, Opaque, Frame, AnyFrame, Vector, EnumLiteral
    106 /**
    107 * @typedef {{
    108      kind: number,
    109      name: string
    110 }} NumberType
    111 */
    112 
    113 /**
    114 * @typedef {{
    115      kind: number,
    116      size: number,
    117      child: WalkResult
    118      align: number,
    119      bitOffsetInHost: number,
    120      hostIntBytes: number,
    121      volatile: boolean,
    122      const: boolean,
    123 }} PointerType
    124 */
    125 
    126 /**
    127 * @typedef {{
    128      kind: number,
    129      len: WalkResult
    130      child: WalkResult
    131 }} ArrayType
    132 */
    133 
    134 /**
    135 * @typedef {{
    136      kind: number,
    137      name: string,
    138      child: Expr,
    139 }} OptionalType
    140 */
    141 
    142 /**
    143  * @typedef {
    144     | OptionalType
    145     | ArrayType
    146     | PointerType
    147     | ContainerType
    148     | Fn
    149     | ErrSetType
    150     | ErrUnionType
    151     | NumberType
    152    } Type
    153 */
    154 
    155 
    156 /**
    157  * @typedef {{
    158        func: Expr,
    159        args: Expr[],
    160        ret: Expr,
    161    }} Call
    162 */
    163 
    164 /**
    165  * @typedef {{
    166        file: number,
    167        line: number,
    168        col: number,
    169        name?: string,
    170        docs?: string,
    171        fields?: number[],
    172        comptime: boolean,
    173        noalias: boolean,
    174        varArgs: boolean,
    175    }} AstNode
    176 */
    177 
    178 /**
    179  * @typedef {{
    180       name: string,
    181       kind: string,
    182       src: number,
    183       value: WalkResult,
    184       decltest?: number,
    185       isTest: boolean,
    186    }} Decl
    187 */
    188 
    189 /**
    190  * @typedef {{
    191       name: string,
    192       file: number,
    193       main: number,
    194       table: Record<string, number>,
    195    }} Package
    196 */
    197 
    198 /**
    199  * @typedef {{
    200       typeRef: WalkResult,
    201       data: WalkResult[],
    202   }} ZigArray
    203 */
    204 
    205 /**
    206  * @typedef {{
    207       code: string,
    208       typeRef: WalkResult,
    209    }} ComptimeExpr
    210 */
    211 
    212 /**
    213  * @typedef {{
    214        typeKinds: TypeKind[];
    215        rootPkg: number;
    216        params: {
    217            zigId: string;
    218            zigVersion: string;
    219            target: string;
    220            rootName: string;
    221            builds: { target: string };
    222        };
    223        packages: Package[];
    224        errors: {};
    225        astNodes: AstNode[];
    226        calls: Call[];
    227        files: Record<string, string>;
    228        types: Type[];
    229        decls: Decl[];
    230        comptimeExprs: ComptimeExpr[];
    231        fns: Fn[];
    232    }} DocData
    233 */
    234 
    235 /** @type {DocData} */
    236 var zigAnalysis;
    237 
    238 (function() {
    239     let domStatus = /** @type HTMLElement */(document.getElementById("status"));
    240     let domSectNav = /** @type HTMLElement */(document.getElementById("sectNav"));
    241     let domListNav = /** @type HTMLElement */(document.getElementById("listNav"));
    242     let domSectMainPkg = /** @type HTMLElement */(document.getElementById("sectMainPkg"));
    243     let domSectPkgs = /** @type HTMLElement */(document.getElementById("sectPkgs"));
    244     let domListPkgs = /** @type HTMLElement */(document.getElementById("listPkgs"));
    245     let domSectTypes = /** @type HTMLElement */(document.getElementById("sectTypes"));
    246     let domListTypes = /** @type HTMLElement */(document.getElementById("listTypes"));
    247     let domSectTests = /** @type HTMLElement */(document.getElementById("sectTests"));
    248     let domListTests = /** @type HTMLElement */(document.getElementById("listTests"));
    249     let domSectNamespaces = /** @type HTMLElement */(document.getElementById("sectNamespaces"));
    250     let domListNamespaces = /** @type HTMLElement */(document.getElementById("listNamespaces"));
    251     let domSectErrSets = /** @type HTMLElement */(document.getElementById("sectErrSets"));
    252     let domListErrSets = /** @type HTMLElement */(document.getElementById("listErrSets"));
    253     let domSectFns = /** @type HTMLElement */(document.getElementById("sectFns"));
    254     let domListFns = /** @type HTMLElement */(document.getElementById("listFns"));
    255     let domSectFields = /** @type HTMLElement */(document.getElementById("sectFields"));
    256     let domListFields = /** @type HTMLElement */(document.getElementById("listFields"));
    257     let domSectGlobalVars = /** @type HTMLElement */(document.getElementById("sectGlobalVars"));
    258     let domListGlobalVars = /** @type HTMLElement */(document.getElementById("listGlobalVars"));
    259     let domSectValues = /** @type HTMLElement */(document.getElementById("sectValues"));
    260     let domListValues = /** @type HTMLElement */(document.getElementById("listValues"));
    261     let domFnProto = /** @type HTMLElement */(document.getElementById("fnProto"));
    262     let domFnProtoCode = /** @type HTMLElement */(document.getElementById("fnProtoCode"));
    263     let domSectParams = /** @type HTMLElement */(document.getElementById("sectParams"));
    264     let domListParams = /** @type HTMLElement */(document.getElementById("listParams"));
    265     let domTldDocs = /** @type HTMLElement */(document.getElementById("tldDocs"));
    266     let domSectFnErrors = /** @type HTMLElement */(document.getElementById("sectFnErrors"));
    267     let domListFnErrors = /** @type HTMLElement */(document.getElementById("listFnErrors"));
    268     let domTableFnErrors =/** @type HTMLElement */(document.getElementById("tableFnErrors"));
    269     let domFnErrorsAnyError = /** @type HTMLElement */(document.getElementById("fnErrorsAnyError"));
    270     let domFnExamples = /** @type HTMLElement */(document.getElementById("fnExamples"));
    271     // let domListFnExamples = /** @type HTMLElement */(document.getElementById("listFnExamples"));
    272     let domFnNoExamples = /** @type HTMLElement */(document.getElementById("fnNoExamples"));
    273     let domDeclNoRef = /** @type HTMLElement */(document.getElementById("declNoRef"));
    274     let domSearch = /** @type HTMLInputElement */(document.getElementById("search"));
    275     let domSectSearchResults = /** @type HTMLElement */(document.getElementById("sectSearchResults"));
    276 
    277     let domListSearchResults = /** @type HTMLElement */(document.getElementById("listSearchResults"));
    278     let domSectSearchNoResults = /** @type HTMLElement */(document.getElementById("sectSearchNoResults"));
    279     let domSectInfo = /** @type HTMLElement */(document.getElementById("sectInfo"));
    280     // let domTdTarget = /** @type HTMLElement */(document.getElementById("tdTarget"));
    281     let domPrivDeclsBox = /** @type HTMLInputElement */(document.getElementById("privDeclsBox"));
    282     let domTdZigVer = /** @type HTMLElement */(document.getElementById("tdZigVer"));
    283     let domHdrName = /** @type HTMLElement */(document.getElementById("hdrName"));
    284     let domHelpModal = /** @type HTMLElement */(document.getElementById("helpDialog"));
    285 
    286     /** @type number | null */
    287     let searchTimer = null;
    288 
    289     /** @type Object<string, string> */
    290     let escapeHtmlReplacements = { "&": "&amp;", '"': "&quot;", "<": "&lt;", ">": "&gt;" };
    291 
    292     let typeKinds = /** @type {Record<string, number>} */(indexTypeKinds());
    293     let typeTypeId = /** @type {number} */ (findTypeTypeId());
    294     let pointerSizeEnum = { One: 0, Many: 1, Slice: 2, C: 3 };
    295 
    296     // for each package, is an array with packages to get to this one
    297     let canonPkgPaths = computeCanonicalPackagePaths();
    298 
    299     /** @typedef {{declNames: string[], pkgNames: string[]}} CanonDecl */
    300 
    301     // for each decl, is an array with {declNames, pkgNames} to get to this one
    302     /** @type CanonDecl[] | null */
    303     let canonDeclPaths = null; // lazy; use getCanonDeclPath
    304 
    305     // for each type, is an array with {declNames, pkgNames} to get to this one
    306     /** @type  number[] | null */
    307     let canonTypeDecls = null; // lazy; use getCanonTypeDecl
    308 
    309     /** @typedef {{
    310     *       showPrivDecls: boolean,
    311     *       pkgNames: string[],
    312     *       pkgObjs: Package[],
    313     *       declNames: string[],
    314     *       declObjs: (Decl | Type)[],
    315     *       callName: any,
    316     *   }} CurNav
    317     */
    318 
    319     /** @type {CurNav} */
    320     let curNav = {
    321         showPrivDecls: false,
    322         // each element is a package name, e.g. @import("a") then within there @import("b")
    323         // starting implicitly from root package
    324         pkgNames: [],
    325         // same as above except actual packages, not names
    326         pkgObjs: [],
    327         // Each element is a decl name, `a.b.c`, a is 0, b is 1, c is 2, etc.
    328         // empty array means refers to the package itself
    329         declNames: [],
    330         // these will be all types, except the last one may be a type or a decl
    331         declObjs: [],
    332 
    333         // (a, b, c, d) comptime call; result is the value the docs refer to
    334         callName: null,
    335     };
    336 
    337     let curNavSearch = "";
    338     let curSearchIndex = -1;
    339     let imFeelingLucky = false;
    340 
    341     let rootIsStd = detectRootIsStd();
    342 
    343     // map of decl index to list of non-generic fn indexes
    344     // let nodesToFnsMap = indexNodesToFns();
    345     // map of decl index to list of comptime fn calls
    346     // let nodesToCallsMap = indexNodesToCalls();
    347 
    348     domSearch.addEventListener('keydown', onSearchKeyDown, false);
    349     domPrivDeclsBox.addEventListener('change', function() {
    350         if (this.checked != curNav.showPrivDecls) {
    351             if (this.checked && location.hash.length > 1 && location.hash[1] != '*'){
    352                 location.hash = "#*" + location.hash.substring(1);
    353                 return;
    354             }
    355             if (!this.checked && location.hash.length > 1 && location.hash[1] == '*') {
    356                 location.hash = "#" + location.hash.substring(2);
    357                 return;
    358             }
    359         }
    360     }, false);
    361     window.addEventListener('hashchange', onHashChange, false);
    362     window.addEventListener('keydown', onWindowKeyDown, false);
    363     onHashChange();
    364 
    365     function renderTitle() {
    366         let list = curNav.pkgNames.concat(curNav.declNames);
    367         let suffix = " - Zig";
    368         if (list.length === 0) {
    369             if (rootIsStd) {
    370                 document.title = "std" + suffix;
    371             } else {
    372                 document.title = zigAnalysis.params.rootName + suffix;
    373             }
    374         } else {
    375             document.title = list.join('.') + suffix;
    376         }
    377     }
    378 
    379     /** @param {Type | Decl} x */
    380     function isDecl(x) {
    381         return "value" in x;
    382     }
    383 
    384     /** @param {Type | Decl} x */
    385     function isType(x) {
    386         return "kind" in x && !("value" in x);
    387     }
    388 
    389     /** @param {Type | Decl} x */
    390     function isContainerType(x) {
    391         return isType(x) && typeKindIsContainer(/** @type {Type} */(x).kind) ;
    392     }
    393 
    394     /** @param {Expr} expr */
    395     function typeShorthandName(expr) {
    396         let resolvedExpr = resolveValue({expr: expr});
    397         if (!("type" in resolvedExpr)) {
    398             return null;
    399         }
    400         let type = /** @type {Type} */(zigAnalysis.types[resolvedExpr.type]);
    401 
    402         outer: for (let i = 0; i < 10000; i += 1) {
    403             switch (type.kind) {
    404                 case typeKinds.Optional:
    405                 case typeKinds.Pointer:
    406                     let child = /** @type {PointerType | OptionalType} */(type).child;
    407                     let resolvedChild = resolveValue(child);
    408                     if ("type" in resolvedChild) {
    409                         type = zigAnalysis.types[resolvedChild.type];
    410                         continue;
    411                     } else {
    412                         return null;
    413                     }
    414                 default:
    415                     break outer;
    416             }
    417 
    418             if (i == 9999) throw "Exhausted typeShorthandName quota";
    419         }
    420 
    421 
    422 
    423         let name = undefined;
    424         if (type.kind === typeKinds.Struct) {
    425             name = "struct";
    426         } else if (type.kind === typeKinds.Enum) {
    427             name = "enum";
    428         } else if (type.kind === typeKinds.Union) {
    429             name = "union";
    430         } else {
    431             console.log("TODO: unhalndled case in typeShortName");
    432             return null;
    433         }
    434 
    435         return escapeHtml(name);
    436     }
    437 
    438     /** @param {number} typeKind */
    439     function typeKindIsContainer(typeKind) {
    440         return typeKind === typeKinds.Struct ||
    441             typeKind === typeKinds.Union ||
    442             typeKind === typeKinds.Enum;
    443     }
    444 
    445     /** @param {number} typeKind */
    446     function declCanRepresentTypeKind(typeKind) {
    447         return typeKind === typeKinds.ErrorSet || typeKindIsContainer(typeKind);
    448     }
    449 
    450     // /**
    451     //     * @param {WalkResult[]} path
    452     //     * @return {WalkResult | null}
    453     // */
    454     // function findCteInRefPath(path) {
    455     //     for (let i = path.length - 1; i >= 0; i -= 1) {
    456     //         const ref = path[i];
    457     //         if ("string" in ref) continue;
    458     //         if ("comptimeExpr" in ref) return ref;
    459     //         if ("refPath" in ref) return findCteInRefPath(ref.refPath);
    460     //         return null;
    461     //     }
    462 
    463     //     return null;
    464     // }
    465 
    466     /**
    467         * @param {WalkResult} value
    468         * @return {WalkResult}
    469     */
    470     function resolveValue(value) {
    471         let i = 0;
    472         while(i < 1000) {
    473             i += 1;
    474 
    475             if ("refPath" in value.expr) {
    476                 value = {expr: value.expr.refPath[value.expr.refPath.length -1]};
    477                 continue;
    478             }
    479 
    480             if ("declRef" in value.expr) {
    481                 value = zigAnalysis.decls[value.expr.declRef].value;
    482                 continue;
    483             }
    484 
    485 //            if ("as" in value.expr) {
    486 //                value = {
    487 //                  typeRef: zigAnalysis.exprs[value.expr.as.typeRefArg],
    488 //                  expr: zigAnalysis.exprs[value.expr.as.exprArg],
    489 //                };
    490 //                continue;
    491 //            }
    492 
    493             return value;
    494 
    495         }
    496         console.assert(false);
    497         return /** @type {WalkResult} */({});
    498     }
    499 
    500     /**
    501         * @param {Decl} decl
    502         * @return {WalkResult}
    503     */
    504 //    function typeOfDecl(decl){
    505 //        return decl.value.typeRef;
    506 //
    507 //        let i = 0;
    508 //        while(i < 1000) {
    509 //            i += 1;
    510 //            console.assert(isDecl(decl));
    511 //            if ("type" in decl.value) {
    512 //                return /** @type {WalkResult} */({ type: typeTypeId });
    513 //            }
    514 //
    515 ////            if ("string" in decl.value) {
    516 ////                return /** @type {WalkResult} */({ type: {
    517 ////                  kind: typeKinds.Pointer,
    518 ////                  size: pointerSizeEnum.One,
    519 ////                  child: });
    520 ////            }
    521 //
    522 //            if ("refPath" in decl.value) {
    523 //                decl =  /** @type {Decl} */({
    524 //                  value: decl.value.refPath[decl.value.refPath.length -1]
    525 //                });
    526 //                continue;
    527 //            }
    528 //
    529 //            if ("declRef" in decl.value) {
    530 //                decl = zigAnalysis.decls[decl.value.declRef];
    531 //                continue;
    532 //            }
    533 //
    534 //            if ("int" in decl.value) {
    535 //                return decl.value.int.typeRef;
    536 //            }
    537 //
    538 //            if ("float" in decl.value) {
    539 //                return decl.value.float.typeRef;
    540 //            }
    541 //
    542 //            if ("array" in decl.value) {
    543 //                return decl.value.array.typeRef;
    544 //            }
    545 //
    546 //            if ("struct" in decl.value) {
    547 //                return decl.value.struct.typeRef;
    548 //            }
    549 //
    550 //            if ("comptimeExpr" in decl.value) {
    551 //                const cte = zigAnalysis.comptimeExprs[decl.value.comptimeExpr];
    552 //                return cte.typeRef;
    553 //            }
    554 //
    555 //            if ("call" in decl.value) {
    556 //                const fn_call = zigAnalysis.calls[decl.value.call];
    557 //                let fn_decl = undefined;
    558 //                if ("declRef" in fn_call.func) {
    559 //                    fn_decl = zigAnalysis.decls[fn_call.func.declRef];
    560 //                } else if ("refPath" in fn_call.func) {
    561 //                    console.assert("declRef" in fn_call.func.refPath[fn_call.func.refPath.length -1]);
    562 //                    fn_decl = zigAnalysis.decls[fn_call.func.refPath[fn_call.func.refPath.length -1].declRef];
    563 //                } else throw {};
    564 //
    565 //                const fn_decl_value = resolveValue(fn_decl.value);
    566 //                console.assert("type" in fn_decl_value); //TODO handle comptimeExpr
    567 //                const fn_type = /** @type {Fn} */(zigAnalysis.types[fn_decl_value.type]);
    568 //                console.assert(fn_type.kind === typeKinds.Fn);
    569 //                return fn_type.ret;
    570 //            }
    571 //
    572 //            if ("void" in decl.value) {
    573 //                return /** @type {WalkResult} */({ type: typeTypeId });
    574 //            }
    575 //
    576 //            if ("bool" in decl.value) {
    577 //                return /** @type {WalkResult} */({ type: typeKinds.Bool });
    578 //            }
    579 //
    580 //            console.log("TODO: handle in `typeOfDecl` more cases: ", decl);
    581 //            console.assert(false);
    582 //            throw {};
    583 //        }
    584 //        console.assert(false);
    585 //        return /** @type {WalkResult} */({});
    586 //    }
    587 
    588     function render() {
    589         domStatus.classList.add("hidden");
    590         domFnProto.classList.add("hidden");
    591         domSectParams.classList.add("hidden");
    592         domTldDocs.classList.add("hidden");
    593         domSectMainPkg.classList.add("hidden");
    594         domSectPkgs.classList.add("hidden");
    595         domSectTypes.classList.add("hidden");
    596         domSectTests.classList.add("hidden");
    597         domSectNamespaces.classList.add("hidden");
    598         domSectErrSets.classList.add("hidden");
    599         domSectFns.classList.add("hidden");
    600         domSectFields.classList.add("hidden");
    601         domSectSearchResults.classList.add("hidden");
    602         domSectSearchNoResults.classList.add("hidden");
    603         domSectInfo.classList.add("hidden");
    604         domHdrName.classList.add("hidden");
    605         domSectNav.classList.add("hidden");
    606         domSectFnErrors.classList.add("hidden");
    607         domFnExamples.classList.add("hidden");
    608         domFnNoExamples.classList.add("hidden");
    609         domDeclNoRef.classList.add("hidden");
    610         domFnErrorsAnyError.classList.add("hidden");
    611         domTableFnErrors.classList.add("hidden");
    612         domSectGlobalVars.classList.add("hidden");
    613         domSectValues.classList.add("hidden");
    614 
    615         renderTitle();
    616         renderInfo();
    617         renderPkgList();
    618 
    619         domPrivDeclsBox.checked = curNav.showPrivDecls;
    620 
    621         if (curNavSearch !== "") {
    622             return renderSearch();
    623         }
    624 
    625         let rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg];
    626         let pkg = rootPkg;
    627         curNav.pkgObjs = [pkg];
    628         for (let i = 0; i < curNav.pkgNames.length; i += 1) {
    629             let childPkg = zigAnalysis.packages[pkg.table[curNav.pkgNames[i]]];
    630             if (childPkg == null) {
    631                 return render404();
    632             }
    633             pkg = childPkg;
    634             curNav.pkgObjs.push(pkg);
    635         }
    636 
    637         /** @type {Decl | Type} */
    638         let currentType = zigAnalysis.types[pkg.main];
    639         curNav.declObjs = [currentType];
    640         for (let i = 0; i < curNav.declNames.length; i += 1) {
    641 
    642             /** @type {Decl | Type | null} */
    643             let childDecl = findSubDecl(/** @type {ContainerType} */(currentType), curNav.declNames[i]);
    644             if (childDecl == null) {
    645                 return render404();
    646             }
    647 
    648             let childDeclValue = resolveValue(/** @type {Decl} */(childDecl).value).expr;
    649             if ("type" in childDeclValue) {
    650 
    651                 const t = zigAnalysis.types[childDeclValue.type];
    652                 if (t.kind != typeKinds.Fn) {
    653                     childDecl = t;
    654                 }
    655             }
    656 
    657             currentType = /** @type {Decl | Type} */(childDecl);
    658             curNav.declObjs.push(currentType);
    659         }
    660 
    661         renderNav();
    662 
    663         let last = curNav.declObjs[curNav.declObjs.length - 1];
    664         let lastIsDecl = isDecl(last);
    665         let lastIsType = isType(last);
    666         let lastIsContainerType = isContainerType(last);
    667 
    668         if (lastIsContainerType) {
    669             return renderContainer(/** @type {ContainerType} */(last));
    670         }
    671 
    672         if (!lastIsDecl && !lastIsType) {
    673             return renderUnknownDecl(/** @type {Decl} */(last));
    674         }
    675 
    676         if (lastIsType) {
    677             return renderType(/** @type {Type} */(last));
    678         }
    679 
    680         if (lastIsDecl && last.kind === 'var') {
    681             return renderVar(/** @type {Decl} */(last));
    682         }
    683 
    684         if (lastIsDecl && last.kind === 'const') {
    685             let typeObj = zigAnalysis.types[resolveValue(/** @type {Decl} */(last).value).expr.type];
    686             if (typeObj && typeObj.kind === typeKinds.Fn) {
    687                 return renderFn(/** @type {Decl} */(last));
    688             }
    689 
    690             return renderValue(/** @type {Decl} */(last));
    691         }
    692     }
    693 
    694     /** @param {Decl} decl */
    695     function renderUnknownDecl(decl) {
    696         domDeclNoRef.classList.remove("hidden");
    697 
    698         let docs = zigAnalysis.astNodes[decl.src].docs;
    699         if (docs != null) {
    700             domTldDocs.innerHTML = markdown(docs);
    701         } else {
    702             domTldDocs.innerHTML = '<p>There are no doc comments for this declaration.</p>';
    703         }
    704         domTldDocs.classList.remove("hidden");
    705     }
    706 
    707     /** @param {number} typeIndex */
    708     function typeIsErrSet(typeIndex) {
    709         let typeObj = zigAnalysis.types[typeIndex];
    710         return typeObj.kind === typeKinds.ErrorSet;
    711     }
    712 
    713     /** @param {number} typeIndex */
    714     function typeIsStructWithNoFields(typeIndex) {
    715         let typeObj = zigAnalysis.types[typeIndex];
    716         if (typeObj.kind !== typeKinds.Struct)
    717             return false;
    718         return /** @type {ContainerType} */(typeObj).fields.length == 0;
    719     }
    720 
    721     /** @param {number} typeIndex */
    722     function typeIsGenericFn(typeIndex) {
    723         let typeObj = zigAnalysis.types[typeIndex];
    724         if (typeObj.kind !== typeKinds.Fn) {
    725             return false;
    726         }
    727         return  /** @type {Fn} */(typeObj).generic;
    728     }
    729 
    730     /** @param {Decl} fnDecl */
    731     function renderFn(fnDecl) {
    732         if ("refPath" in fnDecl.value.expr) {
    733             let last = fnDecl.value.expr.refPath.length - 1;
    734             let lastExpr = fnDecl.value.expr.refPath[last];
    735             console.assert("declRef" in lastExpr);
    736             fnDecl = zigAnalysis.decls[lastExpr.declRef];
    737         }
    738 
    739         let value = resolveValue(fnDecl.value);
    740         console.assert("type" in value.expr);
    741         let typeObj = /** @type {Fn} */(zigAnalysis.types[value.expr.type]);
    742 
    743         domFnProtoCode.innerHTML = exprName(value.expr, {
    744           wantHtml: true,
    745           wantLink: true,
    746           fnDecl,
    747         });
    748 
    749         let docsSource = null;
    750         let srcNode = zigAnalysis.astNodes[fnDecl.src];
    751         if (srcNode.docs != null) {
    752             docsSource = srcNode.docs;
    753         }
    754 
    755         renderFnParamDocs(fnDecl, typeObj);
    756 
    757         let retExpr = resolveValue({expr:typeObj.ret}).expr;
    758         if ("type" in retExpr) {
    759             let retIndex = retExpr.type;
    760             let errSetTypeIndex = /** @type {number | null} */(null);
    761             let retType = zigAnalysis.types[retIndex];
    762             if (retType.kind === typeKinds.ErrorSet) {
    763                 errSetTypeIndex = retIndex;
    764             } else if (retType.kind === typeKinds.ErrorUnion) {
    765                 errSetTypeIndex = /** @type {ErrUnionType} */(retType).err.type;
    766             }
    767             if (errSetTypeIndex != null) {
    768                 let errSetType = /** @type {ErrSetType} */(zigAnalysis.types[errSetTypeIndex]);
    769                 renderErrorSet(errSetType);
    770             }
    771         }
    772 
    773         let protoSrcIndex = fnDecl.src;
    774         if (typeIsGenericFn(value.expr.type)) {
    775             throw "TODO";
    776             // let instantiations = nodesToFnsMap[protoSrcIndex];
    777             // let calls = nodesToCallsMap[protoSrcIndex];
    778             // if (instantiations == null && calls == null) {
    779             //     domFnNoExamples.classList.remove("hidden");
    780             // } else if (calls != null) {
    781             //     // if (fnObj.combined === undefined) fnObj.combined = allCompTimeFnCallsResult(calls);
    782             //     if (fnObj.combined != null) renderContainer(fnObj.combined);
    783 
    784             //     resizeDomList(domListFnExamples, calls.length, '<li></li>');
    785 
    786             //     for (let callI = 0; callI < calls.length; callI += 1) {
    787             //         let liDom = domListFnExamples.children[callI];
    788             //         liDom.innerHTML = getCallHtml(fnDecl, calls[callI]);
    789             //     }
    790 
    791             //     domFnExamples.classList.remove("hidden");
    792             // } else if (instantiations != null) {
    793             //     // TODO
    794             // }
    795         } else {
    796 
    797             domFnExamples.classList.add("hidden");
    798             domFnNoExamples.classList.add("hidden");
    799         }
    800 
    801         let protoSrcNode = zigAnalysis.astNodes[protoSrcIndex];
    802         if (docsSource == null && protoSrcNode != null && protoSrcNode.docs != null) {
    803             docsSource = protoSrcNode.docs;
    804         }
    805         if (docsSource != null) {
    806             domTldDocs.innerHTML = markdown(docsSource);
    807             domTldDocs.classList.remove("hidden");
    808         }
    809         domFnProto.classList.remove("hidden");
    810     }
    811 
    812     /**
    813     * @param {Decl} fnDecl
    814     * @param {Fn} typeObj
    815     */
    816     function renderFnParamDocs(fnDecl, typeObj) {
    817         let docCount = 0;
    818 
    819         let fnNode = zigAnalysis.astNodes[fnDecl.src];
    820         let fields = /** @type {number[]} */(fnNode.fields);
    821         let isVarArgs = fnNode.varArgs;
    822 
    823         for (let i = 0; i < fields.length; i += 1) {
    824             let field = fields[i];
    825             let fieldNode = zigAnalysis.astNodes[field];
    826             if (fieldNode.docs != null) {
    827                 docCount += 1;
    828             }
    829         }
    830         if (docCount == 0) {
    831             return;
    832         }
    833 
    834         resizeDomList(domListParams, docCount, '<div></div>');
    835         let domIndex = 0;
    836 
    837         for (let i = 0; i < fields.length; i += 1) {
    838             let field = fields[i];
    839             let fieldNode = zigAnalysis.astNodes[field];
    840             if (fieldNode.docs == null) {
    841                 continue;
    842             }
    843             let divDom = domListParams.children[domIndex];
    844             domIndex += 1;
    845 
    846 
    847             let value = typeObj.params[i];
    848             let html = '<pre>' + escapeHtml(/** @type {string} */(fieldNode.name)) + ": ";
    849             if (isVarArgs && i === typeObj.params.length - 1) {
    850                 html += '...';
    851             } else {
    852                 let name = exprName(value, {wantHtml: false, wantLink: false});
    853                 html += '<span class="tok-kw">' + name + '</span>';
    854             }
    855 
    856             html += ',</pre>';
    857 
    858             let docs = fieldNode.docs;
    859             if (docs != null) {
    860                 html += markdown(docs);
    861             }
    862             divDom.innerHTML = html;
    863         }
    864         domSectParams.classList.remove("hidden");
    865     }
    866 
    867     function renderNav() {
    868         let len = curNav.pkgNames.length + curNav.declNames.length;
    869         resizeDomList(domListNav, len, '<li><a href="#"></a></li>');
    870         let list = [];
    871         let hrefPkgNames = [];
    872         let hrefDeclNames = /** @type {string[]} */([]);
    873         for (let i = 0; i < curNav.pkgNames.length; i += 1) {
    874             hrefPkgNames.push(curNav.pkgNames[i]);
    875             list.push({
    876                 name: curNav.pkgNames[i],
    877                 link: navLink(hrefPkgNames, hrefDeclNames),
    878             });
    879         }
    880         for (let i = 0; i < curNav.declNames.length; i += 1) {
    881             hrefDeclNames.push(curNav.declNames[i]);
    882             list.push({
    883                 name: curNav.declNames[i],
    884                 link: navLink(hrefPkgNames, hrefDeclNames),
    885             });
    886         }
    887 
    888         for (let i = 0; i < list.length; i += 1) {
    889             let liDom = domListNav.children[i];
    890             let aDom = liDom.children[0];
    891             aDom.textContent = list[i].name;
    892             aDom.setAttribute('href', list[i].link);
    893             if (i + 1 == list.length) {
    894                 aDom.classList.add("active");
    895             } else {
    896                 aDom.classList.remove("active");
    897             }
    898         }
    899 
    900         domSectNav.classList.remove("hidden");
    901     }
    902 
    903     function renderInfo() {
    904         domTdZigVer.textContent = zigAnalysis.params.zigVersion;
    905         //domTdTarget.textContent = zigAnalysis.params.builds[0].target;
    906 
    907         domSectInfo.classList.remove("hidden");
    908     }
    909 
    910     function render404() {
    911         domStatus.textContent = "404 Not Found";
    912         domStatus.classList.remove("hidden");
    913     }
    914 
    915     function renderPkgList() {
    916         let rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg];
    917         let list = [];
    918         for (let key in rootPkg.table) {
    919             let pkgIndex = rootPkg.table[key];
    920             if (zigAnalysis.packages[pkgIndex] == null) continue;
    921             list.push({
    922                 name: key,
    923                 pkg: pkgIndex,
    924             });
    925         }
    926 
    927         {
    928             let aDom = domSectMainPkg.children[1].children[0].children[0];
    929             aDom.textContent = zigAnalysis.params.rootName;
    930             aDom.setAttribute('href', navLinkPkg(zigAnalysis.rootPkg));
    931             if (zigAnalysis.params.rootName === curNav.pkgNames[0]) {
    932                 aDom.classList.add("active");
    933             } else {
    934                 aDom.classList.remove("active");
    935             }
    936             domSectMainPkg.classList.remove("hidden");
    937         }
    938 
    939         list.sort(function(a, b) {
    940             return operatorCompare(a.name.toLowerCase(), b.name.toLowerCase());
    941         });
    942 
    943         if (list.length !== 0) {
    944             resizeDomList(domListPkgs, list.length, '<li><a href="#"></a></li>');
    945             for (let i = 0; i < list.length; i += 1) {
    946                 let liDom = domListPkgs.children[i];
    947                 let aDom = liDom.children[0];
    948                 aDom.textContent = list[i].name;
    949                 aDom.setAttribute('href', navLinkPkg(list[i].pkg));
    950                 if (list[i].name === curNav.pkgNames[0]) {
    951                     aDom.classList.add("active");
    952                 } else {
    953                     aDom.classList.remove("active");
    954                 }
    955             }
    956 
    957             domSectPkgs.classList.remove("hidden");
    958         }
    959     }
    960 
    961     /**
    962     * @param {string[]} pkgNames
    963     * @param {string[]} declNames
    964     * @param {string} [callName]
    965     */
    966 
    967     function navLink(pkgNames, declNames, callName) {
    968         let base = '#';
    969         if (curNav.showPrivDecls) {
    970             base += "*";
    971         }
    972 
    973         if (pkgNames.length === 0 && declNames.length === 0) {
    974             return base;
    975         } else if (declNames.length === 0 && callName == null) {
    976             return base + pkgNames.join('.');
    977         } else if (callName == null) {
    978             return base + pkgNames.join('.') + ';' + declNames.join('.');
    979         } else {
    980             return base + pkgNames.join('.') + ';' + declNames.join('.') + ';' + callName;
    981         }
    982     }
    983 
    984     /** @param {number} pkgIndex */
    985     function navLinkPkg(pkgIndex) {
    986         return navLink(canonPkgPaths[pkgIndex], []);
    987     }
    988 
    989     /** @param {string} childName */
    990     function navLinkDecl(childName) {
    991         return navLink(curNav.pkgNames, curNav.declNames.concat([childName]));
    992     }
    993 
    994    //  /** @param {Call} callObj */
    995    //  function navLinkCall(callObj) {
    996    //      let declNamesCopy = curNav.declNames.concat([]);
    997    //      let callName = /** @type {string} */(declNamesCopy.pop());
    998 
    999    //      callName += '(';
   1000    //          for (let arg_i = 0; arg_i < callObj.args.length; arg_i += 1) {
   1001    //              if (arg_i !== 0) callName += ',';
   1002    //              let argObj = callObj.args[arg_i];
   1003    //              callName += getValueText(argObj, argObj, false, false);
   1004    //          }
   1005    //          callName += ')';
   1006 
   1007    //      declNamesCopy.push(callName);
   1008    //      return navLink(curNav.pkgNames, declNamesCopy);
   1009    //  }
   1010 
   1011     /**
   1012     * @param {any} dlDom
   1013     * @param {number} desiredLen
   1014     */
   1015     function resizeDomListDl(dlDom, desiredLen) {
   1016         // add the missing dom entries
   1017         for (let i = dlDom.childElementCount / 2; i < desiredLen; i += 1) {
   1018             dlDom.insertAdjacentHTML('beforeend', '<dt></dt><dd></dd>');
   1019         }
   1020         // remove extra dom entries
   1021         while (desiredLen < dlDom.childElementCount / 2) {
   1022             dlDom.removeChild(dlDom.lastChild);
   1023             dlDom.removeChild(dlDom.lastChild);
   1024         }
   1025     }
   1026 
   1027     /**
   1028     * @param {any} listDom
   1029     * @param {number} desiredLen
   1030     * @param {string} templateHtml
   1031     */
   1032     function resizeDomList(listDom, desiredLen, templateHtml) {
   1033         // add the missing dom entries
   1034         for (let i = listDom.childElementCount; i < desiredLen; i += 1) {
   1035             listDom.insertAdjacentHTML('beforeend', templateHtml);
   1036         }
   1037         // remove extra dom entries
   1038         while (desiredLen < listDom.childElementCount) {
   1039             listDom.removeChild(listDom.lastChild);
   1040         }
   1041     }
   1042      /**
   1043       * @param {WalkResult} wr,
   1044       * @return {Expr}
   1045     */
   1046     function walkResultTypeRef(wr) {
   1047       if (wr.typeRef) return wr.typeRef;
   1048       let resolved = resolveValue(wr);
   1049       if (wr === resolved) {
   1050         return {type: 0};
   1051       }
   1052       return walkResultTypeRef(resolved);
   1053     }
   1054      /**
   1055       * @typedef {{
   1056           wantHtml: boolean,
   1057       }} RenderWrOptions
   1058       * @param {Expr} expr,
   1059       * @param {RenderWrOptions} opts,
   1060       * @return {string}
   1061     */
   1062     function exprName(expr, opts) {
   1063         switch (Object.keys(expr)[0]) {
   1064           default: throw "oh no";
   1065           case "errorUnion": {
   1066             const errUnionObj = zigAnalysis.types[expr.errorUnion];
   1067             let lhs = exprName(errUnionObj.lhs, opts);
   1068             let rhs = exprName(errUnionObj.rhs, opts);
   1069             return lhs + "!" + rhs;
   1070 
   1071           }
   1072           case "struct": {
   1073             const struct_name = zigAnalysis.decls[expr.struct[0].val.typeRef.refPath[0].declRef].name;
   1074             let struct_body = "";
   1075             struct_body += struct_name + "{ ";
   1076             for (let i = 0; i < expr.struct.length; i++) {
   1077               const val = expr.struct[i].name
   1078               const exprArg = zigAnalysis.exprs[expr.struct[i].val.expr.as.exprArg];
   1079               let value_field = exprArg[Object.keys(exprArg)[0]];
   1080               if (value_field instanceof Object) {
   1081                 value_field = zigAnalysis.decls[value_field[0].val.typeRef.refPath[0].declRef].name;
   1082               };
   1083               struct_body += "." + val + " = " + value_field;
   1084               if (i !== expr.struct.length - 1) {
   1085                 struct_body += ", ";
   1086               }  else {
   1087                 struct_body += " ";
   1088               }
   1089             }
   1090               struct_body += "}";
   1091             return struct_body;
   1092           }
   1093           case "typeOf_peer": {
   1094             let payloadHtml = "@TypeOf("
   1095             for (let i = 0; i < expr.typeOf_peer.length; i++) {
   1096               let elem = zigAnalysis.exprs[expr.typeOf_peer[i]];
   1097               payloadHtml += exprName(elem, {wantHtml: true, wantLink:true});
   1098               if (i !== expr.typeOf_peer.length - 1) {
   1099                 payloadHtml += ", ";
   1100               }
   1101             }
   1102             payloadHtml += ")";
   1103             return payloadHtml;
   1104 
   1105           }
   1106           case "typeOf": {
   1107             const typeRefArg = zigAnalysis.exprs[expr.typeOf];
   1108             let payloadHtml = "@TypeOf(" + exprName(typeRefArg, {wantHtml: true, wantLink:true}) + ")";
   1109             return payloadHtml;
   1110           }
   1111           case "null": {
   1112             return "null";
   1113           }
   1114           case "array": {
   1115             let payloadHtml = ".{";
   1116             for (let i = 0; i < expr.array.length; i++) {
   1117                 if (i != 0) payloadHtml += ", ";
   1118                 let elem = zigAnalysis.exprs[expr.array[i]];
   1119                 payloadHtml += exprName(elem, opts);
   1120             }
   1121             return payloadHtml + "}";
   1122           }
   1123           case "comptimeExpr": {
   1124               return zigAnalysis.comptimeExprs[expr.comptimeExpr].code;
   1125           }
   1126           case "call": {
   1127               let call = zigAnalysis.calls[expr.call];
   1128               let payloadHtml = "";
   1129 
   1130 
   1131               switch(Object.keys(call.func)[0]){
   1132                 default: throw "TODO";
   1133                 case "declRef":
   1134                 case "refPath": {
   1135                     payloadHtml += exprName(call.func, opts);
   1136                     break;
   1137                 }
   1138               }
   1139               payloadHtml += "(";
   1140 
   1141               for (let i = 0; i < call.args.length; i++) {
   1142                   if (i != 0) payloadHtml += ", ";
   1143                   payloadHtml += exprName(call.args[i], opts);
   1144               }
   1145 
   1146               payloadHtml += ")";
   1147               return payloadHtml;
   1148           }
   1149           case "as": {
   1150               const typeRefArg = zigAnalysis.exprs[expr.as.typeRefArg];
   1151               const exprArg = zigAnalysis.exprs[expr.as.exprArg];
   1152               return "@as(" + exprName(typeRefArg, opts) +
   1153                 ", " + exprName(exprArg, opts) + ")";
   1154           }
   1155           case "declRef": {
   1156             return zigAnalysis.decls[expr.declRef].name;
   1157           }
   1158           case "refPath": {
   1159             return expr.refPath.map(x => exprName(x, opts)).join(".");
   1160           }
   1161           case "int": {
   1162               return "" + expr.int;
   1163           }
   1164           case "string": {
   1165             return "\"" + escapeHtml(expr.string) + "\"";
   1166           }
   1167 
   1168           case "anytype": {
   1169             return "anytype";
   1170           }
   1171 
   1172           case "this":{
   1173             return "this";
   1174           }
   1175 
   1176           case "type": {
   1177               let name = "";
   1178 
   1179               let typeObj = expr.type;
   1180               if (typeof typeObj === 'number') typeObj = zigAnalysis.types[typeObj];
   1181               switch (typeObj.kind) {
   1182                   default: throw "TODO";
   1183                   case typeKinds.Unanalyzed:
   1184                   {
   1185                       return "Unanalyzed";
   1186                   }
   1187                   case typeKinds.ComptimeExpr:
   1188                   {
   1189                       return "anyopaque";
   1190                   }
   1191                   case typeKinds.Array:
   1192                   {
   1193                     let arrayObj = /** @type {ArrayType} */ (typeObj);
   1194                     let name = "[";
   1195                     let lenName = exprName(arrayObj.len, opts);
   1196                     let sentinel = arrayObj.sentinel ? ":"+exprName(arrayObj.sentinel, opts) : "";
   1197                     // let is_mutable = arrayObj.is_multable ? "const " : "";
   1198 
   1199                     if (opts.wantHtml) {
   1200                       name +=
   1201                         '<span class="tok-number">' + lenName + sentinel + "</span>";
   1202                     } else {
   1203                       name += lenName + sentinel;
   1204                     }
   1205                     name += "]";
   1206                     // name += is_mutable;
   1207                     name += exprName(arrayObj.child, opts);
   1208                     return name;
   1209                   }
   1210                   case typeKinds.Optional:
   1211                       return "?" + exprName(/**@type {OptionalType} */(typeObj).child, opts);
   1212                   case typeKinds.Pointer:
   1213                   {
   1214                       let ptrObj = /** @type {PointerType} */(typeObj);
   1215                     let sentinel = ptrObj.sentinel ? ":"+exprName(ptrObj.sentinel, opts) : "";
   1216                       let is_mutable = !ptrObj.is_mutable ? "const " : "";
   1217                       let name = "";
   1218                       switch (ptrObj.size) {
   1219                           default:
   1220                               console.log("TODO: implement unhandled pointer size case");
   1221                           case pointerSizeEnum.One:
   1222                               name += "*";
   1223                               name += is_mutable;
   1224                               break;
   1225                           case pointerSizeEnum.Many:
   1226                               name += "[*";
   1227                               name += sentinel;
   1228                               name += "]";
   1229                               name += is_mutable;
   1230                               break;
   1231                           case pointerSizeEnum.Slice:
   1232                               name += "[";
   1233                               name += sentinel;
   1234                               name += "]";
   1235                               name += is_mutable;
   1236                               break;
   1237                           case pointerSizeEnum.C:
   1238                               name += "[*c";
   1239                               name += sentinel;
   1240                               name += "]";
   1241                               name += is_mutable;
   1242                               break;
   1243                       }
   1244                       // @check: after the major changes in arrays the consts are came from switch above
   1245                       // if (!ptrObj.is_mutable) {
   1246                       //     if (opts.wantHtml) {
   1247                       //         name += '<span class="tok-kw">const</span> ';
   1248                       //     } else {
   1249                       //         name += "const ";
   1250                       //     }
   1251                       // }
   1252                       if (ptrObj.is_allowzero) {
   1253                               name += "allowzero ";
   1254                       }
   1255                       if (ptrObj.is_volatile) {
   1256                               name += "volatile ";
   1257                       }
   1258                       if (ptrObj.has_addrspace) {
   1259                               name += "addrspace(";
   1260                               name += "." + "";
   1261                               name += ") ";
   1262                       }
   1263                       if (ptrObj.has_align) {
   1264                           if (opts.wantHtml) {
   1265                               name += '<span class="tok-kw">align</span>(';
   1266                           } else {
   1267                               name += "align(";
   1268                           }
   1269                           if (opts.wantHtml) {
   1270                               name += '<span class="tok-number">' + ptrObj.align + '</span>';
   1271                           } else {
   1272                               name += ptrObj.align;
   1273                           }
   1274                           if (ptrObj.hostIntBytes != null) {
   1275                               name += ":";
   1276                               if (opts.wantHtml) {
   1277                                   name += '<span class="tok-number">' + ptrObj.bitOffsetInHost + '</span>';
   1278                               } else {
   1279                                   name += ptrObj.bitOffsetInHost;
   1280                               }
   1281                               name += ":";
   1282                               if (opts.wantHtml) {
   1283                                   name += '<span class="tok-number">' + ptrObj.hostIntBytes + '</span>';
   1284                               } else {
   1285                                   name += ptrObj.hostIntBytes;
   1286                               }
   1287                           }
   1288                           name += ") ";
   1289                       }
   1290                       //name += typeValueName(ptrObj.child, wantHtml, wantSubLink, null);
   1291                       name += exprName(ptrObj.child, opts);
   1292                       return name;
   1293                   }
   1294                   case typeKinds.Float:
   1295                   {
   1296                       let floatObj = /** @type {NumberType} */ (typeObj);
   1297 
   1298                       if (opts.wantHtml) {
   1299                           return '<span class="tok-type">' + floatObj.name + '</span>';
   1300                       } else {
   1301                           return floatObj.name;
   1302                       }
   1303                   }
   1304                   case typeKinds.Int:
   1305                   {
   1306                       let intObj = /** @type {NumberType} */(typeObj);
   1307                       let name = intObj.name;
   1308                       if (opts.wantHtml) {
   1309                           return '<span class="tok-type">' + name + '</span>';
   1310                       } else {
   1311                           return name;
   1312                       }
   1313                   }
   1314                   case typeKinds.ComptimeInt:
   1315                       if (opts.wantHtml) {
   1316                           return '<span class="tok-type">comptime_int</span>';
   1317                       } else {
   1318                           return "comptime_int";
   1319                       }
   1320                   case typeKinds.ComptimeFloat:
   1321                       if (opts.wantHtml) {
   1322                           return '<span class="tok-type">comptime_float</span>';
   1323                       } else {
   1324                           return "comptime_float";
   1325                       }
   1326                   case typeKinds.Type:
   1327                       if (opts.wantHtml) {
   1328                           return '<span class="tok-type">type</span>';
   1329                       } else {
   1330                           return "type";
   1331                       }
   1332                   case typeKinds.Bool:
   1333                       if (opts.wantHtml) {
   1334                           return '<span class="tok-type">bool</span>';
   1335                       } else {
   1336                           return "bool";
   1337                       }
   1338                   case typeKinds.Void:
   1339                       if (opts.wantHtml) {
   1340                           return '<span class="tok-type">void</span>';
   1341                       } else {
   1342                           return "void";
   1343                       }
   1344                   case typeKinds.EnumLiteral:
   1345                       if (opts.wantHtml) {
   1346                           return '<span class="tok-type">(enum literal)</span>';
   1347                       } else {
   1348                           return "(enum literal)";
   1349                       }
   1350                   case typeKinds.NoReturn:
   1351                       if (opts.wantHtml) {
   1352                           return '<span class="tok-type">noreturn</span>';
   1353                       } else {
   1354                           return "noreturn";
   1355                       }
   1356                   case typeKinds.ErrorSet:
   1357                   {
   1358                       let errSetObj = /** @type {ErrSetType} */(typeObj);
   1359                       if (errSetObj.fields == null) {
   1360                               return '<span class="tok-type">anyerror</span>';
   1361                       } else {
   1362                           // throw "TODO";
   1363                         let html = "error{" + errSetObj.fields[0].name + "}";
   1364                         return html;
   1365                       }
   1366                   }
   1367                   
   1368                   case typeKinds.ErrorUnion:
   1369                   {
   1370                     let errUnionObj = /** @type {ErrUnionType} */(typeObj);
   1371                     let lhs = exprName(errUnionObj.lhs, opts);
   1372                     let rhs = exprName(errUnionObj.rhs, opts);
   1373                     return lhs + "!" + rhs;
   1374                   }
   1375                   case typeKinds.Fn:
   1376                   {
   1377                       let fnObj = /** @type {Fn} */(typeObj);
   1378                       let payloadHtml = "";
   1379                       if (opts.wantHtml) {
   1380                           if (fnObj.is_extern) {
   1381                             payloadHtml += "pub \"extern\" ";
   1382                           }
   1383                           payloadHtml += '<span class="tok-kw">fn</span>';
   1384                           if (opts.fnDecl) {
   1385                               payloadHtml += ' <span class="tok-fn">';
   1386                               if (opts.linkFnNameDecl) {
   1387                                   payloadHtml += '<a href="' + opts.linkFnNameDecl + '">' +
   1388                                       escapeHtml(opts.fnDecl.name) + '</a>';
   1389                               } else {
   1390                                   payloadHtml += escapeHtml(opts.fnDecl.name);
   1391                               }
   1392                               payloadHtml += '</span>';
   1393                           }
   1394                       } else {
   1395                           payloadHtml += 'fn ';
   1396                       }
   1397                       payloadHtml += '(';
   1398                           if (fnObj.params) {
   1399                               let fields = null;
   1400                               let isVarArgs = false;
   1401                               let fnNode = zigAnalysis.astNodes[fnObj.src];
   1402                               fields = fnNode.fields;
   1403                               isVarArgs = fnNode.varArgs;
   1404 
   1405                               for (let i = 0; i < fnObj.params.length; i += 1) {
   1406                                   if (i != 0) {
   1407                                       payloadHtml += ', ';
   1408                                   }
   1409 
   1410                                   let value = fnObj.params[i];
   1411                                   let paramValue = resolveValue({expr: value});
   1412 
   1413                                   if (fields != null) {
   1414                                       let paramNode = zigAnalysis.astNodes[fields[i]];
   1415 
   1416                                       if (paramNode.varArgs) {
   1417                                           payloadHtml += '...';
   1418                                           continue;
   1419                                       }
   1420 
   1421                                       if (paramNode.noalias) {
   1422                                           if (opts.wantHtml) {
   1423                                               payloadHtml += '<span class="tok-kw">noalias</span> ';
   1424                                           } else {
   1425                                               payloadHtml += 'noalias ';
   1426                                           }
   1427                                       }
   1428 
   1429                                       if (paramNode.comptime) {
   1430                                           if (opts.wantHtml) {
   1431                                               payloadHtml += '<span class="tok-kw">comptime</span> ';
   1432                                           } else {
   1433                                               payloadHtml += 'comptime ';
   1434                                           }
   1435                                       }
   1436 
   1437                                       let paramName = paramNode.name;
   1438                                       if (paramName != null) {
   1439                                           // skip if it matches the type name
   1440                                           if (!shouldSkipParamName(paramValue, paramName)) {
   1441                                               payloadHtml += paramName + ': ';
   1442                                           }
   1443                                       }
   1444                                   }
   1445 
   1446                                   if (isVarArgs && i === fnObj.params.length - 1) {
   1447                                       payloadHtml += '...';
   1448                                   } 
   1449                                   else if ("typeOf" in value) {
   1450                                     if (opts.wantHtml) {
   1451                                       payloadHtml += '<a href="">';
   1452                                       payloadHtml +=
   1453                                         '<span class="tok-kw" style="color:lightblue;">'
   1454                                         + exprName(value, opts) + '</span>';
   1455                                       payloadHtml += '</a>';
   1456                                     } else {
   1457                                       payloadHtml += exprName(value, opts);
   1458                                     }
   1459 
   1460                                   } 
   1461                                   else if ("typeOf_peer" in value) {
   1462                                     if (opts.wantHtml) {
   1463                                       payloadHtml += '<a href="">';
   1464                                       payloadHtml +=
   1465                                         '<span class="tok-kw" style="color:lightblue;">'
   1466                                         + exprName(value, opts) + '</span>';
   1467                                       payloadHtml += '</a>';
   1468                                     } else {
   1469                                       payloadHtml += exprName(value, opts);
   1470                                     }
   1471 
   1472                                   } 
   1473                                   else if ("declRef" in value) {
   1474                                     if (opts.wantHtml) {
   1475                                       payloadHtml += '<a href="">';
   1476                                       payloadHtml +=
   1477                                         '<span class="tok-kw" style="color:lightblue;">'
   1478                                         + exprName(value, opts) + '</span>';
   1479                                       payloadHtml += '</a>';
   1480                                     } else {
   1481                                       payloadHtml += exprName(value, opts);
   1482                                     }
   1483 
   1484                                   } 
   1485                                   else if ("call" in value) {
   1486                                     if (opts.wantHtml) {
   1487                                       payloadHtml += '<a href="">';
   1488                                       payloadHtml +=
   1489                                         '<span class="tok-kw" style="color:lightblue;">'
   1490                                         + exprName(value, opts) + '</span>';
   1491                                       payloadHtml += '</a>';
   1492                                     } else {
   1493                                       payloadHtml += exprName(value, opts);
   1494                                     }
   1495                                   } 
   1496                                   else if ("refPath" in value) {
   1497                                     if (opts.wantHtml) {
   1498                                       payloadHtml += '<a href="">';
   1499                                       payloadHtml +=
   1500                                         '<span class="tok-kw" style="color:lightblue;">'
   1501                                         + exprName(value, opts) + '</span>';
   1502                                       payloadHtml += '</a>';
   1503                                     } else {
   1504                                       payloadHtml += exprName(value, opts);
   1505                                     }
   1506                                   } else if ("type" in value) {
   1507                                       let name = exprName(value, {
   1508                                         wantHtml: false,
   1509                                         wantLink: false,
   1510                                         fnDecl: opts.fnDecl,
   1511                                         linkFnNameDecl: opts.linkFnNameDecl,
   1512                                       });
   1513                                       payloadHtml += '<span class="tok-kw">' + escapeHtml(name) + '</span>';
   1514                                   } else if ("comptimeExpr" in value) {
   1515                                       let comptimeExpr = zigAnalysis.comptimeExprs[value.comptimeExpr].code;
   1516                                       if (opts.wantHtml) {
   1517                                         payloadHtml += '<span class="tok-kw">' + comptimeExpr + '</span>';
   1518                                       } else {
   1519                                         payloadHtml += comptimeExpr;
   1520                                       }
   1521                                   } else if (opts.wantHtml) {
   1522                                       payloadHtml += '<span class="tok-kw">anytype</span>';
   1523                                   } else {
   1524                                       payloadHtml += 'anytype';
   1525                                   }
   1526                               }
   1527                           }
   1528 
   1529                       payloadHtml += ') ';
   1530                     if (fnObj.has_cc) {
   1531                       let cc = zigAnalysis.types[fnObj.cc]
   1532                       payloadHtml += "callconv(." + cc.name + ") ";
   1533                     }
   1534 
   1535                       if (fnObj.is_inferred_error) {
   1536                           payloadHtml += "!";
   1537                       }
   1538                       if (fnObj.ret != null) {
   1539                           payloadHtml += exprName(fnObj.ret, opts);
   1540                       } else if (opts.wantHtml) {
   1541                           payloadHtml += '<span class="tok-kw">anytype</span>';
   1542                       } else {
   1543                           payloadHtml += 'anytype';
   1544                       }
   1545                       return payloadHtml;
   1546                   }
   1547                       // if (wantHtml) {
   1548                       //     return escapeHtml(typeObj.name);
   1549                       // } else {
   1550                       //     return typeObj.name;
   1551                       // }
   1552               }
   1553         }
   1554 
   1555         }
   1556     }
   1557 
   1558 
   1559     /**
   1560     * @param {Expr} typeRef
   1561     * @param {string} paramName
   1562     */
   1563     function shouldSkipParamName(typeRef, paramName) {
   1564         let resolvedTypeRef = resolveValue({expr: typeRef});
   1565         if ("type" in resolvedTypeRef) {
   1566             let typeObj = zigAnalysis.types[resolvedTypeRef.type];
   1567             if (typeObj.kind === typeKinds.Pointer){
   1568                 let ptrObj = /** @type {PointerType} */(typeObj);
   1569                 if (getPtrSize(ptrObj) === pointerSizeEnum.One) {
   1570                     const value = resolveValue(ptrObj.child);
   1571                     return typeValueName(value, false, true).toLowerCase() === paramName;
   1572                 }
   1573             }
   1574         }
   1575         return false;
   1576     }
   1577 
   1578     /** @param {PointerType} typeObj */
   1579     function getPtrSize(typeObj) {
   1580         return (typeObj.size == null) ? pointerSizeEnum.One : typeObj.size;
   1581     }
   1582 
   1583     /** @param {Type} typeObj */
   1584     function renderType(typeObj) {
   1585         let name;
   1586         if (rootIsStd && typeObj === zigAnalysis.types[zigAnalysis.packages[zigAnalysis.rootPkg].main]) {
   1587             name = "std";
   1588         } else {
   1589             name = exprName({type:typeObj}, false, false);
   1590         }
   1591         if (name != null && name != "") {
   1592             domHdrName.innerText = name + " (" + zigAnalysis.typeKinds[typeObj.kind] + ")";
   1593             domHdrName.classList.remove("hidden");
   1594         }
   1595         if (typeObj.kind == typeKinds.ErrorSet) {
   1596             renderErrorSet(/** @type {ErrSetType} */(typeObj));
   1597         }
   1598     }
   1599 
   1600     /** @param {ErrSetType} errSetType */
   1601     function renderErrorSet(errSetType) {
   1602         if (errSetType.fields == null) {
   1603             domFnErrorsAnyError.classList.remove("hidden");
   1604         } else {
   1605             let errorList = [];
   1606             for (let i = 0; i < errSetType.fields.length; i += 1) {
   1607                 let errObj = errSetType.fields[i];
   1608                 //let srcObj = zigAnalysis.astNodes[errObj.src];
   1609                 errorList.push(errObj);
   1610             }
   1611             errorList.sort(function(a, b) {
   1612                 return operatorCompare(a.name.toLowerCase(), b.name.toLowerCase());
   1613             });
   1614 
   1615             resizeDomListDl(domListFnErrors, errorList.length);
   1616             for (let i = 0; i < errorList.length; i += 1) {
   1617                 let nameTdDom = domListFnErrors.children[i * 2 + 0];
   1618                 let descTdDom = domListFnErrors.children[i * 2 + 1];
   1619                 nameTdDom.textContent = errorList[i].name;
   1620                 let docs = errorList[i].docs;
   1621                 if (docs != null) {
   1622                     descTdDom.innerHTML = markdown(docs);
   1623                 } else {
   1624                     descTdDom.textContent = "";
   1625                 }
   1626             }
   1627             domTableFnErrors.classList.remove("hidden");
   1628         }
   1629         domSectFnErrors.classList.remove("hidden");
   1630     }
   1631 
   1632 //     function allCompTimeFnCallsHaveTypeResult(typeIndex, value) {
   1633 //         let srcIndex = zigAnalysis.fns[value].src;
   1634 //         let calls = nodesToCallsMap[srcIndex];
   1635 //         if (calls == null) return false;
   1636 //         for (let i = 0; i < calls.length; i += 1) {
   1637 //             let call = zigAnalysis.calls[calls[i]];
   1638 //             if (call.result.type !== typeTypeId) return false;
   1639 //         }
   1640 //         return true;
   1641 //     }
   1642 //
   1643 //     function allCompTimeFnCallsResult(calls) {
   1644 //         let firstTypeObj = null;
   1645 //         let containerObj = {
   1646 //             privDecls: [],
   1647 //         };
   1648 //         for (let callI = 0; callI < calls.length; callI += 1) {
   1649 //             let call = zigAnalysis.calls[calls[callI]];
   1650 //             if (call.result.type !== typeTypeId) return null;
   1651 //             let typeObj = zigAnalysis.types[call.result.value];
   1652 //             if (!typeKindIsContainer(typeObj.kind)) return null;
   1653 //             if (firstTypeObj == null) {
   1654 //                 firstTypeObj = typeObj;
   1655 //                 containerObj.src = typeObj.src;
   1656 //             } else if (firstTypeObj.src !== typeObj.src) {
   1657 //                 return null;
   1658 //             }
   1659 //
   1660 //             if (containerObj.fields == null) {
   1661 //                 containerObj.fields = (typeObj.fields || []).concat([]);
   1662 //             } else for (let fieldI = 0; fieldI < typeObj.fields.length; fieldI += 1) {
   1663 //                 let prev = containerObj.fields[fieldI];
   1664 //                 let next = typeObj.fields[fieldI];
   1665 //                 if (prev === next) continue;
   1666 //                 if (typeof(prev) === 'object') {
   1667 //                     if (prev[next] == null) prev[next] = typeObj;
   1668 //                 } else {
   1669 //                     containerObj.fields[fieldI] = {};
   1670 //                     containerObj.fields[fieldI][prev] = firstTypeObj;
   1671 //                     containerObj.fields[fieldI][next] = typeObj;
   1672 //                 }
   1673 //             }
   1674 //
   1675 //             if (containerObj.pubDecls == null) {
   1676 //                 containerObj.pubDecls = (typeObj.pubDecls || []).concat([]);
   1677 //             } else for (let declI = 0; declI < typeObj.pubDecls.length; declI += 1) {
   1678 //                 let prev = containerObj.pubDecls[declI];
   1679 //                 let next = typeObj.pubDecls[declI];
   1680 //                 if (prev === next) continue;
   1681 //                 // TODO instead of showing "examples" as the public declarations,
   1682 //                     // do logic like this:
   1683 //                 //if (typeof(prev) !== 'object') {
   1684 //                     //    let newDeclId = zigAnalysis.decls.length;
   1685 //                     //    prev = clone(zigAnalysis.decls[prev]);
   1686 //                     //    prev.id = newDeclId;
   1687 //                     //    zigAnalysis.decls.push(prev);
   1688 //                     //    containerObj.pubDecls[declI] = prev;
   1689 //                     //}
   1690 //                 //mergeDecls(prev, next, firstTypeObj, typeObj);
   1691 //             }
   1692 //         }
   1693 //         for (let declI = 0; declI < containerObj.pubDecls.length; declI += 1) {
   1694 //             let decl = containerObj.pubDecls[declI];
   1695 //             if (typeof(decl) === 'object') {
   1696 //                 containerObj.pubDecls[declI] = containerObj.pubDecls[declI].id;
   1697 //             }
   1698 //         }
   1699 //         return containerObj;
   1700 //     }
   1701 
   1702 
   1703 
   1704     /** @param {Decl} decl */
   1705     function renderValue(decl) {
   1706         let resolvedValue = resolveValue(decl.value)
   1707 
   1708         domFnProtoCode.innerHTML = '<span class="tok-kw">const</span> ' +
   1709             escapeHtml(decl.name) + ': ' + exprName(resolvedValue.typeRef, {wantHtml: true, wantLink:true}) +
   1710             " = " + exprName(decl.value.expr, {wantHtml: true, wantLink:true}) + ";";
   1711 
   1712         let docs = zigAnalysis.astNodes[decl.src].docs;
   1713         if (docs != null) {
   1714             domTldDocs.innerHTML = markdown(docs);
   1715             domTldDocs.classList.remove("hidden");
   1716         }
   1717 
   1718         domFnProto.classList.remove("hidden");
   1719     }
   1720 
   1721     /** @param {Decl} decl */
   1722     function renderVar(decl) {
   1723         let declTypeRef = typeOfDecl(decl);
   1724         domFnProtoCode.innerHTML = '<span class="tok-kw">var</span> ' +
   1725             escapeHtml(decl.name) + ': ' + typeValueName(declTypeRef, true, true);
   1726 
   1727         let docs = zigAnalysis.astNodes[decl.src].docs;
   1728         if (docs != null) {
   1729             domTldDocs.innerHTML = markdown(docs);
   1730             domTldDocs.classList.remove("hidden");
   1731         }
   1732 
   1733         domFnProto.classList.remove("hidden");
   1734     }
   1735 
   1736 
   1737     /**
   1738     * @param {number[]} decls
   1739     * @param {Decl[]} typesList
   1740     * @param {Decl[]} namespacesList,
   1741     * @param {Decl[]} errSetsList,
   1742     * @param {Decl[]} fnsList,
   1743     * @param {Decl[]} varsList,
   1744     * @param {Decl[]} valsList,
   1745     * @param {Decl[]} testsList
   1746     */
   1747     function categorizeDecls(decls,
   1748         typesList, namespacesList, errSetsList,
   1749         fnsList, varsList, valsList, testsList) {
   1750 
   1751         for (let i = 0; i < decls.length; i += 1) {
   1752             let decl = zigAnalysis.decls[decls[i]];
   1753             let declValue = resolveValue(decl.value);
   1754 
   1755             if (decl.isTest) {
   1756                 testsList.push(decl);
   1757                 continue;
   1758             }
   1759 
   1760             if (decl.kind === 'var') {
   1761                 varsList.push(decl);
   1762                 continue;
   1763             }
   1764 
   1765             if (decl.kind === 'const') {
   1766                 if ("type" in declValue.expr) {
   1767                     // We have the actual type expression at hand.
   1768                     const typeExpr = zigAnalysis.types[declValue.expr.type];
   1769                     if (typeExpr.kind == typeKinds.Fn) {
   1770                         const funcRetExpr = resolveValue({
   1771                             expr: /** @type {Fn} */(typeExpr).ret
   1772                         });
   1773                         if ("type" in funcRetExpr.expr && funcRetExpr.expr.type == typeTypeId) {
   1774                             if (typeIsErrSet(declValue.expr.type)) {
   1775                                 errSetsList.push(decl);
   1776                             } else if (typeIsStructWithNoFields(declValue.expr.type)) {
   1777                                 namespacesList.push(decl);
   1778                             } else {
   1779                                 typesList.push(decl);
   1780                             }
   1781                         } else {
   1782                             fnsList.push(decl);
   1783                         }
   1784                     } else {
   1785                         if (typeIsErrSet(declValue.expr.type)) {
   1786                             errSetsList.push(decl);
   1787                         } else if (typeIsStructWithNoFields(declValue.expr.type)) {
   1788                             namespacesList.push(decl);
   1789                         } else {
   1790                             typesList.push(decl);
   1791                         }
   1792                     }
   1793                  } else if ("typeRef" in declValue) {
   1794                       if ("type" in declValue.typeRef && declValue.typeRef == typeTypeId) {
   1795                           // We don't know what the type expression is, but we know it's a type.
   1796                           typesList.push(decl);
   1797                       } else {
   1798                           valsList.push(decl);
   1799                       }
   1800                 } else {
   1801                     valsList.push(decl);
   1802                 }
   1803             }
   1804         }
   1805     }
   1806 
   1807     /**
   1808      * @param {ContainerType} container
   1809      */
   1810     function renderContainer(container) {
   1811         /** @type {Decl[]} */
   1812         let typesList = [];
   1813         /** @type {Decl[]} */
   1814         let namespacesList = [];
   1815         /** @type {Decl[]} */
   1816         let errSetsList = [];
   1817         /** @type {Decl[]} */
   1818         let fnsList = [];
   1819         /** @type {Decl[]} */
   1820         let varsList = [];
   1821         /** @type {Decl[]} */
   1822         let valsList = [];
   1823         /** @type {Decl[]} */
   1824         let testsList = [];
   1825 
   1826         categorizeDecls(container.pubDecls,
   1827             typesList, namespacesList, errSetsList,
   1828             fnsList, varsList, valsList, testsList);
   1829         if (curNav.showPrivDecls) categorizeDecls(container.privDecls,
   1830             typesList, namespacesList, errSetsList,
   1831             fnsList, varsList, valsList, testsList);
   1832 
   1833 
   1834         typesList.sort(byNameProperty);
   1835         namespacesList.sort(byNameProperty);
   1836         errSetsList.sort(byNameProperty);
   1837         fnsList.sort(byNameProperty);
   1838         varsList.sort(byNameProperty);
   1839         valsList.sort(byNameProperty);
   1840         testsList.sort(byNameProperty);
   1841 
   1842         if (container.src != null) {
   1843             let docs = zigAnalysis.astNodes[container.src].docs;
   1844             if (docs != null) {
   1845                 domTldDocs.innerHTML = markdown(docs);
   1846                 domTldDocs.classList.remove("hidden");
   1847             }
   1848         }
   1849 
   1850         if (typesList.length !== 0) {
   1851             resizeDomList(domListTypes, typesList.length, '<li><a href="#"></a></li>');
   1852             for (let i = 0; i < typesList.length; i += 1) {
   1853                 let liDom = domListTypes.children[i];
   1854                 let aDom = liDom.children[0];
   1855                 let decl = typesList[i];
   1856                 aDom.textContent = decl.name;
   1857                 aDom.setAttribute('href', navLinkDecl(decl.name));
   1858             }
   1859             domSectTypes.classList.remove("hidden");
   1860         }
   1861         if (namespacesList.length !== 0) {
   1862             resizeDomList(domListNamespaces, namespacesList.length, '<li><a href="#"></a></li>');
   1863             for (let i = 0; i < namespacesList.length; i += 1) {
   1864                 let liDom = domListNamespaces.children[i];
   1865                 let aDom = liDom.children[0];
   1866                 let decl = namespacesList[i];
   1867                 aDom.textContent = decl.name;
   1868                 aDom.setAttribute('href', navLinkDecl(decl.name));
   1869             }
   1870             domSectNamespaces.classList.remove("hidden");
   1871         }
   1872 
   1873         if (errSetsList.length !== 0) {
   1874             resizeDomList(domListErrSets, errSetsList.length, '<li><a href="#"></a></li>');
   1875             for (let i = 0; i < errSetsList.length; i += 1) {
   1876                 let liDom = domListErrSets.children[i];
   1877                 let aDom = liDom.children[0];
   1878                 let decl = errSetsList[i];
   1879                 aDom.textContent = decl.name;
   1880                 aDom.setAttribute('href', navLinkDecl(decl.name));
   1881             }
   1882             domSectErrSets.classList.remove("hidden");
   1883         }
   1884 
   1885         if (fnsList.length !== 0) {
   1886             resizeDomList(domListFns, fnsList.length, '<tr><td></td><td></td></tr>');
   1887             for (let i = 0; i < fnsList.length; i += 1) {
   1888                 let decl = fnsList[i];
   1889                 let trDom = domListFns.children[i];
   1890 
   1891                 let tdFnCode = trDom.children[0];
   1892                 let tdDesc = trDom.children[1];
   1893 
   1894                 let declType = resolveValue(decl.value);
   1895                 console.assert("type" in declType.expr);
   1896 
   1897                 tdFnCode.innerHTML = exprName(declType.expr,{
   1898                   wantHtml: true,
   1899                   wantLink: true,
   1900                   fnDecl: decl,
   1901                   linkFnNameDecl: navLinkDecl(decl.name),
   1902                 });
   1903 
   1904                 let docs = zigAnalysis.astNodes[decl.src].docs;
   1905                 if (docs != null) {
   1906                     tdDesc.innerHTML = shortDescMarkdown(docs);
   1907                 } else {
   1908                     tdDesc.textContent = "";
   1909                 }
   1910             }
   1911             domSectFns.classList.remove("hidden");
   1912         }
   1913 
   1914         let containerNode = zigAnalysis.astNodes[container.src];
   1915         if (containerNode.fields && containerNode.fields.length > 0) {
   1916             resizeDomList(domListFields, containerNode.fields.length, '<div></div>');
   1917 
   1918             for (let i = 0; i < containerNode.fields.length; i += 1) {
   1919                 let fieldNode = zigAnalysis.astNodes[containerNode.fields[i]];
   1920                 let divDom = domListFields.children[i];
   1921                 let fieldName = /** @type {string} */(fieldNode.name);
   1922 
   1923                 let html = '<div class="mobile-scroll-container"><pre class="scroll-item">' + escapeHtml(fieldName);
   1924 
   1925                 if (container.kind === typeKinds.Enum) {
   1926                     html += ' = <span class="tok-number">' + fieldName + '</span>';
   1927                 } else {
   1928                     let fieldTypeExpr = container.fields[i];
   1929                     html += ": ";
   1930                     let name = exprName(fieldTypeExpr, false, false);
   1931                     html += '<span class="tok-kw">'+ name +'</span>';
   1932                     let tsn = typeShorthandName(fieldTypeExpr);
   1933                     if (tsn) {
   1934                         html += '<span> ('+ tsn +')</span>';
   1935 
   1936                     }
   1937                 }
   1938 
   1939                 html += ',</pre></div>';
   1940 
   1941                 let docs = fieldNode.docs;
   1942                 if (docs != null) {
   1943                     html += markdown(docs);
   1944                 }
   1945                 divDom.innerHTML = html;
   1946             }
   1947             domSectFields.classList.remove("hidden");
   1948         }
   1949 
   1950         if (varsList.length !== 0) {
   1951             resizeDomList(domListGlobalVars, varsList.length,
   1952                 '<tr><td><a href="#"></a></td><td></td><td></td></tr>');
   1953             for (let i = 0; i < varsList.length; i += 1) {
   1954                 let decl = varsList[i];
   1955                 let trDom = domListGlobalVars.children[i];
   1956 
   1957                 let tdName = trDom.children[0];
   1958                 let tdNameA = tdName.children[0];
   1959                 let tdType = trDom.children[1];
   1960                 let tdDesc = trDom.children[2];
   1961 
   1962                 tdNameA.setAttribute('href', navLinkDecl(decl.name));
   1963                 tdNameA.textContent = decl.name;
   1964 
   1965                 tdType.innerHTML = typeValueName(typeOfDecl(decl), true, true);
   1966 
   1967                 let docs = zigAnalysis.astNodes[decl.src].docs;
   1968                 if (docs != null) {
   1969                     tdDesc.innerHTML = shortDescMarkdown(docs);
   1970                 } else {
   1971                     tdDesc.textContent = "";
   1972                 }
   1973             }
   1974             domSectGlobalVars.classList.remove("hidden");
   1975         }
   1976 
   1977         if (valsList.length !== 0) {
   1978             resizeDomList(domListValues, valsList.length,
   1979                 '<tr><td><a href="#"></a></td><td></td><td></td></tr>');
   1980             for (let i = 0; i < valsList.length; i += 1) {
   1981                 let decl = valsList[i];
   1982                 let trDom = domListValues.children[i];
   1983 
   1984                 let tdName = trDom.children[0];
   1985                 let tdNameA = tdName.children[0];
   1986                 let tdType = trDom.children[1];
   1987                 let tdDesc = trDom.children[2];
   1988 
   1989                 tdNameA.setAttribute('href', navLinkDecl(decl.name));
   1990                 tdNameA.textContent = decl.name;
   1991 
   1992                 tdType.innerHTML = exprName(walkResultTypeRef(decl.value),
   1993                   {wantHtml:true, wantLink:true});
   1994 
   1995                 let docs = zigAnalysis.astNodes[decl.src].docs;
   1996                 if (docs != null) {
   1997                     tdDesc.innerHTML = shortDescMarkdown(docs);
   1998                 } else {
   1999                     tdDesc.textContent = "";
   2000                 }
   2001             }
   2002             domSectValues.classList.remove("hidden");
   2003         }
   2004 
   2005         if (testsList.length !== 0) {
   2006             resizeDomList(domListTests, testsList.length,
   2007                 '<tr><td><a href="#"></a></td><td></td><td></td></tr>');
   2008             for (let i = 0; i < testsList.length; i += 1) {
   2009                 let decl = testsList[i];
   2010                 let trDom = domListTests.children[i];
   2011 
   2012                 let tdName = trDom.children[0];
   2013                 let tdNameA = tdName.children[0];
   2014                 let tdType = trDom.children[1];
   2015                 let tdDesc = trDom.children[2];
   2016 
   2017                 tdNameA.setAttribute('href', navLinkDecl(decl.name));
   2018                 tdNameA.textContent = decl.name;
   2019 
   2020                 tdType.innerHTML = exprName(walkResultTypeRef(decl.value),
   2021                   {wantHtml:true, wantLink:true});
   2022 
   2023                 let docs = zigAnalysis.astNodes[decl.src].docs;
   2024                 if (docs != null) {
   2025                     tdDesc.innerHTML = shortDescMarkdown(docs);
   2026                 } else {
   2027                     tdDesc.textContent = "";
   2028                 }
   2029             }
   2030             domSectTests.classList.remove("hidden");
   2031         }
   2032     }
   2033 
   2034 
   2035     /**
   2036     * @param {string | number} a
   2037     * @param {string | number} b
   2038     */
   2039     function operatorCompare(a, b) {
   2040         if (a === b) {
   2041             return 0;
   2042         } else if (a < b) {
   2043             return -1;
   2044         } else {
   2045             return 1;
   2046         }
   2047     }
   2048 
   2049     function detectRootIsStd() {
   2050         let rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg];
   2051         if (rootPkg.table["std"] == null) {
   2052             // no std mapped into the root package
   2053             return false;
   2054         }
   2055         let stdPkg = zigAnalysis.packages[rootPkg.table["std"]];
   2056         if (stdPkg == null) return false;
   2057         return rootPkg.file === stdPkg.file;
   2058     }
   2059 
   2060     function indexTypeKinds() {
   2061         let map = /** @type {Record<string, number>} */({});
   2062         for (let i = 0; i < zigAnalysis.typeKinds.length; i += 1) {
   2063             map[zigAnalysis.typeKinds[i]] = i;
   2064         }
   2065         // This is just for debugging purposes, not needed to function
   2066         let assertList = ["Type","Void","Bool","NoReturn","Int","Float","Pointer","Array","Struct",
   2067             "ComptimeFloat","ComptimeInt","Undefined","Null","Optional","ErrorUnion","ErrorSet","Enum",
   2068             "Union","Fn","BoundFn","Opaque","Frame","AnyFrame","Vector","EnumLiteral"];
   2069         for (let i = 0; i < assertList.length; i += 1) {
   2070             if (map[assertList[i]] == null) throw new Error("No type kind '" + assertList[i] + "' found");
   2071         }
   2072         return map;
   2073     }
   2074 
   2075     function findTypeTypeId() {
   2076         for (let i = 0; i < zigAnalysis.types.length; i += 1) {
   2077             if (zigAnalysis.types[i].kind == typeKinds.Type) {
   2078                 return i;
   2079             }
   2080         }
   2081         throw new Error("No type 'type' found");
   2082     }
   2083 
   2084     function updateCurNav() {
   2085 
   2086         curNav = {
   2087             showPrivDecls: false,
   2088             pkgNames: [],
   2089             pkgObjs: [],
   2090             declNames: [],
   2091             declObjs: [],
   2092             callName: null,
   2093         };
   2094         curNavSearch = "";
   2095 
   2096         if (location.hash[0] === '#' && location.hash.length > 1) {
   2097             let query = location.hash.substring(1);
   2098             if (query[0] === '*') {
   2099               curNav.showPrivDecls = true;
   2100               query = query.substring(1);
   2101             }
   2102 
   2103             let qpos = query.indexOf("?");
   2104             let nonSearchPart;
   2105             if (qpos === -1) {
   2106                 nonSearchPart = query;
   2107             } else {
   2108                 nonSearchPart = query.substring(0, qpos);
   2109                 curNavSearch = decodeURIComponent(query.substring(qpos + 1));
   2110             }
   2111 
   2112             let parts = nonSearchPart.split(";");
   2113             curNav.pkgNames = decodeURIComponent(parts[0]).split(".");
   2114             if (parts[1] != null) {
   2115                 curNav.declNames = decodeURIComponent(parts[1]).split(".");
   2116             }
   2117         }
   2118 
   2119         if (curNav.pkgNames.length === 0 && rootIsStd) {
   2120             curNav.pkgNames = ["std"];
   2121         }
   2122     }
   2123 
   2124     function onHashChange() {
   2125         updateCurNav();
   2126         if (domSearch.value !== curNavSearch) {
   2127             domSearch.value = curNavSearch;
   2128         }
   2129         render();
   2130         if (imFeelingLucky) {
   2131             imFeelingLucky = false;
   2132             activateSelectedResult();
   2133         }
   2134     }
   2135 
   2136     /**
   2137     * @param {ContainerType} parentType
   2138     * @param {string} childName
   2139     */
   2140     function findSubDecl(parentType, childName) {
   2141         if (!parentType.pubDecls) return null;
   2142         for (let i = 0; i < parentType.pubDecls.length; i += 1) {
   2143             let declIndex = parentType.pubDecls[i];
   2144             let childDecl = zigAnalysis.decls[declIndex];
   2145             if (childDecl.name === childName) {
   2146                 return childDecl;
   2147             }
   2148         }
   2149         if (!parentType.privDecls) return null;
   2150         for (let i = 0; i < parentType.privDecls.length; i += 1) {
   2151             let declIndex = parentType.privDecls[i];
   2152             let childDecl = zigAnalysis.decls[declIndex];
   2153             if (childDecl.name === childName) {
   2154                 return childDecl;
   2155             }
   2156         }
   2157         return null;
   2158     }
   2159 
   2160 
   2161 
   2162 
   2163     function computeCanonicalPackagePaths() {
   2164         let list = new Array(zigAnalysis.packages.length);
   2165         // Now we try to find all the packages from root.
   2166             let rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg];
   2167         // Breadth-first to keep the path shortest possible.
   2168             let stack = [{
   2169                 path: /** @type {string[]} */([]),
   2170                 pkg: rootPkg,
   2171             }];
   2172         while (stack.length !== 0) {
   2173             let item = /** @type {{path: string[], pkg: Package}} */(stack.shift());
   2174             for (let key in item.pkg.table) {
   2175                 let childPkgIndex = item.pkg.table[key];
   2176                 if (list[childPkgIndex] != null) continue;
   2177                 let childPkg = zigAnalysis.packages[childPkgIndex];
   2178                 if (childPkg == null) continue;
   2179 
   2180                 let newPath = item.path.concat([key])
   2181                 list[childPkgIndex] = newPath;
   2182                 stack.push({
   2183                     path: newPath,
   2184                     pkg: childPkg,
   2185                 });
   2186             }
   2187         }
   2188         return list;
   2189     }
   2190 
   2191 
   2192     /** @return {CanonDecl[]} */
   2193     function computeCanonDeclPaths() {
   2194         let list = new Array(zigAnalysis.decls.length);
   2195         canonTypeDecls = new Array(zigAnalysis.types.length);
   2196 
   2197         for (let pkgI = 0; pkgI < zigAnalysis.packages.length; pkgI += 1) {
   2198             if (pkgI === zigAnalysis.rootPkg && rootIsStd) continue;
   2199             let pkg = zigAnalysis.packages[pkgI];
   2200             let pkgNames = canonPkgPaths[pkgI];
   2201             let stack = [{
   2202                 declNames: /** @type {string[]} */([]),
   2203                 type: zigAnalysis.types[pkg.main],
   2204             }];
   2205             while (stack.length !== 0) {
   2206                 let item = /** @type {{declNames: string[], type: Type}} */(stack.shift());
   2207 
   2208                 if (isContainerType(item.type)) {
   2209                     let t = /** @type {ContainerType} */(item.type);
   2210 
   2211                     let len = t.pubDecls ? t.pubDecls.length : 0;
   2212                     for (let declI = 0; declI < len; declI += 1) {
   2213                         let mainDeclIndex = t.pubDecls[declI];
   2214                         if (list[mainDeclIndex] != null) continue;
   2215 
   2216                         let decl = zigAnalysis.decls[mainDeclIndex];
   2217                         let declVal =  decl.value; //resolveValue(decl.value);
   2218                         let declNames = item.declNames.concat([decl.name]);
   2219                         list[mainDeclIndex] = {
   2220                             pkgNames: pkgNames,
   2221                             declNames: declNames,
   2222                         };
   2223                         if ("type" in declVal.expr) {
   2224                             let value = zigAnalysis.types[declVal.expr.type];
   2225                             if (declCanRepresentTypeKind(value.kind))
   2226                             {
   2227                                 canonTypeDecls[declVal.type] = mainDeclIndex;
   2228                             }
   2229 
   2230                             if (isContainerType(value)) {
   2231                                 stack.push({
   2232                                     declNames: declNames,
   2233                                     type:value,
   2234                                 });
   2235                             }
   2236                         }
   2237                     }
   2238                 }
   2239             }
   2240         }
   2241         return list;
   2242     }
   2243 
   2244     /** @param {number} index */
   2245     function getCanonDeclPath(index) {
   2246         if (canonDeclPaths == null) {
   2247             canonDeclPaths = computeCanonDeclPaths();
   2248         }
   2249         //let cd = /** @type {CanonDecl[]}*/(canonDeclPaths);
   2250         return canonDeclPaths[index];
   2251     }
   2252 
   2253     /** @param {number} index */
   2254     function getCanonTypeDecl(index) {
   2255         getCanonDeclPath(0);
   2256         //let ct = /** @type {number[]}*/(canonTypeDecls);
   2257         return canonTypeDecls[index];
   2258     }
   2259 
   2260     /** @param {string} text */
   2261     function escapeHtml(text) {
   2262         return text.replace(/[&"<>]/g, function (m) {
   2263             return escapeHtmlReplacements[m];
   2264         });
   2265     }
   2266 
   2267     /** @param {string} docs */
   2268     function shortDescMarkdown(docs) {
   2269         let parts = docs.trim().split("\n");
   2270         let firstLine = parts[0];
   2271         return markdown(firstLine);
   2272     }
   2273 
   2274     /** @param {string} input */
   2275     function markdown(input) {
   2276         const raw_lines = input.split('\n'); // zig allows no '\r', so we don't need to split on CR
   2277         /**
   2278         * @type Array<{
   2279         *   indent: number,
   2280         *   raw_text: string,
   2281         *   text: string,
   2282         *   type: string,
   2283         *   ordered_number: number,
   2284         * }>
   2285         */
   2286         const lines = [];
   2287 
   2288         // PHASE 1:
   2289         // Dissect lines and determine the type for each line.
   2290             // Also computes indentation level and removes unnecessary whitespace
   2291 
   2292         let is_reading_code = false;
   2293         let code_indent = 0;
   2294         for (let line_no = 0; line_no < raw_lines.length; line_no++) {
   2295             const raw_line = raw_lines[line_no];
   2296 
   2297             const line = {
   2298                 indent: 0,
   2299                 raw_text: raw_line,
   2300                 text: raw_line.trim(),
   2301                 type: "p", // p, h1 … h6, code, ul, ol, blockquote, skip, empty
   2302                 ordered_number: -1, // NOTE: hack to make the type checker happy
   2303             };
   2304 
   2305             if (!is_reading_code) {
   2306                 while ((line.indent < line.raw_text.length) && line.raw_text[line.indent] == ' ') {
   2307                     line.indent += 1;
   2308                 }
   2309 
   2310                 if (line.text.startsWith("######")) {
   2311                     line.type = "h6";
   2312                     line.text = line.text.substr(6);
   2313                 }
   2314                 else if (line.text.startsWith("#####")) {
   2315                     line.type = "h5";
   2316                     line.text = line.text.substr(5);
   2317                 }
   2318                 else if (line.text.startsWith("####")) {
   2319                     line.type = "h4";
   2320                     line.text = line.text.substr(4);
   2321                 }
   2322                 else if (line.text.startsWith("###")) {
   2323                     line.type = "h3";
   2324                     line.text = line.text.substr(3);
   2325                 }
   2326                 else if (line.text.startsWith("##")) {
   2327                     line.type = "h2";
   2328                     line.text = line.text.substr(2);
   2329                 }
   2330                 else if (line.text.startsWith("#")) {
   2331                     line.type = "h1";
   2332                     line.text = line.text.substr(1);
   2333                 }
   2334                 else if (line.text.startsWith("-")) {
   2335                     line.type = "ul";
   2336                     line.text = line.text.substr(1);
   2337                 }
   2338                 else if (line.text.match(/^\d+\..*$/)) { // if line starts with {number}{dot}
   2339                     const match = /** @type {RegExpMatchArray} */(line.text.match(/(\d+)\./));
   2340                     line.type = "ul";
   2341                     line.text = line.text.substr(match[0].length);
   2342                     line.ordered_number = Number(match[1].length);
   2343                 }
   2344                 else if (line.text == "```") {
   2345                     line.type = "skip";
   2346                     is_reading_code = true;
   2347                     code_indent = line.indent;
   2348                 }
   2349                 else if (line.text == "") {
   2350                     line.type = "empty";
   2351                 }
   2352             }
   2353             else {
   2354                 if (line.text == "```") {
   2355                     is_reading_code = false;
   2356                     line.type = "skip";
   2357                 } else {
   2358                     line.type = "code";
   2359                     line.text = line.raw_text.substr(code_indent); // remove the indent of the ``` from all the code block
   2360                 }
   2361             }
   2362 
   2363             if (line.type != "skip") {
   2364                 lines.push(line);
   2365             }
   2366         }
   2367 
   2368         // PHASE 2:
   2369         // Render HTML from markdown lines.
   2370             // Look at each line and emit fitting HTML code
   2371 
   2372         /**
   2373         * @param {string } innerText
   2374         */
   2375         function markdownInlines(innerText) {
   2376 
   2377             // inline types:
   2378             // **{INLINE}**       : <strong>
   2379                 // __{INLINE}__       : <u>
   2380                 // ~~{INLINE}~~       : <s>
   2381                 //  *{INLINE}*        : <emph>
   2382                 //  _{INLINE}_        : <emph>
   2383                 //  `{TEXT}`          : <code>
   2384                 //  [{INLINE}]({URL}) : <a>
   2385                 // ![{TEXT}]({URL})   : <img>
   2386                 // [[std;format.fmt]] : <a> (inner link)
   2387 
   2388             /** @typedef {{marker: string, tag: string}} Fmt*/
   2389             /** @type {Array<Fmt>} */
   2390             const formats = [
   2391                 {
   2392                     marker: "**",
   2393                     tag: "strong",
   2394                 },
   2395                 {
   2396                     marker: "~~",
   2397                     tag: "s",
   2398                 },
   2399                 {
   2400                     marker: "__",
   2401                     tag: "u",
   2402                 },
   2403                 {
   2404                     marker: "*",
   2405                     tag: "em",
   2406                 }
   2407             ];
   2408 
   2409             /** @type {Array<Fmt>} */
   2410             const stack = [];
   2411 
   2412             let innerHTML = "";
   2413             let currentRun = "";
   2414 
   2415             function flushRun() {
   2416                 if (currentRun != "") {
   2417                     innerHTML += escapeHtml(currentRun);
   2418                 }
   2419                 currentRun = "";
   2420             }
   2421 
   2422             let parsing_code = false;
   2423             let codetag = "";
   2424             let in_code = false;
   2425 
   2426             for (let i = 0; i < innerText.length; i++) {
   2427 
   2428                 if (parsing_code && in_code) {
   2429                     if (innerText.substr(i, codetag.length) == codetag) {
   2430                         // remove leading and trailing whitespace if string both starts and ends with one.
   2431                             if (currentRun[0] == " " && currentRun[currentRun.length - 1] == " ") {
   2432                                 currentRun = currentRun.substr(1, currentRun.length - 2);
   2433                             }
   2434                         flushRun();
   2435                         i += codetag.length - 1;
   2436                         in_code = false;
   2437                         parsing_code = false;
   2438                         innerHTML += "</code>";
   2439                         codetag = "";
   2440                     } else {
   2441                         currentRun += innerText[i];
   2442                     }
   2443                     continue;
   2444                 }
   2445 
   2446                 if (innerText[i] == "`") {
   2447                     flushRun();
   2448                     if (!parsing_code) {
   2449                         innerHTML += "<code>";
   2450                     }
   2451                     parsing_code = true;
   2452                     codetag += "`";
   2453                     continue;
   2454                 }
   2455 
   2456                 if (parsing_code) {
   2457                     currentRun += innerText[i];
   2458                     in_code = true;
   2459                 } else {
   2460                     let any = false;
   2461                     for (let idx = /** @type {number} */(stack.length > 0 ? -1 : 0); idx < formats.length; idx++) {
   2462                         const fmt = idx >= 0 ? formats[idx] : stack[stack.length - 1];
   2463                         if (innerText.substr(i, fmt.marker.length) == fmt.marker) {
   2464                             flushRun();
   2465                             if (stack[stack.length - 1] == fmt) {
   2466                                 stack.pop();
   2467                                 innerHTML += "</" + fmt.tag + ">";
   2468                             } else {
   2469                                 stack.push(fmt);
   2470                                 innerHTML += "<" + fmt.tag + ">";
   2471                             }
   2472                             i += fmt.marker.length - 1;
   2473                             any = true;
   2474                             break;
   2475                         }
   2476                     }
   2477                     if (!any) {
   2478                         currentRun += innerText[i];
   2479                     }
   2480                 }
   2481             }
   2482             flushRun();
   2483 
   2484             while (stack.length > 0) {
   2485                 const fmt = /** @type {Fmt} */(stack.pop());
   2486                 innerHTML += "</" + fmt.tag + ">";
   2487             }
   2488 
   2489             return innerHTML;
   2490         }
   2491 
   2492         /**
   2493         * @param {string} type
   2494         * @param {number} line_no
   2495         */
   2496         function previousLineIs(type, line_no) {
   2497             if (line_no > 0) {
   2498                 return (lines[line_no - 1].type == type);
   2499             } else {
   2500                 return false;
   2501             }
   2502         }
   2503 
   2504         /**
   2505         * @param {string} type
   2506         * @param {number} line_no
   2507         */
   2508         function nextLineIs(type, line_no) {
   2509             if (line_no < (lines.length - 1)) {
   2510                 return (lines[line_no + 1].type == type);
   2511             } else {
   2512                 return false;
   2513             }
   2514         }
   2515 
   2516         /** @param {number} line_no */
   2517         function getPreviousLineIndent(line_no) {
   2518             if (line_no > 0) {
   2519                 return lines[line_no - 1].indent;
   2520             } else {
   2521                 return 0;
   2522             }
   2523         }
   2524 
   2525         /** @param {number} line_no */
   2526         function getNextLineIndent(line_no) {
   2527             if (line_no < (lines.length - 1)) {
   2528                 return lines[line_no + 1].indent;
   2529             } else {
   2530                 return 0;
   2531             }
   2532         }
   2533 
   2534         let html = "";
   2535         for (let line_no = 0; line_no < lines.length; line_no++) {
   2536             const line = lines[line_no];
   2537 
   2538 
   2539 
   2540             switch (line.type) {
   2541                 case "h1":
   2542                 case "h2":
   2543                 case "h3":
   2544                 case "h4":
   2545                 case "h5":
   2546                 case "h6":
   2547                     html += "<" + line.type + ">" + markdownInlines(line.text) + "</" + line.type + ">\n";
   2548                     break;
   2549 
   2550                 case "ul":
   2551                 case "ol":
   2552                     if (!previousLineIs("ul", line_no) || getPreviousLineIndent(line_no) < line.indent) {
   2553                         html += "<" + line.type + ">\n";
   2554                     }
   2555 
   2556                     html += "<li>" + markdownInlines(line.text) + "</li>\n";
   2557 
   2558                     if (!nextLineIs("ul", line_no) || getNextLineIndent(line_no) < line.indent) {
   2559                         html += "</" + line.type + ">\n";
   2560                     }
   2561                     break;
   2562 
   2563                 case "p":
   2564                     if (!previousLineIs("p", line_no)) {
   2565                         html += "<p>\n";
   2566                     }
   2567                     html += markdownInlines(line.text) + "\n";
   2568                     if (!nextLineIs("p", line_no)) {
   2569                         html += "</p>\n";
   2570                     }
   2571                     break;
   2572 
   2573                 case "code":
   2574                     if (!previousLineIs("code", line_no)) {
   2575                         html += "<pre><code>";
   2576                     }
   2577                     html += escapeHtml(line.text) + "\n";
   2578                     if (!nextLineIs("code", line_no)) {
   2579                         html += "</code></pre>\n";
   2580                     }
   2581                     break;
   2582             }
   2583         }
   2584 
   2585         return html;
   2586     }
   2587 
   2588     function activateSelectedResult() {
   2589         if (domSectSearchResults.classList.contains("hidden")) {
   2590             return;
   2591         }
   2592 
   2593         let liDom = domListSearchResults.children[curSearchIndex];
   2594         if (liDom == null && domListSearchResults.children.length !== 0) {
   2595             liDom = domListSearchResults.children[0];
   2596         }
   2597         if (liDom != null) {
   2598             let aDom = liDom.children[0];
   2599             location.href = /** @type {string} */(aDom.getAttribute("href"));
   2600             curSearchIndex = -1;
   2601         }
   2602         domSearch.blur();
   2603     }
   2604 
   2605     /** @param {KeyboardEvent} ev */
   2606     function onSearchKeyDown(ev) {
   2607         switch (getKeyString(ev)) {
   2608             case "Enter":
   2609                 // detect if this search changes anything
   2610                 let terms1 = getSearchTerms();
   2611                 startSearch();
   2612                 updateCurNav();
   2613                 let terms2 = getSearchTerms();
   2614                 // we might have to wait for onHashChange to trigger
   2615                 imFeelingLucky = (terms1.join(' ') !== terms2.join(' '));
   2616                 if (!imFeelingLucky) activateSelectedResult();
   2617 
   2618                 ev.preventDefault();
   2619                 ev.stopPropagation();
   2620                 return;
   2621             case "Esc":
   2622                 domSearch.value = "";
   2623                 domSearch.blur();
   2624                 curSearchIndex = -1;
   2625                 ev.preventDefault();
   2626                 ev.stopPropagation();
   2627                 startSearch();
   2628                 return;
   2629             case "Up":
   2630                 moveSearchCursor(-1);
   2631                 ev.preventDefault();
   2632                 ev.stopPropagation();
   2633                 return;
   2634             case "Down":
   2635                 moveSearchCursor(1);
   2636                 ev.preventDefault();
   2637                 ev.stopPropagation();
   2638                 return;
   2639             default:
   2640                 if (ev.shiftKey || ev.ctrlKey || ev.altKey) return;
   2641 
   2642                 curSearchIndex = -1;
   2643                 ev.stopPropagation();
   2644                 startAsyncSearch();
   2645                 return;
   2646         }
   2647     }
   2648 
   2649 
   2650     /** @param {number} dir */
   2651     function moveSearchCursor(dir) {
   2652         if (curSearchIndex < 0 || curSearchIndex >= domListSearchResults.children.length) {
   2653             if (dir > 0) {
   2654                 curSearchIndex = -1 + dir;
   2655             } else if (dir < 0) {
   2656                 curSearchIndex = domListSearchResults.children.length + dir;
   2657             }
   2658         } else {
   2659             curSearchIndex += dir;
   2660         }
   2661         if (curSearchIndex < 0) {
   2662             curSearchIndex = 0;
   2663         }
   2664         if (curSearchIndex >= domListSearchResults.children.length) {
   2665             curSearchIndex = domListSearchResults.children.length - 1;
   2666         }
   2667         renderSearchCursor();
   2668     }
   2669 
   2670     /** @param {KeyboardEvent} ev */
   2671     function getKeyString(ev) {
   2672         let name;
   2673         let ignoreShift = false;
   2674         switch (ev.which) {
   2675             case 13:
   2676                 name = "Enter";
   2677                 break;
   2678             case 27:
   2679                 name = "Esc";
   2680                 break;
   2681             case 38:
   2682                 name = "Up";
   2683                 break;
   2684             case 40:
   2685                 name = "Down";
   2686                 break;
   2687             default:
   2688                 ignoreShift = true;
   2689                 name = (ev.key != null) ? ev.key : String.fromCharCode(ev.charCode || ev.keyCode);
   2690         }
   2691         if (!ignoreShift && ev.shiftKey) name = "Shift+" + name;
   2692         if (ev.altKey) name = "Alt+" + name;
   2693         if (ev.ctrlKey) name = "Ctrl+" + name;
   2694         return name;
   2695     }
   2696 
   2697     /** @param {KeyboardEvent} ev */
   2698     function onWindowKeyDown(ev) {
   2699         switch (getKeyString(ev)) {
   2700             case "Esc":
   2701                 if (!domHelpModal.classList.contains("hidden")) {
   2702                     domHelpModal.classList.add("hidden");
   2703                     ev.preventDefault();
   2704                     ev.stopPropagation();
   2705                 }
   2706                 break;
   2707             case "s":
   2708                 domSearch.focus();
   2709                 domSearch.select();
   2710                 ev.preventDefault();
   2711                 ev.stopPropagation();
   2712                 startAsyncSearch();
   2713                 break;
   2714             case "?":
   2715                     ev.preventDefault();
   2716                 ev.stopPropagation();
   2717                 showHelpModal();
   2718                 break;
   2719         }
   2720     }
   2721 
   2722 function showHelpModal() {
   2723     domHelpModal.classList.remove("hidden");
   2724     domHelpModal.style.left = (window.innerWidth / 2 - domHelpModal.clientWidth / 2) + "px";
   2725     domHelpModal.style.top = (window.innerHeight / 2 - domHelpModal.clientHeight / 2) + "px";
   2726     domHelpModal.focus();
   2727 }
   2728 
   2729 function clearAsyncSearch() {
   2730     if (searchTimer != null) {
   2731         clearTimeout(searchTimer);
   2732         searchTimer = null;
   2733     }
   2734 }
   2735 
   2736 function startAsyncSearch() {
   2737     clearAsyncSearch();
   2738     searchTimer = setTimeout(startSearch, 100);
   2739 }
   2740 function startSearch() {
   2741     clearAsyncSearch();
   2742     let oldHash = location.hash;
   2743     let parts = oldHash.split("?");
   2744     let newPart2 = (domSearch.value === "") ? "" : ("?" + domSearch.value);
   2745     location.hash = (parts.length === 1) ? (oldHash + newPart2) : (parts[0] + newPart2);
   2746 }
   2747 function getSearchTerms() {
   2748     let list = curNavSearch.trim().split(/[ \r\n\t]+/);
   2749     list.sort();
   2750     return list;
   2751 }
   2752 function renderSearch() {
   2753     let matchedItems = [];
   2754     let ignoreCase = (curNavSearch.toLowerCase() === curNavSearch);
   2755     let terms = getSearchTerms();
   2756 
   2757     decl_loop: for (let declIndex = 0; declIndex < zigAnalysis.decls.length; declIndex += 1) {
   2758         let canonPath = getCanonDeclPath(declIndex);
   2759         if (canonPath == null) continue;
   2760 
   2761         let decl = zigAnalysis.decls[declIndex];
   2762         let lastPkgName = canonPath.pkgNames[canonPath.pkgNames.length - 1];
   2763         let fullPathSearchText = lastPkgName + "." + canonPath.declNames.join('.');
   2764         let astNode = zigAnalysis.astNodes[decl.src];
   2765         let fileAndDocs = "" //zigAnalysis.files[astNode.file];
   2766         // TODO: understand what this piece of code is trying to achieve
   2767         //       also right now `files` are expressed as a hashmap.
   2768         if (astNode.docs != null) {
   2769             fileAndDocs += "\n" + astNode.docs;
   2770         }
   2771         let fullPathSearchTextLower = fullPathSearchText;
   2772         if (ignoreCase) {
   2773             fullPathSearchTextLower = fullPathSearchTextLower.toLowerCase();
   2774             fileAndDocs = fileAndDocs.toLowerCase();
   2775         }
   2776 
   2777         let points = 0;
   2778         for (let termIndex = 0; termIndex < terms.length; termIndex += 1) {
   2779             let term = terms[termIndex];
   2780 
   2781             // exact, case sensitive match of full decl path
   2782             if (fullPathSearchText === term) {
   2783                 points += 4;
   2784                 continue;
   2785             }
   2786             // exact, case sensitive match of just decl name
   2787             if (decl.name == term) {
   2788                 points += 3;
   2789                 continue;
   2790             }
   2791             // substring, case insensitive match of full decl path
   2792             if (fullPathSearchTextLower.indexOf(term) >= 0) {
   2793                 points += 2;
   2794                 continue;
   2795             }
   2796             if (fileAndDocs.indexOf(term) >= 0) {
   2797                 points += 1;
   2798                 continue;
   2799             }
   2800 
   2801             continue decl_loop;
   2802         }
   2803 
   2804         matchedItems.push({
   2805             decl: decl,
   2806             path: canonPath,
   2807             points: points,
   2808         });
   2809     }
   2810 
   2811     if (matchedItems.length !== 0) {
   2812         resizeDomList(domListSearchResults, matchedItems.length, '<li><a href="#"></a></li>');
   2813 
   2814         matchedItems.sort(function(a, b) {
   2815             let cmp = operatorCompare(b.points, a.points);
   2816             if (cmp != 0) return cmp;
   2817             return operatorCompare(a.decl.name, b.decl.name);
   2818         });
   2819 
   2820         for (let i = 0; i < matchedItems.length; i += 1) {
   2821             let liDom = domListSearchResults.children[i];
   2822             let aDom = liDom.children[0];
   2823             let match = matchedItems[i];
   2824             let lastPkgName = match.path.pkgNames[match.path.pkgNames.length - 1];
   2825             aDom.textContent = lastPkgName + "." + match.path.declNames.join('.');
   2826             aDom.setAttribute('href', navLink(match.path.pkgNames, match.path.declNames));
   2827         }
   2828         renderSearchCursor();
   2829 
   2830         domSectSearchResults.classList.remove("hidden");
   2831     } else {
   2832         domSectSearchNoResults.classList.remove("hidden");
   2833     }
   2834 }
   2835 
   2836 function renderSearchCursor() {
   2837     for (let i = 0; i < domListSearchResults.children.length; i += 1) {
   2838         let liDom = /** @type HTMLElement */(domListSearchResults.children[i]);
   2839         if (curSearchIndex === i) {
   2840             liDom.classList.add("selected");
   2841         } else {
   2842             liDom.classList.remove("selected");
   2843         }
   2844     }
   2845 }
   2846 
   2847 
   2848 
   2849 // function indexNodesToCalls() {
   2850 //     let map = {};
   2851 //     for (let i = 0; i < zigAnalysis.calls.length; i += 1) {
   2852 //         let call = zigAnalysis.calls[i];
   2853 //         let fn = zigAnalysis.fns[call.fn];
   2854 //         if (map[fn.src] == null) {
   2855 //             map[fn.src] = [i];
   2856 //         } else {
   2857 //             map[fn.src].push(i);
   2858 //         }
   2859 //     }
   2860 //     return map;
   2861 // }
   2862 
   2863 
   2864 /**
   2865 * @param {{ name: string }} a
   2866 * @param {{ name: string }} b
   2867 */
   2868 function byNameProperty(a, b) {
   2869     return operatorCompare(a.name, b.name);
   2870 }
   2871 
   2872 
   2873 
   2874 })();