Ensure nav is sorted regardless of the order inside Files

This means plugins don't have to carefully find the sort order to put files in.
This commit is contained in:
Oleh Prypin
2023-10-22 11:09:55 +02:00
parent bf3e76c4c8
commit ee2a7ab45c
3 changed files with 31 additions and 6 deletions

View File

@@ -8,7 +8,7 @@ import posixpath
import shutil
import warnings
from functools import cached_property
from pathlib import PurePath
from pathlib import PurePath, PurePosixPath
from typing import TYPE_CHECKING, Callable, Iterable, Iterator, Mapping, Sequence, overload
from urllib.parse import quote as urlquote
@@ -582,12 +582,25 @@ def get_files(config: MkDocsConfig) -> Files:
return Files(files)
def file_sort_key(f: File, /):
"""
Replicates the sort order how `get_files` produces it - index first, directories last.
To sort a list of `File`, pass as the `key` argument to `sort`.
"""
parts = PurePosixPath(f.src_uri).parts
if not parts:
return ()
return (parts[:-1], f.name != "index", parts[-1])
def _file_sort_key(f: str):
"""Always sort `index` or `README` as first filename in list."""
"""Always sort `index` or `README` as first filename in list. This works only on basenames of files."""
return (os.path.splitext(f)[0] not in ('index', 'README'), f)
def _sort_files(filenames: Iterable[str]) -> list[str]:
"""Soft-deprecated, do not use."""
return sorted(filenames, key=_file_sort_key)

View File

@@ -6,6 +6,7 @@ from urllib.parse import urlsplit
from mkdocs.exceptions import BuildError
from mkdocs.structure import StructureItem
from mkdocs.structure.files import file_sort_key
from mkdocs.structure.pages import Page, _AbsoluteLinksValidationValue
from mkdocs.utils import nest_paths
@@ -129,9 +130,10 @@ class Link(StructureItem):
def get_navigation(files: Files, config: MkDocsConfig) -> Navigation:
"""Build site navigation from config and files."""
documentation_pages = files.documentation_pages()
nav_config = config['nav'] or nest_paths(
f.src_uri for f in documentation_pages if f.inclusion.is_in_nav()
)
nav_config = config['nav']
if nav_config is None:
documentation_pages = sorted(documentation_pages, key=file_sort_key)
nav_config = nest_paths(f.src_uri for f in documentation_pages if f.inclusion.is_in_nav())
items = _data_to_navigation(nav_config, files, config)
if not isinstance(items, list):
items = [items]

View File

@@ -3,7 +3,7 @@ import sys
import unittest
from unittest import mock
from mkdocs.structure.files import File, Files, _sort_files, get_files
from mkdocs.structure.files import File, Files, _sort_files, file_sort_key, get_files
from mkdocs.tests.base import PathAssertionMixin, load_config, tempdir
@@ -57,6 +57,16 @@ class TestFiles(PathAssertionMixin, unittest.TestCase):
['README.md', 'A.md', 'B.md'],
)
def test_file_sort_key(self):
for case in [
["a/b.md", "b/index.md", "b/a.md"],
["SUMMARY.md", "foo/z.md", "foo/bar/README.md", "foo/bar/index.md", "foo/bar/a.md"],
]:
with self.subTest(case):
files = [File(f, "", "", use_directory_urls=True) for f in case]
for a, b in zip(files, files[1:]):
self.assertLess(file_sort_key(a), file_sort_key(b))
def test_md_file(self):
for use_directory_urls in True, False:
with self.subTest(use_directory_urls=use_directory_urls):