Skip to content

Commit

Permalink
Respect the html_use_smartypants config option
Browse files Browse the repository at this point in the history
Previously, this was ignored because the config option is implemented
by using a different HTMLTranslator in the case that the option is
True (which is the default). This PR switches from subclassing the
non-SmartyPants HTMLTranslator and instead uses calls to
`app.add_node` to override the visit/depart functions.

This removes the need to use `app.set_translator`, making our
Sphinx requirement more flexible. I tested building the demo with
the current version of Sphinx and earlier, and it worked all the
way to Sphinx 1.1, which failed for something unrelated to this change.
`setup.py` was updated accordingly.
  • Loading branch information
tbekolay committed Jan 31, 2017
1 parent 06a0a1d commit 47d080f
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 86 deletions.
189 changes: 104 additions & 85 deletions guzzle_sphinx_theme/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import xml.etree.ElementTree as ET

from docutils import nodes
from sphinx import version_info as sphinx_version
from sphinx.locale import admonitionlabels
from sphinx.writers.html import HTMLTranslator as SphinxHTMLTranslator

from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, \
Expand All @@ -16,7 +16,18 @@ def setup(app):
"""Setup conntects events to the sitemap builder"""
app.connect('html-page-context', add_html_link)
app.connect('build-finished', create_sitemap)
app.set_translator('html', HTMLTranslator)

# Override several HTML translator methods
kwargs = {} if sphinx_version[:2] <= (1, 3) else {"override": True}
app.add_node(nodes.table, html=(visit_table, depart_table), **kwargs)
app.add_node(nodes.field, html=(visit_field, depart_field), **kwargs)
app.add_node(nodes.field_name,
html=(visit_field_name, depart_field_name), **kwargs)
app.add_node(nodes.field_body,
html=(visit_field_body, depart_field_body), **kwargs)
app.add_node(nodes.field_list,
html=(visit_field_list, depart_field_list), **kwargs)

app.sitemap_links = []


Expand Down Expand Up @@ -51,88 +62,96 @@ def html_theme_path():
return [os.path.dirname(os.path.abspath(__file__))]


class HTMLTranslator(SphinxHTMLTranslator):
# The subsequent functions affect how the document is translated to HTML,
# in most cases to work nicely with bootstrap.

def visit_table(self, node, name=''):
"""
Override docutils default table formatter to not include a border
and to use Bootstrap CSS
See: http://sourceforge.net/p/docutils/code/HEAD/tree/trunk/docutils/docutils/writetml4css1/__init__.py#l1550
"""
self.context.append(self.compact_p)
self.compact_p = True
classes = 'table table-bordered ' + self.settings.table_style
classes = classes.strip()
self.body.append(
self.starttag(node, 'table', CLASS=classes))


def depart_table(self, node):
"""
Handle translating to bootstrap structure.
This needs overridin' too
"""
def visit_table(self, node, name=''):
"""
Override docutils default table formatter to not include a border
and to use Bootstrap CSS
See: http://sourceforge.net/p/docutils/code/HEAD/tree/trunk/docutils/docutils/writers/html4css1/__init__.py#l1550
"""
self.context.append(self.compact_p)
self.compact_p = True
classes = 'table table-bordered ' + self.settings.table_style
classes = classes.strip()
self.body.append(
self.starttag(node, 'table', CLASS=classes))

def depart_table(self, node):
"""
This needs overridin' too
"""
self.compact_p = self.context.pop()
self.body.append('</table>\n')

def visit_field(self, node):
pass

def depart_field(self, node):
pass

def visit_field_name(self, node):
atts = {}
if self.in_docinfo:
atts['class'] = 'docinfo-name'
else:
atts['class'] = 'field-name'
self.context.append('')
self.body.append(self.starttag(node, 'dt', '', **atts))

def depart_field_name(self, node):
self.body.append('</dt>')
self.body.append(self.context.pop())

def visit_field_body(self, node):
self.body.append(self.starttag(node, 'dd', '', CLASS='field-body'))
self.set_class_on_child(node, 'first', 0)
field = node.parent
if (self.compact_field_list or
isinstance(field.parent, nodes.docinfo) or
field.parent.index(field) == len(field.parent) - 1):
# If we are in a compact list, the docinfo, or if this is
# the last field of the field list, do not add vertical
# space after last element.
self.set_class_on_child(node, 'last', -1)

def depart_field_body(self, node):
self.body.append('</dd>\n')

def visit_field_list(self, node):
self.context.append((self.compact_field_list, self.compact_p))
self.compact_p = None
if 'compact' in node['classes']:
self.compact_field_list = True
elif (self.settings.compact_field_lists
and 'open' not in node['classes']):
self.compact_field_list = True
if self.compact_field_list:
for field in node:
field_body = field[-1]
assert isinstance(field_body, nodes.field_body)
children = [n for n in field_body
if not isinstance(n, nodes.Invisible)]
if not (len(children) == 0 or
len(children) == 1 and
isinstance(children[0],
(nodes.paragraph, nodes.line_block))):
self.compact_field_list = False
break
self.body.append(self.starttag(node, 'dl', frame='void',
rules='none',
CLASS='docutils field-list'))

def depart_field_list(self, node):
self.body.append('</dl>\n')
self.compact_field_list, self.compact_p = self.context.pop()
self.compact_p = self.context.pop()
self.body.append('</table>\n')


def visit_field(self, node):
pass


def depart_field(self, node):
pass


def visit_field_name(self, node):
atts = {}
if self.in_docinfo:
atts['class'] = 'docinfo-name'
else:
atts['class'] = 'field-name'
self.context.append('')
self.body.append(self.starttag(node, 'dt', '', **atts))


def depart_field_name(self, node):
self.body.append('</dt>')
self.body.append(self.context.pop())


def visit_field_body(self, node):
self.body.append(self.starttag(node, 'dd', '', CLASS='field-body'))
self.set_class_on_child(node, 'first', 0)
field = node.parent
if (self.compact_field_list or
isinstance(field.parent, nodes.docinfo) or
field.parent.index(field) == len(field.parent) - 1):
# If we are in a compact list, the docinfo, or if this is
# the last field of the field list, do not add vertical
# space after last element.
self.set_class_on_child(node, 'last', -1)


def depart_field_body(self, node):
self.body.append('</dd>\n')


def visit_field_list(self, node):
self.context.append((self.compact_field_list, self.compact_p))
self.compact_p = None
if 'compact' in node['classes']:
self.compact_field_list = True
elif (self.settings.compact_field_lists
and 'open' not in node['classes']):
self.compact_field_list = True
if self.compact_field_list:
for field in node:
field_body = field[-1]
assert isinstance(field_body, nodes.field_body)
children = [n for n in field_body
if not isinstance(n, nodes.Invisible)]
if not (len(children) == 0 or
len(children) == 1 and
isinstance(children[0],
(nodes.paragraph, nodes.line_block))):
self.compact_field_list = False
break
self.body.append(self.starttag(node, 'dl', frame='void',
rules='none',
CLASS='docutils field-list'))


def depart_field_list(self, node):
self.body.append('</dl>\n')
self.compact_field_list, self.compact_p = self.context.pop()
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
url='https://github.com/guzzle/guzzle_sphinx_theme',
packages=['guzzle_sphinx_theme'],
include_package_data=True,
install_requires=['Sphinx>1.3'],
install_requires=['Sphinx>=1.2'],
classifiers=(
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
Expand Down

0 comments on commit 47d080f

Please sign in to comment.