diff --git a/docs/lib/default.nix b/docs/lib/default.nix index 8659cad6..3690b50d 100644 --- a/docs/lib/default.nix +++ b/docs/lib/default.nix @@ -10,19 +10,14 @@ }: let - pageConfiguration = lib.evalModules { + menuConfiguration = lib.evalModules { modules = [ pageSpecs - { - freeformType = lib.types.attrsOf ( - lib.types.submoduleWith { - modules = [ ../modules/page.nix ]; - } - ); - } + ../modules/menu.nix ]; }; - pages = pageConfiguration.config; + cfg = menuConfiguration.config; + pages = cfg.functions; # Collect all page nodes into a list of page entries collectPages = @@ -33,7 +28,7 @@ let children = builtins.removeAttrs node [ "_page" ]; in lib.optional (node ? _page) node._page ++ lib.optionals (children != { }) (collectPages children) - ) (builtins.attrValues pages); + ) (builtins.attrValues (builtins.removeAttrs pages [ "_category" ])); # Normalised page specs pageList = collectPages pages; @@ -60,11 +55,9 @@ let } ); - passthru.config = pageConfiguration; + passthru.config = menuConfiguration; - passthru.menu = import ./menu.nix { - inherit lib pages; - }; + passthru.menu = cfg._menu.text; passthru.pages = map (page: "${result}/${page.target}") pagesToRender; } diff --git a/docs/lib/menu.nix b/docs/lib/menu.nix deleted file mode 100644 index cd6eb954..00000000 --- a/docs/lib/menu.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ - lib, - pages, - indentSize ? " ", -}: -let - pageToLines = - indent: parent: node: - let - - children = lib.pipe node [ - (lib.flip builtins.removeAttrs [ "_page" ]) - builtins.attrValues - ]; - # Only add node to the menu if it has content or multiple children - useNodeInMenu = node._page.target != "" || node._page.children > 1; - nextParent = if useNodeInMenu then node else parent; - nextIndent = if useNodeInMenu then indent + indentSize else indent; - loc = lib.lists.removePrefix (parent._page.loc or [ ]) node._page.loc; - menuName = lib.attrsets.showAttrPath loc; - in - lib.optional useNodeInMenu "${indent}- [${menuName}](${node._page.target})" - ++ lib.optionals (children != [ ]) ( - builtins.concatMap (pageToLines nextIndent nextParent) children - ); -in -lib.pipe pages [ - builtins.attrValues - (builtins.concatMap (pageToLines "" null)) - lib.concatLines -] diff --git a/docs/lib/pages.nix b/docs/lib/pages.nix index deeb9051..c9cc3625 100644 --- a/docs/lib/pages.nix +++ b/docs/lib/pages.nix @@ -4,19 +4,23 @@ # If there is an issue parsing the file, the resulting markdown will not contain any function docs. { - lib.nixvim = { - _page = { - title = "lib.nixvim: Nixvim's functions"; - source = ./index.md; - }; + functions = { + _category.name = "Functions"; - utils._page = { - title = "lib.nixvim.utils: utility functions"; - functions.file = ../../lib/utils.nix; - }; - lua._page = { - title = "lib.nixvim.lua: lua functions"; - functions.file = ../../lib/to-lua.nix; + lib.nixvim = { + _page = { + title = "lib.nixvim: Nixvim's functions"; + source = ./index.md; + }; + + utils._page = { + title = "lib.nixvim.utils: utility functions"; + functions.file = ../../lib/utils.nix; + }; + lua._page = { + title = "lib.nixvim.lua: lua functions"; + functions.file = ../../lib/to-lua.nix; + }; }; }; } diff --git a/docs/mdbook/SUMMARY.md b/docs/mdbook/SUMMARY.md index 77ecb37b..0b1223c1 100644 --- a/docs/mdbook/SUMMARY.md +++ b/docs/mdbook/SUMMARY.md @@ -9,8 +9,6 @@ - [Configuration examples](./user-guide/config-examples.md) - [Lazy Loading](./user-guide/lazy-loading.md) -# Functions - @FUNCTIONS_MENU@ # Platforms diff --git a/docs/modules/category.nix b/docs/modules/category.nix new file mode 100644 index 00000000..749a29af --- /dev/null +++ b/docs/modules/category.nix @@ -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 . + ''; + }; + + 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") + ]} + ''; + }; +} diff --git a/docs/modules/menu.nix b/docs/modules/menu.nix new file mode 100644 index 00000000..eaa90d6b --- /dev/null +++ b/docs/modules/menu.nix @@ -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") + ]; + }; +} diff --git a/docs/modules/page-options.nix b/docs/modules/page-options.nix index 942c9be9..6300cc05 100644 --- a/docs/modules/page-options.nix +++ b/docs/modules/page-options.nix @@ -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; + }; }; } diff --git a/docs/modules/to-menu.nix b/docs/modules/to-menu.nix new file mode 100644 index 00000000..f0cab7f7 --- /dev/null +++ b/docs/modules/to-menu.nix @@ -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)