Repository: pantsbuild/pants

Issue:
Add `toml` syntax in backend option documentation
**Is your feature request related to a problem? Please describe.**

Right now all (most?) documentation for backends uses command-line syntax to show to configure options:
```
--python-resolves-to-interpreter-constraints="{'key1': val1, 'key2': val2, ...}"
```

However, most configuration is actually provided in `pants.toml`. Overriding on the command line often causes `pantsd` to restart as well, making it an inefficient pattern for general use.

**Describe the solution you'd like**

It might make more sense to focus on `toml` syntax for configuration examples in the references like we do in the manuals.

**Additional context**

From a thread on Slack:

> I don't understand how to specify this option. It seems like it's supposed to be provided as an object, but AFAIK that's not possible in TOML directly? Or is this something that must be provided at runtime?




Discussion:
Since this is generated I'd guess it should be fairly easy to change all, but I can't find the script:
```shell
✦ ❯ rg 'generate-docs'
docs/NOTES.md
3:Pants currently hosts documentation at Readme.com, and we use a combination of their `rdme` tool to sync handwritten markdown docs, and a custom `generate-docs` script to update Pants' reference documentation.
✦ ❯ fd generate-docs
✦ ❯ 
```

Rotten docs?
Yeah the docs are overly abbreviated: it's a `.py` file and has an underscore...: https://github.com/pantsbuild/pants/blob/7a292e4bc3b6e5406b7a74398a9963b34d38e909/build-support/bin/generate_docs.py
There are two unrelated "docs generation" processes. One for the guides, whose sources are markdown pages, another for the reference docs, whose sources are the code. 

In this case you want to look at src/python/pants/help for cmd-line help, and build-support/bin/generate_docs.py for the pantsbuild.org reference docs pages.

Resolving patch:
diff --git a/build-support/bin/generate_docs.py b/build-support/bin/generate_docs.py
--- a/build-support/bin/generate_docs.py
+++ b/build-support/bin/generate_docs.py
@@ -320,6 +320,59 @@ def _link(scope: str, *, sync: bool) -> str:
         url_safe_scope = scope.replace(".", "-")
         return f"reference-{url_safe_scope}" if sync else f"{url_safe_scope}.md"
 
+    @staticmethod
+    def _generate_toml_snippet(option_data, scope) -> str:
+        """Generate TOML snippet for a single option."""
+
+        # Generate a toml block for the option to help users fill out their `pants.toml`.  For
+        # scalars and arrays, we put them inline directly in the scope, while for maps we put
+        # them in a nested table. Since the metadata doesn't contain type info, we'll need to
+        # parse command line args a bit.
+
+        if not option_data.get("config_key", None):
+            # This option is not configurable.
+            return ""
+
+        toml_lines = []
+        example_cli = option_data["display_args"][0]
+        config_key = option_data["config_key"]
+
+        if "[no-]" in example_cli:
+            val = "<bool>"
+        else:
+            _, val = example_cli.split("=", 1)
+
+        is_map = val.startswith('"{') and val.endswith('}"')
+        is_array = val.startswith('"[') and val.endswith(']"')
+        if is_map:
+            toml_lines.append(f"[{scope}.{config_key}]")
+            val = val[2:-2]  # strip the quotes and brackets
+
+            pairs = val.split(", ")
+            for pair in pairs:
+                if ":" in pair:
+                    k, v = pair.split(": ", 1)
+                    if k.startswith('"') or k.startswith("'"):
+                        k = k[1:-1]
+
+                    toml_lines.append(f"{k} = {v}")
+                else:
+                    # generally just the trailing ...
+                    toml_lines.append(pair)
+
+        elif is_array:
+            toml_lines.append(f"[{scope}]")
+            toml_lines.append(f"{config_key} = [")
+            val = val[2:-2]
+            for item in val.split(", "):
+                toml_lines.append(f"    {item},")
+            toml_lines.append("]")
+        else:
+            toml_lines.append(f"[{scope}]")
+            toml_lines.append(f"{config_key} = {val}")
+
+        return "\n".join(toml_lines)
+
     @classmethod
     def process_options_input(cls, help_info: dict[str, Any], *, sync: bool) -> dict:
         scope_to_help_info = help_info["scope_to_help_info"]
@@ -342,7 +395,7 @@ def process_options_input(cls, help_info: dict[str, Any], *, sync: bool) -> dict
 
         # Process the option data.
 
-        def munge_option(option_data):
+        def munge_option(option_data, scope):
             # Munge the default so we can display it nicely when it's multiline, while
             # still displaying it inline if it's not.
             default_help_repr = option_data.get("default_help_repr")
@@ -369,13 +422,15 @@ def munge_option(option_data):
             else:
                 option_data["marked_up_default"] = f"<code>{escaped_default_str}</code>"
 
-        for shi in scope_to_help_info.values():
+            option_data["toml"] = cls._generate_toml_snippet(option_data, scope)
+
+        for scope, shi in scope_to_help_info.items():
             for opt in shi["basic"]:
-                munge_option(opt)
+                munge_option(opt, scope)
             for opt in shi["advanced"]:
-                munge_option(opt)
+                munge_option(opt, scope)
             for opt in shi["deprecated"]:
-                munge_option(opt)
+                munge_option(opt, scope)
 
         return help_info
 


Test patch: