mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #3666 from mitya57/1.6-release
Remove the custom smartypants code [rebased]
This commit is contained in:
commit
961432ed96
@ -768,6 +768,12 @@ that use Sphinx's HTMLWriter class.
|
||||
will be used to convert quotes and dashes to typographically correct
|
||||
entities. Default: ``True``.
|
||||
|
||||
.. deprecated:: 1.6
|
||||
Use the `smart_quotes option`_ in the Docutils configuration file
|
||||
(``docutils.conf``) instead.
|
||||
|
||||
.. _`smart_quotes option`: http://docutils.sourceforge.net/docs/user/config.html#smart-quotes
|
||||
|
||||
.. confval:: html_add_permalinks
|
||||
|
||||
Sphinx will add "permalinks" for each heading and description environment as
|
||||
|
@ -25,6 +25,7 @@ except ImportError:
|
||||
Image = None
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.utils import smartquotes
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
@ -32,7 +33,6 @@ from sphinx.util import logging
|
||||
from sphinx.util import status_iterator
|
||||
from sphinx.util.osutil import ensuredir, copyfile
|
||||
from sphinx.util.fileutil import copy_asset_file
|
||||
from sphinx.util.smartypants import sphinx_smarty_pants as ssp
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@ -94,6 +94,18 @@ Guide = namedtuple('Guide', ['type', 'title', 'uri'])
|
||||
NavPoint = namedtuple('NavPoint', ['navpoint', 'playorder', 'text', 'refuri', 'children'])
|
||||
|
||||
|
||||
def sphinx_smarty_pants(t, language='en'):
|
||||
# type: (unicode, str) -> unicode
|
||||
t = t.replace('"', '"')
|
||||
t = smartquotes.educateDashesOldSchool(t)
|
||||
t = smartquotes.educateQuotes(t, language)
|
||||
t = t.replace('"', '"')
|
||||
return t
|
||||
|
||||
|
||||
ssp = sphinx_smarty_pants
|
||||
|
||||
|
||||
# The epub publisher
|
||||
|
||||
class EpubBuilder(StandaloneHTMLBuilder):
|
||||
|
@ -45,8 +45,7 @@ from sphinx.builders import Builder
|
||||
from sphinx.application import ENV_PICKLE_FILENAME
|
||||
from sphinx.highlighting import PygmentsBridge
|
||||
from sphinx.util.console import bold, darkgreen # type: ignore
|
||||
from sphinx.writers.html import HTMLWriter, HTMLTranslator, \
|
||||
SmartyPantsHTMLTranslator
|
||||
from sphinx.writers.html import HTMLWriter, HTMLTranslator
|
||||
from sphinx.environment.adapters.asset import ImageAdapter
|
||||
from sphinx.environment.adapters.toctree import TocTree
|
||||
from sphinx.environment.adapters.indexentries import IndexEntries
|
||||
@ -59,7 +58,7 @@ if False:
|
||||
|
||||
# Experimental HTML5 Writer
|
||||
if is_html5_writer_available():
|
||||
from sphinx.writers.html5 import HTML5Translator, SmartyPantsHTML5Translator
|
||||
from sphinx.writers.html5 import HTML5Translator
|
||||
html5_ready = True
|
||||
else:
|
||||
html5_ready = False
|
||||
@ -220,15 +219,9 @@ class StandaloneHTMLBuilder(Builder):
|
||||
@property
|
||||
def default_translator_class(self):
|
||||
if self.config.html_experimental_html5_writer and html5_ready:
|
||||
if self.config.html_use_smartypants:
|
||||
return SmartyPantsHTML5Translator
|
||||
else:
|
||||
return HTML5Translator
|
||||
return HTML5Translator
|
||||
else:
|
||||
if self.config.html_use_smartypants:
|
||||
return SmartyPantsHTMLTranslator
|
||||
else:
|
||||
return HTMLTranslator
|
||||
return HTMLTranslator
|
||||
|
||||
def get_outdated_docs(self):
|
||||
# type: () -> Iterator[unicode]
|
||||
@ -1319,7 +1312,7 @@ def setup(app):
|
||||
app.add_config_value('html_static_path', [], 'html')
|
||||
app.add_config_value('html_extra_path', [], 'html')
|
||||
app.add_config_value('html_last_updated_fmt', None, 'html', string_classes)
|
||||
app.add_config_value('html_use_smartypants', True, 'html')
|
||||
app.add_config_value('html_use_smartypants', None, 'html')
|
||||
app.add_config_value('html_sidebars', {}, 'html')
|
||||
app.add_config_value('html_additional_pages', {}, 'html')
|
||||
app.add_config_value('html_domain_indices', True, 'html', [list])
|
||||
|
@ -26,6 +26,7 @@ from six.moves import cPickle as pickle
|
||||
from docutils.io import NullOutput
|
||||
from docutils.core import Publisher
|
||||
from docutils.utils import Reporter, get_source_line
|
||||
from docutils.utils.smartquotes import smartchars
|
||||
from docutils.parsers.rst import roles
|
||||
from docutils.parsers.rst.languages import en as english
|
||||
from docutils.frontend import OptionParser
|
||||
@ -671,6 +672,16 @@ class BuildEnvironment(object):
|
||||
self.settings['trim_footnote_reference_space'] = \
|
||||
self.config.trim_footnote_reference_space
|
||||
self.settings['gettext_compact'] = self.config.gettext_compact
|
||||
language = (self.config.language or 'en').replace('_', '-')
|
||||
self.settings['language_code'] = language
|
||||
if self.config.html_use_smartypants is not None:
|
||||
warnings.warn("html_use_smartypants option is deprecated. Use the "
|
||||
"smart_quotes option in docutils.conf instead.",
|
||||
RemovedInSphinx17Warning)
|
||||
if language in smartchars.quotes:
|
||||
self.settings['smart_quotes'] = self.config.html_use_smartypants
|
||||
elif language in smartchars.quotes: # We enable smartypants by default
|
||||
self.settings['smart_quotes'] = True
|
||||
|
||||
docutilsconf = path.join(self.srcdir, 'docutils.conf')
|
||||
# read docutils.conf from source dir, not from current dir
|
||||
|
@ -1388,10 +1388,6 @@
|
||||
\protected\def\sphinxstyleabbreviation {\textsc}
|
||||
\protected\def\sphinxstyleliteralintitle {\sphinxcode}
|
||||
|
||||
% LaTeX writer uses macros to hide double quotes from \sphinxcode's \@noligs
|
||||
\protected\def\sphinxquotedblleft{``}
|
||||
\protected\def\sphinxquotedblright{''}
|
||||
|
||||
% Tell TeX about pathological hyphenation cases:
|
||||
\hyphenation{Base-HTTP-Re-quest-Hand-ler}
|
||||
\endinput
|
||||
|
@ -34,6 +34,7 @@ from sphinx.util import logging
|
||||
from sphinx.util.console import strip_colors, colorize, bold, term_width_line # type: ignore
|
||||
from sphinx.util.fileutil import copy_asset_file
|
||||
from sphinx.util.osutil import fs_encoding
|
||||
from sphinx.util import smartypants # noqa
|
||||
|
||||
# import other utilities; partly for backwards compatibility, so don't
|
||||
# prune unused ones indiscriminately
|
||||
|
@ -1,312 +1,280 @@
|
||||
r"""
|
||||
This is based on SmartyPants.py by `Chad Miller`_ <smartypantspy@chad.org>,
|
||||
version 1.5_1.6.
|
||||
|
||||
Copyright and License
|
||||
=====================
|
||||
|
||||
SmartyPants_ license::
|
||||
|
||||
Copyright (c) 2003 John Gruber
|
||||
(http://daringfireball.net/)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name "SmartyPants" nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as
|
||||
is" and any express or implied warranties, including, but not limited
|
||||
to, the implied warranties of merchantability and fitness for a
|
||||
particular purpose are disclaimed. In no event shall the copyright
|
||||
owner or contributors be liable for any direct, indirect, incidental,
|
||||
special, exemplary, or consequential damages (including, but not
|
||||
limited to, procurement of substitute goods or services; loss of use,
|
||||
data, or profits; or business interruption) however caused and on any
|
||||
theory of liability, whether in contract, strict liability, or tort
|
||||
(including negligence or otherwise) arising in any way out of the use
|
||||
of this software, even if advised of the possibility of such damage.
|
||||
|
||||
|
||||
smartypants.py license::
|
||||
|
||||
smartypants.py is a derivative work of SmartyPants.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as
|
||||
is" and any express or implied warranties, including, but not limited
|
||||
to, the implied warranties of merchantability and fitness for a
|
||||
particular purpose are disclaimed. In no event shall the copyright
|
||||
owner or contributors be liable for any direct, indirect, incidental,
|
||||
special, exemplary, or consequential damages (including, but not
|
||||
limited to, procurement of substitute goods or services; loss of use,
|
||||
data, or profits; or business interruption) however caused and on any
|
||||
theory of liability, whether in contract, strict liability, or tort
|
||||
(including negligence or otherwise) arising in any way out of the use
|
||||
of this software, even if advised of the possibility of such damage.
|
||||
|
||||
.. _Chad Miller: http://web.chad.org/
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.util.smartypants
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This code is copied from docutils’ docutils/utils/smartquotes.py
|
||||
version 1.7.1 (from 2017-03-19). It should be removed in the future.
|
||||
|
||||
:copyright: © 2010 Günter Milde,
|
||||
original `SmartyPants`_: © 2003 John Gruber
|
||||
smartypants.py: © 2004, 2007 Chad Miller
|
||||
:license: Released under the terms of the `2-Clause BSD license`_, in short:
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notices and this notice are preserved.
|
||||
This file is offered as-is, without any warranty.
|
||||
|
||||
.. _SmartyPants: http://daringfireball.net/projects/smartypants/
|
||||
.. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause
|
||||
|
||||
See the LICENSE file and the original docutils code for details.
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import re
|
||||
from docutils.utils import smartquotes
|
||||
from sphinx.util.docutils import __version_info__ as docutils_version
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Tuple # NOQA
|
||||
if False: # For type annotation
|
||||
from typing import Iterable, Iterator, Tuple # NOQA
|
||||
|
||||
|
||||
def sphinx_smarty_pants(t):
|
||||
# type: (unicode) -> unicode
|
||||
t = t.replace('"', '"')
|
||||
t = educate_dashes_oldschool(t)
|
||||
t = educate_quotes(t) # type: ignore
|
||||
t = t.replace('"', '"')
|
||||
return t
|
||||
|
||||
|
||||
# Constants for quote education.
|
||||
punct_class = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]"""
|
||||
end_of_word_class = r"""[\s.,;:!?)]"""
|
||||
close_class = r"""[^\ \t\r\n\[\{\(\-]"""
|
||||
dec_dashes = r"""–|—"""
|
||||
|
||||
# Special case if the very first character is a quote
|
||||
# followed by punctuation at a non-word-break. Close the quotes by brute force:
|
||||
single_quote_start_re = re.compile(r"""^'(?=%s\\B)""" % (punct_class,))
|
||||
double_quote_start_re = re.compile(r"""^"(?=%s\\B)""" % (punct_class,))
|
||||
|
||||
# Special case for double sets of quotes, e.g.:
|
||||
# <p>He said, "'Quoted' words in a larger quote."</p>
|
||||
double_quote_sets_re = re.compile(r""""'(?=\w)""")
|
||||
single_quote_sets_re = re.compile(r"""'"(?=\w)""")
|
||||
|
||||
# Special case for decade abbreviations (the '80s):
|
||||
decade_abbr_re = re.compile(r"""\b'(?=\d{2}s)""")
|
||||
|
||||
# Get most opening double quotes:
|
||||
opening_double_quotes_regex = re.compile(r"""
|
||||
(
|
||||
\s | # a whitespace char, or
|
||||
  | # a non-breaking space entity, or
|
||||
-- | # dashes, or
|
||||
&[mn]dash; | # named dash entities
|
||||
%s | # or decimal entities
|
||||
&\#x201[34]; # or hex
|
||||
)
|
||||
" # the quote
|
||||
(?=\w) # followed by a word character
|
||||
""" % (dec_dashes,), re.VERBOSE)
|
||||
|
||||
# Double closing quotes:
|
||||
closing_double_quotes_regex = re.compile(r"""
|
||||
#(%s)? # character that indicates the quote should be closing
|
||||
"
|
||||
(?=%s)
|
||||
""" % (close_class, end_of_word_class), re.VERBOSE)
|
||||
|
||||
closing_double_quotes_regex_2 = re.compile(r"""
|
||||
(%s) # character that indicates the quote should be closing
|
||||
"
|
||||
""" % (close_class,), re.VERBOSE)
|
||||
|
||||
# Get most opening single quotes:
|
||||
opening_single_quotes_regex = re.compile(r"""
|
||||
(
|
||||
\s | # a whitespace char, or
|
||||
  | # a non-breaking space entity, or
|
||||
-- | # dashes, or
|
||||
&[mn]dash; | # named dash entities
|
||||
%s | # or decimal entities
|
||||
&\#x201[34]; # or hex
|
||||
)
|
||||
' # the quote
|
||||
(?=\w) # followed by a word character
|
||||
""" % (dec_dashes,), re.VERBOSE)
|
||||
|
||||
closing_single_quotes_regex = re.compile(r"""
|
||||
(%s)
|
||||
'
|
||||
(?!\s | s\b | \d)
|
||||
""" % (close_class,), re.VERBOSE)
|
||||
|
||||
closing_single_quotes_regex_2 = re.compile(r"""
|
||||
(%s)
|
||||
'
|
||||
(\s | s\b)
|
||||
""" % (close_class,), re.VERBOSE)
|
||||
|
||||
|
||||
def educate_quotes(s):
|
||||
# type: (str) -> str
|
||||
def educateQuotes(text, language='en'):
|
||||
# type: (unicode, unicode) -> unicode
|
||||
"""
|
||||
Parameter: String.
|
||||
|
||||
Returns: The string, with "educated" curly quote HTML entities.
|
||||
Parameter: - text string (unicode or bytes).
|
||||
- language (`BCP 47` language tag.)
|
||||
Returns: The `text`, with "educated" curly quote characters.
|
||||
|
||||
Example input: "Isn't this fun?"
|
||||
Example output: “Isn’t this fun?”
|
||||
Example output: “Isn’t this fun?“;
|
||||
"""
|
||||
|
||||
smart = smartquotes.smartchars(language)
|
||||
smart.apostrophe = u'’'
|
||||
|
||||
# oldtext = text
|
||||
punct_class = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]"""
|
||||
|
||||
# Special case if the very first character is a quote
|
||||
# followed by punctuation at a non-word-break. Close the quotes
|
||||
# by brute force:
|
||||
s = single_quote_start_re.sub("’", s)
|
||||
s = double_quote_start_re.sub("”", s)
|
||||
# followed by punctuation at a non-word-break.
|
||||
# Close the quotes by brute force:
|
||||
text = re.sub(r"""^'(?=%s\\B)""" % (punct_class,), smart.csquote, text)
|
||||
text = re.sub(r"""^"(?=%s\\B)""" % (punct_class,), smart.cpquote, text)
|
||||
|
||||
# Special case for double sets of quotes, e.g.:
|
||||
# <p>He said, "'Quoted' words in a larger quote."</p>
|
||||
s = double_quote_sets_re.sub("“‘", s)
|
||||
s = single_quote_sets_re.sub("‘“", s)
|
||||
text = re.sub(r""""'(?=\w)""", smart.opquote + smart.osquote, text)
|
||||
text = re.sub(r"""'"(?=\w)""", smart.osquote + smart.opquote, text)
|
||||
|
||||
# Special case for decade abbreviations (the '80s):
|
||||
s = decade_abbr_re.sub("’", s)
|
||||
text = re.sub(r"""\b'(?=\d{2}s)""", smart.csquote, text)
|
||||
|
||||
s = opening_single_quotes_regex.sub(r"\1‘", s)
|
||||
s = closing_single_quotes_regex.sub(r"\1’", s)
|
||||
s = closing_single_quotes_regex_2.sub(r"\1’\2", s)
|
||||
close_class = r"""[^\ \t\r\n\[\{\(\-]"""
|
||||
dec_dashes = r"""–|—"""
|
||||
|
||||
# Get most opening single quotes:
|
||||
opening_single_quotes_regex = re.compile(r"""
|
||||
(
|
||||
\s | # a whitespace char, or
|
||||
| # a non-breaking space entity, or
|
||||
-- | # dashes, or
|
||||
&[mn]dash; | # named dash entities
|
||||
%s | # or decimal entities
|
||||
&\#x201[34]; # or hex
|
||||
)
|
||||
' # the quote
|
||||
(?=\w) # followed by a word character
|
||||
""" % (dec_dashes,), re.VERBOSE | re.UNICODE)
|
||||
text = opening_single_quotes_regex.sub(r'\1' + smart.osquote, text)
|
||||
|
||||
# In many locales, single closing quotes are different from apostrophe:
|
||||
if smart.csquote != smart.apostrophe:
|
||||
apostrophe_regex = re.compile(r"(?<=(\w|\d))'(?=\w)", re.UNICODE)
|
||||
text = apostrophe_regex.sub(smart.apostrophe, text)
|
||||
|
||||
closing_single_quotes_regex = re.compile(r"""
|
||||
(%s)
|
||||
'
|
||||
(?!\s | # whitespace
|
||||
s\b |
|
||||
\d # digits ('80s)
|
||||
)
|
||||
""" % (close_class,), re.VERBOSE | re.UNICODE)
|
||||
text = closing_single_quotes_regex.sub(r'\1' + smart.csquote, text)
|
||||
|
||||
closing_single_quotes_regex = re.compile(r"""
|
||||
(%s)
|
||||
'
|
||||
(\s | s\b)
|
||||
""" % (close_class,), re.VERBOSE | re.UNICODE)
|
||||
text = closing_single_quotes_regex.sub(r'\1%s\2' % smart.csquote, text)
|
||||
|
||||
# Any remaining single quotes should be opening ones:
|
||||
s = s.replace("'", "‘")
|
||||
text = re.sub(r"""'""", smart.osquote, text)
|
||||
|
||||
s = opening_double_quotes_regex.sub(r"\1“", s)
|
||||
s = closing_double_quotes_regex.sub(r"”", s)
|
||||
s = closing_double_quotes_regex_2.sub(r"\1”", s)
|
||||
# Get most opening double quotes:
|
||||
opening_double_quotes_regex = re.compile(r"""
|
||||
(
|
||||
\s | # a whitespace char, or
|
||||
| # a non-breaking space entity, or
|
||||
-- | # dashes, or
|
||||
&[mn]dash; | # named dash entities
|
||||
%s | # or decimal entities
|
||||
&\#x201[34]; # or hex
|
||||
)
|
||||
" # the quote
|
||||
(?=\w) # followed by a word character
|
||||
""" % (dec_dashes,), re.VERBOSE)
|
||||
text = opening_double_quotes_regex.sub(r'\1' + smart.opquote, text)
|
||||
|
||||
# Double closing quotes:
|
||||
closing_double_quotes_regex = re.compile(r"""
|
||||
#(%s)? # character that indicates the quote should be closing
|
||||
"
|
||||
(?=\s)
|
||||
""" % (close_class,), re.VERBOSE)
|
||||
text = closing_double_quotes_regex.sub(smart.cpquote, text)
|
||||
|
||||
closing_double_quotes_regex = re.compile(r"""
|
||||
(%s) # character that indicates the quote should be closing
|
||||
"
|
||||
""" % (close_class,), re.VERBOSE)
|
||||
text = closing_double_quotes_regex.sub(r'\1' + smart.cpquote, text)
|
||||
|
||||
# Any remaining quotes should be opening ones.
|
||||
return s.replace('"', "“")
|
||||
text = re.sub(r'"', smart.opquote, text)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def educate_quotes_latex(s, dquotes=("``", "''")):
|
||||
# type: (str, Tuple[str, str]) -> unicode
|
||||
"""
|
||||
Parameter: String.
|
||||
|
||||
Returns: The string, with double quotes corrected to LaTeX quotes.
|
||||
|
||||
Example input: "Isn't this fun?"
|
||||
Example output: ``Isn't this fun?'';
|
||||
def educate_tokens(text_tokens, attr='1', language='en'):
|
||||
# type: (Iterable[Tuple[str, unicode]], unicode, unicode) -> Iterator
|
||||
"""Return iterator that "educates" the items of `text_tokens`.
|
||||
"""
|
||||
|
||||
# Special case if the very first character is a quote
|
||||
# followed by punctuation at a non-word-break. Close the quotes
|
||||
# by brute force:
|
||||
s = single_quote_start_re.sub("\x04", s)
|
||||
s = double_quote_start_re.sub("\x02", s)
|
||||
# Parse attributes:
|
||||
# 0 : do nothing
|
||||
# 1 : set all
|
||||
# 2 : set all, using old school en- and em- dash shortcuts
|
||||
# 3 : set all, using inverted old school en and em- dash shortcuts
|
||||
#
|
||||
# q : quotes
|
||||
# b : backtick quotes (``double'' only)
|
||||
# B : backtick quotes (``double'' and `single')
|
||||
# d : dashes
|
||||
# D : old school dashes
|
||||
# i : inverted old school dashes
|
||||
# e : ellipses
|
||||
# w : convert " entities to " for Dreamweaver users
|
||||
|
||||
# Special case for double sets of quotes, e.g.:
|
||||
# <p>He said, "'Quoted' words in a larger quote."</p>
|
||||
s = double_quote_sets_re.sub("\x01\x03", s)
|
||||
s = single_quote_sets_re.sub("\x03\x01", s)
|
||||
convert_quot = False # translate " entities into normal quotes?
|
||||
do_dashes = 0
|
||||
do_backticks = 0
|
||||
do_quotes = False
|
||||
do_ellipses = False
|
||||
do_stupefy = False
|
||||
|
||||
# Special case for decade abbreviations (the '80s):
|
||||
s = decade_abbr_re.sub("\x04", s)
|
||||
if attr == "0": # Do nothing.
|
||||
pass
|
||||
elif attr == "1": # Do everything, turn all options on.
|
||||
do_quotes = True
|
||||
do_backticks = 1
|
||||
do_dashes = 1
|
||||
do_ellipses = True
|
||||
elif attr == "2":
|
||||
# Do everything, turn all options on, use old school dash shorthand.
|
||||
do_quotes = True
|
||||
do_backticks = 1
|
||||
do_dashes = 2
|
||||
do_ellipses = True
|
||||
elif attr == "3":
|
||||
# Do everything, use inverted old school dash shorthand.
|
||||
do_quotes = True
|
||||
do_backticks = 1
|
||||
do_dashes = 3
|
||||
do_ellipses = True
|
||||
elif attr == "-1": # Special "stupefy" mode.
|
||||
do_stupefy = True
|
||||
else:
|
||||
if "q" in attr:
|
||||
do_quotes = True
|
||||
if "b" in attr:
|
||||
do_backticks = 1
|
||||
if "B" in attr:
|
||||
do_backticks = 2
|
||||
if "d" in attr:
|
||||
do_dashes = 1
|
||||
if "D" in attr:
|
||||
do_dashes = 2
|
||||
if "i" in attr:
|
||||
do_dashes = 3
|
||||
if "e" in attr:
|
||||
do_ellipses = True
|
||||
if "w" in attr:
|
||||
convert_quot = True
|
||||
|
||||
s = opening_single_quotes_regex.sub("\\1\x03", s)
|
||||
s = closing_single_quotes_regex.sub("\\1\x04", s)
|
||||
s = closing_single_quotes_regex_2.sub("\\1\x04\\2", s)
|
||||
prev_token_last_char = " "
|
||||
# Last character of the previous text token. Used as
|
||||
# context to curl leading quote characters correctly.
|
||||
|
||||
# Any remaining single quotes should be opening ones:
|
||||
s = s.replace("'", "\x03")
|
||||
for (ttype, text) in text_tokens:
|
||||
|
||||
s = opening_double_quotes_regex.sub("\\1\x01", s)
|
||||
s = closing_double_quotes_regex.sub("\x02", s)
|
||||
s = closing_double_quotes_regex_2.sub("\\1\x02", s)
|
||||
# skip HTML and/or XML tags as well as emtpy text tokens
|
||||
# without updating the last character
|
||||
if ttype == 'tag' or not text:
|
||||
yield text
|
||||
continue
|
||||
|
||||
# Any remaining quotes should be opening ones.
|
||||
s = s.replace('"', "\x01")
|
||||
# skip literal text (math, literal, raw, ...)
|
||||
if ttype == 'literal':
|
||||
prev_token_last_char = text[-1:]
|
||||
yield text
|
||||
continue
|
||||
|
||||
# Finally, replace all helpers with quotes.
|
||||
return s.replace("\x01", dquotes[0]).replace("\x02", dquotes[1]).\
|
||||
replace("\x03", "`").replace("\x04", "'")
|
||||
last_char = text[-1:] # Remember last char before processing.
|
||||
|
||||
text = smartquotes.processEscapes(text)
|
||||
|
||||
if convert_quot:
|
||||
text = re.sub('"', '"', text)
|
||||
|
||||
if do_dashes == 1:
|
||||
text = smartquotes.educateDashes(text)
|
||||
elif do_dashes == 2:
|
||||
text = smartquotes.educateDashesOldSchool(text)
|
||||
elif do_dashes == 3:
|
||||
text = smartquotes.educateDashesOldSchoolInverted(text)
|
||||
|
||||
if do_ellipses:
|
||||
text = smartquotes.educateEllipses(text)
|
||||
|
||||
# Note: backticks need to be processed before quotes.
|
||||
if do_backticks:
|
||||
text = smartquotes.educateBackticks(text, language)
|
||||
|
||||
if do_backticks == 2:
|
||||
text = smartquotes.educateSingleBackticks(text, language)
|
||||
|
||||
if do_quotes:
|
||||
# Replace plain quotes to prevent converstion to
|
||||
# 2-character sequence in French.
|
||||
context = prev_token_last_char.replace('"', ';').replace("'", ';')
|
||||
text = educateQuotes(context + text, language)[1:]
|
||||
|
||||
if do_stupefy:
|
||||
text = smartquotes.stupefyEntities(text, language)
|
||||
|
||||
# Remember last char as context for the next token
|
||||
prev_token_last_char = last_char
|
||||
|
||||
text = smartquotes.processEscapes(text, restore=True)
|
||||
|
||||
yield text
|
||||
|
||||
|
||||
def educate_backticks(s):
|
||||
# type: (unicode) -> unicode
|
||||
"""
|
||||
Parameter: String.
|
||||
Returns: The string, with ``backticks'' -style double quotes
|
||||
translated into HTML curly quote entities.
|
||||
Example input: ``Isn't this fun?''
|
||||
Example output: “Isn't this fun?”
|
||||
"""
|
||||
return s.replace("``", "“").replace("''", "”")
|
||||
if docutils_version < (0, 13, 2):
|
||||
# Monkey patch the old docutils versions to fix the issue mentioned
|
||||
# at https://sourceforge.net/p/docutils/bugs/313/
|
||||
smartquotes.educateQuotes = educateQuotes
|
||||
|
||||
# And the one mentioned at https://sourceforge.net/p/docutils/bugs/317/
|
||||
smartquotes.educate_tokens = educate_tokens
|
||||
|
||||
def educate_single_backticks(s):
|
||||
# type: (unicode) -> unicode
|
||||
"""
|
||||
Parameter: String.
|
||||
Returns: The string, with `backticks' -style single quotes
|
||||
translated into HTML curly quote entities.
|
||||
|
||||
Example input: `Isn't this fun?'
|
||||
Example output: ‘Isn’t this fun?’
|
||||
"""
|
||||
return s.replace('`', "‘").replace("'", "’")
|
||||
|
||||
|
||||
def educate_dashes_oldschool(s):
|
||||
# type: (unicode) -> unicode
|
||||
"""
|
||||
Parameter: String.
|
||||
|
||||
Returns: The string, with each instance of "--" translated to
|
||||
an en-dash HTML entity, and each "---" translated to
|
||||
an em-dash HTML entity.
|
||||
"""
|
||||
return s.replace('---', "—").replace('--', "–")
|
||||
|
||||
|
||||
def educate_dashes_oldschool_inverted(s):
|
||||
# type: (unicode) -> unicode
|
||||
"""
|
||||
Parameter: String.
|
||||
|
||||
Returns: The string, with each instance of "--" translated to
|
||||
an em-dash HTML entity, and each "---" translated to
|
||||
an en-dash HTML entity. Two reasons why: First, unlike the
|
||||
en- and em-dash syntax supported by
|
||||
educate_dashes_oldschool(), it's compatible with existing
|
||||
entries written before SmartyPants 1.1, back when "--" was
|
||||
only used for em-dashes. Second, em-dashes are more
|
||||
common than en-dashes, and so it sort of makes sense that
|
||||
the shortcut should be shorter to type. (Thanks to Aaron
|
||||
Swartz for the idea.)
|
||||
"""
|
||||
return s.replace('---', "–").replace('--', "—")
|
||||
|
||||
|
||||
def educate_ellipses(s):
|
||||
# type: (unicode) -> unicode
|
||||
"""
|
||||
Parameter: String.
|
||||
Returns: The string, with each instance of "..." translated to
|
||||
an ellipsis HTML entity.
|
||||
|
||||
Example input: Huh...?
|
||||
Example output: Huh…?
|
||||
"""
|
||||
return s.replace('...', "…").replace('. . .', "…")
|
||||
# Fix the issue with French quotes mentioned at
|
||||
# https://sourceforge.net/p/docutils/mailman/message/35760696/
|
||||
quotes = smartquotes.smartchars.quotes
|
||||
quotes['fr'] = (u'«\u00a0', u'\u00a0»', u'“', u'”')
|
||||
quotes['fr-ch'] = u'«»‹›'
|
||||
|
@ -22,7 +22,6 @@ from sphinx import addnodes
|
||||
from sphinx.locale import admonitionlabels, _
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.images import get_image_size
|
||||
from sphinx.util.smartypants import sphinx_smarty_pants
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@ -74,7 +73,6 @@ class HTMLTranslator(BaseTranslator):
|
||||
# type: (StandaloneHTMLBuilder, Any, Any) -> None
|
||||
BaseTranslator.__init__(self, *args, **kwds)
|
||||
self.highlighter = builder.highlighter
|
||||
self.no_smarty = 0
|
||||
self.builder = builder
|
||||
self.highlightlang = self.highlightlang_base = \
|
||||
builder.config.highlight_language
|
||||
@ -685,10 +683,6 @@ class HTMLTranslator(BaseTranslator):
|
||||
BaseTranslator.visit_option_group(self, node)
|
||||
self.context[-2] = self.context[-2].replace(' ', ' ')
|
||||
|
||||
def bulk_text_processor(self, text):
|
||||
# type: (unicode) -> unicode
|
||||
return text
|
||||
|
||||
# overwritten
|
||||
def visit_Text(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
@ -710,8 +704,6 @@ class HTMLTranslator(BaseTranslator):
|
||||
else:
|
||||
if self.in_mailto and self.settings.cloak_email_addresses:
|
||||
encoded = self.cloak_email(encoded)
|
||||
else:
|
||||
encoded = self.bulk_text_processor(encoded)
|
||||
self.body.append(encoded)
|
||||
|
||||
def visit_note(self, node):
|
||||
@ -786,7 +778,6 @@ class HTMLTranslator(BaseTranslator):
|
||||
# type: (nodes.Node) -> None
|
||||
self.depart_admonition(node)
|
||||
|
||||
# these are only handled specially in the SmartyPantsHTMLTranslator
|
||||
def visit_literal_emphasis(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
return self.visit_emphasis(node)
|
||||
@ -875,94 +866,3 @@ class HTMLTranslator(BaseTranslator):
|
||||
def unknown_visit(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
|
||||
|
||||
|
||||
class SmartyPantsHTMLTranslator(HTMLTranslator):
|
||||
"""
|
||||
Handle ordinary text via smartypants, converting quotes and dashes
|
||||
to the correct entities.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
# type: (Any, Any) -> None
|
||||
self.no_smarty = 0
|
||||
HTMLTranslator.__init__(self, *args, **kwds)
|
||||
|
||||
def visit_literal(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
try:
|
||||
# this raises SkipNode
|
||||
HTMLTranslator.visit_literal(self, node)
|
||||
finally:
|
||||
self.no_smarty -= 1
|
||||
|
||||
def visit_literal_block(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
try:
|
||||
HTMLTranslator.visit_literal_block(self, node)
|
||||
except nodes.SkipNode:
|
||||
# HTMLTranslator raises SkipNode for simple literal blocks,
|
||||
# but not for parsed literal blocks
|
||||
self.no_smarty -= 1
|
||||
raise
|
||||
|
||||
def depart_literal_block(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
HTMLTranslator.depart_literal_block(self, node)
|
||||
self.no_smarty -= 1
|
||||
|
||||
def visit_literal_emphasis(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
self.visit_emphasis(node)
|
||||
|
||||
def depart_literal_emphasis(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.depart_emphasis(node)
|
||||
self.no_smarty -= 1
|
||||
|
||||
def visit_literal_strong(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
self.visit_strong(node)
|
||||
|
||||
def depart_literal_strong(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.depart_strong(node)
|
||||
self.no_smarty -= 1
|
||||
|
||||
def visit_desc_signature(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
HTMLTranslator.visit_desc_signature(self, node)
|
||||
|
||||
def depart_desc_signature(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty -= 1
|
||||
HTMLTranslator.depart_desc_signature(self, node)
|
||||
|
||||
def visit_productionlist(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
try:
|
||||
HTMLTranslator.visit_productionlist(self, node)
|
||||
finally:
|
||||
self.no_smarty -= 1
|
||||
|
||||
def visit_option(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
HTMLTranslator.visit_option(self, node)
|
||||
|
||||
def depart_option(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty -= 1
|
||||
HTMLTranslator.depart_option(self, node)
|
||||
|
||||
def bulk_text_processor(self, text):
|
||||
# type: (unicode) -> unicode
|
||||
if self.no_smarty <= 0:
|
||||
return sphinx_smarty_pants(text)
|
||||
return text
|
||||
|
@ -21,7 +21,6 @@ from sphinx import addnodes
|
||||
from sphinx.locale import admonitionlabels, _
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.images import get_image_size
|
||||
from sphinx.util.smartypants import sphinx_smarty_pants
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@ -44,7 +43,6 @@ class HTML5Translator(BaseTranslator):
|
||||
# type: (StandaloneHTMLBuilder, Any, Any) -> None
|
||||
BaseTranslator.__init__(self, *args, **kwds)
|
||||
self.highlighter = builder.highlighter
|
||||
self.no_smarty = 0
|
||||
self.builder = builder
|
||||
self.highlightlang = self.highlightlang_base = \
|
||||
builder.config.highlight_language
|
||||
@ -628,10 +626,6 @@ class HTML5Translator(BaseTranslator):
|
||||
# type: (nodes.Node) -> None
|
||||
self.body.append('</td>')
|
||||
|
||||
def bulk_text_processor(self, text):
|
||||
# type: (unicode) -> unicode
|
||||
return text
|
||||
|
||||
# overwritten
|
||||
def visit_Text(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
@ -653,8 +647,6 @@ class HTML5Translator(BaseTranslator):
|
||||
else:
|
||||
if self.in_mailto and self.settings.cloak_email_addresses:
|
||||
encoded = self.cloak_email(encoded)
|
||||
else:
|
||||
encoded = self.bulk_text_processor(encoded)
|
||||
self.body.append(encoded)
|
||||
|
||||
def visit_note(self, node):
|
||||
@ -729,7 +721,6 @@ class HTML5Translator(BaseTranslator):
|
||||
# type: (nodes.Node) -> None
|
||||
self.depart_admonition(node)
|
||||
|
||||
# these are only handled specially in the SmartyPantsHTML5Translator
|
||||
def visit_literal_emphasis(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
return self.visit_emphasis(node)
|
||||
@ -830,94 +821,3 @@ class HTML5Translator(BaseTranslator):
|
||||
def unknown_visit(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
|
||||
|
||||
|
||||
class SmartyPantsHTML5Translator(HTML5Translator):
|
||||
"""
|
||||
Handle ordinary text via smartypants, converting quotes and dashes
|
||||
to the correct entities.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
# type: (Any, Any) -> None
|
||||
self.no_smarty = 0
|
||||
HTML5Translator.__init__(self, *args, **kwds)
|
||||
|
||||
def visit_literal(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
try:
|
||||
# this raises SkipNode
|
||||
HTML5Translator.visit_literal(self, node)
|
||||
finally:
|
||||
self.no_smarty -= 1
|
||||
|
||||
def visit_literal_block(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
try:
|
||||
HTML5Translator.visit_literal_block(self, node)
|
||||
except nodes.SkipNode:
|
||||
# HTML5Translator raises SkipNode for simple literal blocks,
|
||||
# but not for parsed literal blocks
|
||||
self.no_smarty -= 1
|
||||
raise
|
||||
|
||||
def depart_literal_block(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
HTML5Translator.depart_literal_block(self, node)
|
||||
self.no_smarty -= 1
|
||||
|
||||
def visit_literal_emphasis(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
self.visit_emphasis(node)
|
||||
|
||||
def depart_literal_emphasis(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.depart_emphasis(node)
|
||||
self.no_smarty -= 1
|
||||
|
||||
def visit_literal_strong(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
self.visit_strong(node)
|
||||
|
||||
def depart_literal_strong(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.depart_strong(node)
|
||||
self.no_smarty -= 1
|
||||
|
||||
def visit_desc_signature(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
HTML5Translator.visit_desc_signature(self, node)
|
||||
|
||||
def depart_desc_signature(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty -= 1
|
||||
HTML5Translator.depart_desc_signature(self, node)
|
||||
|
||||
def visit_productionlist(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
try:
|
||||
HTML5Translator.visit_productionlist(self, node)
|
||||
finally:
|
||||
self.no_smarty -= 1
|
||||
|
||||
def visit_option(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty += 1
|
||||
HTML5Translator.visit_option(self, node)
|
||||
|
||||
def depart_option(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.no_smarty -= 1
|
||||
HTML5Translator.depart_option(self, node)
|
||||
|
||||
def bulk_text_processor(self, text):
|
||||
# type: (unicode) -> unicode
|
||||
if self.no_smarty <= 0:
|
||||
return sphinx_smarty_pants(text)
|
||||
return text
|
||||
|
@ -30,7 +30,6 @@ from sphinx.util.i18n import format_date
|
||||
from sphinx.util.nodes import clean_astext, traverse_parent
|
||||
from sphinx.util.template import LaTeXRenderer
|
||||
from sphinx.util.texescape import tex_escape_map, tex_replace_map
|
||||
from sphinx.util.smartypants import educate_quotes_latex
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@ -2533,10 +2532,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
def visit_Text(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
text = self.encode(node.astext())
|
||||
if not self.no_contractions and not self.in_parsed_literal:
|
||||
text = educate_quotes_latex(text, # type: ignore
|
||||
dquotes=("\\sphinxquotedblleft{}",
|
||||
"\\sphinxquotedblright{}"))
|
||||
self.body.append(text)
|
||||
|
||||
def depart_Text(self, node):
|
||||
|
@ -426,6 +426,26 @@ More domains:
|
||||
.. default-role::
|
||||
|
||||
|
||||
Smart quotes
|
||||
------------
|
||||
|
||||
* Smart "quotes" in English 'text'.
|
||||
* Smart --- long and -- short dashes.
|
||||
* Ellipsis...
|
||||
* No smartypants in literal blocks: ``foo--"bar"...``.
|
||||
|
||||
.. only:: html
|
||||
|
||||
.. LaTeX does not like Cyrillic letters in this test, so it is HTML only.
|
||||
|
||||
.. rst-class:: language-ru
|
||||
|
||||
Этот "абзац" должен использовать 'русские' кавычки.
|
||||
|
||||
.. rst-class:: language-fr
|
||||
|
||||
Il dit : "C'est 'super' !"
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#] Like footnotes.
|
||||
|
@ -28,15 +28,6 @@ def test_html_translator(app, status, warning):
|
||||
# no set_translator()
|
||||
translator_class = app.builder.get_translator_class()
|
||||
assert translator_class
|
||||
assert translator_class.__name__ == 'SmartyPantsHTMLTranslator'
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', confoverrides={
|
||||
'html_use_smartypants': False})
|
||||
def test_html_with_smartypants(app, status, warning):
|
||||
# no set_translator(), html_use_smartypants=False
|
||||
translator_class = app.builder.get_translator_class()
|
||||
assert translator_class
|
||||
assert translator_class.__name__ == 'HTMLTranslator'
|
||||
|
||||
|
||||
|
@ -162,7 +162,7 @@ def test_nested_toc(app):
|
||||
app.build()
|
||||
|
||||
# toc.ncx
|
||||
toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').text())
|
||||
toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').bytes())
|
||||
assert toc.find("./ncx:docTitle/ncx:text").text == 'Python documentation'
|
||||
|
||||
# toc.ncx / navPoint
|
||||
@ -175,7 +175,7 @@ def test_nested_toc(app):
|
||||
navpoints = toc.findall("./ncx:navMap/ncx:navPoint")
|
||||
assert len(navpoints) == 4
|
||||
assert navinfo(navpoints[0]) == ('navPoint1', '1', 'index.xhtml',
|
||||
"Welcome to Sphinx Tests's documentation!")
|
||||
u"Welcome to Sphinx Tests’s documentation!")
|
||||
assert navpoints[0].findall("./ncx:navPoint") == []
|
||||
|
||||
# toc.ncx / nested navPoints
|
||||
@ -192,11 +192,11 @@ def test_nested_toc(app):
|
||||
anchor = elem.find("./xhtml:a")
|
||||
return (anchor.get('href'), anchor.text)
|
||||
|
||||
nav = EPUBElementTree.fromstring((app.outdir / 'nav.xhtml').text())
|
||||
nav = EPUBElementTree.fromstring((app.outdir / 'nav.xhtml').bytes())
|
||||
toc = nav.findall("./xhtml:body/xhtml:nav/xhtml:ol/xhtml:li")
|
||||
assert len(toc) == 4
|
||||
assert navinfo(toc[0]) == ('index.xhtml',
|
||||
"Welcome to Sphinx Tests's documentation!")
|
||||
u"Welcome to Sphinx Tests’s documentation!")
|
||||
assert toc[0].findall("./xhtml:ol") == []
|
||||
|
||||
# nav.xhtml / nested toc
|
||||
|
@ -297,6 +297,13 @@ def test_static_output(app):
|
||||
# tests for ``any`` role
|
||||
(".//a[@href='#with']/span", 'headings'),
|
||||
(".//a[@href='objects.html#func_without_body']/code/span", 'objects'),
|
||||
# tests for smartypants
|
||||
(".//li", u'Smart “quotes” in English ‘text’.'),
|
||||
(".//li", u'Smart — long and – short dashes.'),
|
||||
(".//li", u'Ellipsis…'),
|
||||
(".//li//code//span[@class='pre']", 'foo--"bar"...'),
|
||||
(".//p", u'Этот «абзац» должен использовать „русские“ кавычки.'),
|
||||
(".//p", u'Il dit : « C’est “super” ! »'),
|
||||
],
|
||||
'objects.html': [
|
||||
(".//dt[@id='mod.Cls.meth1']", ''),
|
||||
|
@ -36,7 +36,7 @@ def test_process_doc(app):
|
||||
list_item)])
|
||||
|
||||
assert_node(toctree[0][0],
|
||||
[compact_paragraph, reference, "Welcome to Sphinx Tests's documentation!"])
|
||||
[compact_paragraph, reference, u"Welcome to Sphinx Tests’s documentation!"])
|
||||
assert_node(toctree[0][0][0], reference, anchorname='')
|
||||
assert_node(toctree[0][1][0], addnodes.toctree,
|
||||
caption="Table of Contents", glob=False, hidden=False,
|
||||
@ -150,7 +150,7 @@ def test_get_toc_for(app):
|
||||
addnodes.toctree)])],
|
||||
[list_item, compact_paragraph])]) # [2][0]
|
||||
assert_node(toctree[0][0],
|
||||
[compact_paragraph, reference, "Welcome to Sphinx Tests's documentation!"])
|
||||
[compact_paragraph, reference, u"Welcome to Sphinx Tests’s documentation!"])
|
||||
assert_node(toctree[0][1][2],
|
||||
([compact_paragraph, reference, "subsection"],
|
||||
[bullet_list, list_item, compact_paragraph, reference, "subsubsection"]))
|
||||
@ -177,7 +177,7 @@ def test_get_toc_for_only(app):
|
||||
addnodes.toctree)])],
|
||||
[list_item, compact_paragraph])]) # [2][0]
|
||||
assert_node(toctree[0][0],
|
||||
[compact_paragraph, reference, "Welcome to Sphinx Tests's documentation!"])
|
||||
[compact_paragraph, reference, u"Welcome to Sphinx Tests’s documentation!"])
|
||||
assert_node(toctree[0][1][1],
|
||||
([compact_paragraph, reference, "Section for HTML"],
|
||||
[bullet_list, addnodes.toctree]))
|
||||
|
@ -14,11 +14,12 @@ import pickle
|
||||
|
||||
from docutils import frontend, utils, nodes
|
||||
from docutils.parsers.rst import Parser as RstParser
|
||||
from docutils.transforms.universal import SmartQuotes
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.util import texescape
|
||||
from sphinx.util.docutils import sphinx_domains
|
||||
from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator
|
||||
from sphinx.writers.html import HTMLWriter, HTMLTranslator
|
||||
from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
|
||||
import pytest
|
||||
|
||||
@ -31,6 +32,7 @@ def settings(app):
|
||||
optparser = frontend.OptionParser(
|
||||
components=(RstParser, HTMLWriter, LaTeXWriter))
|
||||
settings = optparser.get_default_values()
|
||||
settings.smart_quotes = True
|
||||
settings.env = app.builder.env
|
||||
settings.env.temp_data['docname'] = 'dummy'
|
||||
domain_context = sphinx_domains(settings.env)
|
||||
@ -46,6 +48,7 @@ def parse(settings):
|
||||
document['file'] = 'dummy'
|
||||
parser = RstParser()
|
||||
parser.parse(rst, document)
|
||||
SmartQuotes(document, startnode=None).apply()
|
||||
for msg in document.traverse(nodes.system_message):
|
||||
if msg['level'] == 1:
|
||||
msg.replace_self([])
|
||||
@ -62,7 +65,7 @@ class ForgivingTranslator:
|
||||
pass
|
||||
|
||||
|
||||
class ForgivingHTMLTranslator(SmartyPantsHTMLTranslator, ForgivingTranslator):
|
||||
class ForgivingHTMLTranslator(HTMLTranslator, ForgivingTranslator):
|
||||
pass
|
||||
|
||||
|
||||
@ -178,8 +181,8 @@ def get_verifier(verify, verify_re):
|
||||
# verify smarty-pants quotes
|
||||
'verify',
|
||||
'"John"',
|
||||
'<p>“John”</p>',
|
||||
r'\sphinxquotedblleft{}John\sphinxquotedblright{}',
|
||||
u'<p>“John”</p>',
|
||||
u"“John”",
|
||||
),
|
||||
(
|
||||
# ... but not in literal text
|
||||
|
@ -34,14 +34,14 @@ def test_docinfo(app, status, warning):
|
||||
'field name': u'This is a generic bibliographic field.',
|
||||
'field name 2': (u'Generic bibliographic fields may contain multiple '
|
||||
u'body elements.\n\nLike this.'),
|
||||
'status': u'This is a "work in progress"',
|
||||
'status': u'This is a “work in progress”',
|
||||
'version': u'1',
|
||||
'copyright': (u'This document has been placed in the public domain. '
|
||||
u'You\nmay do with it as you wish. You may copy, modify,'
|
||||
u'\nredistribute, reattribute, sell, buy, rent, lease,\n'
|
||||
u'destroy, or improve it, quote it at length, excerpt,\n'
|
||||
u'incorporate, collate, fold, staple, or mutilate it, or '
|
||||
u'do\nanything else to it that your or anyone else\'s '
|
||||
u'do\nanything else to it that your or anyone else’s '
|
||||
u'heart\ndesires.'),
|
||||
'contact': u'goodger@python.org',
|
||||
'date': u'2006-05-21',
|
||||
|
Loading…
Reference in New Issue
Block a user