Merged in tk0miya/sphinx (pull request #292)

do nested_parse() :caption: of code-block directive
This commit is contained in:
Takayuki Shimizukawa 2014-09-26 20:29:38 +09:00
commit 7984f81a5e
8 changed files with 75 additions and 36 deletions

View File

@ -13,6 +13,7 @@ from difflib import unified_diff
from docutils import nodes
from docutils.parsers.rst import Directive, directives
from docutils.statemachine import ViewList
from six import string_types
@ -61,6 +62,17 @@ def dedent_lines(lines, dedent):
return new_lines
def container_wrapper(directive, literal_node, caption):
caption_node = nodes.caption()
directive.state.nested_parse(ViewList([caption], source=''),
directive.content_offset, caption_node)
container_node = nodes.container('', literal_block=True)
container_node += caption_node
container_node += literal_node
return container_node
class CodeBlock(Directive):
"""
Directive for a code block with special highlighting or line numbering
@ -100,9 +112,6 @@ class CodeBlock(Directive):
literal = nodes.literal_block(code, code)
literal['language'] = self.arguments[0]
caption = self.options.get('caption')
if caption:
literal['caption'] = caption
literal['linenos'] = 'linenos' in self.options or \
'lineno-start' in self.options
extra_args = literal['highlight_args'] = {}
@ -111,6 +120,11 @@ class CodeBlock(Directive):
if 'lineno-start' in self.options:
extra_args['linenostart'] = self.options['lineno-start']
set_source_info(self, literal)
caption = self.options.get('caption')
if caption:
literal = container_wrapper(self, literal, caption)
return [literal]
@ -268,17 +282,20 @@ class LiteralInclude(Directive):
retnode['language'] = self.options['language']
retnode['linenos'] = 'linenos' in self.options or \
'lineno-start' in self.options
caption = self.options.get('caption')
if caption is not None:
if not caption:
caption = self.arguments[0]
retnode['caption'] = caption
extra_args = retnode['highlight_args'] = {}
if hl_lines is not None:
extra_args['hl_lines'] = hl_lines
if 'lineno-start' in self.options:
extra_args['linenostart'] = self.options['lineno-start']
env.note_dependency(rel_filename)
caption = self.options.get('caption')
if caption is not None:
if caption:
retnode = container_wrapper(self, retnode, caption)
else:
retnode = container_wrapper(self, retnode, self.arguments[0])
return [retnode]

View File

@ -562,9 +562,11 @@ class StandardDomain(Domain):
break
else:
continue
elif node.tagname == 'literal_block':
if 'caption' in node:
sectname = node['caption']
elif node.tagname == 'container' and node.get('literal_block'):
for n in node:
if n.tagname == 'caption':
sectname = clean_astext(n)
break
else:
continue
else:

View File

@ -484,8 +484,7 @@ div.code-block-filename code {
background-color: transparent;
}
div.code-block-caption + pre,
div.code-block-caption + div.highlight > pre {
div.code-block-caption + div > div.highlight > pre {
margin-top: 0;
}

View File

@ -283,12 +283,21 @@ class HTMLTranslator(BaseTranslator):
**highlight_args)
starttag = self.starttag(node, 'div', suffix='',
CLASS='highlight-%s' % lang)
if 'caption' in node:
starttag += '<div class="code-block-caption"><code>%s</code></div>' % (
node['caption'],)
self.body.append(starttag + highlighted + '</div>\n')
raise nodes.SkipNode
def visit_caption(self, node):
if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'):
self.body.append(self.starttag(node, 'div', '', CLASS='code-block-caption'))
else:
BaseTranslator.visit_caption(self, node)
def depart_caption(self, node):
if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'):
self.body.append('</div>\n')
else:
BaseTranslator.depart_caption(self, node)
def visit_doctest_block(self, node):
self.visit_literal_block(node)

View File

@ -266,6 +266,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.next_section_ids = set()
self.next_figure_ids = set()
self.next_table_ids = set()
self.next_literal_ids = set()
# flags
self.in_title = 0
self.in_production_list = 0
@ -1109,6 +1110,12 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.next_table_ids.add(node['refid'])
self.next_table_ids.update(node['ids'])
return
elif isinstance(next, nodes.container) and next.get('literal_block'):
# same for literal_block, but only if they have a caption
if node.get('refid'):
self.next_literal_ids.add(node['refid'])
self.next_literal_ids.update(node['ids'])
return
except IndexError:
pass
if 'refuri' in node:
@ -1345,10 +1352,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
highlight_args['force'] = True
if 'linenos' in node:
linenos = node['linenos']
caption = node.get('caption')
if caption:
self.body.append('\n\\begin{literal-block}\caption{%s}\n' %
(caption,))
def warner(msg):
self.builder.warn(msg, (self.curfilestack[-1], node.line))
hlcode = self.highlighter.highlight_block(code, lang, warn=warner,
@ -1366,8 +1369,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
hlcode = hlcode.rstrip() + '\n'
self.body.append('\n' + hlcode + '\\end{%sVerbatim}\n' %
(self.table and 'Original' or ''))
if caption:
self.body.append('\n\\end{literal-block}\n')
raise nodes.SkipNode
def depart_literal_block(self, node):
self.body.append('\n\\end{alltt}\n')
@ -1495,9 +1496,16 @@ class LaTeXTranslator(nodes.NodeVisitor):
pass
def visit_container(self, node):
pass
if node.get('literal_block'):
ids = ''
for id in self.next_literal_ids:
ids += self.hypertarget(id, anchor=False)
self.next_literal_ids.clear()
self.body.append('\n\\begin{literal-block}' + ids)
def depart_container(self, node):
pass
if node.get('literal_block'):
self.body.append('\\end{literal-block}\n')
def visit_decoration(self, node):
pass

View File

@ -1054,9 +1054,11 @@ class TexinfoTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
def visit_container(self, node):
pass
if node.get('literal_block'):
self.body.append('\n\n@float LiteralBlock\n')
def depart_container(self, node):
pass
if node.get('literal_block'):
self.body.append('\n@end float\n\n')
def visit_decoration(self, node):
pass
@ -1095,13 +1097,15 @@ class TexinfoTranslator(nodes.NodeVisitor):
self.body.append('\n@end float\n\n')
def visit_caption(self, node):
if not isinstance(node.parent, nodes.figure):
if (isinstance(node.parent, nodes.figure) or
(isinstance(node.parent, nodes.container) and node.parent.get('literal_block'))):
self.body.append('\n@caption{')
else:
self.builder.warn('caption not inside a figure.',
(self.curfilestack[-1], node.line))
return
self.body.append('\n@caption{')
def depart_caption(self, node):
if isinstance(node.parent, nodes.figure):
if (isinstance(node.parent, nodes.figure) or
(isinstance(node.parent, nodes.container) and node.parent.get('literal_block'))):
self.body.append('}\n')
def visit_image(self, node):

View File

@ -5,7 +5,7 @@ Code blocks
-----------
.. code-block:: ruby
:caption: caption-test.rb
:caption: caption *test* rb
def ruby?
false
@ -17,5 +17,5 @@ Literal Include
.. literalinclude:: literal.inc
:language: python
:caption: caption-test.py
:caption: caption **test** py
:lines: 10-11

View File

@ -53,7 +53,7 @@ def test_code_block_dedent(app, status, warning):
def test_code_block_caption_html(app, status, warning):
app.builder.build(['caption'])
html = (app.outdir / 'caption.html').text()
caption = '<div class="code-block-caption"><code>caption-test.rb</code></div>'
caption = '<div class="code-block-caption">caption <em>test</em> rb</div>'
assert caption in html
@ -61,7 +61,7 @@ def test_code_block_caption_html(app, status, warning):
def test_code_block_caption_latex(app, status, warning):
app.builder.build_all()
latex = (app.outdir / 'Python.tex').text()
caption = '\\caption{caption-test.rb}'
caption = '\\caption{\ncaption \\emph{test} rb\n}'
assert caption in latex
@ -99,7 +99,7 @@ def test_literal_include_dedent(app, status, warning):
def test_literalinclude_caption_html(app, status, warning):
app.builder.build('index')
html = (app.outdir / 'caption.html').text()
caption = '<div class="code-block-caption"><code>caption-test.py</code></div>'
caption = '<div class="code-block-caption">caption <strong>test</strong> py</div>'
assert caption in html
@ -107,5 +107,5 @@ def test_literalinclude_caption_html(app, status, warning):
def test_literalinclude_caption_latex(app, status, warning):
app.builder.build('index')
latex = (app.outdir / 'Python.tex').text()
caption = '\\caption{caption-test.py}'
caption = '\\caption{\ncaption \\textbf{test} py\n}'
assert caption in latex