From d0764521996ffdae0fa9e8dba120fcbd182fafbd Mon Sep 17 00:00:00 2001 From: tk0miya Date: Fri, 19 Sep 2014 12:04:25 +0900 Subject: [PATCH 1/3] do nested_parse() :caption: of code-block directive --- sphinx/directives/code.py | 38 ++++++++++++++++----- sphinx/domains/std.py | 5 +++ sphinx/themes/basic/static/basic.css_t | 3 +- sphinx/writers/html.py | 15 ++++++-- sphinx/writers/latex.py | 23 +++++++++---- tests/roots/test-directive-code/caption.rst | 4 +-- tests/test_directive_code.py | 11 +++--- 7 files changed, 70 insertions(+), 29 deletions(-) diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index 6ea525b0f..855437594 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -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] diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index bb044e304..7a769221e 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -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 diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index 17547d0fd..3616288c8 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -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; } diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 14b11fcff..56657ee72 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -283,12 +283,21 @@ class HTMLTranslator(BaseTranslator): **highlight_args) starttag = self.starttag(node, 'div', suffix='', CLASS='highlight-%s' % lang) - if 'caption' in node: - starttag += '
%s
' % ( - node['caption'],) self.body.append(starttag + highlighted + '\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('\n') + else: + BaseTranslator.depart_caption(self, node) + def visit_doctest_block(self, node): self.visit_literal_block(node) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index a7459c924..11ea8c87f 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -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 diff --git a/tests/roots/test-directive-code/caption.rst b/tests/roots/test-directive-code/caption.rst index 274d0f19d..5a2fe4a1f 100644 --- a/tests/roots/test-directive-code/caption.rst +++ b/tests/roots/test-directive-code/caption.rst @@ -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 diff --git a/tests/test_directive_code.py b/tests/test_directive_code.py index 4dbdff881..140483bf4 100644 --- a/tests/test_directive_code.py +++ b/tests/test_directive_code.py @@ -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 = '
caption-test.rb
' + caption = '
caption test rb
' + 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 = '
caption-test.py
' + caption = '
caption test py
' 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 From 07e52ff22dc63ceb110f25e9ea40bad227bcf260 Mon Sep 17 00:00:00 2001 From: tk0miya Date: Wed, 24 Sep 2014 00:54:27 +0900 Subject: [PATCH 2/3] Fix @caption should be placed to inside of @float element in texinfo --- sphinx/writers/texinfo.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index a1051eb6a..61d2e62f2 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -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): From 7c7a563426fa6ac1825e078d9980ff882cedce23 Mon Sep 17 00:00:00 2001 From: tk0miya Date: Fri, 26 Sep 2014 20:15:17 +0900 Subject: [PATCH 3/3] Fix by review comment --- sphinx/directives/code.py | 12 ++++-------- sphinx/domains/std.py | 2 ++ sphinx/writers/latex.py | 2 +- tests/test_directive_code.py | 4 ++-- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index c61380ecc..ff642ba70 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -63,15 +63,11 @@ def dedent_lines(lines, dedent): def container_wrapper(directive, literal_node, caption): - container_node = nodes.container('', literal_block=True) - - parsed = nodes.Element() + caption_node = nodes.caption() 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 + directive.content_offset, caption_node) + + container_node = nodes.container('', literal_block=True) container_node += caption_node container_node += literal_node return container_node diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 5beb9feb7..0338a6b57 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -567,6 +567,8 @@ class StandardDomain(Domain): if n.tagname == 'caption': sectname = clean_astext(n) break + else: + continue else: # anonymous-only labels continue diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 11ea8c87f..abc7ed75a 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1500,7 +1500,7 @@ class LaTeXTranslator(nodes.NodeVisitor): ids = '' for id in self.next_literal_ids: ids += self.hypertarget(id, anchor=False) - self.next_figure_ids.clear() + self.next_literal_ids.clear() self.body.append('\n\\begin{literal-block}' + ids) def depart_container(self, node): diff --git a/tests/test_directive_code.py b/tests/test_directive_code.py index 3f0a6d3f0..0c2cead62 100644 --- a/tests/test_directive_code.py +++ b/tests/test_directive_code.py @@ -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 \\emph{test} rb}' + caption = '\\caption{\ncaption \\emph{test} rb\n}' assert caption in latex @@ -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 \\textbf{test} py}' + caption = '\\caption{\ncaption \\textbf{test} py\n}' assert caption in latex