diff --git a/lib/options.nix b/lib/options.nix index 637188d24d4f2..40fcca647385a 100644 --- a/lib/options.nix +++ b/lib/options.nix @@ -113,7 +113,11 @@ rec { : Optional boolean indicating whether the option is for NixOS developers only. `visible` - : Optional boolean indicating whether the option shows up in the manual. Default: true. Use false to hide the option and any sub-options from submodules. Use "shallow" to hide only sub-options. + : Optional, whether the option and/or sub-options show up in the manual. + Use false to hide the option and any sub-options from submodules. + Use "shallow" to hide only sub-options. + Use "transparent" to hide this option, but not its sub-options. + Default: true. `readOnly` : Optional boolean indicating whether the option can be set only once. @@ -572,13 +576,14 @@ rec { opt: let name = showOption opt.loc; + visible = opt.visible or true; docOption = { loc = opt.loc; inherit name; description = opt.description or null; declarations = filter (x: x != unknownModule) opt.declarations; internal = opt.internal or false; - visible = if (opt ? visible && opt.visible == "shallow") then true else opt.visible or true; + visible = if isBool visible then visible else visible == "shallow"; readOnly = opt.readOnly or false; type = opt.type.description or "unspecified"; } @@ -601,7 +606,7 @@ rec { ss = opt.type.getSubOptions opt.loc; in if ss != { } then optionAttrSetToDocList' opt.loc ss else [ ]; - subOptionsVisible = docOption.visible && opt.visible or null != "shallow"; + subOptionsVisible = if isBool visible then visible else visible == "transparent"; in # To find infinite recursion in NixOS option docs: # builtins.trace opt.loc diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index 9eb887a7b6950..a9c78defdffb3 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -3210,6 +3210,111 @@ runTests { ]; }; + testDocOptionVisiblity = { + expr = + let + submodule = + { lib, ... }: + { + freeformType = lib.types.attrsOf ( + lib.types.submodule { + options.bar = lib.mkOption { }; + } + ); + options.foo = lib.mkOption { }; + }; + + module = + { lib, ... }: + { + options = { + shallow = lib.mkOption { + type = lib.types.submodule submodule; + visible = "shallow"; + }; + transparent = lib.mkOption { + type = lib.types.submodule submodule; + visible = "transparent"; + }; + "true" = lib.mkOption { + type = lib.types.submodule submodule; + visible = true; + }; + "false" = lib.mkOption { + type = lib.types.submodule submodule; + visible = false; + }; + "internal" = lib.mkOption { + type = lib.types.submodule submodule; + internal = true; + }; + }; + }; + + options = + (evalModules { + modules = [ module ]; + }).options; + in + pipe options [ + optionAttrSetToDocList + (filter (opt: !(builtins.elem "_module" opt.loc))) + (map ( + opt: + nameValuePair opt.name { + inherit (opt) visible internal; + } + )) + listToAttrs + ]; + expected = { + shallow = { + visible = true; + internal = false; + }; + transparent = { + visible = false; + internal = false; + }; + "transparent.foo" = { + visible = true; + internal = false; + }; + "transparent..bar" = { + visible = true; + internal = false; + }; + "true" = { + visible = true; + internal = false; + }; + "true.foo" = { + visible = true; + internal = false; + }; + "true..bar" = { + visible = true; + internal = false; + }; + "false" = { + visible = false; + internal = false; + }; + "internal" = { + visible = true; + internal = true; + }; + "internal.foo" = { + visible = true; + internal = false; + }; + "internal..bar" = { + visible = true; + internal = false; + }; + }; + }; + testAttrsWithName = { expr = let