Skip to content

Commit e4c1cb2

Browse files
committed
modules/docs: construct docs from docs.* options
Introduce various `docs.*` options where doc pages, menu entries, etc can be defined. The options themselves are responsible for rendering this to markdown and HTML.
1 parent 3aa81c7 commit e4c1cb2

16 files changed

+1108
-2
lines changed

modules/default.nix

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,9 @@
2323
./performance.nix
2424
./plugins.nix
2525
];
26+
27+
docs.options.options = {
28+
enable = true;
29+
optionScopes = [ ];
30+
};
2631
}

modules/docs/_util.nix

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
{
2+
lib,
3+
config,
4+
pkgs,
5+
...
6+
}:
7+
let
8+
inherit (config.docs.menu)
9+
sections
10+
;
11+
12+
transformOption =
13+
let
14+
root = builtins.toString ../../.;
15+
mkGitHubDeclaration = user: repo: branch: subpath: {
16+
url = "https://github.com/${user}/${repo}/blob/${branch}/${subpath}";
17+
name = "<${repo}/${subpath}>";
18+
};
19+
transformDeclaration =
20+
decl:
21+
if lib.hasPrefix root (builtins.toString decl) then
22+
mkGitHubDeclaration "nix-community" "nixvim" "main" (
23+
lib.removePrefix "/" (lib.strings.removePrefix root (builtins.toString decl))
24+
)
25+
else if decl == "lib/modules.nix" then
26+
mkGitHubDeclaration "NixOS" "nixpkgs" "master" decl
27+
else
28+
decl;
29+
in
30+
opt: opt // { declarations = builtins.map transformDeclaration opt.declarations; };
31+
32+
docsPageModule =
33+
{ name, config, ... }:
34+
{
35+
options = {
36+
enable = lib.mkOption {
37+
type = lib.types.bool;
38+
description = "Whether to enable this page/menu item.";
39+
default = true;
40+
example = false;
41+
};
42+
target = lib.mkOption {
43+
type = lib.types.str;
44+
description = ''
45+
The target filepath, relative to the root of the docs.
46+
'';
47+
default = lib.optionalString (name != "") (name + "/") + "index.md";
48+
defaultText = lib.literalMD ''
49+
`<name>` joined with `"index.md"`. Separated by `"/"` if `<name>` is non-empty.
50+
'';
51+
};
52+
source = lib.mkOption {
53+
type = with lib.types; nullOr path;
54+
description = ''
55+
Markdown page. Set to null to create a menu entry without a corresponding file.
56+
'';
57+
};
58+
menu.location = lib.mkOption {
59+
type = with lib.types; listOf str;
60+
description = ''
61+
A location path that represents the page's position in the menu tree.
62+
63+
The text displayed in the menu is derived from this value,
64+
after the location of any parent nodes in the tree is removed.
65+
66+
For example, if this page has the location `[ "foo" "bar" ]`
67+
and there is another page with the location `[ "foo" ]`,
68+
then the menu will render as:
69+
```markdown
70+
- foo
71+
- bar
72+
```
73+
74+
However if there was no other page with the `[ "foo" ]` location,
75+
the menu would instead render as:
76+
```markdown
77+
- foo.bar
78+
```
79+
'';
80+
default =
81+
let
82+
list = lib.splitString "/" config.target;
83+
last = lib.last list;
84+
rest = lib.dropEnd 1 list;
85+
in
86+
if last == "index.md" then
87+
rest
88+
else if lib.hasSuffix ".md" last then
89+
rest ++ [ (lib.removeSuffix ".md" last) ]
90+
else
91+
list;
92+
defaultText = lib.literalMD ''
93+
`target`, split by `"/"`, with any trailing `"index.md` or `".md"` suffixes removed.
94+
'';
95+
};
96+
menu.section = lib.mkOption {
97+
type = lib.types.enum (builtins.attrNames sections);
98+
description = ''
99+
Determines the menu section.
100+
101+
Must be a section defined in `docs.menu.sections`.
102+
'';
103+
};
104+
};
105+
};
106+
in
107+
{
108+
options.docs._utils = lib.mkOption {
109+
type = with lib.types; lazyAttrsOf raw;
110+
description = "internal utils, modules, functions, etc";
111+
default = { };
112+
internal = true;
113+
visible = false;
114+
};
115+
116+
config.docs._utils = {
117+
# A liberal type that permits any superset of docsPageModule
118+
docsPageLiberalType = lib.types.submodule [
119+
{ _module.check = false; }
120+
docsPageModule
121+
];
122+
123+
/**
124+
Uses `lib.optionAttrSetToDocList` to produce a list of docs-options.
125+
126+
A doc-option has the following attrs, as expected by `nixos-render-docs`:
127+
128+
```
129+
{
130+
loc,
131+
name, # rendered with `showOption loc`
132+
description,
133+
declarations,
134+
internal,
135+
visible, # normalised to a boolean
136+
readOnly,
137+
type, # normalised to `type.description`
138+
default,? # rendered with `lib.options.renderOptionValue`
139+
example,? # rendered with `lib.options.renderOptionValue`
140+
relatedPackages,?
141+
}
142+
```
143+
144+
Additionally, sub-options are recursively flattened into the list,
145+
unless `visible == "shallow"` or `visible == false`.
146+
147+
This function extends `lib.optionAttrSetToDocList` by also filtering out
148+
invisible and internal options, and by applying Nixvim's `transformOption`
149+
function.
150+
151+
The implementation is based on `pkgs.nixosOptionsDoc`:
152+
https://github.com/NixOS/nixpkgs/blob/e2078ef3/nixos/lib/make-options-doc/default.nix#L117-L126
153+
*/
154+
mkOptionList = lib.flip lib.pipe [
155+
(lib.flip builtins.removeAttrs [ "_module" ])
156+
lib.optionAttrSetToDocList
157+
(builtins.map transformOption)
158+
(builtins.filter (opt: opt.visible && !opt.internal))
159+
# TODO: consider supporting `relatedPackages`
160+
# See https://github.com/NixOS/nixpkgs/blob/61235d44/lib/options.nix#L103-L104
161+
# and https://github.com/NixOS/nixpkgs/blob/61235d44/nixos/lib/make-options-doc/default.nix#L128-L165
162+
];
163+
164+
inherit docsPageModule;
165+
};
166+
}

modules/docs/all.nix

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
lib,
3+
config,
4+
pkgs,
5+
...
6+
}:
7+
let
8+
inherit (config.docs._utils)
9+
docsPageLiberalType
10+
;
11+
in
12+
{
13+
options.docs = {
14+
_allInputs = lib.mkOption {
15+
type = with lib.types; listOf str;
16+
description = "`docs.*` option names that should be included in `docs.all`.";
17+
defaultText = config.docs._allInputs;
18+
default = [ ];
19+
internal = true;
20+
visible = false;
21+
};
22+
all = lib.mkOption {
23+
type = with lib.types; listOf docsPageLiberalType;
24+
description = ''
25+
All enabled doc pages defined in:
26+
${lib.concatMapStringsSep "\n" (name: "- `docs.${name}`") config.docs._allInputs}.
27+
'';
28+
visible = "shallow";
29+
readOnly = true;
30+
};
31+
src = lib.mkOption {
32+
type = lib.types.package;
33+
description = "All source files for the docs.";
34+
readOnly = true;
35+
};
36+
};
37+
38+
config.docs = {
39+
# Copy all pages from options listed in _allInputs
40+
all = builtins.filter (page: page.enable or true) (
41+
builtins.concatMap (name: builtins.attrValues config.docs.${name}) config.docs._allInputs
42+
);
43+
44+
# A directory with all the files in it
45+
src = pkgs.callPackage ./src.nix {
46+
pages = builtins.filter (page: page.source or null != null) config.docs.all;
47+
};
48+
};
49+
}

modules/docs/default.nix

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,24 @@
55
default = true;
66
description = "Install the man pages for NixVim options.";
77
};
8+
9+
imports = [
10+
./_util.nix
11+
./all.nix
12+
./files.nix
13+
./mdbook
14+
./menu
15+
./options.nix
16+
./platforms.nix
17+
];
18+
19+
config.docs.options = {
20+
docs = {
21+
menu.location = [ "docs" ];
22+
optionScopes = [ "docs" ];
23+
description = ''
24+
Internal options used to construct these docs.
25+
'';
26+
};
27+
};
828
}

modules/docs/files.nix

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
lib,
3+
config,
4+
pkgs,
5+
...
6+
}:
7+
let
8+
inherit (config.docs._utils)
9+
docsPageModule
10+
;
11+
12+
docsPageType = lib.types.submodule (
13+
{
14+
name,
15+
config,
16+
options,
17+
...
18+
}:
19+
let
20+
derivationName = builtins.replaceStrings [ "/" ] [ "-" ] name;
21+
in
22+
{
23+
imports = [
24+
docsPageModule
25+
];
26+
options.text = lib.mkOption {
27+
type = with lib.types; nullOr lines;
28+
default = null;
29+
description = "Text of the file.";
30+
};
31+
config.source = lib.mkIf (config.text != null) (
32+
lib.mkDerivedConfig options.text (builtins.toFile derivationName)
33+
);
34+
}
35+
);
36+
37+
user-guide = ../../docs/user-guide;
38+
39+
sourceTransformers = {
40+
config-examples =
41+
template:
42+
pkgs.callPackage ./user-configs.nix {
43+
inherit template;
44+
};
45+
};
46+
in
47+
{
48+
options.docs = {
49+
files = lib.mkOption {
50+
type = with lib.types; lazyAttrsOf docsPageType;
51+
description = ''
52+
A set of pages to include in the docs.
53+
'';
54+
default = { };
55+
};
56+
};
57+
58+
config.docs = {
59+
files =
60+
# TODO: contributing file
61+
{
62+
"" = {
63+
menu.section = "header";
64+
menu.location = [ "Home" ];
65+
source = pkgs.callPackage ./readme.nix {
66+
# TODO: get `availableVersions` and `baseHref` from module options
67+
};
68+
};
69+
}
70+
// lib.concatMapAttrs (
71+
name: type:
72+
let
73+
title = lib.removeSuffix ".md" name;
74+
transformer = sourceTransformers.${title} or lib.id;
75+
in
76+
lib.optionalAttrs (type == "regular") {
77+
"user-guide/${title}" = {
78+
menu.section = "user-guide";
79+
# TODO: define user-facing titles to show in the menu...
80+
menu.location = [ title ];
81+
source = transformer "${user-guide}/${name}";
82+
};
83+
}
84+
) (builtins.readDir user-guide);
85+
86+
# Register for inclusion in `all`
87+
_allInputs = [ "files" ];
88+
};
89+
}

modules/docs/mdbook/default.nix

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
lib,
3+
config,
4+
pkgs,
5+
...
6+
}:
7+
let
8+
settingsFormat = pkgs.formats.toml { };
9+
defaultSettings = {
10+
book = {
11+
language = "en";
12+
multilingual = false;
13+
title = "nixvim docs";
14+
};
15+
build.create-missing = false;
16+
output.html.site-url = "/";
17+
output.html.fold = {
18+
enable = true;
19+
level = 0;
20+
};
21+
preprocessor.alerts = { };
22+
};
23+
in
24+
{
25+
options.docs.html = {
26+
site = lib.mkOption {
27+
type = lib.types.package;
28+
description = "HTML docs rendered by mdbook.";
29+
readOnly = true;
30+
};
31+
settings = lib.mkOption {
32+
inherit (settingsFormat) type;
33+
description = ''
34+
Freeform settings written to `book.toml`.
35+
36+
See MDBook's [Configuration](https://rust-lang.github.io/mdBook/format/configuration/index.html) docs.
37+
'';
38+
defaultText = defaultSettings;
39+
};
40+
};
41+
config.docs.html = {
42+
site = pkgs.callPackage ./package.nix {
43+
inherit (config.docs) src;
44+
inherit (config.docs.html) settings;
45+
menu = config.docs.menu.src;
46+
writeTOML = settingsFormat.generate;
47+
};
48+
settings = defaultSettings;
49+
};
50+
}

0 commit comments

Comments
 (0)