Skip to content

Commit

Permalink
Fix 1.19 book links to other extension books (fix #75)
Browse files Browse the repository at this point in the history
  • Loading branch information
object-Object committed Jun 9, 2024
1 parent ae03eb4 commit 63dc692
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
### Fixed

* Add missing handler for nested list styles (`$(li2)`, `$(li3)`, etc).
* Fix book links to other extension books on 1.19 and below (also fixes [#75](https://github.com/hexdoc-dev/hexdoc/issues/75))

## `1!0.1.0a17`

Expand Down
6 changes: 3 additions & 3 deletions src/hexdoc/_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def default_rendered_templates_v2(
templates: DefaultRenderedTemplates = {}

for category in book.categories.values():
templates[redirect_path(category.raw_link)] = (
templates[redirect_path(category.redirect_path)] = (
"redirects/category.html.jinja",
{
"category": category,
Expand All @@ -106,7 +106,7 @@ def default_rendered_templates_v2(
)

for entry in category.entries.values():
templates[redirect_path(entry.raw_link)] = (
templates[redirect_path(entry.redirect_path)] = (
"redirects/entry.html.jinja",
{
"category": category,
Expand All @@ -116,7 +116,7 @@ def default_rendered_templates_v2(
)

for page in entry.pages:
page_path = page.raw_link_path(entry.raw_link)
page_path = page.redirect_path(entry.redirect_path)
if page_path is not None:
templates[redirect_path(page_path)] = (
"redirects/page.html.jinja",
Expand Down
10 changes: 5 additions & 5 deletions src/hexdoc/patchouli/book.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def _load_categories(self, context: ContextSource, book_ctx: BookContext):
self._categories[id] = category

link_base = book_ctx.get_link_base(category.resource_dir)
book_ctx.book_links[category.raw_link] = link_base.with_fragment(
book_ctx.book_links[category.book_link_key] = link_base.with_fragment(
category.fragment
)

Expand Down Expand Up @@ -122,15 +122,15 @@ def _load_entries(
internal_entries[entry.category_id][entry.id] = entry

link_base = book_ctx.get_link_base(resource_dir)
book_ctx.book_links[entry.raw_link] = link_base.with_fragment(
book_ctx.book_links[entry.book_link_key] = link_base.with_fragment(
entry.fragment
)

for page in entry.pages:
page_raw_link = page.raw_link(entry.raw_link)
page_key = page.book_link_key(entry.book_link_key)
page_fragment = page.fragment(entry.fragment)
if page_raw_link is not None and page_fragment is not None:
book_ctx.book_links[page_raw_link] = link_base.with_fragment(
if page_key is not None and page_fragment is not None:
book_ctx.book_links[page_key] = link_base.with_fragment(
page_fragment
)

Expand Down
11 changes: 9 additions & 2 deletions src/hexdoc/patchouli/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,18 @@ def load_all(
return sorted_dict(categories)

@property
def raw_link(self):
return self.id.path
def book_link_key(self):
"""Key to look up this category in `BookContext.book_links`."""
return str(self.id)

@property
def fragment(self):
"""URL fragment for this category in `BookContext.book_links`."""
return self.id.path

@property
def redirect_path(self):
"""Path to this category when generating redirect pages."""
return self.id.path

@property
Expand Down
11 changes: 9 additions & 2 deletions src/hexdoc/patchouli/entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,18 @@ def anchors(self) -> Iterable[str]:
yield page.anchor

@property
def raw_link(self):
return self.id.path
def book_link_key(self):
"""Key to look up this entry in `BookContext.book_links`."""
return str(self.id)

@property
def fragment(self):
"""URL fragment for this entry in `BookContext.book_links`."""
return self.id.path

@property
def redirect_path(self):
"""Path to this entry when generating redirect pages."""
return self.id.path

@property
Expand Down
18 changes: 12 additions & 6 deletions src/hexdoc/patchouli/page/abstract_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,24 @@ def _pre_root(cls, value: str | Any, handler: ModelWrapValidatorHandler[Self]):
def template(cls) -> str:
return cls.template_id.template_path("pages")

def raw_link(self, entry_raw_link: str):
def book_link_key(self, entry_key: str):
"""Key to look up this page in `BookContext.book_links`, or `None` if this page
has no anchor."""
if self.anchor is not None:
return f"{entry_raw_link}#{self.anchor}"

def raw_link_path(self, entry_raw_link: str):
if self.anchor is not None:
return f"{entry_raw_link}/{self.anchor}"
return f"{entry_key}#{self.anchor}"

def fragment(self, entry_fragment: str):
"""URL fragment for this page in `BookContext.book_links`, or `None` if this
page has no anchor."""
if self.anchor is not None:
return f"{entry_fragment}@{self.anchor}"

def redirect_path(self, entry_path: str):
"""Path to this page when generating redirect pages, or `None` if this page has
no anchor."""
if self.anchor is not None:
return f"{entry_path}/{self.anchor}"

def _get_advancement(self):
# implements AdvancementSpoilered
return self.advancement
Expand Down
28 changes: 17 additions & 11 deletions src/hexdoc/patchouli/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import logging
import re
from enum import Enum, auto
from fnmatch import fnmatch
Expand All @@ -20,6 +21,9 @@
from hexdoc.utils import PydanticURL, TryGetEnum, classproperty
from hexdoc.utils.json_schema import inherited, json_schema_extra_config, type_str

logger = logging.getLogger(__name__)


DEFAULT_MACROS = {
"$(obf)": "$(k)",
"$(bold)": "$(l)",
Expand Down Expand Up @@ -80,20 +84,14 @@ def from_str(cls, raw_value: str, book_id: ResourceLocation) -> Self:
# anchor
if "#" in raw_value:
id_str, anchor = raw_value.split("#", 1)

# id should be case-insensitive, so lowercase it and update raw_value
# TODO: this is pretty gross
id_str = id_str.lower()
raw_value = f"{id_str}#{anchor}"
else:
id_str, anchor = raw_value.lower(), None
raw_value = id_str
id_str, anchor = raw_value, None

# id of category or entry being linked to
# case-insensitive id of the category or entry being linked to
# namespace defaults to the namespace of the book, not minecraft
id_str = id_str.lower()
if ":" in id_str:
id = ResourceLocation.from_str(id_str)
# eg. link to patterns/spells/links instead of hexal:patterns/spells/links
raw_value = raw_value.removeprefix(f"{id.namespace}:")
else:
id = book_id.with_path(id_str)

Expand All @@ -103,6 +101,13 @@ def from_str(cls, raw_value: str, book_id: ResourceLocation) -> Self:
def as_tuple(self) -> tuple[ResourceLocation, str | None]:
return (self.id, self.anchor)

@property
def book_links_key(self) -> str:
key = str(self.id)
if self.anchor is not None:
key += f"#{self.anchor}"
return key

@property
def fragment(self) -> str:
return f"#{self.raw_value.replace('#', '@')}"
Expand Down Expand Up @@ -313,9 +318,10 @@ def href(self, context: Context | dict[{"book_links": BookLinks}]): # noqa
match self.value:
case str(href):
return href
case BookLink(raw_value=key) as book_link:
case BookLink(book_links_key=key) as book_link:
book_links: BookLinks = context["book_links"]
if key not in book_links:
logger.debug(f"{key=}\n{book_link=}\n{book_links=}")
raise ValueError(f"broken link: {book_link}")
return str(book_links[key])

Expand Down
6 changes: 3 additions & 3 deletions test/patchouli/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def test_case_insensitive_entry_id():
link_overrides={},
)

href = style.href({"book_links": {"items/staff": URL("link")}})
href = style.href({"book_links": {"namespace:items/staff": URL("link")}})

assert href == "link"

Expand All @@ -292,7 +292,7 @@ def test_case_sensitive_page_anchor_match():
link_overrides={},
)

href = style.href({"book_links": {"items/staff#anchor": URL("link")}})
href = style.href({"book_links": {"namespace:items/staff#anchor": URL("link")}})

assert href == "link"

Expand All @@ -305,4 +305,4 @@ def test_case_sensitive_page_anchor_broken():
)

with pytest.raises(ValueError):
style.href({"book_links": {"items/staff#anchor": URL("link")}})
style.href({"book_links": {"namespace:items/staff#anchor": URL("link")}})

0 comments on commit 63dc692

Please sign in to comment.