diff --git a/hosts/common/programs/docsets.nix b/hosts/common/programs/docsets.nix index 027b4c765..8521a215d 100644 --- a/hosts/common/programs/docsets.nix +++ b/hosts/common/programs/docsets.nix @@ -60,6 +60,7 @@ in { ]; sane.programs.docsets.config.pkgs = with pkgs; [ # packages which ship docsets natively: + docsets.nixpkgs-lib docsets.rust-std ] ++ lib.map # reconfigure all the rustPkgs so they ship docsets: diff --git a/pkgs/by-name/docsets/nixpkgs-lib/Info.plist b/pkgs/by-name/docsets/nixpkgs-lib/Info.plist new file mode 100644 index 000000000..0920fdd4d --- /dev/null +++ b/pkgs/by-name/docsets/nixpkgs-lib/Info.plist @@ -0,0 +1,16 @@ + + + + + CFBundleIdentifier + nixpkgs-lib + CFBundleName + nixpkgs.lib + DocSetPlatformFamily + nix + isDashDocset + + dashIndexFilePath + manual.html + + diff --git a/pkgs/by-name/docsets/nixpkgs-lib/generate_index b/pkgs/by-name/docsets/nixpkgs-lib/generate_index new file mode 100755 index 000000000..c5362384f --- /dev/null +++ b/pkgs/by-name/docsets/nixpkgs-lib/generate_index @@ -0,0 +1,104 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i python3 -p python3 + +import argparse +import json +import logging +import re +import sqlite3 + +from dataclasses import dataclass +from pathlib import Path + +logger = logging.getLogger(__name__) + +@dataclass +class Items: + functions: list[str] + sections: list[str] + +def read_items(locations_json: Path) -> Items: + # TODO: this seems to yield some items which don't have documentation? like `lib.gvariant.type` + with open(locations_json, "r") as f: + functions = json.load(f).keys() + + sections = set(parent_attrpath(f) for f in functions) + while True: + expanded_sections = sections.union(parent_attrpath(s) for s in sections) + if expanded_sections == sections: + break + else: + sections = expanded_sections + + # the above algorithm includes a nameless root section `""`: remove that + sections = [ s for s in sections if s ] + + # for some cases (lib.gvariant.type), both the section and its members show up in `locations.json` + functions = [ f for f in functions if f not in sections ] + + return Items(functions=sort_attrpaths(functions), sections=sort_attrpaths(sections)) + +def sort_attrpaths(ps: list[str]) -> list[str]: + ps_expanded = sorted(p.split(".") for p in ps) + return [ ".".join(p) for p in ps_expanded ] + +def parent_attrpath(p: str) -> str: + """ `lib.foo.bar` -> `lib.foo` """ + components = p.split(".") + return ".".join(components[:-1]) + +def chomp_attrpath(p: str) -> str: + """ `lib.foo.bar` -> `foo.bar` """ + components = p.split(".") + return ".".join(components[1:]) + +def init_db(db) -> None: + db.execute("CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT);") + db.execute("CREATE UNIQUE INDEX anchor ON searchIndex (name, type, path);") + +def register_item(db, attrpath: str, kind: str, path: str) -> None: + logger.debug(f"register ({attrpath}, {kind}, {path})") + db.execute("INSERT INTO searchIndex(name, type, path) values (?, ?, ?);", (attrpath, kind, path)) + +def register_function(db, attrpath: str) -> None: + register_item(db, attrpath, kind="Function", path=f"manual.html#function-library-{attrpath}") + +def register_section(db, attrpath: str) -> None: + library = chomp_attrpath(attrpath) + if library == "": + path = "manual.html#sec-functions-library" + else: + path = f"manual.html#sec-functions-library-{library}" + register_item(db, attrpath, kind="Property", path=path) + +def main() -> None: + logging.basicConfig() + parser = argparse.ArgumentParser(description = "Generate Dash/Zeal docset index for use with nixpkgs html manual") + parser.add_argument("locations", type=Path, help="path to locations.json, a dict mapping attrpath to code location for all nixpkgs.lib attrs") + parser.add_argument("--output", type=Path) + parser.add_argument("--verbose", action="store_true") + + args = parser.parse_args() + + if args.verbose: + logging.getLogger().setLevel(logging.DEBUG) + + items = read_items(args.locations) + + conn = sqlite3.connect(args.output) + logger.debug("database opened") + db = conn.cursor() + init_db(db) + + for section in items.sections: + register_section(db, section) + + for fn in items.functions: + register_function(db, fn) + + conn.commit() + conn.close() + logger.debug("database closed") + +if __name__ == "__main__": + main() diff --git a/pkgs/by-name/docsets/nixpkgs-lib/package.nix b/pkgs/by-name/docsets/nixpkgs-lib/package.nix new file mode 100644 index 000000000..76c195b2d --- /dev/null +++ b/pkgs/by-name/docsets/nixpkgs-lib/package.nix @@ -0,0 +1,60 @@ +# dash nixpkgs docset tracking issue: +# this package is heavily based on: +# - +# - +{ + lib, + nixpkgs-manual, + static-nix-shell, + stdenv, +}: +let + # nixpkgs has logic to build an attrset of all the items which make it into nixpkgs-manual. + # this is a json dictionary with each entry like: + # - `"lib.asserts.assertEachOneOf": "[lib/asserts.nix:135](https://github.com/NixOS/nixpkgs/blob/master/lib/asserts.nix#L135) in ``"` + lib-locations = nixpkgs-manual.lib-docs.overrideAttrs (base: { + installPhase = base.installPhase + '' + cp locations.json $out/locations.json + ''; + }); + generate_index = static-nix-shell.mkPython3 { + pname = "generate_index"; + srcRoot = ./.; + }; + + docset = stdenv.mkDerivation { + pname = "nixpkgs-lib"; + version = lib.version; + + nativeBuildInputs = [ generate_index ]; + + unpackPhase = '' + cp ${./Info.plist} Info.plist + cp ${lib-locations}/locations.json locations.json + cp -R ${nixpkgs-manual}/share/doc/nixpkgs nixpkgs-manual + ''; + + buildPhase = '' + runHook preBuild + + mkdir -p nixpkgs-lib.docset/Contents/Resources/ + cp Info.plist nixpkgs-lib.docset/Contents/ + cp -R nixpkgs-manual nixpkgs-lib.docset/Contents/Resources/Documents + + generate_index --verbose locations.json --output nixpkgs-lib.docset/Contents/Resources/docSet.dsidx + + runHook postBuild + ''; + + # docsets are usually distributed as .tgz, but compression is actually optional at least for tools like `dasht` + installPhase = '' + mkdir -p $out/share/docsets + cp -R nixpkgs-lib.docset $out/share/docsets/nixpkgs-lib.docset + ''; + + passthru = { + inherit generate_index lib-locations; + }; + }; +in + docset