Repository: hynek/doc2dash

Issue:
Add support for Docset Playgrounds
https://kapeli.com/docsets#docsetPlaygrounds

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aa5dda5..12fe351 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Support for 2x icons using the `--icon-2x` option.
   [#200](https://github.com/hynek/doc2dash/pull/200)
 
+- Support for linking to [docset playgrounds](https://kapeli.com/docsets#docsetPlaygrounds) using the `--playground-url` option.
+  [#201](https://github.com/hynek/doc2dash/pull/201)
+
 
 ### Fixed
 
diff --git a/docs/usage.md b/docs/usage.md
index 5bae7d0..c50e7fc 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -18,3 +18,5 @@ Basic usage is as simple as:
     :prog_name: doc2dash
     :style: table
     :depth: 1
+
+Refer to our [how-to](how-to.md) and the official [*Docset Generation Guide*](https://kapeli.com/docsets) to learn what those options are good for.
diff --git a/src/doc2dash/__main__.py b/src/doc2dash/__main__.py
index 39d0eb6..e457c6a 100644
--- a/src/doc2dash/__main__.py
+++ b/src/doc2dash/__main__.py
@@ -136,6 +136,7 @@ IMPORTABLE = ImportableType()
     "-u",
     help="The base URL of the online documentation.",
 )
+@click.option("--playground-url", help="The URL to a docset playground.")
 @click.option(
     "--parser",
     "parser_type",
@@ -160,6 +161,7 @@ def main(
     enable_js: bool,
     online_redirect_url: str | None,
     parser_type: type[Parser] | None,
+    playground_url: str | None,
 ) -> None:
     """
     Convert docs from SOURCE to Dash's docset format.
@@ -222,6 +224,7 @@ def main(
         index_page,
         enable_js,
         online_redirect_url,
+        playground_url,
         icon,
         icon_2x,
     )
diff --git a/src/doc2dash/docsets.py b/src/doc2dash/docsets.py
index 1a4146e..5763dbc 100644
--- a/src/doc2dash/docsets.py
+++ b/src/doc2dash/docsets.py
@@ -37,6 +37,7 @@ def prepare_docset(
     index_page: Path | None,
     enable_js: bool,
     online_redirect_url: str | None,
+    playground_url: str | None,
     icon: Path | None,
     icon_2x: Path | None,
 ) -> DocSet:
@@ -71,6 +72,8 @@ def prepare_docset(
         plist_cfg["dashIndexFilePath"] = str(index_page)
     if online_redirect_url is not None:
         plist_cfg["DashDocSetFallbackURL"] = online_redirect_url
+    if playground_url is not None:
+        plist_cfg["DashDocSetPlayURL"] = playground_url
 
     write_plist(plist_cfg, plist_path)
 


Test patch:
diff --git a/tests/test_docsets.py b/tests/test_docsets.py
index edbcae1..572419e 100644
--- a/tests/test_docsets.py
+++ b/tests/test_docsets.py
@@ -28,6 +28,7 @@ class TestPrepareDocset:
             index_page=None,
             enable_js=False,
             online_redirect_url=None,
+            playground_url=None,
             icon=None,
             icon_2x=None,
         )
@@ -72,6 +73,7 @@ class TestPrepareDocset:
             index_page="foo.html",
             enable_js=False,
             online_redirect_url=None,
+            playground_url=None,
             icon=None,
             icon_2x=None,
         )
@@ -105,6 +107,7 @@ class TestPrepareDocset:
             index_page="foo.html",
             enable_js=True,
             online_redirect_url=None,
+            playground_url=None,
             icon=None,
             icon_2x=None,
         )
@@ -138,6 +141,7 @@ class TestPrepareDocset:
             index_page="foo.html",
             enable_js=False,
             online_redirect_url="https://domain.com",
+            playground_url=None,
             icon=None,
             icon_2x=None,
         )
@@ -156,6 +160,41 @@ class TestPrepareDocset:
             "DashDocSetFallbackURL": "https://domain.com",
         }
 
+    def test_with_playground_url(self, monkeypatch, tmp_path):
+        """
+        If a playground URL is passed, it is added to the plist.
+        """
+        monkeypatch.chdir(tmp_path)
+        m_ct = Mock()
+        monkeypatch.setattr(shutil, "copytree", m_ct)
+        (tmp_path / "bar").mkdir()
+
+        docset = docsets.prepare_docset(
+            Path("some/path/foo"),
+            Path("bar"),
+            name="foo",
+            index_page="foo.html",
+            enable_js=False,
+            online_redirect_url=None,
+            playground_url="https://repl.it/F9J7/1",
+            icon=None,
+            icon_2x=None,
+        )
+
+        p = docsets.read_plist(docset.plist)
+
+        assert p == {
+            "CFBundleIdentifier": "foo",
+            "CFBundleName": "foo",
+            "DocSetPlatformFamily": "foo",
+            "DashDocSetFamily": "python",
+            "DashDocSetDeclaredInStyle": "originalName",
+            "isDashDocset": True,
+            "dashIndexFilePath": "foo.html",
+            "isJavaScriptEnabled": False,
+            "DashDocSetPlayURL": "https://repl.it/F9J7/1",
+        }
+
     def test_with_icon(self, tmp_path, sphinx_built):
         """
         If an icon is passed, it's copied to the root of the docset.
@@ -170,6 +209,7 @@ class TestPrepareDocset:
             index_page=None,
             enable_js=False,
             online_redirect_url=None,
+            playground_url=None,
             icon=icon,
             icon_2x=None,
         )
@@ -190,6 +230,7 @@ class TestPrepareDocset:
             index_page=None,
             enable_js=False,
             online_redirect_url=None,
+            playground_url=None,
             icon=None,
             icon_2x=icon,
         )
diff --git a/tests/test_main.py b/tests/test_main.py
index 6897951..783f0e7 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -198,6 +198,7 @@ def test_normal_flow(monkeypatch, tmp_path, runner):
         index_page,
         enable_js,
         online_redirect_url,
+        playground_url,
         icon,
         icon_2x,
     ):