diff options
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | sphinx/builders/htmlhelp.py | 2 | ||||
-rw-r--r-- | sphinx/builders/latex/__init__.py (renamed from sphinx/builders/latex.py) | 0 | ||||
-rw-r--r-- | sphinx/builders/latex/transforms.py | 161 | ||||
-rw-r--r-- | sphinx/ext/jsmath.py | 6 | ||||
-rw-r--r-- | sphinx/ext/mathjax.py | 4 | ||||
-rw-r--r-- | sphinx/themes/basic/static/doctools.js_t | 4 | ||||
-rw-r--r-- | sphinx/writers/latex.py | 116 | ||||
-rw-r--r-- | tests/test_ext_math.py | 22 |
9 files changed, 192 insertions, 127 deletions
@@ -104,6 +104,9 @@ Bugs fixed * #4817: wrong URLs on warning messages * #4784: latex: :confval:`latex_show_urls` assigns incorrect footnote numbers if hyperlinks exists inside substitutions +* #4837: latex with class memoir Error: Font command ``\sf`` is not supported +* #4803: latex: too slow in proportion to number of auto numbered footnotes +* #4838: htmlhelp: The entries in .hhp file is not ordered Testing -------- @@ -138,6 +141,7 @@ Bugs fixed * #1435: qthelp builder should htmlescape keywords * epub: Fix docTitle elements of toc.ncx is not escaped * #4520: apidoc: Subpackage not in toc (introduced in 1.6.6) now fixed +* #4767: html: search highlighting breaks mathjax equations Release 1.7.1 (released Feb 23, 2018) ===================================== diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index 8d4ded246..49d48361e 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -248,6 +248,8 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): outdir += os.sep olen = len(outdir) for root, dirs, files in os.walk(outdir): + dirs.sort() + files.sort() staticdir = root.startswith(path.join(outdir, '_static')) for fn in sorted(files): if (staticdir and not fn.endswith('.js')) or \ diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex/__init__.py index d0357c6a0..d0357c6a0 100644 --- a/sphinx/builders/latex.py +++ b/sphinx/builders/latex/__init__.py diff --git a/sphinx/builders/latex/transforms.py b/sphinx/builders/latex/transforms.py new file mode 100644 index 000000000..201129989 --- /dev/null +++ b/sphinx/builders/latex/transforms.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.latex.transforms + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Transforms for LaTeX builder. + + :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from docutils import nodes + +from sphinx.transforms import SphinxTransform + +if False: + # For type annotation + from typing import Dict, List, Set, Union # NOQA + +URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:') + + +class ShowUrlsTransform(SphinxTransform): + """Expand references to inline text or footnotes. + + For more information, see :confval:`latex_show_urls`. + """ + default_priority = 400 + + # references are expanded to footnotes (or not) + expanded = False + + def apply(self): + # type: () -> None + # replace id_prefix temporarily + id_prefix = self.document.settings.id_prefix + self.document.settings.id_prefix = 'show_urls' + + self.expand_show_urls() + if self.expanded: + self.renumber_footnotes() + + # restore id_prefix + self.document.settings.id_prefix = id_prefix + + def expand_show_urls(self): + # type: () -> None + show_urls = self.document.settings.env.config.latex_show_urls + if show_urls is False or show_urls == 'no': + return + + for node in self.document.traverse(nodes.reference): + uri = node.get('refuri', '') + if uri.startswith(URI_SCHEMES): + if uri.startswith('mailto:'): + uri = uri[7:] + if node.astext() != uri: + index = node.parent.index(node) + if show_urls == 'footnote': + footnote_nodes = self.create_footnote(uri) + for i, fn in enumerate(footnote_nodes): + node.parent.insert(index + i + 1, fn) + + self.expanded = True + else: # all other true values (b/w compat) + textnode = nodes.Text(" (%s)" % uri) + node.parent.insert(index + 1, textnode) + + def create_footnote(self, uri): + # type: (unicode) -> List[Union[nodes.footnote, nodes.footnote_ref]] + label = nodes.label('', '#') + para = nodes.paragraph() + para.append(nodes.reference('', nodes.Text(uri), refuri=uri, nolinkurl=True)) + footnote = nodes.footnote(uri, label, para, auto=1) + footnote['names'].append('#') + self.document.note_autofootnote(footnote) + + label = nodes.Text('#') + footnote_ref = nodes.footnote_reference('[#]_', label, auto=1, + refid=footnote['ids'][0]) + self.document.note_autofootnote_ref(footnote_ref) + footnote.add_backref(footnote_ref['ids'][0]) + + return [footnote, footnote_ref] + + def renumber_footnotes(self): + # type: () -> None + collector = FootnoteCollector(self.document) + self.document.walkabout(collector) + + num = 0 + for document, footnote in collector.auto_footnotes: + # search unused footnote number + while True: + num += 1 + if str(num) not in collector.used_footnote_numbers: + break + + # assign new footnote number + old_label = footnote[0].astext() + footnote[0].replace_self(nodes.label('', str(num))) + if old_label in footnote['names']: + footnote['names'].remove(old_label) + footnote['names'].append(str(num)) + + # update footnote_references by new footnote number + for ref in collector.footnote_refs.get(document, []): + if footnote['ids'][0] == ref['refid']: + ref.remove(ref[0]) + ref += nodes.Text(str(num)) + + +class FootnoteCollector(nodes.NodeVisitor): + """Collect footnotes and footnote references on the document""" + + def __init__(self, document): + # type: (nodes.document) -> None + self.auto_footnotes = [] # type: List[nodes.footnote] + self.used_footnote_numbers = set() # type: Set[unicode] + self.footnote_refs = {} # type: Dict[nodes.Node, List[nodes.footnote_reference]] # NOQA + self.current_document = [] # type: List[nodes.Node] + nodes.NodeVisitor.__init__(self, document) + + def visit_document(self, node): + # type: (nodes.Node) -> None + self.current_document.append(node) + + def depart_document(self, node): + # type: (nodes.Node) -> None + self.current_document.pop() + + def visit_start_of_file(self, node): + # type: (nodes.Node) -> None + self.current_document.append(node) + + def depart_start_of_file(self, node): + # type: (nodes.Node) -> None + self.current_document.pop() + + def unknown_visit(self, node): + # type: (nodes.Node) -> None + pass + + def visit_footnote(self, node): + # type: (nodes.footnote) -> None + document = self.current_document[-1] + if node.get('auto'): + self.auto_footnotes.append((document, node)) + else: + for name in node['names']: + self.used_footnote_numbers.add(name) + + def visit_footnote_reference(self, node): + # type: (nodes.footnote_reference) -> None + document = self.current_document[-1] + footnote_refs = self.footnote_refs.setdefault(document, []) + footnote_refs.append(node) + + def unknown_departure(self, node): + # type: (nodes.Node) -> None + pass diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py index b377a6479..9e8662b03 100644 --- a/sphinx/ext/jsmath.py +++ b/sphinx/ext/jsmath.py @@ -26,7 +26,7 @@ if False: def html_visit_math(self, node): # type: (nodes.NodeVisitor, nodes.Node) -> None - self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate')) + self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate nohighlight')) self.body.append(self.encode(node['latex']) + '</span>') raise nodes.SkipNode @@ -34,7 +34,7 @@ def html_visit_math(self, node): def html_visit_displaymath(self, node): # type: (nodes.NodeVisitor, nodes.Node) -> None if node['nowrap']: - self.body.append(self.starttag(node, 'div', CLASS='math notranslate')) + self.body.append(self.starttag(node, 'div', CLASS='math notranslate nohighlight')) self.body.append(self.encode(node['latex'])) self.body.append('</div>') raise nodes.SkipNode @@ -47,7 +47,7 @@ def html_visit_displaymath(self, node): self.body.append('<span class="eqno">(%s)' % number) self.add_permalink_ref(node, _('Permalink to this equation')) self.body.append('</span>') - self.body.append(self.starttag(node, 'div', CLASS='math notranslate')) + self.body.append(self.starttag(node, 'div', CLASS='math notranslate nohighlight')) else: # but only once! self.body.append('<div class="math">') diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py index e3f7afebe..2bb7eec09 100644 --- a/sphinx/ext/mathjax.py +++ b/sphinx/ext/mathjax.py @@ -27,7 +27,7 @@ if False: def html_visit_math(self, node): # type: (nodes.NodeVisitor, nodes.Node) -> None - self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate')) + self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate nohighlight')) self.body.append(self.builder.config.mathjax_inline[0] + self.encode(node['latex']) + self.builder.config.mathjax_inline[1] + '</span>') @@ -36,7 +36,7 @@ def html_visit_math(self, node): def html_visit_displaymath(self, node): # type: (nodes.NodeVisitor, nodes.Node) -> None - self.body.append(self.starttag(node, 'div', CLASS='math notranslate')) + self.body.append(self.starttag(node, 'div', CLASS='math notranslate nohighlight')) if node['nowrap']: self.body.append(self.encode(node['latex'])) self.body.append('</div>') diff --git a/sphinx/themes/basic/static/doctools.js_t b/sphinx/themes/basic/static/doctools.js_t index b261a44f3..1dca47404 100644 --- a/sphinx/themes/basic/static/doctools.js_t +++ b/sphinx/themes/basic/static/doctools.js_t @@ -70,7 +70,9 @@ jQuery.fn.highlightText = function(text, className) { if (node.nodeType === 3) { var val = node.nodeValue; var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { var span; var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); if (isInSVG) { diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 68acc8e9a..f05959aca 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -23,12 +23,12 @@ from six import itervalues, text_type from sphinx import addnodes from sphinx import highlighting +from sphinx.builders.latex.transforms import URI_SCHEMES, ShowUrlsTransform # NOQA # for compatibility from sphinx.errors import SphinxError from sphinx.locale import admonitionlabels, _, __ -from sphinx.transforms import SphinxTransform from sphinx.util import split_into, logging from sphinx.util.i18n import format_date -from sphinx.util.nodes import clean_astext, traverse_parent +from sphinx.util.nodes import clean_astext from sphinx.util.template import LaTeXRenderer from sphinx.util.texescape import tex_escape_map, tex_replace_map @@ -47,7 +47,6 @@ BEGIN_DOC = r''' ''' -URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:') LATEXSECTIONNAMES = ["part", "chapter", "section", "subsection", "subsubsection", "paragraph", "subparagraph"] @@ -227,112 +226,6 @@ class ExtBabel(Babel): return language -class ShowUrlsTransform(SphinxTransform, object): - def __init__(self, document, startnode=None): - # type: (nodes.document, nodes.Node) -> None - super(ShowUrlsTransform, self).__init__(document, startnode) - self.expanded = False - - def apply(self): - # type: () -> None - # replace id_prefix temporarily - id_prefix = self.document.settings.id_prefix - self.document.settings.id_prefix = 'show_urls' - - self.expand_show_urls() - if self.expanded: - self.renumber_footnotes() - - # restore id_prefix - self.document.settings.id_prefix = id_prefix - - def expand_show_urls(self): - # type: () -> None - show_urls = self.document.settings.env.config.latex_show_urls - if show_urls is False or show_urls == 'no': - return - - for node in self.document.traverse(nodes.reference): - uri = node.get('refuri', '') - if uri.startswith(URI_SCHEMES): - if uri.startswith('mailto:'): - uri = uri[7:] - if node.astext() != uri: - index = node.parent.index(node) - if show_urls == 'footnote': - footnote_nodes = self.create_footnote(uri) - for i, fn in enumerate(footnote_nodes): - node.parent.insert(index + i + 1, fn) - - self.expanded = True - else: # all other true values (b/w compat) - textnode = nodes.Text(" (%s)" % uri) - node.parent.insert(index + 1, textnode) - - def create_footnote(self, uri): - # type: (unicode) -> List[Union[nodes.footnote, nodes.footnote_ref]] - label = nodes.label('', '#') - para = nodes.paragraph() - para.append(nodes.reference('', nodes.Text(uri), refuri=uri, nolinkurl=True)) - footnote = nodes.footnote(uri, label, para, auto=1) - footnote['names'].append('#') - self.document.note_autofootnote(footnote) - - label = nodes.Text('#') - footnote_ref = nodes.footnote_reference('[#]_', label, auto=1, - refid=footnote['ids'][0]) - self.document.note_autofootnote_ref(footnote_ref) - footnote.add_backref(footnote_ref['ids'][0]) - - return [footnote, footnote_ref] - - def renumber_footnotes(self): - # type: () -> None - def is_used_number(number): - # type: (unicode) -> bool - for node in self.document.traverse(nodes.footnote): - if not node.get('auto') and number in node['names']: - return True - - return False - - def is_auto_footnote(node): - # type: (nodes.Node) -> bool - return isinstance(node, nodes.footnote) and node.get('auto') - - def footnote_ref_by(node): - # type: (nodes.Node) -> Callable[[nodes.Node], bool] - ids = node['ids'] - parent = list(traverse_parent(node, (nodes.document, addnodes.start_of_file)))[0] - - def is_footnote_ref(node): - # type: (nodes.Node) -> bool - return (isinstance(node, nodes.footnote_reference) and - ids[0] == node['refid'] and - parent in list(traverse_parent(node))) - - return is_footnote_ref - - startnum = 1 - for footnote in self.document.traverse(is_auto_footnote): - while True: - label = str(startnum) - startnum += 1 - if not is_used_number(label): - break - - old_label = footnote[0].astext() - footnote.remove(footnote[0]) - footnote.insert(0, nodes.label('', label)) - if old_label in footnote['names']: - footnote['names'].remove(old_label) - footnote['names'].append(label) - - for footnote_ref in self.document.traverse(footnote_ref_by(footnote)): - footnote_ref.remove(footnote_ref[0]) - footnote_ref += nodes.Text(label) - - class Table(object): """A table data""" @@ -618,7 +511,10 @@ class LaTeXTranslator(nodes.NodeVisitor): if builder.config.language \ and 'fncychap' not in builder.config.latex_elements: # use Sonny style if any language specified - self.elements['fncychap'] = '\\usepackage[Sonny]{fncychap}' + self.elements['fncychap'] = ('\\usepackage[Sonny]{fncychap}\n' + '\\ChNameVar{\\Large\\normalfont' + '\\sffamily}\n\\ChTitleVar{\\Large' + '\\normalfont\\sffamily}') self.babel = ExtBabel(builder.config.language) if builder.config.language and not self.babel.is_supported_language(): diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py index 755a9e955..28ce094a8 100644 --- a/tests/test_ext_math.py +++ b/tests/test_ext_math.py @@ -35,19 +35,19 @@ def test_jsmath(app, status, warning): app.builder.build_all() content = (app.outdir / 'math.html').text() - assert '<div class="math notranslate">\na^2 + b^2 = c^2</div>' in content - assert ('<div class="math notranslate">\n\\begin{split}a + 1 < b\\end{split}</div>' - in content) + assert '<div class="math notranslate nohighlight">\na^2 + b^2 = c^2</div>' in content + assert ('<div class="math notranslate nohighlight">\n\\begin{split}a + 1 < ' + 'b\\end{split}</div>' in content) assert (u'<span class="eqno">(1)<a class="headerlink" href="#equation-foo" ' u'title="Permalink to this equation">\xb6</a></span>' - u'<div class="math notranslate" id="equation-foo">\ne^{i\\pi} = 1</div>' - in content) + u'<div class="math notranslate nohighlight" id="equation-foo">' + '\ne^{i\\pi} = 1</div>' in content) assert (u'<span class="eqno">(2)<a class="headerlink" href="#equation-math-0" ' u'title="Permalink to this equation">\xb6</a></span>' - u'<div class="math notranslate" id="equation-math-0">\n' + u'<div class="math notranslate nohighlight" id="equation-math-0">\n' u'e^{ix} = \\cos x + i\\sin x</div>' in content) - assert '<div class="math notranslate">\nn \\in \\mathbb N</div>' in content - assert '<div class="math notranslate">\na + 1 < b</div>' in content + assert '<div class="math notranslate nohighlight">\nn \\in \\mathbb N</div>' in content + assert '<div class="math notranslate nohighlight">\na + 1 < b</div>' in content @pytest.mark.skipif(not has_binary('dvipng'), @@ -91,7 +91,7 @@ def test_mathjax_align(app, status, warning): app.builder.build_all() content = (app.outdir / 'index.html').text() - html = (r'<div class="math notranslate">\s*' + html = (r'<div class="math notranslate nohighlight">\s*' r'\\\[ \\begin\{align\}\\begin\{aligned\}S \&= \\pi r\^2\\\\' r'V \&= \\frac\{4\}\{3\} \\pi r\^3\\end\{aligned\}\\end\{align\} \\\]</div>') assert re.search(html, content, re.S) @@ -104,7 +104,7 @@ def test_math_number_all_mathjax(app, status, warning): app.builder.build_all() content = (app.outdir / 'index.html').text() - html = (r'<div class="math notranslate" id="equation-index-0">\s*' + html = (r'<div class="math notranslate nohighlight" id="equation-index-0">\s*' r'<span class="eqno">\(1\)<a .*>\xb6</a></span>\\\[a\^2\+b\^2=c\^2\\\]</div>') assert re.search(html, content, re.S) @@ -169,7 +169,7 @@ def test_mathjax_numfig_html(app, status, warning): app.builder.build_all() content = (app.outdir / 'math.html').text() - html = ('<div class="math notranslate" id="equation-math-0">\n' + html = ('<div class="math notranslate nohighlight" id="equation-math-0">\n' '<span class="eqno">(1.2)') assert html in content html = ('<p>Referencing equation <a class="reference internal" ' |