docs/modules: model page content as sections
Represent page content as a list of typed sections, rather than a fixed set of page attributes. This allows pages to be composed from multiple content sources while simplifying the rendering logic and prepares the new docs system for additional section types such as option documentation.
This commit is contained in:
parent
f60bf47332
commit
91df3d9f91
3 changed files with 116 additions and 140 deletions
|
|
@ -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 <h1> 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
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue