do nested_parse() :caption: of code-block directive

This commit is contained in:
tk0miya 2014-09-19 12:04:25 +09:00
parent 007a15454f
commit d076452199
7 changed files with 70 additions and 29 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
@ -47,7 +48,6 @@ class Highlight(Directive):
linenothreshold=linenothreshold)]
def dedent_lines(lines, dedent):
if not dedent:
return lines
@ -62,6 +62,21 @@ def dedent_lines(lines, dedent):
return new_lines
def container_wrapper(directive, literal_node, caption):
container_node = nodes.container('', literal_block=True)
parsed = nodes.Element()
directive.state.nested_parse(ViewList([caption], source=''),
directive.content_offset, parsed)
caption_node = nodes.caption(parsed[0].rawsource, '',
*parsed[0].children)
caption_node.source = parsed[0].source
caption_node.line = parsed[0].line
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
@ -101,9 +116,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'] = {}
@ -112,6 +124,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]
@ -269,17 +286,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

@ -563,6 +563,11 @@ class StandardDomain(Domain):
break
else:
continue
elif node.tagname == 'container' and node.get('literal_block'):
for n in node:
if n.tagname == 'caption':
sectname = clean_astext(n)
break
else:
# anonymous-only labels
continue

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,11 +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{\\colorbox[rgb]{0.9,0.9,0.9}'
'{\\makebox[\\textwidth][l]'
'{\\small\\texttt{%s}}}}\n' % (caption,))
def warner(msg):
self.builder.warn(msg, (self.curfilestack[-1], node.line))
hlcode = self.highlighter.highlight_block(code, lang, warn=warner,
@ -1494,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_figure_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

@ -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

@ -83,7 +83,8 @@ def test_code_block_dedent(app):
def test_code_block_caption_html(app):
app.builder.build('index')
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>'
print caption, html
assert caption in html
@ -93,8 +94,7 @@ def test_code_block_caption_html(app):
def test_code_block_caption_latex(app):
app.builder.build('index')
latex = (app.outdir / 'Python.tex').text()
caption = ('{\\colorbox[rgb]{0.9,0.9,0.9}{\\makebox[\\textwidth][l]'
'{\\small\\texttt{caption-test.rb}}}}')
caption = '\\caption{caption \\emph{test} rb}'
assert caption in latex
@ -158,7 +158,7 @@ def test_literal_include_dedent(app):
def test_literalinclude_caption_html(app):
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
@ -168,6 +168,5 @@ def test_literalinclude_caption_html(app):
def test_literalinclude_caption_latex(app):
app.builder.build('index')
latex = (app.outdir / 'Python.tex').text()
caption = ('{\\colorbox[rgb]{0.9,0.9,0.9}{\\makebox[\\textwidth][l]'
'{\\small\\texttt{caption-test.py}}}}')
caption = '\\caption{caption \\textbf{test} py}'
assert caption in latex