Skip to content

Commit

Permalink
DOP-3424: "On this page" subnav incorrectly displays headings for all…
Browse files Browse the repository at this point in the history
… language tabs (#625)

* checkpoint

* working

* tests + formatting

* format

* remove comment

* typing

* addressing comments
  • Loading branch information
mayaraman19 authored Oct 15, 2024
1 parent f21d57c commit c3fd35a
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 19 deletions.
40 changes: 29 additions & 11 deletions snooty/postprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,22 +463,36 @@ def enter_node(self, fileid_stack: FileIdStack, node: n.Node) -> None:
node.refuri = refuri


SelectorId = Dict[str, Union[str, "SelectorId"]]


class ContentsHandler(Handler):
"""Identify all headings on a given page. If a contents directive appears on the page, save list of headings as a page-level option."""

class HeadingData(NamedTuple):
depth: int
id: str
title: Sequence[n.InlineNode]
selector_id: Optional[str]
selector_ids: SelectorId

def __init__(self, context: Context) -> None:
super().__init__(context)
self.contents_depth = sys.maxsize
self.current_depth = 0
self.has_contents_directive = False
self.headings: List[ContentsHandler.HeadingData] = []
self.scanned_pattern: List[str] = []
self.scanned_pattern: List[Tuple[str, str]] = []

def scan_pattern(self, arr: List[Tuple[str, str]]) -> SelectorId:
if not arr:
return {}
if len(arr) == 1:
return {arr[0][0]: arr[0][1]}
scanned_pattern: SelectorId = {
arr[0][0]: arr[0][1],
"children": self.scan_pattern(arr[1:]),
}
return scanned_pattern

def enter_page(self, fileid_stack: FileIdStack, page: Page) -> None:
self.contents_depth = sys.maxsize
Expand All @@ -496,7 +510,7 @@ def exit_page(self, fileid_stack: FileIdStack, page: Page) -> None:
"depth": h.depth,
"id": h.id,
"title": [node.serialize() for node in h.title],
"selector_id": h.selector_id,
"selector_ids": h.selector_ids,
}
for h in self.headings
if h.depth - 1 <= self.contents_depth
Expand All @@ -509,8 +523,11 @@ def enter_node(self, fileid_stack: FileIdStack, node: n.Node) -> None:
self.current_depth += 1
return

if isinstance(node, n.Directive) and node.name == "method-option":
self.scanned_pattern.append(node.options["id"])
if isinstance(node, n.Directive):
if node.name == "method-option":
self.scanned_pattern.append((node.name, node.options["id"]))
elif node.name == "tab":
self.scanned_pattern.append((node.name, node.options["tabid"]))

if isinstance(node, n.Directive) and node.name == "contents":
if self.has_contents_directive:
Expand All @@ -526,16 +543,15 @@ def enter_node(self, fileid_stack: FileIdStack, node: n.Node) -> None:
if self.current_depth - 1 > self.contents_depth:
return

selector_id = None
selector_ids = {}
if len(self.scanned_pattern) > 0:
for item in self.scanned_pattern:
selector_id = item
selector_ids = self.scan_pattern(self.scanned_pattern)

# Omit title headings (depth = 1) from heading list
if isinstance(node, n.Heading) and self.current_depth > 1:
self.headings.append(
ContentsHandler.HeadingData(
self.current_depth, node.id, node.children, selector_id
self.current_depth, node.id, node.children, selector_ids
)
)

Expand All @@ -546,12 +562,14 @@ def enter_node(self, fileid_stack: FileIdStack, node: n.Node) -> None:
self.current_depth + 1,
node.options["id"],
[n.Text(node.span, node.options["heading"])],
selector_id,
selector_ids,
)
)

def exit_node(self, fileid_stack: FileIdStack, node: n.Node) -> None:
if isinstance(node, n.Directive) and node.name == "method-option":
if isinstance(node, n.Directive) and (
node.name == "method-option" or node.name == "tab"
):
self.scanned_pattern.pop()
if isinstance(node, n.Section):
self.current_depth -= 1
Expand Down
79 changes: 71 additions & 8 deletions snooty/test_postprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -2347,7 +2347,7 @@ def test_contents_directive() -> None:
check_ast_testing_string(
page.ast,
"""
<root fileid="page.txt" headings="[{'depth': 2, 'id': 'first-heading', 'title': [{'type': 'text', 'position': {'start': {'line': 9}}, 'value': 'First Heading'}], 'selector_id': None}, {'depth': 3, 'id': 'second-heading', 'title': [{'type': 'text', 'position': {'start': {'line': 12}}, 'value': 'Second Heading'}], 'selector_id': None}, {'depth': 2, 'id': 'third-heading', 'title': [{'type': 'text', 'position': {'start': {'line': 18}}, 'value': 'Third Heading'}], 'selector_id': None}]">
<root fileid="page.txt" headings="[{'depth': 2, 'id': 'first-heading', 'title': [{'type': 'text', 'position': {'start': {'line': 9}}, 'value': 'First Heading'}], 'selector_ids': {}}, {'depth': 3, 'id': 'second-heading', 'title': [{'type': 'text', 'position': {'start': {'line': 12}}, 'value': 'Second Heading'}], 'selector_ids': {}}, {'depth': 2, 'id': 'third-heading', 'title': [{'type': 'text', 'position': {'start': {'line': 18}}, 'value': 'Third Heading'}], 'selector_ids': {}}]">
<section>
<heading id="title"><text>Title</text></heading>
<directive name="contents" depth="2" />
Expand Down Expand Up @@ -3580,7 +3580,7 @@ def test_collapsible_headings() -> None:
"value": "Subsection heading",
}
],
"selector_id": None,
"selector_ids": {},
},
{
"depth": 3,
Expand All @@ -3592,7 +3592,7 @@ def test_collapsible_headings() -> None:
"value": "Subsubsection heading",
}
],
"selector_id": None,
"selector_ids": {},
},
]

Expand Down Expand Up @@ -3644,7 +3644,7 @@ def test_collapsible_headings() -> None:
"value": "Subsection heading",
}
],
"selector_id": None,
"selector_ids": {},
}
]

Expand Down Expand Up @@ -3682,7 +3682,7 @@ def test_collapsible_headings() -> None:
"value": "Collapsible heading",
}
],
"selector_id": None,
"selector_ids": {},
}
]

Expand Down Expand Up @@ -4093,12 +4093,12 @@ def test_method_selector_headings() -> None:
"value": "Subsection heading",
}
],
"selector_id": None,
"selector_ids": {},
},
{
"depth": 3,
"id": "what",
"selector_id": "driver",
"selector_ids": {"method-option": "driver"},
"title": [
{
"position": {"start": {"line": 17}},
Expand All @@ -4110,7 +4110,7 @@ def test_method_selector_headings() -> None:
{
"depth": 3,
"id": "this-is-a-heading",
"selector_id": "cli",
"selector_ids": {"method-option": "cli"},
"title": [
{
"position": {"start": {"line": 29}},
Expand All @@ -4122,6 +4122,69 @@ def test_method_selector_headings() -> None:
]


def test_tab_headings() -> None:
with make_test(
{
Path(
"source/index.txt"
): """
.. contents:: On this page
:depth: 3
Title here
==========
.. tabs::
.. tab:: tabs1
:tabid: tabs1
Heading here
------------
This is content in tab1.
.. tabs::
.. tab:: tabby
:tabid: tabby
This is another headinge!
~~~~~~~~~~~~~~~~~~~~~~~~~
Text ext text
""",
}
) as result:
page = result.pages[FileId("index.txt")]
assert (page.ast.options.get("headings")) == [
{
"depth": 2,
"id": "heading-here",
"title": [
{
"type": "text",
"position": {"start": {"line": 13}},
"value": "Heading here",
}
],
"selector_ids": {"tab": "tabs1"},
},
{
"depth": 3,
"id": "this-is-another-headinge-",
"title": [
{
"type": "text",
"position": {"start": {"line": 22}},
"value": "This is another headinge!",
}
],
"selector_ids": {"tab": "tabs1", "children": {"tab": "tabby"}},
},
]


def test_multi_page_tutorials() -> None:
test_page_template = """
.. multi-page-tutorial::
Expand Down

0 comments on commit c3fd35a

Please sign in to comment.