docs: Use a nixpkgs patch instead of copy/pasting nixpkgs functions (#1011)

This makes the code more maintainable, as it only depends on our small
patch, instead of a large number of internals of nixpkgs.
This commit is contained in:
traxys 2024-02-03 19:04:09 +01:00 committed by GitHub
parent 2294a12b0b
commit 507ff5b142
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 97 additions and 251 deletions

View file

@ -1,8 +1,21 @@
{
lib,
modules,
rawModules,
pkgs,
}: let
pkgsDoc =
import (pkgs.applyPatches {
name = "nixpkgs-nixvim-doc";
src = pkgs.path;
patches = [
./either_recursive.patch
];
}) {
inherit (pkgs) system;
config.allowUnfree = true;
};
inherit (pkgsDoc) lib;
nixvimPath = toString ./..;
gitHubDeclaration = user: repo: subpath: {
@ -16,10 +29,10 @@
declarations =
map (
decl:
if pkgs.lib.hasPrefix nixvimPath (toString decl)
if lib.hasPrefix nixvimPath (toString decl)
then
gitHubDeclaration "nix-community" "nixvim"
(pkgs.lib.removePrefix "/" (pkgs.lib.removePrefix nixvimPath (toString decl)))
(lib.removePrefix "/" (lib.removePrefix nixvimPath (toString decl)))
else if decl == "lib/modules.nix"
then gitHubDeclaration "NixOS" "nixpkgs" decl
else decl
@ -27,222 +40,6 @@
opt.declarations;
};
getSubOptions' = type: loc: let
types =
# Composite types
{
either =
(getSubOptions' type.nestedTypes.left loc)
// (getSubOptions' type.nestedTypes.right loc);
nullOr = getSubOptions' type.nestedTypes.elemType loc;
lazyAttrsOf = getSubOptions' type.nestedTypes.elemType (loc ++ ["<name>"]);
attrsOf = getSubOptions' type.nestedTypes.elemType (loc ++ ["<name>"]);
listOf = getSubOptions' type.nestedTypes.elemType (loc ++ ["*"]);
functionTo = getSubOptions' type.nestedTypes.elemType (loc ++ ["<function body>"]);
# Taken from lib/types.nix
submodule = let
base = lib.evalModules {
modules =
[
{
_module.args.name = lib.mkOptionDefault "name";
}
]
++ type.getSubModules;
};
inherit (base._module) freeformType;
in
(base.extendModules
{prefix = loc;})
.options
// lib.optionalAttrs (freeformType != null) {
_freeformOptions = getSubOptions' freeformType loc;
};
}
# Leaf types
// lib.genAttrs [
"raw"
"bool"
"optionType"
"unspecified"
"str"
"attrs"
"rawLua"
"int"
"package"
"numberBetween"
"enum"
"anything"
"separatedString"
"path"
"maintainer"
"unsignedInt"
"float"
"positiveInt"
"intBetween"
"nullType"
"nonEmptyStr"
"nixvim-configuration"
] (_: {});
in
# For recursive types avoid calculating sub options, else this
# will end up in an unbounded recursion
if loc == ["plugins" "packer" "plugins" "*" "requires"]
then {}
else if builtins.hasAttr type.name types
then types.${type.name}
else throw "unhandled type in documentation: ${type.name}";
mkOptionsJSON = options: let
# Mainly present to patch the type.getSubOptions of `either`, but we need to patch all
# the options in order to correctly handle other composite options
# The code that follows is taken almost exactly from nixpkgs,
# by changing type.getSubOptions to getSubOptions'
# lib/options.nix
optionAttrSetToDocList' = _: options:
lib.concatMap (opt: let
name = lib.showOption opt.loc;
docOption =
{
inherit (opt) loc;
inherit name;
description = opt.description or null;
declarations = builtins.filter (x: x != lib.unknownModule) opt.declarations;
internal = opt.internal or false;
visible =
if (opt ? visible && opt.visible == "shallow")
then true
else opt.visible or true;
readOnly = opt.readOnly or false;
type = opt.type.description or "unspecified";
}
// lib.optionalAttrs (opt ? example) {
example = builtins.addErrorContext "while evaluating the example of option `${name}`" (
lib.options.renderOptionValue opt.example
);
}
// lib.optionalAttrs (opt ? defaultText || opt ? default) {
default =
builtins.addErrorContext "while evaluating the ${
if opt ? defaultText
then "defaultText"
else "default value"
} of option `${name}`" (
lib.options.renderOptionValue (opt.defaultText or opt.default)
);
}
// lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) {inherit (opt) relatedPackages;};
subOptions = let
ss = getSubOptions' opt.type opt.loc;
in
if ss != {}
then optionAttrSetToDocList' opt.loc ss
else [];
subOptionsVisible = docOption.visible && opt.visible or null != "shallow";
in
# To find infinite recursion in NixOS option docs:
# builtins.trace opt.loc
[docOption] ++ lib.optionals subOptionsVisible subOptions) (lib.collect lib.isOption options);
# Generate documentation template from the list of option declaration like
# the set generated with filterOptionSets.
optionAttrSetToDocList = optionAttrSetToDocList' [];
# nixos/lib/make-options-doc/default.nix
rawOpts = optionAttrSetToDocList options;
transformedOpts = map transformOptions rawOpts;
filteredOpts = lib.filter (opt: opt.visible && !opt.internal) transformedOpts;
# Generate DocBook documentation for a list of packages. This is
# what `relatedPackages` option of `mkOption` from
# ../../../lib/options.nix influences.
#
# Each element of `relatedPackages` can be either
# - a string: that will be interpreted as an attribute name from `pkgs` and turned into a link
# to search.nixos.org,
# - a list: that will be interpreted as an attribute path from `pkgs` and turned into a link
# to search.nixos.org,
# - an attrset: that can specify `name`, `path`, `comment`
# (either of `name`, `path` is required, the rest are optional).
#
# NOTE: No checks against `pkgs` are made to ensure that the referenced package actually exists.
# Such checks are not compatible with option docs caching.
genRelatedPackages = packages: optName: let
unpack = p:
if lib.isString p
then {name = p;}
else if lib.isList p
then {path = p;}
else p;
describe = args: let
title = args.title or null;
name = args.name or (lib.concatStringsSep "." args.path);
in ''
- [${lib.optionalString (title != null) "${title} aka "}`pkgs.${name}`](
https://search.nixos.org/packages?show=${name}&sort=relevance&query=${name}
)${
lib.optionalString (args ? comment) "\n\n ${args.comment}"
}
'';
in
lib.concatMapStrings (p: describe (unpack p)) packages;
nixvimOptionsList =
lib.flip map filteredOpts
(
opt:
opt
// lib.optionalAttrs (opt ? relatedPackages && opt.relatedPackages != []) {
relatedPackages = genRelatedPackages opt.relatedPackages opt.name;
}
);
nixvimOptionsNix = builtins.listToAttrs (map (o: {
inherit (o) name;
value = removeAttrs o ["name" "visible" "internal"];
})
nixvimOptionsList);
in
pkgs.runCommand "options.json"
{
meta.description = "List of NixOS options in JSON format";
nativeBuildInputs = [
pkgs.brotli
pkgs.python3Minimal
];
options =
builtins.toFile "options.json"
(builtins.unsafeDiscardStringContext (builtins.toJSON nixvimOptionsNix));
baseJSON = builtins.toFile "base.json" "{}";
}
''
# Export list of options in different format.
dst=$out/share/doc/nixos
mkdir -p $dst
TOUCH_IF_DB=$dst/.used-docbook \
python ${pkgs.path}/nixos/lib/make-options-doc/mergeJSON.py \
$baseJSON $options \
> $dst/options.json
if grep /nixpkgs/nixos/modules $dst/options.json; then
echo "The manual appears to depend on the location of Nixpkgs, which is bad"
echo "since this prevents sharing via the NixOS channel. This is typically"
echo "caused by an option default that refers to a relative path (see above"
echo "for hints about the offending path)."
exit 1
fi
brotli -9 < $dst/options.json > $dst/options.json.br
mkdir -p $out/nix-support
echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products
echo "file json-br $dst/options.json.br" >> $out/nix-support/hydra-build-products
'';
nixvmConfigType = lib.mkOptionType {
name = "nixvim-configuration";
description = "nixvim configuration options";
@ -270,17 +67,24 @@
};
})
]
++ modules;
++ (rawModules pkgsDoc);
in
rec {
options-json = mkOptionsJSON (lib.evalModules {modules = topLevelModules;}).options;
man-docs = pkgs.callPackage ./man {inherit options-json;};
options-json =
(pkgsDoc.nixosOptionsDoc
{
inherit (lib.evalModules {modules = topLevelModules;}) options;
inherit transformOptions;
warningsAreErrors = false;
})
.optionsJSON;
man-docs = pkgsDoc.callPackage ./man {inherit options-json;};
}
# Do not check if documentation builds fine on darwin as it fails:
# > sandbox-exec: pattern serialization length 69298 exceeds maximum (65535)
// lib.optionalAttrs (!pkgs.stdenv.isDarwin) {
docs = pkgs.callPackage ./mdbook {
inherit mkOptionsJSON getSubOptions';
// lib.optionalAttrs (!pkgsDoc.stdenv.isDarwin) {
docs = pkgsDoc.callPackage ./mdbook {
inherit transformOptions;
modules = topLevelModules;
};
}

View file

@ -0,0 +1,49 @@
commit e067eb1f6da7994150f10854e5fd635ca8bd0e92
Author: traxys <quentin+dev@familleboyer.net>
Date: Sun Jan 14 17:54:55 2024 +0100
lib.types: Include the suboptions of both types for either
This allows to correctly gather the sub options for types of the form
`either <type> (submodule {...})`.
This requires adding two new types: `eitherRecursive` and
`oneOfRecursive` that avoid infinite recursions for types like
configuration types that are often of the form `oneOf [atom (listOf
configType)]`
diff --git a/lib/types.nix b/lib/types.nix
index cea63c598321..d26982db6cc0 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -906,10 +906,16 @@ rec {
then functor.type mt1 mt2
else null;
functor = (defaultFunctor name) // { wrapped = [ t1 t2 ]; };
+ getSubOptions = prefix: (t1.getSubOptions prefix) // (t2.getSubOptions prefix);
nestedTypes.left = t1;
nestedTypes.right = t2;
};
+ # Handle recursive leaf types, avoiding an infinite recursion
+ eitherRecursive = t1: t2: (either t1 t2) // {
+ getSubOptions = _: {};
+ };
+
# Any of the types in the given list
oneOf = ts:
let
@@ -917,6 +923,13 @@ rec {
tail' = tail ts;
in foldl' either head' tail';
+ # Handle recursive leaf types, avoiding an infinite recursion
+ oneOfRecursive = ts:
+ let
+ head' = if ts == [] then throw "types.oneOfRecursive needs to get at least one type in its argument" else head ts;
+ tail' = tail ts;
+ in foldl' eitherRecursive head' tail';
+
# Either value of type `coercedType` or `finalType`, the former is
# converted to `finalType` using `coerceFunc`.
coercedTo = coercedType: coerceFunc: finalType:

View file

@ -2,10 +2,8 @@
pkgs,
lib,
modules,
mkOptionsJSON,
runCommand,
nixos-render-docs,
getSubOptions',
nixosOptionsDoc,
transformOptions,
}:
with lib; let
options = lib.evalModules {
@ -13,18 +11,12 @@ with lib; let
specialArgs = {inherit pkgs lib;};
};
mkMDDoc = options: let
optionsJson = mkOptionsJSON options;
in
runCommand "options.md" {
nativeBuildInputs = [nixos-render-docs];
} ''
nixos-render-docs -j $NIX_BUILD_CORES options commonmark \
--manpage-urls ${pkgs.path + "/doc/manpage-urls.json"} \
--revision "" \
${optionsJson}/share/doc/nixos/options.json \
$out
'';
mkMDDoc = options:
(nixosOptionsDoc {
inherit options transformOptions;
warningsAreErrors = false;
})
.optionsCommonMark;
removeUnwanted = attrs:
builtins.removeAttrs attrs [
@ -37,7 +29,7 @@ with lib; let
removeWhitespace = builtins.replaceStrings [" "] [""];
getSubOptions = opts: path: removeUnwanted (getSubOptions' opts.type path);
getSubOptions = opts: path: removeUnwanted (opts.type.getSubOptions path);
isVisible = opts:
if isOption opts