summaryrefslogtreecommitdiff
path: root/sphinx/htmlwriter.py
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2007-12-03 22:38:25 +0000
committerGeorg Brandl <georg@python.org>2007-12-03 22:38:25 +0000
commitcfa80454905adfae6f9fb9e52e3930f2c4f3cc43 (patch)
tree5bff8b89ed593e4e4b983a9aff1787df3f59f009 /sphinx/htmlwriter.py
parent4349b88f75ac7e19661f7e08999d43f0defaf607 (diff)
downloadsphinx-git-cfa80454905adfae6f9fb9e52e3930f2c4f3cc43.tar.gz
Apply Tim Golden's patch from #1520, which resolves confusion between
file paths and relative URIs so that building on Windows is flawlessly possible.
Diffstat (limited to 'sphinx/htmlwriter.py')
-rw-r--r--sphinx/htmlwriter.py292
1 files changed, 292 insertions, 0 deletions
diff --git a/sphinx/htmlwriter.py b/sphinx/htmlwriter.py
new file mode 100644
index 000000000..73ee4e94c
--- /dev/null
+++ b/sphinx/htmlwriter.py
@@ -0,0 +1,292 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.htmlwriter
+ ~~~~~~~~~~~~~~~~~
+
+ docutils writers handling Sphinx' custom nodes.
+
+ :copyright: 2007 by Georg Brandl.
+ :license: Python license.
+"""
+
+from docutils import nodes
+from docutils.writers.html4css1 import Writer, HTMLTranslator as BaseTranslator
+
+from .util.smartypants import sphinx_smarty_pants
+
+
+class HTMLWriter(Writer):
+ def __init__(self, config, buildername):
+ Writer.__init__(self)
+ self.translator_class = translator_class(config, buildername)
+
+
+version_text = {
+ 'deprecated': 'Deprecated in version %s',
+ 'versionchanged': 'Changed in version %s',
+ 'versionadded': 'New in version %s',
+}
+
+def translator_class(config, buildername):
+ class HTMLTranslator(BaseTranslator):
+ """
+ Our custom HTML translator.
+ """
+
+ def __init__(self, *args, **kwds):
+ self.no_smarty = 0
+ BaseTranslator.__init__(self, *args, **kwds)
+ self.highlightlang = 'python'
+ self.language.labels['warning'] = 'Caveat'
+
+ def visit_desc(self, node):
+ self.body.append(self.starttag(node, 'dl', CLASS=node['desctype']))
+ def depart_desc(self, node):
+ self.body.append('</dl>\n\n')
+
+ def visit_desc_signature(self, node):
+ # the id is set automatically
+ self.body.append(self.starttag(node, 'dt'))
+ # anchor for per-desc interactive data
+ if node.parent['desctype'] != 'describe' and node['ids'] and node['first']:
+ self.body.append('<!--#%s#-->' % node['ids'][0])
+ if node.parent['desctype'] in ('class', 'exception'):
+ self.body.append('%s ' % node.parent['desctype'])
+ def depart_desc_signature(self, node):
+ if node['ids'] and buildername != 'htmlhelp':
+ self.body.append(u'<a class="headerlink" href="#%s" ' % node['ids'][0] +
+ u'title="Permalink to this definition">\u00B6</a>')
+ self.body.append('</dt>\n')
+
+ def visit_desc_classname(self, node):
+ self.body.append(self.starttag(node, 'tt', '', CLASS='descclassname'))
+ def depart_desc_classname(self, node):
+ self.body.append('</tt>')
+
+ def visit_desc_name(self, node):
+ self.body.append(self.starttag(node, 'tt', '', CLASS='descname'))
+ def depart_desc_name(self, node):
+ self.body.append('</tt>')
+
+ def visit_desc_parameterlist(self, node):
+ self.body.append('<big>(</big>')
+ self.first_param = 1
+ def depart_desc_parameterlist(self, node):
+ self.body.append('<big>)</big>')
+
+ def visit_desc_parameter(self, node):
+ if not self.first_param:
+ self.body.append(', ')
+ else:
+ self.first_param = 0
+ if not node.hasattr('noemph'):
+ self.body.append('<em>')
+ def depart_desc_parameter(self, node):
+ if not node.hasattr('noemph'):
+ self.body.append('</em>')
+
+ def visit_desc_optional(self, node):
+ self.body.append('<span class="optional">[</span>')
+ def depart_desc_optional(self, node):
+ self.body.append('<span class="optional">]</span>')
+
+ def visit_desc_content(self, node):
+ self.body.append(self.starttag(node, 'dd', ''))
+ def depart_desc_content(self, node):
+ self.body.append('</dd>')
+
+ def visit_refcount(self, node):
+ self.body.append(self.starttag(node, 'em', '', CLASS='refcount'))
+ def depart_refcount(self, node):
+ self.body.append('</em>')
+
+ def visit_versionmodified(self, node):
+ self.body.append(self.starttag(node, 'p'))
+ text = version_text[node['type']] % node['version']
+ if len(node):
+ text += ': '
+ else:
+ text += '.'
+ self.body.append('<span class="versionmodified">%s</span>' % text)
+ def depart_versionmodified(self, node):
+ self.body.append('</p>\n')
+
+ # overwritten
+ def visit_reference(self, node):
+ BaseTranslator.visit_reference(self, node)
+ if node.hasattr('reftitle'):
+ # ugly hack to add a title attribute
+ starttag = self.body[-1]
+ if not starttag.startswith('<a '):
+ return
+ self.body[-1] = '<a title="%s"' % self.attval(node['reftitle']) + \
+ starttag[2:]
+
+ # overwritten -- we don't want source comments to show up in the HTML
+ def visit_comment(self, node):
+ raise nodes.SkipNode
+
+ # overwritten
+ def visit_admonition(self, node, name=''):
+ self.body.append(self.start_tag_with_title(
+ node, 'div', CLASS=('admonition ' + name)))
+ if name and name != 'seealso':
+ node.insert(0, nodes.title(name, self.language.labels[name]))
+ self.set_first_last(node)
+
+ def visit_seealso(self, node):
+ self.visit_admonition(node, 'seealso')
+ def depart_seealso(self, node):
+ self.depart_admonition(node)
+
+ # overwritten
+ def visit_title(self, node, move_ids=1):
+ # if we have a section we do our own processing in order
+ # to have ids in the hN-tags and not in additional a-tags
+ if isinstance(node.parent, nodes.section):
+ h_level = self.section_level + self.initial_header_level - 1
+ if node.parent.get('ids'):
+ attrs = {'ids': node.parent['ids']}
+ else:
+ attrs = {}
+ self.body.append(self.starttag(node, 'h%d' % h_level, '', **attrs))
+ self.context.append('</h%d>\n' % h_level)
+ else:
+ BaseTranslator.visit_title(self, node, move_ids)
+
+ # overwritten
+ def visit_literal_block(self, node):
+ from .highlighting import highlight_block
+ self.body.append(highlight_block(node.rawsource, self.highlightlang))
+ raise nodes.SkipNode
+
+ # overwritten
+ def visit_literal(self, node):
+ if len(node.children) == 1 and \
+ node.children[0] in ('None', 'True', 'False'):
+ node['classes'].append('xref')
+ BaseTranslator.visit_literal(self, node)
+
+ def visit_productionlist(self, node):
+ self.body.append(self.starttag(node, 'pre'))
+ names = []
+ for production in node:
+ names.append(production['tokenname'])
+ maxlen = max(len(name) for name in names)
+ for production in node:
+ if production['tokenname']:
+ self.body.append(self.starttag(production, 'strong', ''))
+ self.body.append(production['tokenname'].ljust(maxlen) +
+ '</strong> ::= ')
+ lastname = production['tokenname']
+ else:
+ self.body.append('%s ' % (' '*len(lastname)))
+ production.walkabout(self)
+ self.body.append('\n')
+ self.body.append('</pre>\n')
+ raise nodes.SkipNode
+ def depart_productionlist(self, node):
+ pass
+
+ def visit_production(self, node):
+ pass
+ def depart_production(self, node):
+ pass
+
+ def visit_centered(self, node):
+ self.body.append(self.starttag(node, 'p', CLASS="centered") + '<strong>')
+ def depart_centered(self, node):
+ self.body.append('</strong></p>')
+
+ def visit_compact_paragraph(self, node):
+ pass
+ def depart_compact_paragraph(self, node):
+ pass
+
+ def visit_highlightlang(self, node):
+ self.highlightlang = node['lang']
+ def depart_highlightlang(self, node):
+ pass
+
+ def visit_toctree(self, node):
+ # this only happens when formatting a toc from env.tocs -- in this
+ # case we don't want to include the subtree
+ raise nodes.SkipNode
+
+ def visit_index(self, node):
+ raise nodes.SkipNode
+
+ def visit_glossary(self, node):
+ pass
+ def depart_glossary(self, node):
+ pass
+
+ # these are only handled specially in the SmartyPantsHTMLTranslator
+ def visit_literal_emphasis(self, node):
+ return self.visit_emphasis(node)
+ def depart_literal_emphasis(self, node):
+ return self.depart_emphasis(node)
+
+ def depart_title(self, node):
+ close_tag = self.context[-1]
+ if buildername != 'htmlhelp' and \
+ close_tag.startswith(('</h', '</a></h')) and \
+ node.parent.hasattr('ids') and node.parent['ids']:
+ aname = node.parent['ids'][0]
+ # add permalink anchor
+ self.body.append(u'<a class="headerlink" href="#%s" ' % aname +
+ u'title="Permalink to this headline">\u00B6</a>')
+ BaseTranslator.depart_title(self, node)
+
+
+ class SmartyPantsHTMLTranslator(HTMLTranslator):
+ """
+ Handle ordinary text via smartypants, converting quotes and dashes
+ to the correct entities.
+ """
+
+ def __init__(self, *args, **kwds):
+ self.no_smarty = 0
+ HTMLTranslator.__init__(self, *args, **kwds)
+
+ def visit_literal(self, node):
+ self.no_smarty += 1
+ try:
+ # this raises SkipNode
+ HTMLTranslator.visit_literal(self, node)
+ finally:
+ self.no_smarty -= 1
+
+ def visit_literal_emphasis(self, node):
+ self.no_smarty += 1
+ self.visit_emphasis(node)
+
+ def depart_literal_emphasis(self, node):
+ self.depart_emphasis(node)
+ self.no_smarty -= 1
+
+ def visit_desc_signature(self, node):
+ self.no_smarty += 1
+ HTMLTranslator.visit_desc_signature(self, node)
+
+ def depart_desc_signature(self, node):
+ self.no_smarty -= 1
+ HTMLTranslator.depart_desc_signature(self, node)
+
+ def visit_productionlist(self, node):
+ self.no_smarty += 1
+ try:
+ HTMLTranslator.visit_productionlist(self, node)
+ finally:
+ self.no_smarty -= 1
+
+ def encode(self, text):
+ text = HTMLTranslator.encode(self, text)
+ if self.no_smarty <= 0:
+ text = sphinx_smarty_pants(text)
+ return text
+
+ if config.get('use_smartypants', False):
+ return SmartyPantsHTMLTranslator
+ else:
+ return HTMLTranslator