Skip to content

Commit

Permalink
Ensure nav is sorted regardless of the order inside Files
Browse files Browse the repository at this point in the history
This means plugins don't have to carefully find the sort order to put files in.
  • Loading branch information
oprypin committed Dec 15, 2023
1 parent bf3e76c commit ee2a7ab
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 6 deletions.
17 changes: 15 additions & 2 deletions mkdocs/structure/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
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

Expand Down Expand Up @@ -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)


Expand Down
8 changes: 5 additions & 3 deletions mkdocs/structure/nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

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

Expand Down Expand Up @@ -129,9 +130,10 @@ def __repr__(self):
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]
Expand Down
12 changes: 11 additions & 1 deletion mkdocs/tests/structure/file_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
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


Expand Down Expand Up @@ -57,6 +57,16 @@ def test_sort_files(self):
['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):
Expand Down

0 comments on commit ee2a7ab

Please sign in to comment.