mirror of
https://github.com/mkdocs/mkdocs.git
synced 2026-03-27 09:58:31 +07:00
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:
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user