pax_global_header00006660000000000000000000000064145033010550014506gustar00rootroot0000000000000052 comment=2f02c385376de433ac7470e5f6a92f2f36afce1d mkdocs-section-index-0.3.8/000077500000000000000000000000001450330105500155455ustar00rootroot00000000000000mkdocs-section-index-0.3.8/.github/000077500000000000000000000000001450330105500171055ustar00rootroot00000000000000mkdocs-section-index-0.3.8/.github/FUNDING.yml000066400000000000000000000000201450330105500207120ustar00rootroot00000000000000github: oprypin mkdocs-section-index-0.3.8/.github/workflows/000077500000000000000000000000001450330105500211425ustar00rootroot00000000000000mkdocs-section-index-0.3.8/.github/workflows/ci.yml000066400000000000000000000035701450330105500222650ustar00rootroot00000000000000name: CI on: push: pull_request: schedule: - cron: '0 6 * * *' defaults: run: shell: bash jobs: test: strategy: fail-fast: false matrix: include: - python: '^3.10' os: macos-latest - python: '3.10' os: windows-latest - python: 3.9 os: ubuntu-latest - python: 3.8 os: macos-latest - python: 3.7 os: windows-latest - python: 3.7 os: ubuntu-latest versions: minimal runs-on: ${{matrix.os}} steps: - name: Download source uses: actions/checkout@v3 - name: Install Python uses: actions/setup-python@v4 with: python-version: ${{matrix.python}} - name: Pin to lowest versions if: matrix.versions == 'minimal' run: | sed -i -E 's/#min //; s/\b >=([0-9])/ ==\1/' pyproject.toml - name: Install Hatch run: | pip install hatch - name: Install dependencies run: | hatch run test:pip freeze - name: Run tests run: | hatch run test:test style: runs-on: ubuntu-latest steps: - name: Download source uses: actions/checkout@v3 - name: Install Python uses: actions/setup-python@v4 with: python-version: '3.10' - name: Install Hatch run: | pip install hatch - name: Install dependencies run: | hatch run style:pip freeze hatch run types:pip freeze - name: Check style if: always() run: | hatch run style:fix - name: Check formatting if: always() run: | hatch run style:format git diff --color --exit-code - name: Check types if: always() run: | hatch run types:check mkdocs-section-index-0.3.8/.github/workflows/deploy-docs.yml000066400000000000000000000013161450330105500241100ustar00rootroot00000000000000name: Deploy docs on: push: pull_request: schedule: - cron: '0 6 * * 6' jobs: build: name: Deploy docs runs-on: ubuntu-latest steps: - name: Download source uses: actions/checkout@v3 - name: Install Python uses: actions/setup-python@v4 with: python-version: '3.x' - name: Install dependencies run: pip install --no-deps -r docs/requirements.txt - name: Build site run: mkdocs build - name: Deploy to gh-pages if: github.event_name == 'push' && github.ref == 'refs/heads/master' uses: oprypin/push-to-gh-pages@v3 with: publish_dir: site commit_message: 'Generate docs: ' mkdocs-section-index-0.3.8/.github/workflows/deploy-release.yml000066400000000000000000000007651450330105500246070ustar00rootroot00000000000000name: Deploy release on: push: tags: - '*' jobs: pypi: permissions: id-token: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install dependencies run: pip install -U build - name: Build package run: python -m build - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 mkdocs-section-index-0.3.8/.gitignore000066400000000000000000000001371450330105500175360ustar00rootroot00000000000000/dist/ site*/ .mypy_cache/ .pytest_cache/ __pycache__/ *.egg-info/ .venv/ poetry.lock .vscode/ mkdocs-section-index-0.3.8/.tools/000077500000000000000000000000001450330105500167635ustar00rootroot00000000000000mkdocs-section-index-0.3.8/.tools/docs-dependencies.sh000077500000000000000000000001671450330105500227020ustar00rootroot00000000000000#!/bin/sh set -e hatch env show --json | jq -r ".docs.dependencies | .[]" | pip-compile -U - -o docs/requirements.txt mkdocs-section-index-0.3.8/.tools/release.sh000077500000000000000000000003741450330105500207460ustar00rootroot00000000000000#!/bin/bash set -e -u -x cd "$(dirname "$0")/.." git diff --staged --quiet git diff --quiet HEAD pyproject.toml rm -rf dist hatch version "$1" hatch build git add */__init__.py git commit -m "v$1" git tag -a -m "" "v$1" git push origin master --tags mkdocs-section-index-0.3.8/LICENSE.md000066400000000000000000000020731450330105500171530ustar00rootroot00000000000000MIT License Copyright (c) 2020 Oleh Prypin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mkdocs-section-index-0.3.8/README.md000066400000000000000000000144071450330105500170320ustar00rootroot00000000000000# mkdocs-section-index **[Plugin][] for [MkDocs][] to allow clickable sections that lead to an index page** [![PyPI](https://img.shields.io/pypi/v/mkdocs-section-index)](https://pypi.org/project/mkdocs-section-index/) [![GitHub](https://img.shields.io/github/license/oprypin/mkdocs-section-index)](https://github.com/oprypin/mkdocs-section-index/blob/master/LICENSE.md) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/oprypin/mkdocs-section-index/ci.yml.svg)](https://github.com/oprypin/mkdocs-section-index/actions?query=event%3Apush+branch%3Amaster) ```shell pip install mkdocs-section-index ``` [mkdocs]: https://www.mkdocs.org/ [plugin]: https://www.mkdocs.org/user-guide/plugins/ ## [Example](example/) ![Screencast with comparison](https://user-images.githubusercontent.com/371383/99844559-8c4caa00-2b73-11eb-9e97-fad82447746c.gif) With this `nav` in *mkdocs.yml* (or without `nav` but with [an equivalent directory structure](example/docs/)): ```yaml nav: - Frob: index.md - Baz: baz.md - Borgs: - borgs/index.md - Bar: borgs/bar.md - Foo: borgs/foo.md plugins: - search - section-index ``` The *borgs/index.md* page is merged as the index of the "Borgs" section. Normally sections in [MkDocs][] cannot be clickable as pages themselves, but this plugin makes that possible. **See also: [a realistic demo site](https://oprypin.github.io/crystal-book/syntax_and_semantics/literals/).** ## Theme support This plugin requires per-theme overrides (implemented within the plugin), or [support from themes themselves](#implementation-within-themes). Currently supported [themes][] are: * [material](https://github.com/squidfunk/mkdocs-material) * [readthedocs](https://www.mkdocs.org/user-guide/styling-your-docs/#readthedocs) * [nature](https://github.com/waylan/mkdocs-nature) [themes]: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Themes ## Usage notes The kind of *nav* as shown above also happens to be what MkDocs produces when `nav` is omitted; it detects [`index.md` and `README.md`][nav-gen] pages and automatically puts them as the first item. To make writing this kind of `nav` more natural ([in YAML there's no better option](https://github.com/mkdocs/mkdocs/pull/1042#issuecomment-290787554)), consider using the **[literate-nav][] plugin** along with this; then the above *nav* might be written like this: ```markdown * [Frob](index.md) * [Baz](baz.md) * [Borgs](borgs/index.md) * [Bar](borgs/bar.md) * [Foo](borgs/foo.md) ``` [literate-nav]: https://oprypin.github.io/mkdocs-literate-nav/ ## [Implementation](https://github.com/oprypin/mkdocs-section-index/blob/master/mkdocs_section_index/plugin.py) ### "Protocol" Normally in MkDocs [`nav`][nav], the items can be one of: * a [`Section`][Section], which has a `title` and `children`. * (`url` is always `None`) * a [`Page`][Page], which has a `title` and `url`. * (`title` can be omitted, and later deduced from the page content) * ([`children`][children] is always `None`) * a [`Link`][Link] (inconsequential for our purposes). This plugin introduces a [hybrid kind of `Page`](https://github.com/oprypin/mkdocs-section-index/blob/master/mkdocs_section_index/__init__.py), which has all of these properties: * `title`: `str` * `url`: `str` * `children`: `list` * `is_page` = `True` * `is_section` = `True` Such a special item gets put into a nav in the place of a `Section` which has a `Page` with an intentionally omitted title as its first child. Those two are naturally combined into a special [section-page](https://github.com/oprypin/mkdocs-section-index/blob/master/mkdocs_section_index/__init__.py) that's a hybrid of the two. [nav]: https://www.mkdocs.org/user-guide/custom-themes/#nav [Section]: https://www.mkdocs.org/user-guide/custom-themes/#section [Page]: https://www.mkdocs.org/user-guide/custom-themes/#page [children]: https://github.com/mkdocs/mkdocs/blob/2f833a1a29095733e53a04d062d315629d974ebe/mkdocs/structure/pages.py#L26 [Link]: https://www.mkdocs.org/user-guide/custom-themes/#link ### Implementation within themes Then all that a theme's template needs to do is to meaningfully support such nav items -- ones that have both a `url` and `children`. The item should be directly clickable to go to the corresponding page, and also be able to house sub-items. Of course, currently templates don't expect such a case; or if they did, it would be purely by chance. So currently this plugin "hacks into" templates of supported themes, [patching their source on the fly](https://github.com/oprypin/mkdocs-section-index/blob/master/mkdocs_section_index/rewrites.py) to fit its needs. The hope is that, once this plugin gains enough traction, theme authors will be happy to directly support this scenario (which is totally non-intrusive and backwards-compatible), and then the patches could be dropped. ### "Alternatives considered" Even if all the template patches are gone, this plugin will still remain as the implementation of this special nav "protocol", and as the **opt-in mechanism**. In the author's view, such an approach is advantageous, because: * This is too controversial to be enabled by default, or even be part of MkDocs at all. This has been [discussed in the past and dropped](https://github.com/mkdocs/mkdocs/pull/1042#issuecomment-260813540). The main reason is that in MkDocs there's no requirement for a *nav*'s structure to follow the actual directory structure of the doc files. Consequently, there's no natural way to deduce that a document should become the index page of a section just from its location, even if it's named *index.md*. Although if the *nav* is [omitted & generated][nav-gen], then yes, such an assumption works. It also works in the vast majority of actual usages *with* a *nav*, but that doesn't help. * Themes themselves also probably shouldn't directly try to detect logic such as "first child of a section if it has no title" and manually collapse the child *within Jinja template code*, as that's too messy. This also shouldn't be enabled by default. And even though templates could also make this opt-in, a centralized approach like this one ensures that accessing this feature is done uniformly. Not to mention that templates might never implement this themselves. [nav-gen]: https://www.mkdocs.org/user-guide/writing-your-docs/#configure-pages-and-navigation mkdocs-section-index-0.3.8/docs/000077500000000000000000000000001450330105500164755ustar00rootroot00000000000000mkdocs-section-index-0.3.8/docs/README.md000066400000000000000000000000231450330105500177470ustar00rootroot00000000000000--8<-- "README.md" mkdocs-section-index-0.3.8/docs/requirements.txt000066400000000000000000000020301450330105500217540ustar00rootroot00000000000000# # This file is autogenerated by pip-compile with python 3.10 # To update, run: # # pip-compile --output-file=docs/requirements.txt - # click==8.1.3 # via mkdocs ghp-import==2.1.0 # via mkdocs importlib-metadata==4.11.4 # via mkdocs jinja2==3.1.2 # via # mkdocs # mkdocs-material markdown==3.3.7 # via # mkdocs # mkdocs-material # pymdown-extensions markupsafe==2.1.1 # via jinja2 mergedeep==1.3.4 # via mkdocs mkdocs==1.3.0 # via # -r - # mkdocs-material mkdocs-material==8.3.6 # via -r - mkdocs-material-extensions==1.0.3 # via mkdocs-material packaging==21.3 # via mkdocs pygments==2.12.0 # via mkdocs-material pymdown-extensions==9.5 # via mkdocs-material pyparsing==3.0.9 # via packaging python-dateutil==2.8.2 # via ghp-import pyyaml==6.0 # via # mkdocs # pyyaml-env-tag pyyaml-env-tag==0.1 # via mkdocs six==1.16.0 # via python-dateutil watchdog==2.1.9 # via mkdocs zipp==3.8.0 # via importlib-metadata mkdocs-section-index-0.3.8/example/000077500000000000000000000000001450330105500172005ustar00rootroot00000000000000mkdocs-section-index-0.3.8/example/docs/000077500000000000000000000000001450330105500201305ustar00rootroot00000000000000mkdocs-section-index-0.3.8/example/docs/baz.md000066400000000000000000000000041450330105500212200ustar00rootroot00000000000000baz mkdocs-section-index-0.3.8/example/docs/borgs/000077500000000000000000000000001450330105500212445ustar00rootroot00000000000000mkdocs-section-index-0.3.8/example/docs/borgs/bar.md000066400000000000000000000000471450330105500223330ustar00rootroot00000000000000# Bar bar ## Heading ### Subheading mkdocs-section-index-0.3.8/example/docs/borgs/foo.md000066400000000000000000000000041450330105500223430ustar00rootroot00000000000000foo mkdocs-section-index-0.3.8/example/docs/borgs/index.md000066400000000000000000000002241450330105500226730ustar00rootroot00000000000000# Borgs Frob has two kinds of borgs: [foo](foo.md) and [bar](bar.md). The latter is reserved for more advanced usages. ## Heading ### Subheading mkdocs-section-index-0.3.8/example/docs/index.md000066400000000000000000000001231450330105500215550ustar00rootroot00000000000000# Frob Frob is a great project! Learn about [Baz](baz.md) or the [borgs](borgs). mkdocs-section-index-0.3.8/example/mkdocs.yml000066400000000000000000000003711450330105500212040ustar00rootroot00000000000000site_name: mkdocs-section-index site_url: 'https://example.org' nav: - Frob: index.md - Baz: baz.md - Borgs: - borgs/index.md - Bar: borgs/bar.md - Foo: borgs/foo.md theme: name: material plugins: - search - section-index mkdocs-section-index-0.3.8/example/requirements.txt000066400000000000000000000000541450330105500224630ustar00rootroot00000000000000mkdocs mkdocs-material mkdocs-section-index mkdocs-section-index-0.3.8/mkdocs.yml000066400000000000000000000013421450330105500175500ustar00rootroot00000000000000site_name: "mkdocs-section-index" site_url: "https://oprypin.github.io/mkdocs-section-index" repo_url: "https://github.com/oprypin/mkdocs-section-index" edit_uri: blob/master/docs/ use_directory_urls: false theme: name: material features: - navigation.tabs - toc.integrate icon: repo: fontawesome/brands/github palette: primary: teal accent: purple markdown_extensions: - pymdownx.highlight - pymdownx.magiclink - pymdownx.superfences - pymdownx.details - pymdownx.snippets: check_paths: true - admonition - def_list - toc: permalink: "#" nav: - section-index: README.md - literate-nav: /mkdocs-literate-nav/ - gen-files: /mkdocs-gen-files/ - same-dir: /mkdocs-same-dir/ mkdocs-section-index-0.3.8/mkdocs_section_index/000077500000000000000000000000001450330105500217405ustar00rootroot00000000000000mkdocs-section-index-0.3.8/mkdocs_section_index/__init__.py000066400000000000000000000015461450330105500240570ustar00rootroot00000000000000from __future__ import annotations from mkdocs.structure.nav import Section from mkdocs.structure.pages import Page __version__ = "0.3.8" __all__ = ["SectionPage"] class SectionPage(Section, Page): # type: ignore[misc] def __init__(self, title: str, file, config, children): Page.__init__(self, title=title, file=file, config=config) Section.__init__(self, title=title, children=children) self.is_section = self.is_page = True active = Page.active # type: ignore def __repr__(self): result = Page.__repr__(self) if not result.startswith("Section"): result = "Section" + result return result def __eq__(self, other): return object.__eq__(self, other) def __ne__(self, other): return not (self == other) def __hash__(self): return object.__hash__(self) mkdocs-section-index-0.3.8/mkdocs_section_index/plugin.py000066400000000000000000000053101450330105500236070ustar00rootroot00000000000000from __future__ import annotations import collections import logging from typing import TYPE_CHECKING from mkdocs.plugins import BasePlugin from mkdocs.structure.nav import Navigation, Section from mkdocs.structure.pages import Page from . import SectionPage, rewrites if TYPE_CHECKING: from jinja2 import Environment __all__ = ["SectionIndexPlugin"] log = logging.getLogger(f"mkdocs.plugins.{__name__}") class SectionIndexPlugin(BasePlugin): def on_nav(self, nav: Navigation, config, files) -> Navigation: todo = collections.deque((nav.items,)) while todo: items = todo.popleft() for i, section in enumerate(items): if not isinstance(section, Section) or not section.children: continue todo.append(section.children) page = section.children[0] if not isinstance(page, Page): continue assert not page.children if not page.title and page.url: # The page becomes a section-page. page.__class__ = SectionPage assert isinstance(page, SectionPage) page.is_section = page.is_page = True page.title = section.title page.parent = section.parent # The page leaves the section but takes over children that used to be its peers. section.children.pop(0) 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 self._nav = nav return nav def on_env(self, env: Environment, config, files) -> Environment: assert env.loader is not None env.loader = self._loader = rewrites.TemplateRewritingLoader(env.loader) return env def on_page_context(self, context, page, config, nav): if nav != self._nav: self._nav = nav log.warning( "It seems that the effects of section-index plugin have been lost, because another MkDocs plugin re-wrote the nav! " "Re-order `plugins` in mkdocs.yml so that 'section-index' appears closer to the end." ) def on_post_build(self, config): if not self._loader.found_supported_theme: log.warning( "section-index plugin couldn't detect a supported theme to adapt. " "It probably won't work as expected. " "See https://github.com/oprypin/mkdocs-section-index#theme-support" ) mkdocs-section-index-0.3.8/mkdocs_section_index/py.typed000066400000000000000000000000001450330105500234250ustar00rootroot00000000000000mkdocs-section-index-0.3.8/mkdocs_section_index/rewrites.py000066400000000000000000000122561450330105500241640ustar00rootroot00000000000000from __future__ import annotations import logging import pathlib import textwrap from typing import Callable from jinja2 import BaseLoader, Environment __all__ = ["TemplateRewritingLoader"] log = logging.getLogger(f"mkdocs.plugins.{__name__}") class TemplateRewritingLoader(BaseLoader): def __init__(self, loader: BaseLoader): self.loader = loader self.found_supported_theme = False def get_source( self, environment: Environment, template: str ) -> tuple[str, str, Callable[[], bool] | None]: src: str | None src, filename, uptodate = self.loader.get_source(environment, template) old_src = src assert filename is not None path = pathlib.Path(filename).as_posix() if path.endswith("/mkdocs/templates/sitemap.xml"): src = _transform_mkdocs_sitemap_template(src) else: # the second path is used in MkDocs-Material >= 9.4 if path.endswith( ( "/material/partials/nav-item.html", "/material/templates/partials/nav-item.html", ), ): src = _transform_material_nav_item_template(src) elif path.endswith( ( "/material/partials/tabs-item.html", "/material/templates/partials/tabs-item.html", ), ): src = _transform_material_tabs_item_template(src) elif path.endswith("/themes/readthedocs/base.html"): src = _transform_readthedocs_base_template(src) elif path.endswith("/nature/base.html"): src = None # Just works! else: return src, filename, uptodate self.found_supported_theme = True if old_src == src: log.warning( f"Failed to adapt the theme file '{filename}'. " f"This is likely a bug in mkdocs-section-index, and things won't work as expected." ) return src or old_src, filename, uptodate def _transform_mkdocs_sitemap_template(src: str) -> str | None: if " in pages " not in src: # The below only for versions <= 1.1.2. return src.replace( "{%- else %}", "{%- endif %}{% if item.url %}", ) return None def _transform_material_nav_item_template(src: str) -> str: if "navigation.indexes" in src: return src.replace( "{% set indexes = [] %}", "{% set indexes = [nav_item] if nav_item.url else [] %}", ).replace( "{% if nav_item.children | length > 1 %}", "{% if nav_item.children %}", ) # The above only for versions >= 7.3, the below only for versions < 7.3. src = src.replace( "{% if nav_item.children %}", "{% if nav_item.children and not ('navigation.tabs' in features and level == 1 and not nav_item.active and nav_item.url) %}", ) repl = """\ {% if nav_item.url %} {% endif %} [...] {% if nav_item.url %}{% endif %} """ lines = src.split("\n") for i, (line1, line2) in enumerate(zip(lines, lines[1:])): for a, b in (line1, line2), (line2, line1): if "md-nav__icon" in a and b.endswith("{{ nav_item.title }}"): lines[i : i + 2] = (a, _replace_line(b, repl)) break return "\n".join(lines) def _transform_material_tabs_item_template(src: str) -> str: src = src.replace("{% if first.children %}", "{% if first.children and not first.url %}") # The above only for versions >= 9.2 src = src.replace( "{% if nav_item.children %}", "{% if nav_item.children and not nav_item.url %}" ) # The above only for versions > 6.1.7, the below only for versions <= 6.1.7. return src.replace( "(nav_item.children | first).url", "(nav_item.url or (nav_item.children | first).url)", ).replace( "if (nav_item.children | first).children", "if (nav_item.children | first).children and not nav_item.url", ) def _transform_readthedocs_base_template(src: str) -> str: repl = """\ {% if nav_item.url %} {% endif %} """ lines = src.split("\n") for i, line in enumerate(lines): if "{{ nav_item.title }}" in line: lines[i] = _replace_line(lines[i], repl) return "\n".join(lines) def _replace_line(line: str, wrapper: str, new_line: str | None = None) -> str: leading_space = line[: -len(line.lstrip())] if new_line is None: new_line = line.lstrip() new_text = textwrap.dedent(wrapper.rstrip()).replace("[...]", new_line) return textwrap.indent(new_text, leading_space) mkdocs-section-index-0.3.8/pyproject.toml000066400000000000000000000072331450330105500204660ustar00rootroot00000000000000[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "mkdocs-section-index" description = "MkDocs plugin to allow clickable sections that lead to an index page" readme = "README.md" license = "MIT" keywords = ["mkdocs", "mkdocs-plugin"] authors = [ {name = "Oleh Prypin", email = "oleh@pryp.in"}, ] classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Environment :: Web Environment", "Intended Audience :: Information Technology", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Topic :: Documentation", "Topic :: Software Development :: Documentation", "Topic :: Text Processing :: Markup :: Markdown", "Typing :: Typed", ] dynamic = ["version"] requires-python = ">=3.7" dependencies = [ "mkdocs >=1.2", ] [project.urls] Documentation = "https://oprypin.github.io/mkdocs-section-index/" Source = "https://github.com/oprypin/mkdocs-section-index" Issues = "https://github.com/oprypin/mkdocs-section-index/issues" History = "https://github.com/oprypin/mkdocs-section-index/releases" [project.entry-points."mkdocs.plugins"] section-index = "mkdocs_section_index.plugin:SectionIndexPlugin" [tool.hatch.version] path = "mkdocs_section_index/__init__.py" [tool.hatch.build.targets.sdist] include = ["/mkdocs_section_index", "/tests"] [tool.hatch.envs.default.scripts] all = [ "hatch run style:fix", "hatch run types:check", "hatch run test:test", ] [tool.hatch.envs.test] dependencies = [ "pytest", "pytest-golden", "testfixtures >=6.15.0", "MechanicalSoup >=0.12.0", "mkdocs-material >=6.1.5", "mkdocs-nature >=0.5", #min "jinja2 >=2.11.0", #min "markupsafe >=2.0.1", ] [tool.hatch.envs.test.scripts] test = [ "pytest -q", "mkdocs build -f example/mkdocs.yml --strict", ] [tool.hatch.envs.types] dependencies = [ "mypy", ] [tool.hatch.envs.types.scripts] check = [ "mypy mkdocs_section_index" ] [tool.hatch.envs.style] skip-install = true dependencies = [ "ruff", "isort", "black", ] [tool.hatch.envs.style.scripts] fix = [ "ruff check --fix mkdocs_section_index tests", "format", ] format = [ "isort -q mkdocs_section_index tests", "black -q mkdocs_section_index tests", ] [tool.hatch.envs.docs] detached = true dependencies = [ "mkdocs >=1.2", "mkdocs-material >=7.3.6", ] [tool.black] line-length = 100 [tool.isort] profile = "black" line_length = 100 [tool.ruff] select = [ "F", "W", "E", "UP", "YTT", "C4", "FA", "PIE", "T20", "RSE", "TCH", "DTZ", "B002", "B003", "B005", "B007", "B009", "B012", "B013", "B014", "B015", "B018", "B020", "B021", "B023", "B026", "B033", "B034", "B905", "COM818", "PERF101", "PGH002", "PGH004", "PGH005", "PLE", "PLW0120", "PLW0127", "RUF001", "RUF007", "RUF010", "RUF100", "RUF200", "SIM101", "SIM107", "SIM201", "SIM202", "SIM208", "SIM210", "SIM211", "SIM300", "SIM401", "SIM910", ] ignore = ["E501", "E731"] [tool.ruff.flake8-comprehensions] allow-dict-calls-with-keyword-arguments = true [tool.mypy] ignore_missing_imports = true warn_unreachable = true no_implicit_optional = true show_error_codes = true [tool.pytest.ini_options] addopts = "--tb=native" enable_assertion_pass_hook = true filterwarnings = ["ignore::DeprecationWarning:.*:", "default::DeprecationWarning:mkdocs_section_index.*:"] testpaths = ["tests"] mkdocs-section-index-0.3.8/setup.py000066400000000000000000000004171450330105500172610ustar00rootroot00000000000000"""Installation using setup.py is not supported. Use `pip install .` instead.""" import sys from setuptools import setup sys.exit(__doc__) # Fake reference so GitHub still considers it a real package for statistics purposes. setup( name="mkdocs-section-index", ) mkdocs-section-index-0.3.8/tests/000077500000000000000000000000001450330105500167075ustar00rootroot00000000000000mkdocs-section-index-0.3.8/tests/conftest.py000066400000000000000000000015111450330105500211040ustar00rootroot00000000000000import functools import http.server import logging import threading import pytest import testfixtures @pytest.fixture(autouse=True) def cap_log(): with testfixtures.LogCapture( "mkdocs.plugins.mkdocs_section_index", attributes=("levelname", "getMessage"), ensure_checks_above=logging.WARNING, ) as capture: yield capture @pytest.fixture(scope="session") def http_server(tmp_path_factory): directory = tmp_path_factory.mktemp("http_server") httpd = http.server.HTTPServer( ("localhost", 0), functools.partial(http.server.SimpleHTTPRequestHandler, directory=str(directory)), ) t = threading.Thread(target=httpd.serve_forever) t.daemon = True t.start() httpd.directory = directory httpd.url = f"http://localhost:{httpd.server_port}/" return httpd mkdocs-section-index-0.3.8/tests/navs/000077500000000000000000000000001450330105500176565ustar00rootroot00000000000000mkdocs-section-index-0.3.8/tests/navs/basic.yml000066400000000000000000000020701450330105500214610ustar00rootroot00000000000000input: - Aaa: a.md - Bbb: b.md - Section 1: - Ccc: s1/c.md - Link: http://example.org/ - Section 2: - s2/index.md - Section 3: - s3/index.md - Subsection: - s3s/index.md - Ddd: s3s/d.md - Eee: s3/e.md false: |- Page(title='Aaa', url='a.html') Page(title='Bbb', url='b.html') Section(title='Section 1') Page(title='Ccc', url='s1/c.html') Link(title='Link', url='http://example.org/') SectionPage(title='Section 2', url='s2/index.html') SectionPage(title='Section 3', url='s3/index.html') SectionPage(title='Subsection', url='s3s/index.html') Page(title='Ddd', url='s3s/d.html') Page(title='Eee', url='s3/e.html') true: |- Page(title='Aaa', url='a/') Page(title='Bbb', url='b/') Section(title='Section 1') Page(title='Ccc', url='s1/c/') Link(title='Link', url='http://example.org/') SectionPage(title='Section 2', url='s2/') SectionPage(title='Section 3', url='s3/') SectionPage(title='Subsection', url='s3s/') Page(title='Ddd', url='s3s/d/') Page(title='Eee', url='s3/e/') mkdocs-section-index-0.3.8/tests/rewrites/000077500000000000000000000000001450330105500205535ustar00rootroot00000000000000mkdocs-section-index-0.3.8/tests/rewrites/material-nav-item-1.yml000066400000000000000000000135221450330105500247530ustar00rootroot00000000000000# https://github.com/squidfunk/mkdocs-material/blob/4ffa86660913ee955b33f543840766002bc4d43c/material/partials/nav-item.html input: | {#- This file was automatically generated - do not edit -#} {% set class = "md-nav__item" %} {% if nav_item.active %} {% set class = "md-nav__item md-nav__item--active" %} {% endif %} {% if nav_item.children %}
  • {% if nav_item.active %} {% else %} {% endif %}
  • {% elif nav_item == page %}
  • {% set toc = page.toc %} {% if toc | first is defined and "\x3ch1 id=" in page.content %} {% set toc = (toc | first).children %} {% endif %} {% if toc | first is defined %} {% endif %} {{ nav_item.title }} {% if toc | first is defined %} {% include "partials/toc.html" %} {% endif %}
  • {% else %}
  • {{ nav_item.title }}
  • {% endif %} output: | {#- This file was automatically generated - do not edit -#} {% set class = "md-nav__item" %} {% if nav_item.active %} {% set class = "md-nav__item md-nav__item--active" %} {% endif %} {% if nav_item.children and not ('navigation.tabs' in features and level == 1 and not nav_item.active and nav_item.url) %}
  • {% if nav_item.active %} {% else %} {% endif %}
  • {% elif nav_item == page %}
  • {% set toc = page.toc %} {% if toc | first is defined and "\x3ch1 id=" in page.content %} {% set toc = (toc | first).children %} {% endif %} {% if toc | first is defined %} {% endif %} {{ nav_item.title }} {% if toc | first is defined %} {% include "partials/toc.html" %} {% endif %}
  • {% else %}
  • {{ nav_item.title }}
  • {% endif %} mkdocs-section-index-0.3.8/tests/rewrites/material-nav-item-2.yml000066400000000000000000000124721450330105500247570ustar00rootroot00000000000000# https://github.com/squidfunk/mkdocs-material/blob/86a4c47c0aee5bb42b3d7435c05d24936c32e397/material/partials/nav-item.html input: | {#- This file was automatically generated - do not edit -#} {% set class = "md-nav__item" %} {% if nav_item.active %} {% set class = "md-nav__item md-nav__item--active" %} {% endif %} {% if nav_item.children %}
  • {% set checked = "checked" if nav_item.active %}
  • {% elif nav_item == page %}
  • {% set toc = page.toc %} {% if toc | first is defined and "\x3ch1 id=" in page.content %} {% set toc = (toc | first).children %} {% endif %} {% if toc | first is defined %} {% endif %} {{ nav_item.title }} {% if toc | first is defined %} {% include "partials/toc.html" %} {% endif %}
  • {% else %}
  • {{ nav_item.title }}
  • {% endif %} output: | {#- This file was automatically generated - do not edit -#} {% set class = "md-nav__item" %} {% if nav_item.active %} {% set class = "md-nav__item md-nav__item--active" %} {% endif %} {% if nav_item.children and not ('navigation.tabs' in features and level == 1 and not nav_item.active and nav_item.url) %}
  • {% set checked = "checked" if nav_item.active %}
  • {% elif nav_item == page %}
  • {% set toc = page.toc %} {% if toc | first is defined and "\x3ch1 id=" in page.content %} {% set toc = (toc | first).children %} {% endif %} {% if toc | first is defined %} {% endif %} {{ nav_item.title }} {% if toc | first is defined %} {% include "partials/toc.html" %} {% endif %}
  • {% else %}
  • {{ nav_item.title }}
  • {% endif %} mkdocs-section-index-0.3.8/tests/rewrites/material-nav-item-3.yml000066400000000000000000000143621450330105500247600ustar00rootroot00000000000000# https://github.com/squidfunk/mkdocs-material/blob/d754bd9a7172e8afbfdcb505180776caf7c3c839/material/partials/nav-item.html input: | {#- This file was automatically generated - do not edit -#} {% set features = config.theme.features or [] %} {% set class = "md-nav__item" %} {% if nav_item.active %} {% set class = class ~ " md-nav__item--active" %} {% endif %} {% if nav_item.children %} {% if "navigation.sections" in features and level == 1 + ( "navigation.tabs" in features ) %} {% set class = class ~ " md-nav__item--section" %} {% endif %}
  • {% set checked = "checked" if nav_item.active %} {% if "navigation.expand" in features and not checked %} {% else %} {% endif %}
  • {% elif nav_item == page %}
  • {% set toc = page.toc %} {% if toc | first is defined and "\x3ch1 id=" in page.content %} {% set toc = (toc | first).children %} {% endif %} {% if toc | first is defined %} {% endif %} {{ nav_item.title }} {% if toc | first is defined %} {% include "partials/toc.html" %} {% endif %}
  • {% else %}
  • {{ nav_item.title }}
  • {% endif %} output: | {#- This file was automatically generated - do not edit -#} {% set features = config.theme.features or [] %} {% set class = "md-nav__item" %} {% if nav_item.active %} {% set class = class ~ " md-nav__item--active" %} {% endif %} {% if nav_item.children and not ('navigation.tabs' in features and level == 1 and not nav_item.active and nav_item.url) %} {% if "navigation.sections" in features and level == 1 + ( "navigation.tabs" in features ) %} {% set class = class ~ " md-nav__item--section" %} {% endif %}
  • {% set checked = "checked" if nav_item.active %} {% if "navigation.expand" in features and not checked %} {% else %} {% endif %}
  • {% elif nav_item == page %}
  • {% set toc = page.toc %} {% if toc | first is defined and "\x3ch1 id=" in page.content %} {% set toc = (toc | first).children %} {% endif %} {% if toc | first is defined %} {% endif %} {{ nav_item.title }} {% if toc | first is defined %} {% include "partials/toc.html" %} {% endif %}
  • {% else %}
  • {{ nav_item.title }}
  • {% endif %} mkdocs-section-index-0.3.8/tests/rewrites/material-nav-item-4.yml000066400000000000000000000163311450330105500247570ustar00rootroot00000000000000# https://github.com/squidfunk/mkdocs-material/blob/149b0dbc475fd77a2cf00fdd6365078a900c023b/material/partials/nav-item.html input: | {#- This file was automatically generated - do not edit -#} {% macro render(nav_item, path, level) %} {% set class = "md-nav__item" %} {% if nav_item.active %} {% set class = class ~ " md-nav__item--active" %} {% endif %} {% if nav_item.children %} {% if "navigation.sections" in features and level == 1 + ( "navigation.tabs" in features ) %} {% set class = class ~ " md-nav__item--section" %} {% endif %}
  • {% set checked = "checked" if nav_item.active %} {% if "navigation.expand" in features and not checked %} {% else %} {% endif %} {% set indexes = [] %} {% if "navigation.indexes" in features %} {% for item in nav_item.children %} {% if item.is_index and not index is defined %} {% set _ = indexes.append(item) %} {% endif %} {% endfor %} {% endif %} {% if not indexes %} {% else %} {% set index = indexes | first %} {% set class = "md-nav__link--active" if index == page %} {% endif %}
  • {% elif nav_item == page %}
  • {% set toc = page.toc %} {% set first = toc | first %} {% if first and first.level == 1 %} {% set toc = first.children %} {% endif %} {% if toc %} {% endif %} {{ nav_item.title }} {% if toc %} {% include "partials/toc.html" %} {% endif %}
  • {% else %}
  • {{ nav_item.title }}
  • {% endif %} {% endmacro %} {{ render(nav_item, path, level) }} output: | {#- This file was automatically generated - do not edit -#} {% macro render(nav_item, path, level) %} {% set class = "md-nav__item" %} {% if nav_item.active %} {% set class = class ~ " md-nav__item--active" %} {% endif %} {% if nav_item.children %} {% if "navigation.sections" in features and level == 1 + ( "navigation.tabs" in features ) %} {% set class = class ~ " md-nav__item--section" %} {% endif %}
  • {% set checked = "checked" if nav_item.active %} {% if "navigation.expand" in features and not checked %} {% else %} {% endif %} {% set indexes = [nav_item] if nav_item.url else [] %} {% if "navigation.indexes" in features %} {% for item in nav_item.children %} {% if item.is_index and not index is defined %} {% set _ = indexes.append(item) %} {% endif %} {% endfor %} {% endif %} {% if not indexes %} {% else %} {% set index = indexes | first %} {% set class = "md-nav__link--active" if index == page %} {% endif %}
  • {% elif nav_item == page %}
  • {% set toc = page.toc %} {% set first = toc | first %} {% if first and first.level == 1 %} {% set toc = first.children %} {% endif %} {% if toc %} {% endif %} {{ nav_item.title }} {% if toc %} {% include "partials/toc.html" %} {% endif %}
  • {% else %}
  • {{ nav_item.title }}
  • {% endif %} {% endmacro %} {{ render(nav_item, path, level) }} mkdocs-section-index-0.3.8/tests/rewrites/material-tabs-item-1.yml000066400000000000000000000053541450330105500251240ustar00rootroot00000000000000# https://github.com/squidfunk/mkdocs-material/blob/acc4a273c4fcb5745e90ba239ef7c7a75b57bea2/material/partials/tabs-item.html input: | {#- This file was automatically generated - do not edit -#} {% if nav_item.is_homepage or nav_item.url == "index.html" %}
  • {% set class = "md-tabs__link" %} {% if not page.ancestors | length and nav | selectattr("url", page.url) %} {% set class = "md-tabs__link md-tabs__link--active" %} {% endif %} {{ nav_item.title }}
  • {% elif nav_item.children and nav_item.children | length > 0 %} {% set title = title | default(nav_item.title) %} {% if (nav_item.children | first).children %} {% set nav_item = nav_item.children | first %} {% include "partials/tabs-item.html" %} {% else %}
  • {% set class = "md-tabs__link" %} {% if nav_item.active %} {% set class = "md-tabs__link md-tabs__link--active" %} {% endif %} {{ title }}
  • {% endif %} {% elif nav_item.url.startswith("http") %}
  • {{ nav_item.title }}
  • {% endif %} output: | {#- This file was automatically generated - do not edit -#} {% if nav_item.is_homepage or nav_item.url == "index.html" %}
  • {% set class = "md-tabs__link" %} {% if not page.ancestors | length and nav | selectattr("url", page.url) %} {% set class = "md-tabs__link md-tabs__link--active" %} {% endif %} {{ nav_item.title }}
  • {% elif nav_item.children and nav_item.children | length > 0 %} {% set title = title | default(nav_item.title) %} {% if (nav_item.children | first).children and not nav_item.url %} {% set nav_item = nav_item.children | first %} {% include "partials/tabs-item.html" %} {% else %}
  • {% set class = "md-tabs__link" %} {% if nav_item.active %} {% set class = "md-tabs__link md-tabs__link--active" %} {% endif %} {{ title }}
  • {% endif %} {% elif nav_item.url.startswith("http") %}
  • {{ nav_item.title }}
  • {% endif %} mkdocs-section-index-0.3.8/tests/rewrites/material-tabs-item-2.yml000066400000000000000000000033551450330105500251240ustar00rootroot00000000000000# https://github.com/squidfunk/mkdocs-material/blob/d754bd9a7172e8afbfdcb505180776caf7c3c839/material/partials/tabs-item.html input: | {#- This file was automatically generated - do not edit -#} {% if not class %} {% set class = "md-tabs__link" %} {% if nav_item.active %} {% set class = class ~ " md-tabs__link--active" %} {% endif %} {% endif %} {% if nav_item.children %} {% set title = title | d(nav_item.title) %} {% set nav_item = nav_item.children | first %} {% if nav_item.children %} {% include "partials/tabs-item.html" %} {% else %}
  • {{ title }}
  • {% endif %} {% else %}
  • {{ nav_item.title }}
  • {% endif %} output: | {#- This file was automatically generated - do not edit -#} {% if not class %} {% set class = "md-tabs__link" %} {% if nav_item.active %} {% set class = class ~ " md-tabs__link--active" %} {% endif %} {% endif %} {% if nav_item.children and not nav_item.url %} {% set title = title | d(nav_item.title) %} {% set nav_item = nav_item.children | first %} {% if nav_item.children and not nav_item.url %} {% include "partials/tabs-item.html" %} {% else %}
  • {{ title }}
  • {% endif %} {% else %}
  • {{ nav_item.title }}
  • {% endif %} mkdocs-section-index-0.3.8/tests/rewrites/mkdocs-sitemap-1.yml000066400000000000000000000030711450330105500243550ustar00rootroot00000000000000# https://github.com/mkdocs/mkdocs/blob/cb85d48851133e6d482deb3405de67c3dbea82be/mkdocs/templates/sitemap.xml input: | {%- macro nav_item(item) -%} {%- if item.children -%} {%- for child in item.children -%} {{ nav_item(child) }} {%- endfor -%} {%- else %} {%- if not item.is_link -%} {% if item.canonical_url %}{{ item.canonical_url|e }}{% else %}{{ item.abs_url|e }}{% endif %} {% if item.update_date %}{{item.update_date}}{% endif %} daily {%- endif -%} {%- endif -%} {%- endmacro -%} {%- for item in nav -%} {{ nav_item(item) }} {%- endfor %} output: | {%- macro nav_item(item) -%} {%- if item.children -%} {%- for child in item.children -%} {{ nav_item(child) }} {%- endfor -%} {%- endif %}{% if item.url %} {%- if not item.is_link -%} {% if item.canonical_url %}{{ item.canonical_url|e }}{% else %}{{ item.abs_url|e }}{% endif %} {% if item.update_date %}{{item.update_date}}{% endif %} daily {%- endif -%} {%- endif -%} {%- endmacro -%} {%- for item in nav -%} {{ nav_item(item) }} {%- endfor %} mkdocs-section-index-0.3.8/tests/rewrites/readthedocs-base-1.yml000066400000000000000000000042701450330105500246340ustar00rootroot00000000000000# https://github.com/mkdocs/mkdocs/blob/273e3baa8e47fa5d121402d98757d3f5a5510383/mkdocs/themes/readthedocs/base.html#L88 input: | {%- set navlevel = 1 %} {%- for nav_item in nav %} {%- if nav_item.is_section %}

    {{ nav_item.title }}

    {%- for nav_item in nav_item.children %} {%- endfor %} {%- elif config.theme.include_homepage_in_sidebar or (not nav_item == nav.homepage) %} {%- endif %} {%- endfor %} output: | {%- set navlevel = 1 %} {%- for nav_item in nav %} {%- if nav_item.is_section %} {% if nav_item.url %} {% endif %} {%- for nav_item in nav_item.children %} {%- endfor %} {%- elif config.theme.include_homepage_in_sidebar or (not nav_item == nav.homepage) %} {%- endif %} {%- endfor %} mkdocs-section-index-0.3.8/tests/test_integration.py000066400000000000000000000124271450330105500226510ustar00rootroot00000000000000import os import re import textwrap from pathlib import Path import mechanicalsoup import pytest from mkdocs.commands.build import build from mkdocs.config.base import load_config class _Browser(mechanicalsoup.StatefulBrowser): def __init__(self, url: str): super().__init__(soup_config={"features": "html.parser"}, raise_on_404=True) self.open(url) def links(self, text=None, *args, **kwargs): all_links = super().links(*args, **kwargs) if text is not None: all_links = [a for a in all_links if a.text.strip() == text] return all_links def build_site(cfg: str, src_dir: os.PathLike, dest_dir: os.PathLike) -> None: cfg_prefix = f""" site_name: SiteName site_dir: {str(dest_dir)!r} plugins: - section-index """ cfg = textwrap.dedent(cfg_prefix) + textwrap.dedent(cfg) docs_dir = Path(src_dir, "docs") docs_dir.mkdir() Path(docs_dir, "README.md").write_text("") for rel_path in re.findall(r"\b[^ ]+\.md$", cfg, flags=re.MULTILINE): path = Path(docs_dir, rel_path) path.parent.mkdir(parents=True, exist_ok=True) path.write_text(f"@{rel_path}@") f = Path(src_dir, "mkdocs.yml") f.write_text(cfg) build(load_config(str(f))) @pytest.mark.parametrize("use_directory_urls", [True, False]) @pytest.mark.parametrize( "theme,features", [ ("readthedocs", []), ("material", []), ("material", ["navigation.tabs"]), ("material", ["navigation.sections"]), ], ) def test_nav_basic(http_server, tmpdir, use_directory_urls, theme, features): cfg = f""" use_directory_urls: {use_directory_urls!r} theme: name: {theme!r} features: {features} nav: - SectionWithIndex1: - Sub1/index.md - Aaa: Sub1/aaa.md - SectionWithIndex2: - Sub2/home.md - Bbb: Sub1/bbb.md - SectionWithoutIndex1: - Ccc: Sub2/ccc.md - Ddd: Sub2/ddd.md """ build_site(cfg=cfg, src_dir=tmpdir, dest_dir=http_server.directory) browser = _Browser(http_server.url) # import webbrowser; webbrowser.open(browser.get_url()); input("...") browser.follow_link(text="SectionWithIndex1") assert browser.get_current_page().find_all(text="@Sub1/index.md@") browser.follow_link(text="SectionWithIndex2") assert browser.get_current_page().find_all(text="@Sub2/home.md@") assert len(browser.links(text="SectionWithIndex2")) >= 1 + features.count("navigation.tabs") assert len(browser.links(text="SectionWithoutIndex1")) >= features.count("navigation.tabs") browser.follow_link(text="Bbb") assert browser.get_current_page().find_all(text="@Sub1/bbb.md@") @pytest.mark.parametrize("use_directory_urls", [True, False]) @pytest.mark.parametrize( "theme,features", [ ("material", []), ("material", ["navigation.tabs"]), ("material", ["navigation.sections"]), ], ) def test_nav_nested_tabs(http_server, tmpdir, use_directory_urls, theme, features): cfg = f""" use_directory_urls: {use_directory_urls!r} theme: name: {theme!r} features: {features} nav: - TabWithoutIndex1: - SectionWithIndex1: - Sub1/index.md - Aaa: Sub1/aaa.md - SectionWithIndex2: - Sub2/home.md - Bbb: Sub1/bbb.md - TabWithoutIndex2: - SectionWithoutIndex1: - Ccc: Sub2/ccc.md - Ddd: Sub2/ddd.md - SectionWithoutIndex2: - Eee: Sub2/eee.md """ build_site(cfg=cfg, src_dir=tmpdir, dest_dir=http_server.directory) browser = _Browser(http_server.url) # import webbrowser; webbrowser.open(browser.get_url()); input("...") browser.follow_link(text="SectionWithIndex1") assert browser.get_current_page().find_all(text="@Sub1/index.md@") browser.follow_link(text="SectionWithIndex2") assert browser.get_current_page().find_all(text="@Sub2/home.md@") if "navigation.tabs" in features: browser.follow_link(text="TabWithoutIndex1") assert browser.get_current_page().find_all(text="@Sub1/index.md@") assert not browser.links(text="SectionWithoutIndex1") @pytest.mark.parametrize("use_directory_urls", [True, False]) @pytest.mark.parametrize( "theme,features", [ ("material", []), ("material", ["navigation.tabs"]), ("material", ["navigation.sections"]), ], ) def test_nav_nested_tabs_2(http_server, tmpdir, use_directory_urls, theme, features): cfg = f""" use_directory_urls: {use_directory_urls!r} theme: name: {theme!r} features: {features} nav: - TabWithIndex1: - Sub1/index.md - SectionWithoutIndex1: - Aaa: Sub1/aaa.md """ build_site(cfg=cfg, src_dir=tmpdir, dest_dir=http_server.directory) browser = _Browser(http_server.url) # import webbrowser; webbrowser.open(browser.get_url()); input("...") browser.follow_link(text="TabWithIndex1") assert browser.get_current_page().find_all(text="@Sub1/index.md@") mkdocs-section-index-0.3.8/tests/test_plugin.py000066400000000000000000000076001450330105500216210ustar00rootroot00000000000000import contextlib import dataclasses import pathlib import pytest from jinja2 import Environment, FileSystemLoader from mkdocs.config import Config, load_config from mkdocs.structure.files import File, get_files from mkdocs.structure.nav import Navigation, get_navigation from testfixtures import StringComparison from mkdocs_section_index import SectionPage, plugin example_dir = pathlib.Path(__file__).parent / ".." / "example" @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): 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, ) files = get_files(config) nav = get_navigation(files, config) nav = plugin.SectionIndexPlugin().on_nav(nav, config, files) assert len(nav.pages) == 5 assert len(nav.items) == 3 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 len(sec.children) == 2 assert sec.children[0].is_page assert sec.children[0].file.name == "bar" assert nav.items[1].next_page == sec assert sec.children[1].parent == sec @dataclasses.dataclass class FakeFiles: config: Config def get_file_from_path(self, path): if ":" not in path: return File( path, src_dir=self.config.get("docs_dir", ""), dest_dir=self.config.get("site_dir", ""), use_directory_urls=self.config.get("use_directory_urls", True), ) @classmethod def documentation_pages(cls): return [] @pytest.mark.golden_test("navs/*.yml") def test_nav_repr(golden, tmpdir): for use_directory_urls in True, False: 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) assert str(nav) == golden.out[use_directory_urls] @contextlib.contextmanager def template_test(directory): config = {} files = FakeFiles(config) env = Environment(loader=FileSystemLoader(directory)) env.filters["url"] = lambda s: s plg = plugin.SectionIndexPlugin() plg.on_nav(Navigation([], []), config, files) env = plg.on_env(env, config, files) yield env plg.on_post_build(config) def test_build_material(tmpdir): directory = tmpdir.mkdir("material") directory.mkdir("partials").join("nav-item.html").write_text( "md-nav__icon\n{{ nav_item.title }}", encoding="utf-8" ) with template_test(directory) as env: env.get_template("partials/nav-item.html") def test_build_wrong_content(cap_log, tmpdir): directory = tmpdir.mkdir("material") directory.mkdir("partials").join("nav-item.html").write_text( "href=\n{{ nav_item.title }}", encoding="utf-8" ) with template_test(directory) as env: env.get_template("partials/nav-item.html") cap_log.check(("WARNING", StringComparison("Failed to adapt.+nav-item.+"))) def test_build_wrong_file(cap_log, tmpdir): directory = tmpdir.mkdir("foo") directory.mkdir("partials").join("nav-item.html").write_text( "{{ nav_item.title }}", encoding="utf-8" ) with template_test(directory) as env: env.get_template("partials/nav-item.html") cap_log.check(("WARNING", StringComparison(".+couldn't detect a supported theme.+"))) mkdocs-section-index-0.3.8/tests/test_rewrites.py000066400000000000000000000015501450330105500221650ustar00rootroot00000000000000import pytest from mkdocs_section_index import rewrites @pytest.mark.golden_test("rewrites/mkdocs-sitemap-*.yml") def test_rewrite_mkdocs_sitemap(golden): assert rewrites._transform_mkdocs_sitemap_template(golden["input"]) == golden.out["output"] @pytest.mark.golden_test("rewrites/readthedocs-base-*.yml") def test_rewrite_readthedocs_base(golden): assert rewrites._transform_readthedocs_base_template(golden["input"]) == golden.out["output"] @pytest.mark.golden_test("rewrites/material-nav-item-*.yml") def test_rewrite_material_nav_item(golden): assert rewrites._transform_material_nav_item_template(golden["input"]) == golden.out["output"] @pytest.mark.golden_test("rewrites/material-tabs-item-*.yml") def test_rewrite_material_tabs_item(golden): assert rewrites._transform_material_tabs_item_template(golden["input"]) == golden.out["output"]