# :coding: utf-8
from sphinx import addnodes
from docutils import nodes
from sphinx.util.nodes import make_refnode
[docs]class ViewCode(object):
"""Helper class to display the code for each :term:`Javascript` module
found and link it to the documentation.
"""
#: Store the modules elements to link to the code
js_modules = None
[docs] @classmethod
def add_source_code_links(cls, app, doctree):
"""Parse *doctree* and add source code link when available.
Create temporary 'js_modules' in *app* builder environment to store all
module information which will be used to create the code page links.
This function is called with the ``doctree-read`` Sphinx event, emitted
when a *doctree* has been parsed and read by the environment, and is
about to be pickled
"""
js_env = app.config.js_environment
builder_env = app.builder.env
if cls.js_modules is None:
cls.js_modules = {}
# Loop through all js signature nodes
for object_node in doctree.traverse(addnodes.desc):
if object_node.get("domain") != "js":
continue
for node in object_node:
if not isinstance(node, addnodes.desc_signature):
continue
node_type = node.get("type", None)
if node_type not in js_env.keys():
continue
node_id = node.get("id", None)
if node_id not in js_env[node_type].keys():
continue
js_env_element = js_env[node_type][node_id]
for element in ["name", "module_id", "line_number"]:
if element not in js_env_element.keys():
continue
module_id = js_env_element["module_id"]
page_name = "_modules/{0}".format(module_id.replace(".", "/"))
if module_id not in cls.js_modules.keys():
cls.js_modules[module_id] = {
"pagename": page_name,
"docname": builder_env.docname,
"entries": {}
}
line_number = js_env_element["line_number"]
cls.js_modules[module_id]["entries"][line_number] = (
js_env_element["name"]
)
link_node = addnodes.only(expr="html")
link_node += addnodes.pending_xref(
"",
reftype="viewcode",
refdomain="std",
refexplicit=False,
reftarget=page_name,
refid=node.get("fullname"),
refdoc=builder_env.docname
)
link_node[0] += nodes.inline(
"", "[source]", classes=["viewcode-link"]
)
node += link_node
[docs] @classmethod
def create_code_pages(cls, app):
"""Create all code pages and the links to the documentation.
This function is called with the ``html-collect-pages`` Sphinx event,
emitted when the HTML builder is starting to write non-document pages.
"""
builder_env = app.builder.env
if cls.js_modules is None:
return
module_env = app.config.js_environment["module"]
file_env = app.config.js_environment["file"]
highlighter = app.builder.highlighter
uri = app.builder.get_relative_uri
all_pages = [elt["pagename"] for elt in cls.js_modules.values()]
for module_id, element in cls.js_modules.items():
file_id = module_env[module_id]["file_id"]
page_name = element["pagename"]
doc_name = element["docname"]
if builder_env.config.highlight_language in (
"js", "default", "none"
):
lexer = builder_env.config.highlight_language
else:
lexer = "js"
highlighted = highlighter.highlight_block(
file_env[file_id]["content"], lexer, linenos=False
)
lines = highlighted.splitlines()
for line_number, name in element["entries"].items():
link = uri(page_name, doc_name) + "#" + name
lines[line_number-1] = (
"<div class='viewcode-block' id='{name}'>"
"<a class='viewcode-back' href='{link}'>[docs]</a>"
"{line}".format(
name=name,
link=link,
line=lines[line_number-1]
)
)
parents = []
for module_name in reversed(module_id.split(".")[:-1]):
link_page = "_modules/{0}".format(module_name.replace(".", "/"))
if link_page in all_pages:
parents.append({
"link": uri(page_name, link_page),
"title": module_name
})
parents.append({
"link": uri(page_name, "_modules/index"),
"title": "Code"
})
parents.reverse()
# putting it all together
context = {
"parents": parents,
"title": module_id,
"body": (
"<h1>Source code for {name}</h1>{content}".format(
name=module_id,
content="\n".join(lines)
)
),
}
yield page_name, context, "page.html"
yield cls.create_code_page_index(app)
[docs] @classmethod
def create_code_page_index(cls, app):
"""Create page index regrouping all code page links.
"""
if cls.js_modules is None:
return
body = ["\n<ul>"]
for module_id in sorted(cls.js_modules.keys()):
link_page = "_modules/{0}".format(module_id.replace(".", "/"))
uri = app.builder.get_relative_uri
body.append(
"<li><a href='{link}'>{name}</a></li>\n".format(
link=uri("_modules/index", link_page),
name=module_id
)
)
body.append("</ul>")
context = {
"title": "Overview: module code",
"body": (
"<h1>All modules for which code is available</h1>{0}".format(
"".join(body)
)
),
}
return "_modules/index", context, "page.html"
[docs] @classmethod
def create_missing_code_link(cls, app, env, node, content_node):
"""Resolve all '[source]' links in Api documentation pages.
This function is called with the ``missing-reference`` Sphinx event,
emitted when a cross-reference to a Python module or object cannot be
resolved.
"""
if node["reftype"] == "viewcode":
return make_refnode(
app.builder,
node["refdoc"],
node["reftarget"],
node["refid"],
content_node
)