Merge branch 'master' into 3606_load_mathjax_async

This commit is contained in:
Takeshi KOMIYA 2018-05-28 23:04:13 +09:00 committed by GitHub
commit 1deceda6da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 365 additions and 149 deletions

15
CHANGES
View File

@ -65,9 +65,14 @@ Deprecated
* ``sphinx.writers.latex.Table.caption_footnotetexts`` is deprecated
* ``sphinx.writers.latex.Table.header_footnotetexts`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.footnotestack`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.in_container_literal_block`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.next_section_ids`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.next_hyperlink_ids`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.restrict_footnote()`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.unrestrict_footnote()`` is deprecated
* ``LaTeXWriter.bibitems`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.push_hyperlink_ids()`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.pop_hyperlink_ids()`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.bibitems`` is deprecated
* ``BuildEnvironment.load()`` is deprecated
* ``BuildEnvironment.loads()`` is deprecated
* ``BuildEnvironment.frompickle()`` is deprecated
@ -117,6 +122,8 @@ Features added
* #4785: napoleon: Add strings to translation file for localisation
* #4927: Display a warning when invalid values are passed to linenothreshold
option of highlight directive
* C++, add a ``cpp:texpr`` role as a sibling to ``cpp:expr``.
* C++, add support for unions.
* #3606: MathJax should be loaded with async attribute
Bugs fixed
@ -175,6 +182,12 @@ Bugs fixed
mocked module
* #4973: latex: glossary directive adds whitespace to each item
* #4980: latex: Explicit labels on code blocks are duplicated
* #4919: node.asdom() crashes if toctree has :numbered: option
* #4914: autodoc: Parsing error when using dataclasses without default values
* #4931: autodoc: crashed when handler for autodoc-skip-member raises an error
* #4931: autodoc: crashed when subclass of mocked class are processed by
napoleon module
* #5007: sphinx-build crashes when error log contains a "%" character
Testing
--------

View File

@ -3,7 +3,7 @@
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python ../sphinx/cmd/build.py
SPHINXBUILD = python3 ../sphinx/cmd/build.py
SPHINXPROJ = sphinx
SOURCEDIR = .
BUILDDIR = _build

View File

@ -73,7 +73,7 @@ package.
.. automethod:: Sphinx.add_js_file(filename, **kwargs)
.. automethod:: Sphinx.add_stylesheet(filename, alternate=None, title=None)
.. automethod:: Sphinx.add_css_file(filename, **kwargs)
.. automethod:: Sphinx.add_latex_package(packagename, options=None)

View File

@ -176,6 +176,21 @@ The following is a list of deprecated interface.
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.in_container_literal_block``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.next_section_ids``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.next_hyperlink_ids``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.restrict_footnote()``
- 1.8
- 3.0
@ -186,6 +201,16 @@ The following is a list of deprecated interface.
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.push_hyperlink_ids()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.pop_hyperlink_ids()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.bibitems``
- 1.8
- 3.0

View File

@ -569,10 +569,10 @@ visibility statement (``public``, ``private`` or ``protected``).
Full and partial template specialisations can be declared::
.. cpp:class:: template<> \
std::array<bool, 256>
std::array<bool, 256>
.. cpp:class:: template<typename T> \
std::array<T, 42>
std::array<T, 42>
.. rst:directive:: .. cpp:function:: (member) function prototype
@ -702,6 +702,10 @@ visibility statement (``public``, ``private`` or ``protected``).
.. cpp:enumerator:: MyEnum::myOtherEnumerator = 42
.. rst:directive:: .. cpp:union:: name
Describe a union.
.. rst:directive:: .. cpp:concept:: template-parameter-list name
.. warning:: The support for concepts is experimental. It is based on the
@ -815,24 +819,31 @@ Inline Expressions and Tpes
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. rst:role:: cpp:expr
cpp:texpr
A role for inserting a C++ expression or type as inline text. For example::
Insert a C++ expression or type either as inline code (``cpp:expr``)
or inline text (``cpp:texpr``). For example::
.. cpp:var:: int a = 42
.. cpp:function:: int f(int i)
An expression: :cpp:expr:`a * f(a)`.
A type: :cpp:expr:`const MySortedContainer<int>&`.
An expression: :cpp:expr:`a * f(a)` (or as text: :cpp:texpr:`a * f(a)`).
A type: :cpp:expr:`const MySortedContainer<int>&`
(or as text :cpp:texpr:`const MySortedContainer<int>&`).
will be rendered as follows:
.. cpp:var:: int a = 42
.. cpp:var:: int a = 42
.. cpp:function:: int f(int i)
.. cpp:function:: int f(int i)
An expression: :cpp:expr:`a * f(a)` (or as text: :cpp:texpr:`a * f(a)`).
A type: :cpp:expr:`const MySortedContainer<int>&`
(or as text :cpp:texpr:`const MySortedContainer<int>&`).
An expression: :cpp:expr:`a * f(a)`. A type: :cpp:expr:`const
MySortedContainer<int>&`.
Namespacing
~~~~~~~~~~~
@ -880,7 +891,7 @@ The ``cpp:namespace-pop`` directive undoes the most recent
.. cpp:function:: std::size_t size() const
or:::
or::
.. cpp:class:: template<typename T> \
std::vector
@ -949,20 +960,23 @@ These roles link to the given declaration types:
.. admonition:: Note on References with Templates Parameters/Arguments
Sphinx's syntax to give references a custom title can interfere with linking
to class templates, if nothing follows the closing angle bracket, i.e. if
the link looks like this: ``:cpp:class:`MyClass<int>```. This is
interpreted as a link to ``int`` with a title of ``MyClass``. In this case,
please escape the opening angle bracket with a backslash, like this:
``:cpp:class:`MyClass\<int>```.
These roles follow the Sphinx :ref:`xref-syntax` rules. This means care must be
taken when referencing a (partial) template specialization, e.g. if the link looks like
this: ``:cpp:class:`MyClass<int>```.
This is interpreted as a link to ``int`` with a title of ``MyClass``.
In this case, escape the opening angle bracket with a backslash,
like this: ``:cpp:class:`MyClass\<int>```.
When a custom title is not needed it may be useful to use the roles for inline expressions,
:rst:role:`cpp:expr` and :rst:role:`cpp:texpr`, where angle brackets do not need escaping.
.. admonition:: Note on References to Overloaded Functions
It is currently impossible to link to a specific version of an overloaded
method. Currently the C++ domain is the first domain that has basic support
for overloaded methods and until there is more data for comparison we don't
want to select a bad syntax to reference a specific overload. Currently
Sphinx will link to the first overloaded version of the method / function.
function. Currently the C++ domain is the first domain that has basic
support for overloaded functions and until there is more data for comparison
we don't want to select a bad syntax to reference a specific overload.
Currently Sphinx will link to the first overloaded version of the function.
Declarations without template parameters and template arguments
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -983,13 +997,13 @@ Assume the following declarations.
.. cpp:class:: template<typename TInner> \
Inner
In general the reference must include the template paraemter declarations,
In general the reference must include the template parameter declarations,
e.g., ``template\<typename TOuter> Wrapper::Outer``
(:cpp:class:`template\<typename TOuter> Wrapper::Outer`). Currently the lookup
only succeed if the template parameter identifiers are equal strings. That is,
``template\<typename UOuter> Wrapper::Outer`` will not work.
The inner class template can not be directly referenced, unless the current
The inner class template cannot be directly referenced, unless the current
namespace is changed or the following shorthand is used. If a template
parameter list is omitted, then the lookup will assume either a template or a
non-template, but not a partial template specialisation. This means the

View File

@ -20,7 +20,8 @@ from sphinx import package_dir, addnodes, highlighting
from sphinx.builders import Builder
from sphinx.builders.latex.transforms import (
BibliographyTransform, CitationReferenceTransform, MathReferenceTransform,
FootnoteDocnameUpdater, LaTeXFootnoteTransform, ShowUrlsTransform
FootnoteDocnameUpdater, LaTeXFootnoteTransform, LiteralBlockTransform,
ShowUrlsTransform, DocumentTargetTransform,
)
from sphinx.config import string_classes, ENUM
from sphinx.environment import NoUri
@ -223,7 +224,9 @@ class LaTeXBuilder(Builder):
transformer.set_environment(self.env)
transformer.add_transforms([BibliographyTransform,
ShowUrlsTransform,
LaTeXFootnoteTransform])
LaTeXFootnoteTransform,
LiteralBlockTransform,
DocumentTargetTransform])
transformer.apply_transforms()
def finish(self):

View File

@ -12,6 +12,11 @@
from docutils import nodes
class captioned_literal_block(nodes.container):
"""A node for a container of literal_block having a caption."""
pass
class footnotemark(nodes.Inline, nodes.Referential, nodes.TextElement):
"""A node represents ``\footnotemark``."""
pass

View File

@ -13,7 +13,7 @@ from docutils import nodes
from sphinx import addnodes
from sphinx.builders.latex.nodes import (
footnotemark, footnotetext, math_reference, thebibliography
captioned_literal_block, footnotemark, footnotetext, math_reference, thebibliography
)
from sphinx.transforms import SphinxTransform
@ -566,3 +566,33 @@ class MathReferenceTransform(SphinxTransform):
if docname:
refnode = math_reference('', docname=docname, target=node['reftarget'])
node.replace_self(refnode)
class LiteralBlockTransform(SphinxTransform):
"""Replace container nodes for literal_block by captioned_literal_block."""
default_priority = 400
def apply(self):
# type: () -> None
if self.app.builder.name != 'latex':
return
for node in self.document.traverse(nodes.container):
if node['literal_block'] is True:
newnode = captioned_literal_block('', *node.children, **node.attributes)
node.replace_self(newnode)
class DocumentTargetTransform(SphinxTransform):
"""Add :doc label to the first section of each document."""
default_priority = 400
def apply(self):
# type: () -> None
if self.app.builder.name != 'latex':
return
for node in self.document.traverse(addnodes.start_of_file):
section = node.next_node(nodes.section)
if section:
section['ids'].append(':doc') # special label for :doc:

View File

@ -3196,6 +3196,27 @@ class ASTClass(ASTBase):
signode.pop()
class ASTUnion(ASTBase):
def __init__(self, name):
# type: (Any) -> None
self.name = name
def get_id(self, version, objectType, symbol):
# type: (int, unicode, Symbol) -> unicode
if version == 1:
raise NoOldIdError()
return symbol.get_full_nested_name().get_id(version)
def __unicode__(self):
# type: () -> unicode
return text_type(self.name)
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None
_verify_description_mode(mode)
self.name.describe_signature(signode, mode, env, symbol=symbol)
class ASTEnum(ASTBase):
def __init__(self, name, scoped, underlyingType):
# type: (Any, unicode, Any) -> None
@ -3361,6 +3382,8 @@ class ASTDeclaration(ASTBase):
pass
elif self.objectType == 'class':
mainDeclNode += addnodes.desc_annotation('class ', 'class ')
elif self.objectType == 'union':
mainDeclNode += addnodes.desc_annotation('union ', 'union ')
elif self.objectType == 'enum':
prefix = 'enum '
if self.scoped: # type: ignore
@ -5259,6 +5282,11 @@ class DefinitionParser(object):
break
return ASTClass(name, final, bases)
def _parse_union(self):
# type: () -> ASTUnion
name = self._parse_nested_name()
return ASTUnion(name)
def _parse_enum(self):
# type: () -> ASTEnum
scoped = None # type: unicode # is set by CPPEnumObject
@ -5466,7 +5494,7 @@ class DefinitionParser(object):
def parse_declaration(self, objectType):
# type: (unicode) -> ASTDeclaration
if objectType not in ('type', 'concept', 'member',
'function', 'class', 'enum', 'enumerator'):
'function', 'class', 'union', 'enum', 'enumerator'):
raise Exception('Internal error, unknown objectType "%s".' % objectType)
visibility = None
templatePrefix = None
@ -5505,6 +5533,8 @@ class DefinitionParser(object):
declaration = self._parse_type(named=True, outer='function')
elif objectType == 'class':
declaration = self._parse_class()
elif objectType == 'union':
declaration = self._parse_union()
elif objectType == 'enum':
declaration = self._parse_enum()
elif objectType == 'enumerator':
@ -5807,6 +5837,16 @@ class CPPClassObject(CPPObject):
return parser.parse_declaration("class")
class CPPUnionObject(CPPObject):
def get_index_text(self, name):
# type: (unicode) -> unicode
return _('%s (C++ union)') % name
def parse_definition(self, parser):
# type: (Any) -> Any
return parser.parse_declaration("union")
class CPPEnumObject(CPPObject):
def get_index_text(self, name):
# type: (unicode) -> unicode
@ -5965,6 +6005,16 @@ class CPPXRefRole(XRefRole):
class CPPExprRole(object):
def __init__(self, asCode):
if asCode:
# render the expression as inline code
self.class_type = 'cpp-expr'
self.node_type = nodes.literal
else:
# render the expression as inline text
self.class_type = 'cpp-texpr'
self.node_type = nodes.inline
def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]):
class Warner(object):
def warn(self, msg):
@ -5972,18 +6022,23 @@ class CPPExprRole(object):
text = utils.unescape(text).replace('\n', ' ')
env = inliner.document.settings.env
parser = DefinitionParser(text, Warner(), env.config)
# attempt to mimic XRefRole classes, except that...
classes = ['xref', 'cpp', self.class_type]
try:
ast = parser.parse_expression()
except DefinitionError as ex:
Warner().warn('Unparseable C++ expression: %r\n%s'
% (text, text_type(ex.description)))
return [nodes.literal(text)], []
# see below
return [self.node_type(text, text, classes=classes)], []
parentSymbol = env.temp_data.get('cpp:parent_symbol', None)
if parentSymbol is None:
parentSymbol = env.domaindata['cpp']['root_symbol']
p = nodes.literal()
ast.describe_signature(p, 'markType', env, parentSymbol)
return [p], []
# ...most if not all of these classes should really apply to the individual references,
# not the container node
signode = self.node_type(classes=classes)
ast.describe_signature(signode, 'markType', env, parentSymbol)
return [signode], []
class CPPDomain(Domain):
@ -5992,6 +6047,7 @@ class CPPDomain(Domain):
label = 'C++'
object_types = {
'class': ObjType(_('class'), 'class', 'type', 'identifier'),
'union': ObjType(_('union'), 'union', 'type', 'identifier'),
'function': ObjType(_('function'), 'function', 'func', 'type', 'identifier'),
'member': ObjType(_('member'), 'member', 'var'),
'type': ObjType(_('type'), 'type', 'identifier'),
@ -6002,6 +6058,7 @@ class CPPDomain(Domain):
directives = {
'class': CPPClassObject,
'union': CPPUnionObject,
'function': CPPFunctionObject,
'member': CPPMemberObject,
'var': CPPMemberObject,
@ -6018,6 +6075,7 @@ class CPPDomain(Domain):
roles = {
'any': CPPXRefRole(),
'class': CPPXRefRole(),
'union': CPPXRefRole(),
'func': CPPXRefRole(fix_parens=True),
'member': CPPXRefRole(),
'var': CPPXRefRole(),
@ -6025,7 +6083,8 @@ class CPPDomain(Domain):
'concept': CPPXRefRole(),
'enum': CPPXRefRole(),
'enumerator': CPPXRefRole(),
'expr': CPPExprRole()
'expr': CPPExprRole(asCode=True),
'texpr': CPPExprRole(asCode=False)
}
initial_data = {
'root_symbol': Symbol(None, None, None, None, None, None),

View File

@ -313,19 +313,6 @@ class BuildEnvironment(object):
"""Like :meth:`warn`, but with source information taken from *node*."""
self._warnfunc(msg, '%s:%s' % get_source_line(node), **kwargs)
def need_refresh(self, app):
# type: (Sphinx) -> Tuple[bool, unicode]
"""Check refresh environment is needed.
If needed, this method returns the reason for refresh.
"""
if self.version != app.registry.get_envversion(app):
return True, __('build environment version not current')
elif self.srcdir != app.srcdir:
return True, __('source directory has changed')
else:
return False, None
def clear_doc(self, docname):
# type: (unicode) -> None
"""Remove all traces of a source file in the inventory."""

View File

@ -172,10 +172,10 @@ class TocTreeCollector(EnvironmentCollector):
number = tuple(numstack)
else:
number = None
secnums[subnode[0]['anchorname']] = \
subnode[0]['secnumber'] = number
secnums[subnode[0]['anchorname']] = number
subnode[0]['secnumber'] = list(number)
if titlenode:
titlenode['secnumber'] = number
titlenode['secnumber'] = list(number)
titlenode = None
elif isinstance(subnode, addnodes.toctree):
_walk_toctree(subnode, depth)

View File

@ -643,11 +643,17 @@ class Documenter(object):
# should be skipped
if self.env.app:
# let extensions preprocess docstrings
skip_user = self.env.app.emit_firstresult(
'autodoc-skip-member', self.objtype, membername, member,
not keep, self.options)
if skip_user is not None:
keep = not skip_user
try:
skip_user = self.env.app.emit_firstresult(
'autodoc-skip-member', self.objtype, membername, member,
not keep, self.options)
if skip_user is not None:
keep = not skip_user
except Exception as exc:
logger.warning(__('autodoc: failed to determine %r to be documented.'
'the following exception was raised:\n%s'),
member, exc)
keep = False
if keep:
ret.append((membername, member, isattr))

View File

@ -23,7 +23,7 @@ from sphinx.util.inspect import isenumclass, safe_getattr
if False:
# For type annotation
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple # NOQA
from typing import Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple # NOQA
logger = logging.getLogger(__name__)
@ -41,7 +41,7 @@ class _MockObject(object):
def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
pass
self.__qualname__ = ''
def __len__(self):
# type: () -> int
@ -52,8 +52,8 @@ class _MockObject(object):
return False
def __iter__(self):
# type: () -> None
pass
# type: () -> Iterator
return iter([])
def __mro_entries__(self, bases):
# type: (Tuple) -> Tuple

View File

@ -225,12 +225,13 @@ class AfterCommentParser(TokenProcessor):
def parse(self):
# type: () -> None
"""Parse the code and obtain comment after assignment."""
# skip lvalue (until '=' operator)
while self.fetch_token() != [OP, '=']:
# skip lvalue (or whole of AnnAssign)
while not self.fetch_token().match([OP, '='], NEWLINE, COMMENT):
assert self.current
# skip rvalue
self.fetch_rvalue()
# skip rvalue (if exists)
if self.current == [OP, '=']:
self.fetch_rvalue()
if self.current == COMMENT:
self.comment = self.current.value

View File

@ -392,7 +392,7 @@ class WarningIsErrorFilter(logging.Filter):
location = getattr(record, 'location', '')
try:
message = record.msg % record.args
except TypeError:
except (TypeError, ValueError):
message = record.msg # use record.msg itself
if location:

View File

@ -56,8 +56,7 @@ def repr_domxml(node, length=80):
returns full of DOM XML representation.
:return: DOM XML representation
"""
# text = node.asdom().toxml() # #4919 crush if node has secnumber with tuple value
text = text_type(node) # workaround for #4919
text = node.asdom().toxml()
if length and len(text) > length:
text = text[:length] + '...'
return text
@ -82,9 +81,8 @@ def apply_source_workaround(node):
get_full_module_name(node), repr_domxml(node))
node.source, node.line = node.parent.source, node.parent.line
if isinstance(node, nodes.title) and node.source is None:
# Uncomment these lines after merging into master(1.8)
# logger.debug('[i18n] PATCH: %r to have source: %s',
# get_full_module_name(node), repr_domxml(node))
logger.debug('[i18n] PATCH: %r to have source: %s',
get_full_module_name(node), repr_domxml(node))
node.source, node.line = node.parent.source, node.parent.line
if isinstance(node, nodes.term):
logger.debug('[i18n] PATCH: %r to have rawsource: %s',

View File

@ -25,7 +25,7 @@ from six import itervalues, text_type
from sphinx import addnodes
from sphinx import highlighting
from sphinx.builders.latex.nodes import footnotetext
from sphinx.builders.latex.nodes import captioned_literal_block, footnotetext
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.errors import SphinxError
from sphinx.locale import admonitionlabels, _, __
@ -58,6 +58,7 @@ HYPERLINK_SUPPORT_NODES = (
nodes.literal_block,
nodes.table,
nodes.section,
captioned_literal_block,
)
DEFAULT_SETTINGS = {
@ -465,7 +466,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.in_production_list = 0
self.in_footnote = 0
self.in_caption = 0
self.in_container_literal_block = 0
self.in_term = 0
self.needs_linetrimming = 0
self.in_minipage = 0
@ -691,8 +691,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.pending_footnotes = [] # type: List[nodes.footnote_reference]
self.curfilestack = [] # type: List[unicode]
self.handled_abbrs = set() # type: Set[unicode]
self.next_hyperlink_ids = {} # type: Dict[unicode, Set[unicode]]
self.next_section_ids = set() # type: Set[unicode]
def pushbody(self, newbody):
# type: (List[unicode]) -> None
@ -705,15 +703,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body = self.bodystack.pop()
return body
def push_hyperlink_ids(self, figtype, ids):
# type: (unicode, Set[unicode]) -> None
hyperlink_ids = self.next_hyperlink_ids.setdefault(figtype, set())
hyperlink_ids.update(ids)
def pop_hyperlink_ids(self, figtype):
# type: (unicode) -> Set[unicode]
return self.next_hyperlink_ids.pop(figtype, set())
def check_latex_elements(self):
# type: () -> None
for key in self.builder.config.latex_elements:
@ -931,8 +920,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_start_of_file(self, node):
# type: (nodes.Node) -> None
# also add a document target
self.next_section_ids.add(':doc')
self.curfilestack.append(node['docname'])
# use default highlight settings for new file
self.hlsettingstack.append(self.hlsettingstack[0])
@ -1067,11 +1054,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
# just use "subparagraph", it's not numbered anyway
self.body.append(r'\%s%s{' % (self.sectionnames[-1], short))
self.context.append('}\n' + self.hypertarget_to(node.parent))
if self.next_section_ids:
for id in self.next_section_ids:
self.context[-1] += self.hypertarget(id, anchor=False)
self.next_section_ids.clear()
elif isinstance(parent, nodes.topic):
self.body.append(r'\sphinxstyletopictitle{')
self.context.append('}\n')
@ -1790,7 +1772,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_caption(self, node):
# type: (nodes.Node) -> None
self.in_caption += 1
if self.in_container_literal_block:
if isinstance(node.parent, captioned_literal_block):
self.body.append('\\sphinxSetupCaptionForVerbatim{')
elif self.in_minipage and isinstance(node.parent, nodes.figure):
self.body.append('\\captionof{figure}{')
@ -1883,35 +1865,13 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(self.hypertarget(id, anchor=anchor))
# skip if visitor for next node supports hyperlink
domain = self.builder.env.get_domain('std')
next_node = node.next_node(ascend=True)
if isinstance(next_node, HYPERLINK_SUPPORT_NODES):
return
elif domain.get_enumerable_node_type(next_node) and domain.get_numfig_title(next_node):
return
# postpone the labels until after the sectioning command
parindex = node.parent.index(node)
try:
try:
next = node.parent[parindex + 1]
except IndexError:
# last node in parent, look at next after parent
# (for section of equal level) if it exists
if node.parent.parent is not None:
next = node.parent.parent[
node.parent.parent.index(node.parent)]
else:
raise
domain = self.builder.env.get_domain('std')
figtype = domain.get_enumerable_node_type(next)
if figtype and domain.get_numfig_title(next):
ids = set()
# labels for figures go in the figure body, not before
if node.get('refid'):
ids.add(node['refid'])
ids.update(node['ids'])
self.push_hyperlink_ids(figtype, ids)
return
except IndexError:
pass
if 'refuri' in node:
return
if node.get('refid'):
@ -2221,6 +2181,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
# the \ignorespaces in particular for after table header use
self.body.append('%\n\\end{footnotetext}\\ignorespaces ')
def visit_captioned_literal_block(self, node):
# type: (nodes.Node) -> None
pass
def depart_captioned_literal_block(self, node):
# type: (nodes.Node) -> None
pass
def visit_literal_block(self, node):
# type: (nodes.Node) -> None
if node.rawsource != node.astext():
@ -2229,9 +2197,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\\begin{sphinxalltt}\n')
else:
labels = self.hypertarget_to(node)
# LaTeX code will insert \phantomsection prior to \label
if isinstance(node.parent, captioned_literal_block):
labels += self.hypertarget_to(node.parent)
if labels and not self.in_footnote:
self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + labels + '}')
code = node.astext()
lang = self.hlsettingstack[-1][0]
linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1
@ -2458,22 +2428,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_container(self, node):
# type: (nodes.Node) -> None
if node.get('literal_block'):
self.in_container_literal_block += 1
ids = '' # type: unicode
for id in self.pop_hyperlink_ids('code-block'):
ids += self.hypertarget(id, anchor=False)
if node['ids']:
# suppress with anchor=False \phantomsection insertion
ids += self.hypertarget(node['ids'][0], anchor=False)
# define label for use in caption.
if ids:
self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + ids + '}\n')
pass
def depart_container(self, node):
# type: (nodes.Node) -> None
if node.get('literal_block'):
self.in_container_literal_block -= 1
pass
def visit_decoration(self, node):
# type: (nodes.Node) -> None
@ -2603,6 +2562,39 @@ class LaTeXTranslator(nodes.NodeVisitor):
RemovedInSphinx30Warning)
return []
@property
def in_container_literal_block(self):
# type: () -> int
warnings.warn('LaTeXTranslator.in_container_literal_block is deprecated.',
RemovedInSphinx30Warning)
return 0
@property
def next_section_ids(self):
# type: () -> Set[unicode]
warnings.warn('LaTeXTranslator.next_section_ids is deprecated.',
RemovedInSphinx30Warning)
return set()
@property
def next_hyperlink_ids(self):
# type: () -> Dict
warnings.warn('LaTeXTranslator.next_hyperlink_ids is deprecated.',
RemovedInSphinx30Warning)
return {}
def push_hyperlink_ids(self, figtype, ids):
# type: (unicode, Set[unicode]) -> None
warnings.warn('LaTeXTranslator.push_hyperlink_ids() is deprecated.',
RemovedInSphinx30Warning)
pass
def pop_hyperlink_ids(self, figtype):
# type: (unicode) -> Set[unicode]
warnings.warn('LaTeXTranslator.pop_hyperlink_ids() is deprecated.',
RemovedInSphinx30Warning)
return set()
# Import old modules here for compatibility
# They should be imported after `LaTeXTranslator` to avoid recursive import.

View File

@ -0,0 +1,12 @@
xref consistency
----------------
.. cpp:namespace:: xref_consistency
.. cpp:class:: item
code-role: :code:`item`
any-role: :any:`item`
cpp-any-role: :cpp:any:`item`
cpp-expr-role: :cpp:expr:`item`
cpp-texpr-role: :cpp:texpr:`item`

View File

@ -491,6 +491,10 @@ def test_class_definitions():
{2: 'I0E7has_varI1TNSt6void_tIDTadN1T3varEEEEE'})
def test_union_definitions():
check('union', 'A', {2: "1A"})
def test_enum_definitions():
check('enum', 'A', {2: "1A"})
check('enum', 'A : std::underlying_type<B>::type', {2: "1A"})
@ -735,3 +739,68 @@ def test_build_domain_cpp_with_add_function_parentheses_is_False(app, status, wa
t = (app.outdir / f).text()
for s in parenPatterns:
check(s, t, f)
@pytest.mark.sphinx(testroot='domain-cpp')
def test_xref_consistency(app, status, warning):
app.builder.build_all()
test = 'xref_consistency.html'
output = (app.outdir / test).text()
def classes(role, tag):
pattern = (r'{role}-role:.*?'
'<(?P<tag>{tag}) .*?class=["\'](?P<classes>.*?)["\'].*?>'
'.*'
'</(?P=tag)>').format(role=role, tag=tag)
result = re.search(pattern, output)
expect = '''\
Pattern for role `{role}` with tag `{tag}`
\t{pattern}
not found in `{test}`
'''.format(role=role, tag=tag, pattern=pattern, test=test)
assert result, expect
return set(result.group('classes').split())
class RoleClasses(object):
"""Collect the classes from the layout that was generated for a given role."""
def __init__(self, role, root, contents):
self.name = role
self.classes = classes(role, root)
self.content_classes = dict()
for tag in contents:
self.content_classes[tag] = classes(role, tag)
# not actually used as a reference point
#code_role = RoleClasses('code', 'code', [])
any_role = RoleClasses('any', 'a', ['code'])
cpp_any_role = RoleClasses('cpp-any', 'a', ['code'])
# NYI: consistent looks
#texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'code'])
expr_role = RoleClasses('cpp-expr', 'code', ['a'])
texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'span'])
# XRefRole-style classes
## any and cpp:any do not put these classes at the root
# n.b. the generic any machinery finds the specific 'cpp-class' object type
expect = 'any uses XRefRole classes'
assert {'xref', 'any', 'cpp', 'cpp-class'} <= any_role.content_classes['code'], expect
expect = 'cpp:any uses XRefRole classes'
assert {'xref', 'cpp-any', 'cpp'} <= cpp_any_role.content_classes['code'], expect
for role in (expr_role, texpr_role):
name = role.name
expect = '`{name}` puts the domain and role classes at its root'.format(name=name)
# NYI: xref should go in the references
assert {'xref', 'cpp', name} <= role.classes, expect
# reference classes
expect = 'the xref roles use the same reference classes'
assert any_role.classes == cpp_any_role.classes, expect
assert any_role.classes == expr_role.content_classes['a'], expect
assert any_role.classes == texpr_role.content_classes['a'], expect

View File

@ -227,11 +227,11 @@ def test_get_toctree_for(app):
[list_item, compact_paragraph, reference, "foo.1"],
[list_item, compact_paragraph, reference, "foo.2"]))
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,))
assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=(1, 1))
assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=(1, 2))
assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=(1, 3))
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,))
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=[1])
assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=[1, 1])
assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=[1, 2])
assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=[1, 3])
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2])
assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/")
assert_node(toctree[2],
@ -258,8 +258,8 @@ def test_get_toctree_for_collapse(app):
([list_item, compact_paragraph, reference, "foo"],
[list_item, compact_paragraph, reference, "bar"],
[list_item, compact_paragraph, reference, "http://sphinx-doc.org/"]))
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,))
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,))
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=[1])
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2])
assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/")
assert_node(toctree[2],
@ -296,13 +296,13 @@ def test_get_toctree_for_maxdepth(app):
assert_node(toctree[1][0][1][1][1],
[bullet_list, list_item, compact_paragraph, reference, "foo.1-1"])
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,))
assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=(1, 1))
assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=(1, 2))
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=[1])
assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=[1, 1])
assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=[1, 2])
assert_node(toctree[1][0][1][1][1][0][0][0],
reference, refuri="foo#foo-1-1", secnumber=(1, 2, 1))
assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=(1, 3))
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,))
reference, refuri="foo#foo-1-1", secnumber=[1, 2, 1])
assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=[1, 3])
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2])
assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/")
assert_node(toctree[2],
@ -335,11 +335,11 @@ def test_get_toctree_for_includehidden(app):
[list_item, compact_paragraph, reference, "foo.1"],
[list_item, compact_paragraph, reference, "foo.2"]))
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,))
assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=(1, 1))
assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=(1, 2))
assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=(1, 3))
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,))
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=[1])
assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=[1, 1])
assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=[1, 2])
assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=[1, 3])
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2])
assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/")
assert_node(toctree[2],

View File

@ -100,11 +100,13 @@ def test_comment_picker_location():
def test_annotated_assignment_py36():
source = ('a: str = "Sphinx" #: comment\n'
'b: int = 1\n'
'"""string on next line"""')
'"""string on next line"""\n'
'c: int #: comment')
parser = Parser(source)
parser.parse()
assert parser.comments == {('', 'a'): 'comment',
('', 'b'): 'string on next line'}
('', 'b'): 'string on next line',
('', 'c'): 'comment'}
assert parser.definitions == {}