Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: experimental re-write #2884

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions docs/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@ let
# The following pkgs attrs are required to eval nixvim, even for the docs:
inherit (pkgs)
_type
callPackage
formats
runCommand
runCommandLocal
stdenv
stdenvNoCC
symlinkJoin
runCommand
runCommandLocal
writeShellApplication
;
}
Expand Down Expand Up @@ -122,7 +124,10 @@ lib.fix (
# > sandbox-exec: pattern serialization length 69298 exceeds maximum (65535)
docs = pkgs.callPackage ./mdbook {
inherit evaledModules transformOptions;
inherit (self) search;
inherit (self) search beta-docs;
};

# Beta docs
beta-docs = evaledModules.config.docs.html.site;
}
)
12 changes: 12 additions & 0 deletions docs/mdbook/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
nixosOptionsDoc,
transformOptions,
search,
beta-docs,
# The root directory of the site
baseHref ? "/",
# A list of all available docs that should be linked to
Expand Down Expand Up @@ -371,6 +372,10 @@ pkgs.stdenv.mkDerivation (finalAttrs: {
cp -r ./book/* $dest
mkdir -p $dest/search
cp -r ${finalAttrs.passthru.search}/* $dest/search

# Also build the beta docs
mkdir -p $dest/beta
cp -r ${finalAttrs.passthru.beta-docs}/* $dest/beta
'';

inherit baseHref;
Expand Down Expand Up @@ -401,6 +406,11 @@ pkgs.stdenv.mkDerivation (finalAttrs: {
search = search.override {
baseHref = finalAttrs.baseHref + "search/";
};
beta-docs = beta-docs.override (old: {
settings = lib.recursiveUpdate old.settings {
output.html.site-url = "${baseHref}/beta";
};
});
docs-versions =
runCommand "docs-versions"
{
Expand Down Expand Up @@ -430,6 +440,8 @@ pkgs.stdenv.mkDerivation (finalAttrs: {

echo "- The $link, for use with nixpkgs \`$nixpkgs\`$suffix" >> "$out"
done
# link to beta-docs
echo "- The [beta-docs](./beta), for use with "
'';
user-configs = callPackage ../user-configs { };
};
Expand Down
32 changes: 32 additions & 0 deletions lib/plugins/utils.nix
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,24 @@
{ options, ... }:
let
opts = lib.getAttrFromPath loc options;
docsfile = lib.concatStringsSep "/" loc;
url =
if args.url or null == null then
opts.package.default.meta.homepage or (throw "unable to get URL for `${lib.showOption loc}`.")
else
args.url;
maintainersString =
let
toMD = m: if m ? github then "[${m.name}](https://github.com/${m.github})" else m.name;
names = builtins.map toMD (lib.unique maintainers);
count = builtins.length names;
in
if count == 1 then
builtins.head names
else if count == 2 then
lib.concatStringsSep " and " names
else
lib.concatMapStrings (name: "\n- ${name}") names;
in
{
meta = {
Expand All @@ -110,5 +123,24 @@
path = loc;
};
};

docs.options.${docsfile} = {
title = lib.last loc;
description = lib.concatLines (
[
"**URL:** [${url}](${url})"
""
]
++ lib.optionals (maintainers != [ ]) [
"**Maintainers:** ${maintainersString}"
""
]
++ lib.optionals (description != null && description != "") [
"---"
""
description
]
);
};
Comment on lines +127 to +144
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a gut reaction it feels a bit strange to have a function called mkMetaModule that sets other things than meta arguments.
It feels a "duplicate" of the metadata in another form, can't we extract it with a well placed library function? (Not entirely sure, as the metadata is a bit of a complicated beast --')

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a lot of deliberately duplicated stuff, with the idea being that the "old" way will eventually be removed.

};
}
7 changes: 6 additions & 1 deletion modules/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
# using this in a submodule nested within another nixvim config.
{
imports = [
./docs
./misc
./autocmd.nix
./clipboard.nix
./colorscheme.nix
./commands.nix
./diagnostics.nix
./doc.nix
./editorconfig.nix
./files.nix
./filetype.nix
Expand All @@ -23,4 +23,9 @@
./performance.nix
./plugins.nix
];

docs.options.options = {
enable = true;
optionScopes = [ ];
};
}
8 changes: 0 additions & 8 deletions modules/doc.nix

This file was deleted.

166 changes: 166 additions & 0 deletions modules/docs/_util.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
{
lib,
config,
pkgs,
...
}:
let
inherit (config.docs.menu)
sections
;

transformOption =
let
root = builtins.toString ../../.;
mkGitHubDeclaration = user: repo: branch: subpath: {
url = "https://github.com/${user}/${repo}/blob/${branch}/${subpath}";
name = "<${repo}/${subpath}>";
};
transformDeclaration =
decl:
if lib.hasPrefix root (builtins.toString decl) then
mkGitHubDeclaration "nix-community" "nixvim" "main" (
lib.removePrefix "/" (lib.strings.removePrefix root (builtins.toString decl))
)
else if decl == "lib/modules.nix" then
mkGitHubDeclaration "NixOS" "nixpkgs" "master" decl
else
decl;
in
opt: opt // { declarations = builtins.map transformDeclaration opt.declarations; };

docsPageModule =
{ name, config, ... }:
{
options = {
enable = lib.mkOption {
type = lib.types.bool;
description = "Whether to enable this page/menu item.";
default = true;
example = false;
};
target = lib.mkOption {
type = lib.types.str;
description = ''
The target filepath, relative to the root of the docs.
'';
default = lib.optionalString (name != "") (name + "/") + "index.md";
defaultText = lib.literalMD ''
`<name>` joined with `"index.md"`. Separated by `"/"` if `<name>` is non-empty.
'';
};
source = lib.mkOption {
type = with lib.types; nullOr path;
description = ''
Markdown page. Set to null to create a menu entry without a corresponding file.
'';
};
menu.location = lib.mkOption {
type = with lib.types; listOf str;
description = ''
A location path that represents the page's position in the menu tree.

The text displayed in the menu is derived from this value,
after the location of any parent nodes in the tree is removed.

For example, if this page has the location `[ "foo" "bar" ]`
and there is another page with the location `[ "foo" ]`,
then the menu will render as:
```markdown
- foo
- bar
```

However if there was no other page with the `[ "foo" ]` location,
the menu would instead render as:
```markdown
- foo.bar
```
'';
default =
let
list = lib.splitString "/" config.target;
last = lib.last list;
rest = lib.dropEnd 1 list;
in
if last == "index.md" then
rest
else if lib.hasSuffix ".md" last then
rest ++ [ (lib.removeSuffix ".md" last) ]
else
list;
defaultText = lib.literalMD ''
`target`, split by `"/"`, with any trailing `"index.md` or `".md"` suffixes removed.
'';
};
menu.section = lib.mkOption {
type = lib.types.enum (builtins.attrNames sections);
description = ''
Determines the menu section.

Must be a section defined in `docs.menu.sections`.
'';
};
};
};
in
{
options.docs._utils = lib.mkOption {
type = with lib.types; lazyAttrsOf raw;
description = "internal utils, modules, functions, etc";
default = { };
internal = true;
visible = false;
};

Comment on lines +108 to +115
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we go with a "utils" module instead of additions to the nixvim lib or to a separate "docs" library? (I'm not suggesting it's better, I genuinely don't know why we should choose one or the other)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thought process was that I wanted to avoid bloating our lib with docs-specific things.

I also wanted things in this util to be able to depend on options and config if needed.

Not saying this is the best approach, though.

config.docs._utils = {
# A liberal type that permits any superset of docsPageModule
docsPageLiberalType = lib.types.submodule [
{ _module.check = false; }
docsPageModule
];
Comment on lines +118 to +121
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between this and a freeform option?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah this is an "extendable" submodule in some sense?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A freeform submodule allows config attrs to be defined in a freeform way.

On the other hand, this "liberal" submodule just discards any definitions that don't correspond to a declared option.

I.e. option existence checks are disabled.


/**
Uses `lib.optionAttrSetToDocList` to produce a list of docs-options.

A doc-option has the following attrs, as expected by `nixos-render-docs`:

```
{
loc,
name, # rendered with `showOption loc`
description,
declarations,
internal,
visible, # normalised to a boolean
readOnly,
type, # normalised to `type.description`
default,? # rendered with `lib.options.renderOptionValue`
example,? # rendered with `lib.options.renderOptionValue`
relatedPackages,?
}
```

Additionally, sub-options are recursively flattened into the list,
unless `visible == "shallow"` or `visible == false`.

This function extends `lib.optionAttrSetToDocList` by also filtering out
invisible and internal options, and by applying Nixvim's `transformOption`
function.

The implementation is based on `pkgs.nixosOptionsDoc`:
https://github.com/NixOS/nixpkgs/blob/e2078ef3/nixos/lib/make-options-doc/default.nix#L117-L126
*/
mkOptionList = lib.flip lib.pipe [
MattSturgeon marked this conversation as resolved.
Show resolved Hide resolved
(lib.flip builtins.removeAttrs [ "_module" ])
lib.optionAttrSetToDocList
(builtins.map transformOption)
(builtins.filter (opt: opt.visible && !opt.internal))
# TODO: consider supporting `relatedPackages`
# See https://github.com/NixOS/nixpkgs/blob/61235d44/lib/options.nix#L103-L104
# and https://github.com/NixOS/nixpkgs/blob/61235d44/nixos/lib/make-options-doc/default.nix#L128-L165
];

inherit docsPageModule;
};
}
49 changes: 49 additions & 0 deletions modules/docs/all.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
lib,
config,
pkgs,
...
}:
let
inherit (config.docs._utils)
docsPageLiberalType
;
in
{
options.docs = {
_allInputs = lib.mkOption {
type = with lib.types; listOf str;
description = "`docs.*` option names that should be included in `docs.all`.";
defaultText = config.docs._allInputs;
default = [ ];
internal = true;
visible = false;
};
all = lib.mkOption {
type = with lib.types; listOf docsPageLiberalType;
description = ''
All enabled doc pages defined in:
${lib.concatMapStringsSep "\n" (name: "- `docs.${name}`") config.docs._allInputs}.
'';
visible = "shallow";
readOnly = true;
};
Comment on lines +14 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dance is to have a readonly option whose definitions are split around the nixvim source right? Is the readonly really important? If it were absent this could be simplified a bit with an apply right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kinda.

I marked it as read-only because it seemed unlikely that someone should define a page directly here, without using one of the more specific implementations.

The other options listed in _allInputs all provide some additional functionality, built on top of the base docsPageModule.

Also, unlike all, they don't set _module.check = false, so their config definitions must strictly match their respective option declarations.

src = lib.mkOption {
type = lib.types.package;
description = "All source files for the docs.";
readOnly = true;
};
};

config.docs = {
# Copy all pages from options listed in _allInputs
all = builtins.filter (page: page.enable or true) (
builtins.concatMap (name: builtins.attrValues config.docs.${name}) config.docs._allInputs
);

# A directory with all the files in it
src = pkgs.callPackage ./src.nix {
pages = builtins.filter (page: page.source or null != null) config.docs.all;
};
};
}
28 changes: 28 additions & 0 deletions modules/docs/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{ lib, ... }:
{
options.enableMan = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Install the man pages for NixVim options.";
};

imports = [
./_util.nix
./all.nix
./files.nix
./mdbook
./menu
./options.nix
./platforms.nix
];

config.docs.options = {
docs = {
menu.location = [ "docs" ];
optionScopes = [ "docs" ];
description = ''
Internal options used to construct these docs.
'';
};
};
}
Loading