diff --git a/.gitignore b/.gitignore
index e326966d6..53442751f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,9 +19,11 @@ perl/Makefile.config
/doc/manual/nix.json
/doc/manual/conf-file.json
/doc/manual/builtins.json
+/doc/manual/xp-features.json
/doc/manual/src/SUMMARY.md
/doc/manual/src/command-ref/new-cli
/doc/manual/src/command-ref/conf-file.md
+/doc/manual/src/command-ref/experimental-features.md
/doc/manual/src/language/builtins.md
# /scripts/
diff --git a/doc/manual/generate-xp-features.nix b/doc/manual/generate-xp-features.nix
new file mode 100644
index 000000000..db1ba6092
--- /dev/null
+++ b/doc/manual/generate-xp-features.nix
@@ -0,0 +1,11 @@
+with builtins;
+with import ./utils.nix;
+
+let
+ showExperimentalFeature = name: doc:
+ squash ''
+ - [`${name}`](#xp-feature-${name})
+
+ ${indent " " doc}
+ '';
+in xps: indent " " (concatStringsSep "\n" (attrValues (mapAttrs showExperimentalFeature xps)))
diff --git a/doc/manual/local.mk b/doc/manual/local.mk
index df941d460..0d4b9d640 100644
--- a/doc/manual/local.mk
+++ b/doc/manual/local.mk
@@ -88,12 +88,12 @@ $(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli
@cp $< $@
@$(call process-includes,$@,$@)
-$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/generate-manpage.nix $(bindir)/nix
+$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/utils.nix $(d)/generate-manpage.nix $(bindir)/nix
@rm -rf $@ $@.tmp
$(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix (builtins.readFile $<)'
@mv $@.tmp $@
-$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/src/command-ref/conf-file-prefix.md $(bindir)/nix
+$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/src/command-ref/conf-file-prefix.md $(d)/src/command-ref/experimental-features.md $(bindir)/nix
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
$(trace-gen) $(nix-eval) --expr '(import doc/manual/utils.nix).showSettings { useAnchors = true; } (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
@mv $@.tmp $@
@@ -106,6 +106,15 @@ $(d)/conf-file.json: $(bindir)/nix
$(trace-gen) $(dummy-env) $(bindir)/nix show-config --json --experimental-features nix-command > $@.tmp
@mv $@.tmp $@
+$(d)/src/command-ref/experimental-features.md: $(d)/xp-features.json $(d)/utils.nix $(d)/generate-xp-features.nix $(bindir)/nix
+ @rm -rf $@ $@.tmp
+ $(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-xp-features.nix (builtins.fromJSON (builtins.readFile $<))'
+ @mv $@.tmp $@
+
+$(d)/xp-features.json: $(bindir)/nix
+ $(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-xp-features > $@.tmp
+ @mv $@.tmp $@
+
$(d)/src/language/builtins.md: $(d)/builtins.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
@cat doc/manual/src/language/builtins-prefix.md > $@.tmp
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
diff --git a/doc/manual/utils.nix b/doc/manual/utils.nix
index 5eacce0dd..f78e6bb02 100644
--- a/doc/manual/utils.nix
+++ b/doc/manual/utils.nix
@@ -74,10 +74,10 @@ rec {
if aliases == [] then "" else
"**Deprecated alias:** ${(concatStringsSep ", " (map (s: "`${s}`") aliases))}";
- indent = prefix: s:
- concatStringsSep "\n" (map (x: if x == "" then x else "${prefix}${x}") (splitLines s));
-
in result;
+ indent = prefix: s:
+ concatStringsSep "\n" (map (x: if x == "" then x else "${prefix}${x}") (splitLines s));
+
showSettings = args: settingsInfo: concatStrings (attrValues (mapAttrs (showSetting args) settingsInfo));
}
diff --git a/src/libutil/config.hh b/src/libutil/config.hh
index a001056f7..8b0fe6555 100644
--- a/src/libutil/config.hh
+++ b/src/libutil/config.hh
@@ -371,8 +371,21 @@ extern GlobalConfig globalConfig;
struct ExperimentalFeatureSettings : Config {
- Setting> experimentalFeatures{this, {}, "experimental-features",
- getExperimentalFeaturesList()};
+ Setting> experimentalFeatures{
+ this, {}, "experimental-features",
+ R"(
+ Experimental features that are enabled.
+
+ Example:
+
+ ```
+ experimental-features = nix-command flakes
+ ```
+
+ Experimental features available:
+
+ {{#include experimental-features.md}}
+ )"};
/**
* Check whether the given experimental feature is enabled.
diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc
index a64e9715a..010ab1d68 100644
--- a/src/libutil/experimental-features.cc
+++ b/src/libutil/experimental-features.cc
@@ -140,32 +140,12 @@ std::string_view showExperimentalFeature(const ExperimentalFeature tag)
return xpFeatureDetails[(size_t)tag].name;
}
-std::string getExperimentalFeaturesList() {
- std::string experimentalFeaturesList = R"(
- Experimental features that can be enabled.
-
- Example:
-
- ```
- experimental-features = nix-command flakes
-
- Experimental features available:
-
-)";
-
- for (auto & xpFeature : xpFeatureDetails) {
- experimentalFeaturesList += std::string {}
- /* length of this first string must be 12, matching the indent of
- the descriptions in the xpFeatureDetails literal. FIXME compute
- markdown in a less hacky way. */
- + " - "
- + "`" + xpFeature.name + "`"
- + "\n"
- + xpFeature.description
- + "\n\n";
- }
-
- return experimentalFeaturesList;
+nlohmann::json documentExperimentalFeatures() {
+ StringMap res;
+ for (auto & xpFeature : xpFeatureDetails)
+ res[std::string { xpFeature.name }] =
+ trim(stripIndentation(xpFeature.description));
+ return (nlohmann::json) res;
}
std::set parseFeatures(const std::set & rawFeatures)
diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh
index 3c479dbd9..8ef66263a 100644
--- a/src/libutil/experimental-features.hh
+++ b/src/libutil/experimental-features.hh
@@ -51,11 +51,9 @@ std::string_view showExperimentalFeature(const ExperimentalFeature);
/**
* Compute the documentation of all experimental features.
*
- * This a markdown bulleted list where each item is first (a) the
- * experimental feature flag name in backticks, and then (b) the
- * description of the experimental feature.
+ * See `doc/manual` for how this information is used.
*/
-std::string getExperimentalFeaturesList();
+nlohmann::json documentExperimentalFeatures();
/**
* Shorthand for `str << showExperimentalFeature(feature)`.
diff --git a/src/nix/main.cc b/src/nix/main.cc
index 4d4164333..62b1f98d1 100644
--- a/src/nix/main.cc
+++ b/src/nix/main.cc
@@ -374,6 +374,11 @@ void mainWrapped(int argc, char * * argv)
return;
}
+ if (argc == 2 && std::string(argv[1]) == "__dump-xp-features") {
+ logger->cout(documentExperimentalFeatures().dump());
+ return;
+ }
+
Finally printCompletions([&]()
{
if (completions) {