diff --git a/README.md b/README.md index 414faea..34f23ad 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,21 @@ To make writing this kind of `nav` more natural ([in YAML there's no better opti [literate-nav]: https://oprypin.github.io/mkdocs-literate-nav/ +### Specifying the page to use for the section + +By default the first child is used as the section page, even if there is no +`index.md`. If you want to change the page to use add the `index_file` config +option: + +```yaml +plugins: + - section-index: + index_file: index.md +``` + +The value is the name of the page to use, in this case `index.md`. If a child +with that name does not exist no section page is generated. + ## [Implementation](https://github.com/oprypin/mkdocs-section-index/blob/master/mkdocs_section_index/plugin.py) ### "Protocol" diff --git a/mkdocs_section_index/plugin.py b/mkdocs_section_index/plugin.py index 956aab0..aab7477 100644 --- a/mkdocs_section_index/plugin.py +++ b/mkdocs_section_index/plugin.py @@ -1,5 +1,6 @@ import collections import logging +import os import mkdocs.utils from jinja2 import Environment @@ -16,6 +17,8 @@ class SectionIndexPlugin(BasePlugin): + config_scheme = (("index_file", mkdocs.config.config_options.Type(str, default=None)),) + def on_nav(self, nav: Navigation, config, files) -> Navigation: todo = collections.deque((nav.items,)) while todo: @@ -24,7 +27,17 @@ def on_nav(self, nav: Navigation, config, files) -> Navigation: if not isinstance(section, Section) or not section.children: continue todo.append(section.children) - page = section.children[0] + index_file = self.config["index_file"] + if index_file is None: + page_index = 0 + page = section.children[0] + else: + for page_index, child in enumerate(section.children): + if os.path.basename(child.file.src_path) == index_file: + page = child + break + else: + continue if not isinstance(page, Page): continue assert not page.children @@ -34,12 +47,14 @@ def on_nav(self, nav: Navigation, config, files) -> Navigation: page.is_section = page.is_page = True page.title = section.title # The page leaves the section but takes over children that used to be its peers. - section.children.pop(0) + section.children.pop(page_index) page.children = section.children for child in page.children: child.parent = page # The page replaces the section; the section will be garbage-collected. items[i] = page + if i > 0: + items[i - 1].next_page = items[i] self._nav = nav return nav diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 6d3284a..9e83a14 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -15,39 +15,64 @@ @pytest.mark.parametrize("directory_urls", ["use_directory_urls", "no_directory_urls"]) -@pytest.mark.parametrize("nav", ["explicit_nav", "derived_nav"]) -def test_real_example(tmpdir, directory_urls, nav): +@pytest.mark.parametrize("nav_src", ["explicit_nav", "derived_nav"]) +@pytest.mark.parametrize("index_file", ["default_index_file", "index.md", "foo.md"]) +def test_real_example(tmpdir, directory_urls, nav_src, index_file): config = dict( docs_dir=str(example_dir / "docs"), site_dir=tmpdir, use_directory_urls=(directory_urls == "use_directory_urls"), - nav=load_config(str(example_dir / "mkdocs.yml"))["nav"] if nav == "explicit_nav" else None, + nav=load_config(str(example_dir / "mkdocs.yml"))["nav"] + if nav_src == "explicit_nav" + else None, ) + if nav_src == "derived_nav" and index_file != "default_index_file": + # only test index_file for derived nav + # if index_file is None we test the default value (which is first child) + config["index_file"] = index_file + else: + index_file = None files = get_files(config) nav = get_navigation(files, config) - nav = plugin.SectionIndexPlugin().on_nav(nav, config, files) + instance = plugin.SectionIndexPlugin() + instance.load_config(config) + if index_file is None: + assert instance.config["index_file"] is None + nav = instance.on_nav(nav, instance.config, files) - assert len(nav.pages) == 5 - assert len(nav.items) == 3 + assert len(nav.pages) == (6 if nav_src == "derived_nav" else 5) + assert len(nav.items) == (4 if nav_src == "derived_nav" else 3) + + # items = index.md, baz.md, borgs/, z_noindex/ assert nav.items[1].is_page assert nav.items[1].file.name == "baz" assert not nav.items[1].is_section - sec = nav.items[2] - assert isinstance(sec, SectionPage) - assert sec.is_section - assert sec.is_page - assert sec.title == "Borgs" - assert sec.url in ("borgs/", "borgs/index.html") - assert sec.file.name == "index" + assert nav.items[0].file.name == "index" + assert not nav.items[0].is_section + + borgs_sec = nav.items[2] + assert isinstance(borgs_sec, SectionPage) + assert borgs_sec.is_section + assert borgs_sec.is_page + assert borgs_sec.title == "Borgs" + if index_file == "foo.md": + assert borgs_sec.url in ("borgs/foo/", "borgs/foo.html") + assert borgs_sec.file.name == "foo" + else: + assert borgs_sec.url in ("borgs/", "borgs/index.html") + assert borgs_sec.file.name == "index" - assert len(sec.children) == 2 - assert sec.children[0].is_page - assert sec.children[0].file.name == "bar" + assert len(borgs_sec.children) == 2 + assert borgs_sec.children[0].is_page + if index_file == "foo.md": + assert borgs_sec.children[0].file.name == "index" + else: + assert borgs_sec.children[0].file.name == "bar" - assert nav.items[1].next_page == sec - assert sec.children[1].parent == sec + assert nav.items[1].next_page == borgs_sec + assert borgs_sec.children[1].parent == borgs_sec @dataclasses.dataclass @@ -74,7 +99,9 @@ def test_nav_repr(golden, tmpdir): config = dict(nav=golden["input"], use_directory_urls=use_directory_urls) files = FakeFiles(config) nav = get_navigation(files, config) - nav = plugin.SectionIndexPlugin().on_nav(nav, config, files) + instance = plugin.SectionIndexPlugin() + instance.load_config(config) + nav = instance.on_nav(nav, instance.config, files) assert str(nav) == golden.out[use_directory_urls]