Skip to content

Commit

Permalink
fix navlinks that contain # anchors
Browse files Browse the repository at this point in the history
  • Loading branch information
schettino72 committed May 26, 2019
1 parent b94f932 commit 1c52703
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Changes
====================

- fix reading cached toctree data
- fix navlinks that contain `#` anchors


0.3.0 (*2019-04-22*)
Expand Down
68 changes: 57 additions & 11 deletions sphinx_press_theme/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from docutils import nodes
from sphinx.environment.collectors import EnvironmentCollector
from sphinx import addnodes
from sphinx.util.osutil import relative_uri

__version__ = (0, 3, 0)

Expand All @@ -12,6 +13,14 @@ class SimpleTocTreeCollector(EnvironmentCollector):
sphinx.environment.collectors.toctree.TocTreeCollector saves
TocTree as docutils.nodes which are hard to work with...
Executed once per document/page, at sphinx's "read" phase.
Saved data example:
{
'sections': [{'title': 'Demo', 'href': '#demo'}],
'toctrees': [<toctree: >]
}
"""
def enable(self, app):
super().enable(app)
Expand Down Expand Up @@ -54,26 +63,44 @@ def process_doc(self, app, doctree):
}


def add_doctree_data(app, pagename, templatename, context, doctree):
"""create toctree_data, used to build sidebar navigation"""

def add_toctree_data(app, pagename, templatename, context, doctree):
"""create toctree_data, used to build sidebar navigation
:context dict:
:doctree docutils.nodes.document:
Add to `toctree_data` to `context` that will be available on templates.
Although data is "global", it is called once per page because current
page is "highlighted", and some part of TOC might be collapsed.
"""
# print(f"---------- Context\n{pagename}\n-------------\n")

# start from master_doc
master = app.env.get_doctree(app.env.config.master_doc)

# each toctree will create navigation section
res = [] # list of top level toctrees in master_doc
for tree in master.traverse(addnodes.toctree):
entries = []
current0 = False

# special case for toctree that includes a single item
# that contains a nested toctree.
# In this case, just use the referenced toctree directly
if len(tree['entries']) == 1:
toctrees = app.env.toc_dict[tree['entries'][0][1]]['toctrees']
entry_docname = tree['entries'][0][1]
toctrees = app.env.toc_dict[entry_docname]['toctrees']

if toctrees:
# FIXME
assert len(toctrees) == 1, "Press: Not supported more then one toctree on nested toctree"
tree = toctrees[0]

current0 = False # same page might have multiple tocs

# add toc tree items, expand one more level if toctree is current page
entries = []
for title, name in tree['entries']:
if not title:
title = app.env.titles[name].astext()
Expand All @@ -84,29 +111,48 @@ def add_doctree_data(app, pagename, templatename, context, doctree):
current0 = True
# if current, add another level
children = app.env.toc_dict[name]['sections']
# add page_toc for current page
# add page_toc for current page
entries.append({
'name': name,
'title': title,
'current': current1,
'children': children,
})

toc_docname = tree['parent'] # docname where this toc appears
title = tree['caption']
if not title:
title = app.env.titles[tree['parent']].astext()

# Anchor element is the section containing the toc,
# as the toc itself does not contain ID.
anchor_id = ''

# tree.parent is the parent docutils node.
# First parent is "compound" node toctree-wrapper,
# second parent is the section containing the toctree
toc_section = tree.parent.parent
if toc_section['ids']: # no id means toc actually not in a section
# TODO: should we be strict about toc being inside a section
anchor_id = toc_section['ids'][0]
if not title:
title = toc_section['names'][0]

# sphinx `pathto` does not play nice with anchors when
# `allow_sharp_as_current_path` is True
baseuri = app.builder.get_target_uri(pagename).rsplit('#', 1)[0]
toc_uri = app.builder.get_target_uri(toc_docname).rsplit('#', 1)[0]
toc_href = f'{relative_uri(baseuri, toc_uri)}#{anchor_id}'
res.append({
'docname': tree['parent'],
'anchor': '#{}'.format(tree.parent.parent['ids'][0]),
'docname': toc_docname,
'href': toc_href,
'title': title,
'current': current0,
'entries': entries,
})
context['toctree_data'] = res



def setup(app):
app.add_env_collector(SimpleTocTreeCollector)
app.connect('html-page-context', add_doctree_data)
app.connect('html-page-context', add_toctree_data)
app.add_html_theme('press', path.abspath(path.dirname(__file__)))
2 changes: 1 addition & 1 deletion sphinx_press_theme/util/navlinks.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{% if toctree_data|length > 1 %}
{% for tree in toctree_data %}
<div class="nav-item">
<a href="{{ pathto(tree.docname) + tree.anchor }}"
<a href="{{ tree.href }}"
class="nav-link {% if tree.current %} router-link-active{% endif %}">
{{tree.title}}
</a>
Expand Down
2 changes: 1 addition & 1 deletion sphinx_press_theme/util/sidetoc.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{% for toc in toctree_data %}
<div class="sidebar-group">
<p class="caption">
<span class="caption-text"><a href="{{ pathto(toc.docname) + toc.anchor }}">{{toc.title}}</a></span>
<span class="caption-text"><a href="{{ toc.href }}">{{toc.title}}</a></span>
</p>
<ul class="{% if toc.current %}current{% endif %}">
{% for entry in toc.entries %}
Expand Down

0 comments on commit 1c52703

Please sign in to comment.