1
0
Fork 0
mirror of https://github.com/nix-community/nixvim.git synced 2025-12-16 05:51:10 +01:00

docs/lib: generalise menu impl using module system

Move the mdbook menu rendering code into the module system and
generalise it to apply to multiple "categories" (mdbook parts) and
"types" of category (prefix, suffix, etc).
This commit is contained in:
Matt Sturgeon 2025-09-23 18:25:39 +01:00
parent 2f952af4a7
commit 4f03ca05d9
8 changed files with 228 additions and 60 deletions

89
docs/modules/category.nix Normal file
View file

@ -0,0 +1,89 @@
{
lib,
name,
config,
options,
...
}:
let
cfg = config._category;
pageType = lib.types.submoduleWith {
modules = [ ./page.nix ];
};
pages = builtins.removeAttrs config (builtins.attrNames options);
in
{
freeformType = lib.types.attrsOf pageType;
options._category = {
name = lib.mkOption {
type = lib.types.str;
default = name;
defaultText = lib.literalMD "attribute name";
};
order = lib.mkOption {
type = lib.types.int;
default = 100;
description = "Priority for where this category will appear in the menu.";
};
type = lib.mkOption {
type = lib.types.enum [
"prefix"
"normal"
"suffix"
];
default = "normal";
description = ''
The kind of mdbook chapters this category contains.
**Prefix Chapter**
: Before the main numbered chapters, prefix chapters can be added that
will not be numbered. This is useful for forewords, introductions, etc.
There are, however, some constraints.
Prefix chapters cannot be nested; they should all be on the root level.
And you cannot add prefix chapters once you have added numbered chapters.
**Normal Chapter**
: Called a "Numbered Chapter" in the MDBook docs.
Numbered chapters outline the main content of the book and can be
nested, resulting in a nice hierarchy (chapters, sub-chapters, etc.).
**Suffix Chapter**
: Like prefix chapters, suffix chapters are unnumbered, but they come
after numbered chapters.
See <https://rust-lang.github.io/mdBook/format/summary.html>.
'';
};
text = lib.mkOption {
type = lib.types.str;
description = "The rendered menu.";
readOnly = true;
};
};
config._category = {
text = lib.optionalString (pages != { }) ''
# ${cfg.name}
${lib.pipe pages [
builtins.attrValues
(map (
page:
page._page.toMenu {
nested = cfg.type == "normal";
indent = "";
prefix = [ ];
inherit page;
}
))
(builtins.concatStringsSep "\n")
]}
'';
};
}

43
docs/modules/menu.nix Normal file
View file

@ -0,0 +1,43 @@
{
lib,
config,
options,
...
}:
let
categoryType = lib.types.submoduleWith {
modules = [ ./category.nix ];
};
categories = builtins.removeAttrs config (builtins.attrNames options);
in
{
freeformType = lib.types.attrsOf categoryType;
options._menu = {
text = lib.mkOption {
type = lib.types.str;
description = "The rendered menu.";
readOnly = true;
};
};
config._menu = {
text = lib.pipe categories [
builtins.attrValues
(map (x: x._category))
(lib.sortOn (x: x.order))
(builtins.groupBy (x: x.type))
(
{
prefix ? [ ],
normal ? [ ],
suffix ? [ ],
}:
prefix ++ normal ++ suffix
)
(map (x: x.text))
(builtins.concatStringsSep "\n\n")
];
};
}

View file

@ -49,7 +49,7 @@ in
};
functions.loc = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = lib.lists.removePrefix [ "lib" ] cfg.loc;
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"
'';
@ -72,6 +72,30 @@ in
If an attrset is provided, it will be coerced using `lib.options.optionAttrSetToDocList`.
'';
};
toMenu = lib.mkOption {
type = lib.types.functionTo lib.types.str;
description = ''
A function to render the menu for this sub-tree.
Typically, this involves invoking `_page.toMenu` for all children.
**Inputs**
`settings`
: `nested`
: Whether this menu category supports nesting.
`indent`
: The indentation to use before non-empty lines.
`page`
: This page node.
`prefix`
: The menu loc prefix, to be omitted from menu entry text.
Usually the `loc` of the parent page node.
'';
};
children = lib.mkOption {
type = lib.types.ints.unsigned;
description = ''
@ -100,5 +124,10 @@ in
cfg.functions.file # doc-comments
cfg.options # module options
];
toMenu = import ./to-menu.nix {
inherit lib;
optionNames = builtins.attrNames options;
};
};
}

43
docs/modules/to-menu.nix Normal file
View file

@ -0,0 +1,43 @@
{
lib,
optionNames,
}:
/**
The default `toMenu` function renders a page node into a menu subtree.
*/
{
page,
prefix ? [ ],
indent ? "",
nested ? true,
}:
let
inherit (page._page) loc target;
count = page._page.children;
# Only add node to the menu if it has content or multiple children
showInMenu = target != "" || count > 1;
nextPrefix = if showInMenu then loc else prefix;
nextIndent = if showInMenu && nested then indent + " " else indent;
children = builtins.removeAttrs page optionNames;
submenu = lib.pipe children [
builtins.attrValues
(map (
subpage:
page._page.toMenu {
inherit nested;
page = subpage;
indent = nextIndent;
prefix = nextPrefix;
}
))
];
loc' = if lib.lists.hasPrefix prefix loc then lib.lists.drop (builtins.length prefix) loc else loc;
menuText = lib.attrsets.showAttrPath loc';
menuitem = lib.optionals showInMenu [
(indent + lib.optionalString nested "- " + "[${menuText}](${target})")
];
in
builtins.concatStringsSep "\n" (menuitem ++ submenu)