Skip to content

Commit

Permalink
Merge pull request #70 from kamilkrzyskow/gothic-modding-community
Browse files Browse the repository at this point in the history
Order by title, global fallback for attributes
  • Loading branch information
lukasgeiter authored Apr 11, 2023
2 parents 8360982 + 659ef23 commit affe19a
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 17 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ This can be combined with `order` above.
sort_type: natural
```

### Order Navigation By Preference

Create a file named `.pages` in a directory and set the `order_by` attribute to `filename` or `title` to change the order of navigation items.

```yaml
order_by: title
```

This can be combined with `order` and/or `sort_type` above. If `order` is not set it will order ascending. If no preference is set, it will order by filename.

### Collapse Single Nested Pages

> **Note:** This feature is disabled by default. More on how to use it below
Expand Down Expand Up @@ -380,6 +390,9 @@ plugins:
filename: .index
collapse_single_pages: true
strict: false
order: asc
sort_type: natural
order_by: title
```

### `filename`
Expand All @@ -399,6 +412,10 @@ Raise errors instead of warnings when:

Default is `true`

### `order`, `sort_type` and `order_by`

Global fallback values for the Meta attributes. Default is `None` or `filename`.

<br/>

## Contributing
Expand Down
21 changes: 21 additions & 0 deletions mkdocs_awesome_pages_plugin/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,13 @@ class Meta:
HIDE_ATTRIBUTE = "hide"
ORDER_ATTRIBUTE = "order"
SORT_TYPE_ATTRIBUTE = "sort_type"
ORDER_BY_ATTRIBUTE = "order_by"

ORDER_ASC = "asc"
ORDER_DESC = "desc"
SORT_NATURAL = "natural"
ORDER_BY_FILENAME = "filename"
ORDER_BY_TITLE = "title"

def __init__(
self,
Expand All @@ -126,6 +129,7 @@ def __init__(
hide: bool = None,
order: Optional[str] = None,
sort_type: Optional[str] = None,
order_by: Optional[str] = None,
):
if nav is None and arrange is not None:
nav = [MetaNavItem.from_yaml(value, path) for value in arrange]
Expand All @@ -140,6 +144,7 @@ def __init__(
self.hide = hide
self.order = order
self.sort_type = sort_type
self.order_by = order_by

@staticmethod
def try_load_from(path: Optional[str]) -> "Meta":
Expand All @@ -162,6 +167,7 @@ def load_from(path: str) -> "Meta":
hide = contents.get(Meta.HIDE_ATTRIBUTE)
order = contents.get(Meta.ORDER_ATTRIBUTE)
sort_type = contents.get(Meta.SORT_TYPE_ATTRIBUTE)
order_by = contents.get(Meta.ORDER_BY_ATTRIBUTE)

if title is not None:
if not isinstance(title, str):
Expand Down Expand Up @@ -242,6 +248,20 @@ def load_from(path: str) -> "Meta":
)
)

if order_by is not None:
if order_by != Meta.ORDER_BY_TITLE and order_by != Meta.ORDER_BY_FILENAME:
raise TypeError(
'Expected "{attribute}" attribute to be one of {those} - got "{order_by}" [{context}]'.format(
attribute=Meta.ORDER_BY_ATTRIBUTE,
those=[
Meta.ORDER_BY_FILENAME,
Meta.ORDER_BY_TITLE,
],
order_by=order_by,
context=path,
)
)

return Meta(
title=title,
arrange=arrange,
Expand All @@ -252,4 +272,5 @@ def load_from(path: str) -> "Meta":
hide=hide,
order=order,
sort_type=sort_type,
order_by=order_by,
)
84 changes: 68 additions & 16 deletions mkdocs_awesome_pages_plugin/navigation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import warnings
from pathlib import Path

import mkdocs.utils
import mkdocs.utils.meta
from natsort import natsort_keygen
from typing import List, Optional, Union, Set
from typing import List, Optional, Union, Set, Dict

from mkdocs.structure.nav import (
Navigation as MkDocsNavigation,
Expand Down Expand Up @@ -79,12 +82,24 @@ def _process_children(self, children: List[NavigationItem], collapse: bool, meta
return result

def _order(self, items: List[NavigationItem], meta: Meta):
order, sort_type = meta.order, meta.sort_type
if order is None and sort_type is None:
if len(items) < 2:
return

order = meta.order or self.options.order
sort_type = meta.sort_type or self.options.sort_type
order_by = meta.order_by or self.options.order_by

if order is None and sort_type is None and order_by is None:
return
key = lambda i: basename(self._get_item_path(i))

if order_by == Meta.ORDER_BY_TITLE:
key = lambda i: get_title(i)
else:
key = lambda i: basename(self._get_item_path(i))

if sort_type == Meta.SORT_NATURAL:
key = natsort_keygen(key)

items.sort(key=key, reverse=order == Meta.ORDER_DESC)

def _nav(self, items: List[NavigationItem], meta: Meta) -> List[NavigationItem]:
Expand Down Expand Up @@ -206,32 +221,34 @@ def __init__(
explicit_sections: Set[Section],
):
self.options = options
self.sections = {}
self.sections: Dict[Section, Meta] = {}
self.docs_dir = docs_dir
self.explicit_sections = explicit_sections

root_path = self._gather_metadata(items)
self.root = Meta.try_load_from(join_paths(root_path, self.options.filename))
self.root: Meta = self._gather_metadata(items)

def _gather_metadata(self, items: List[NavigationItem]) -> Optional[str]:
paths = []
def _gather_metadata(self, items: List[NavigationItem]) -> Meta:
paths: List[str] = []
for item in items:
if isinstance(item, Page):
if Path(self.docs_dir) in Path(item.file.abs_src_path).parents:
paths.append(item.file.abs_src_path)
elif isinstance(item, Section):
section_dir = self._gather_metadata(item.children)
section_meta = self._gather_metadata(item.children)

if item in self.explicit_sections:
self.sections[item] = Meta()
else:
if section_dir is not None:
paths.append(section_dir)
self.sections[item] = Meta.try_load_from(join_paths(section_dir, self.options.filename))
continue

if section_meta.path is not None:
paths.append(dirname(section_meta.path))

return self._common_dirname(paths)
self.sections[item] = section_meta

return Meta.try_load_from(join_paths(self._common_dirname(paths), self.options.filename))

@staticmethod
def _common_dirname(paths: List[Optional[str]]) -> Optional[str]:
def _common_dirname(paths: List[str]) -> Optional[str]:
if paths:
dirnames = [dirname(path) for path in paths]
if len(set(dirnames)) == 1:
Expand All @@ -248,3 +265,38 @@ def get_by_type(nav, T):
if item.children:
ret.extend(get_by_type(item.children, T))
return ret


# Copy of mkdocs.structure.pages.Page._set_title and Page.read_source
def get_title(item: NavigationItem) -> str:
if item.title is not None:
return item.title

if not isinstance(item, Page):
return str(item.title)

try:
with open(item.file.abs_src_path, encoding="utf-8-sig", errors="strict") as f:
source = f.read()
except OSError:
raise OSError(f"File not found: {item.file.src_path}")
except ValueError:
raise ValueError(f"Encoding error reading file: {item.file.src_path}")

page_markdown, page_meta = mkdocs.utils.meta.get_data(source)

if "title" in page_meta:
return page_meta["title"]

title = mkdocs.utils.get_markdown_title(page_markdown)

if title is None:
if item.is_homepage:
title = "Home"
else:
title = item.file.name.replace("-", " ").replace("_", " ")
# Capitalize if the filename was all lowercase, otherwise leave it as-is.
if title.lower() == title:
title = title.capitalize()

return title
14 changes: 13 additions & 1 deletion mkdocs_awesome_pages_plugin/options.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
class Options:
def __init__(self, *, filename: str, collapse_single_pages: bool, strict: bool):
def __init__(
self,
*,
filename: str,
collapse_single_pages: bool,
strict: bool,
order: str = None,
sort_type: str = None,
order_by: str = None,
):
self.filename = filename
self.collapse_single_pages = collapse_single_pages
self.strict = strict
self.order = order
self.sort_type = sort_type
self.order_by = order_by
3 changes: 3 additions & 0 deletions mkdocs_awesome_pages_plugin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class AwesomePagesPlugin(BasePlugin):
("filename", config_options.Type(str, default=DEFAULT_META_FILENAME)),
("collapse_single_pages", config_options.Type(bool, default=False)),
("strict", config_options.Type(bool, default=True)),
("order", config_options.Choice(["asc", "desc"], default=None)),
("sort_type", config_options.Choice(["natural"], default=None)),
("order_by", config_options.Choice(["filename", "title"], default=None)),
)

def __init__(self):
Expand Down
8 changes: 8 additions & 0 deletions mkdocs_awesome_pages_plugin/tests/e2e/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def pagesFile(
hide: bool = None,
order: Optional[str] = None,
sort_type: Optional[str] = None,
order_by: Optional[str] = None,
) -> Tuple[str, str]:
data = self._removeDictNoneValues(
{
Expand All @@ -42,6 +43,7 @@ def pagesFile(
"hide": hide,
"order": order,
"sort_type": sort_type,
"order_by": order_by,
}
)

Expand All @@ -53,12 +55,18 @@ def createConfig(
collapse_single_pages: Optional[bool] = None,
mkdocs_nav: Optional[List[Union[str, Dict[str, Union[str, list]]]]] = None,
strict: Optional[bool] = None,
order: Optional[str] = None,
sort_type: Optional[str] = None,
order_by: Optional[str] = None,
) -> dict:
plugin_options = self._removeDictNoneValues(
{
"filename": filename,
"collapse_single_pages": collapse_single_pages,
"strict": strict,
"order": order,
"sort_type": sort_type,
"order_by": order_by,
}
)
plugins_entry = "awesome-pages"
Expand Down
7 changes: 7 additions & 0 deletions mkdocs_awesome_pages_plugin/tests/e2e/test_mkdocs_nav.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import pytest
from mkdocs import __version__ as mkdocs_version

from .base import E2ETestCase
from ...meta import DuplicateRestItemError
from ...navigation import NavEntryNotFound
Expand Down Expand Up @@ -176,6 +179,10 @@ def test_sections_nested_rest(self):
],
)

@pytest.mark.skipif(
mkdocs_version >= "1.3.0",
reason="Since version 1.3 MkDocs validates nav and Dict type is invalid.",
)
def test_sections_nested_rest_dict(self):
navigation = self.mkdocs(
self.createConfig(
Expand Down
Loading

0 comments on commit affe19a

Please sign in to comment.