diff --git a/docs/lib/default.nix b/docs/lib/default.nix index c5fea47f..5b63fb0c 100644 --- a/docs/lib/default.nix +++ b/docs/lib/default.nix @@ -1,5 +1,3 @@ -# Generates the documentation for library functions using nixdoc. -# See https://github.com/nix-community/nixdoc { lib, runCommand, @@ -32,23 +30,51 @@ let # Normalised page specs pageList = collectPages pages; - pagesToRender = builtins.filter (page: page.hasContent) pageList; + pagesToRender = builtins.filter (page: page.target != "") pageList; + + # Function(s) to render page sections + renderSection = { + __functor = + self: section: + let + names = builtins.attrNames section; + tag = builtins.head names; + in + assert + builtins.length names == 1 + || throw "Section type should have 1 attribute, found ${toString (builtins.length names)}."; + self.${tag} section.${tag}; + + # Embed text as-is + title = title: "printf '# %s\\n' ${lib.escapeShellArg title}"; + file = file: "cat ${file}"; + text = text: "printf '%s\\n' ${lib.escapeShellArg text}"; + + # Generates the documentation for library functions using nixdoc. + # See https://github.com/nix-community/nixdoc + functions = { file, loc }: '' + ${lib.getExe nixdoc} \ + --file ${file} \ + --locs "$locations" \ + --category ${lib.escapeShellArg (lib.showAttrPath loc)} \ + --description "REMOVED BY TAIL" \ + --prefix "lib" \ + --anchor-prefix "" \ + | tail --lines +2 + ''; + }; result = runCommand "nixvim-lib-docs" { - nativeBuildInputs = [ - nixdoc - ]; - locations = writers.writeJSON "locations.json" ( import ./function-locations.nix { inherit lib; rootPath = nixvim; functionSet = lib.extend nixvim.lib.overlay; pathsToScan = lib.pipe pageList [ - (map (x: x.functions)) - (builtins.filter (x: x.file != null)) + (lib.concatMap (page: page.content)) + (lib.catAttrs "functions") (map (x: x.loc)) ]; revision = nixvim.rev or "main"; @@ -62,80 +88,29 @@ let passthru.pages = map (page: "${result}/${page.target}") pagesToRender; } '' - function docgen { - md_file="$1" - in_file="$2" - category="$3" - out_file="$out/$4" - title="$5" - - if [[ -z "$in_file" ]]; then - if [[ -z "$md_file" ]]; then - >&2 echo "No markdown or nix file for $category" - exit 1 - fi - elif [[ -f "$in_file/default.nix" ]]; then - in_file+="/default.nix" - elif [[ ! -f "$in_file" ]]; then - >&2 echo "File not found: $in_file" - exit 1 - fi - - if [[ -n "$in_file" ]]; then - nixdoc \ - --file "$in_file" \ - --locs "$locations" \ - --category "$category" \ - --description "REMOVED BY TAIL" \ - --prefix "lib" \ - --anchor-prefix "" \ - | tail --lines +2 \ - > functions.md - fi - - print_title=true - if [[ -f "$md_file" ]] && [[ "$(head --lines 1 "$md_file")" == '# '* ]]; then - if [[ -n "$title" ]]; then - >&2 echo "NOTE: markdown file for $category starts with a

heading. Skipping title \"$title\"." - >&2 echo " Found \"$(head --lines 1 "$md_file")\" in: $md_file" - fi - print_title=false - fi - - mkdir -p $(dirname "$out_file") - ( - if [[ "$print_title" = true ]]; then - echo "# $title" - echo - fi - if [[ -f "$md_file" ]]; then - cat "$md_file" - echo - fi - if [[ -f functions.md ]]; then - cat functions.md - fi - ) > "$out_file" - } - mkdir -p "$out" ${lib.concatMapStringsSep "\n" ( { - functions, - source, + title, + content, target, - title ? "", ... }: - lib.escapeShellArgs [ - "docgen" - "${lib.optionalString (source != null) source}" # md_file - "${lib.optionalString (functions.file != null) functions.file}" # in_file - (lib.showAttrPath functions.loc) # category - target # out_file - title # title - ] + let + sections = lib.optional (title != null) { inherit title; } ++ content; + in + '' + ${lib.toShellVars { inherit target; }} + mkdir -p "$(dirname "$out/$target")" + { + ${lib.pipe sections [ + (map renderSection) + (lib.intersperse "echo") + lib.concatLines + ]} + } >"$out/$target" + '' ) pagesToRender} ''; in diff --git a/docs/lib/pages.nix b/docs/lib/pages.nix index 8bc5f0a5..3d746e3f 100644 --- a/docs/lib/pages.nix +++ b/docs/lib/pages.nix @@ -10,22 +10,30 @@ lib.nixvim = { _page = { title = "lib.nixvim: Nixvim's functions"; - source = ./index.md; + content = [ + ./index.md + ]; }; modules._page = { title = "lib.nixvim.modules: module functions"; - functions.file = ../../lib/modules.nix; + content = [ + { functions.file = ../../lib/modules.nix; } + ]; }; utils._page = { title = "lib.nixvim.utils: utility functions"; - functions.file = ../../lib/utils.nix; + content = [ + { functions.file = ../../lib/utils.nix; } + ]; }; lua._page = { title = "lib.nixvim.lua: lua functions"; - functions.file = ../../lib/to-lua.nix; + content = [ + { functions.file = ../../lib/to-lua.nix; } + ]; }; }; }; diff --git a/docs/modules/page-options.nix b/docs/modules/page-options.nix index 6300cc05..a84db706 100644 --- a/docs/modules/page-options.nix +++ b/docs/modules/page-options.nix @@ -8,7 +8,52 @@ }: let cfg = config._page; - opts = options._page; + + contentType = lib.types.coercedTo lib.types.path (file: { inherit file; }) ( + lib.types.attrTag { + file = lib.mkOption { + description = "A markdown file."; + type = lib.types.path; + }; + text = lib.mkOption { + description = "Lines of markdown."; + type = lib.types.lines; + }; + functions = lib.mkOption { + description = "Nix file to scan for RFC145 doc comments."; + type = lib.types.submodule { + options.file = lib.mkOption { + type = lib.types.path; + description = "Nix file."; + }; + options.loc = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = if lib.lists.hasPrefix [ "lib" ] cfg.loc then builtins.tail cfg.loc else cfg.loc; + defaultText = lib.literalMD '' + `loc`'s attrpath, without any leading "lib" + ''; + description = '' + Optional attrpath where functions are defined. + Provided to `nixdoc` as `--category`. + + Will scan `lib` for attribute locations in the functions set at this attrpath. + + Used in conjunction with `nix`. + ''; + }; + }; + }; + options = lib.mkOption { + description = '' + Set of options or list of option docs-templates. + + If an attrset is provided, it will be coerced using `lib.options.optionAttrSetToDocList`. + ''; + type = lib.types.raw; + apply = opts: if builtins.isAttrs opts then lib.options.optionAttrSetToDocList opts else opts; + }; + } + ); in { options._page = { @@ -21,7 +66,9 @@ in }; target = lib.mkOption { type = lib.types.str; - default = lib.optionalString cfg.hasContent (lib.concatStringsSep "/" (cfg.loc ++ [ "index.md" ])); + default = lib.optionalString (cfg.content != [ ]) ( + lib.concatStringsSep "/" (cfg.loc ++ [ "index.md" ]) + ); defaultText = lib.literalMD '' `""` if page has no content, otherwise a filepath derived from the page's `loc`. ''; @@ -32,45 +79,10 @@ in default = null; description = "Page's heading title."; }; - text = lib.mkOption { - type = lib.types.nullOr lib.types.lines; - default = null; - description = "Optional markdown text to include after the title."; - }; - source = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - description = "Optional markdown file to include after the title."; - }; - functions.file = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - description = "Optional nix file to scan for RFC145 doc comments."; - }; - functions.loc = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = if lib.lists.hasPrefix [ "lib" ] cfg.loc then builtins.tail cfg.loc else cfg.loc; - defaultText = lib.literalMD '' - `loc`'s attrpath, without any leading "lib" - ''; - description = '' - Optional attrpath where functions are defined. - Provided to `nixdoc` as `--category`. - - Will scan `lib` for attribute locations in the functions set at this attrpath. - - Used in conjunction with `nix`. - ''; - }; - options = lib.mkOption { - type = lib.types.nullOr lib.types.raw; - default = null; - apply = opts: if builtins.isAttrs opts then lib.options.optionAttrSetToDocList opts else opts; - description = '' - Optional set of options or list of option docs-templates. - - If an attrset is provided, it will be coerced using `lib.options.optionAttrSetToDocList`. - ''; + content = lib.mkOption { + type = lib.types.listOf contentType; + default = [ ]; + description = "Optional content sections rendered after the title."; }; toMenu = lib.mkOption { type = lib.types.functionTo lib.types.str; @@ -103,28 +115,9 @@ in ''; readOnly = true; }; - hasContent = lib.mkOption { - type = lib.types.bool; - description = '' - Whether this page has any docs content. - - When `false`, this page represents an _empty_ menu entry. - ''; - readOnly = true; - }; }; config._page = { - source = lib.mkIf (cfg.text != null) ( - lib.mkDerivedConfig opts.text (builtins.toFile "docs-${lib.attrsets.showAttrPath cfg.loc}-text.md") - ); - - hasContent = builtins.any (x: x != null) [ - cfg.source # markdown - cfg.functions.file # doc-comments - cfg.options # module options - ]; - toMenu = import ./to-menu.nix { inherit lib; optionNames = builtins.attrNames options;