Merge pull request #2415 from jfbu/jumptorightplace-onstable

Fix #2405 and #2414 related to PDF hyperlinks to numbered code listings
This commit is contained in:
Takeshi KOMIYA 2016-04-11 10:53:34 +09:00
commit 5b80365409
6 changed files with 145 additions and 16 deletions

View File

@ -186,8 +186,12 @@
\newcommand*\SphinxVerbatimTitle {} \newcommand*\SphinxVerbatimTitle {}
\newcommand*\SphinxSetupCaptionForVerbatim [2] \newcommand*\SphinxSetupCaptionForVerbatim [2]
{% {%
\def\SphinxVerbatimTitle{\captionof{#1}{#2}\smallskip }% \needspace{\literalblockneedspace}\vspace{\literalblockcaptiontopvspace}%
\def\SphinxVerbatimTitle
{\captionof{#1}{\SphinxLiteralBlockLabel #2}\smallskip }%
} }
% \SphinxLiteralBlockLabel will be set dynamically to hold the label for links
\newcommand*\SphinxLiteralBlockLabel {}
% \SphinxCustomFBox is copied from framed.sty's \CustomFBox, but % \SphinxCustomFBox is copied from framed.sty's \CustomFBox, but
% #1=title/caption is to be set _above_ the top rule, not _below_ % #1=title/caption is to be set _above_ the top rule, not _below_
@ -243,9 +247,19 @@
\renewcommand{\Verbatim}[1][1]{% \renewcommand{\Verbatim}[1][1]{%
% list starts new par, but we don't want it to be set apart vertically % list starts new par, but we don't want it to be set apart vertically
\bgroup\parskip\z@skip \parskip\z@skip
\smallskip \smallskip
% use customized framed environment % first, let's check if there is a caption
\ifx\SphinxVerbatimTitle\empty
% there was no caption. Check if nevertheless a label was set.
\ifx\SphinxLiteralBlockLabel\empty\else
% we require some space to be sure hyperlink target from \phantomsection
% will not be separated from upcoming verbatim by a page break
\needspace{\literalblockwithoutcaptionneedspace}%
\phantomsection\SphinxLiteralBlockLabel
\fi
\fi
% non-empty \SphinxVerbatimTitle has label inside it (in case there is one)
\let\SphinxFrameTitle\SphinxVerbatimTitle \let\SphinxFrameTitle\SphinxVerbatimTitle
\global\Sphinx@myfirstframedpasstrue \global\Sphinx@myfirstframedpasstrue
% The list environement is needed to control perfectly the vertical % The list environement is needed to control perfectly the vertical
@ -265,10 +279,7 @@
\endOriginalVerbatim \endOriginalVerbatim
\endMakeFramed \endMakeFramed
\endlist \endlist
% close group to restore \parskip (and \SphinxFrameTitle) % LaTeX environments always revert local changes on exit, here e.g. \parskip
\egroup
% reset to empty \SphinxVerbatimTitle
\global\let\SphinxVerbatimTitle\empty
} }
@ -625,5 +636,6 @@
\RequirePackage{needspace} \RequirePackage{needspace}
% if the left page space is less than \literalblockneedsapce, insert page-break % if the left page space is less than \literalblockneedsapce, insert page-break
\newcommand{\literalblockneedspace}{5\baselineskip} \newcommand{\literalblockneedspace}{5\baselineskip}
\newcommand{\literalblockwithoutcaptionneedspace}{1.5\baselineskip}
% margin before the caption of literal-block % margin before the caption of literal-block
\newcommand{\literalblockcaptiontopvspace}{0.5\baselineskip} \newcommand{\literalblockcaptiontopvspace}{0.5\baselineskip}

View File

@ -1443,9 +1443,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_caption(self, node): def visit_caption(self, node):
self.in_caption += 1 self.in_caption += 1
if self.in_container_literal_block: if self.in_container_literal_block:
self.body.append('\\needspace{\\literalblockneedspace}') self.body.append('\\SphinxSetupCaptionForVerbatim{literal-block}{')
self.body.append('\\vspace{\\literalblockcaptiontopvspace}%')
self.body.append('\n\\SphinxSetupCaptionForVerbatim{literal-block}{')
elif self.in_minipage and isinstance(node.parent, nodes.figure): elif self.in_minipage and isinstance(node.parent, nodes.figure):
self.body.append('\\captionof{figure}{') self.body.append('\\captionof{figure}{')
else: else:
@ -1800,6 +1798,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
# most probably a parsed-literal block -- don't highlight # most probably a parsed-literal block -- don't highlight
self.body.append('\\begin{alltt}\n') self.body.append('\\begin{alltt}\n')
else: else:
ids = ''
for id in self.pop_hyperlink_ids('code-block'):
ids += self.hypertarget(id, anchor=False)
if node['ids']:
# suppress with anchor=False \phantomsection insertion
ids += self.hypertarget(node['ids'][0], anchor=False)
# LaTeX code will insert \phantomsection prior to \label
if ids:
self.body.append('\n\\def\\SphinxLiteralBlockLabel{' + ids + '}')
code = node.astext() code = node.astext()
lang = self.hlsettingstack[-1][0] lang = self.hlsettingstack[-1][0]
linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1 linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1
@ -1833,6 +1840,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim} hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim}
self.body.append('\n' + hlcode + '\\end{%sVerbatim}\n' % self.body.append('\n' + hlcode + '\\end{%sVerbatim}\n' %
(self.table and 'Original' or '')) (self.table and 'Original' or ''))
if ids:
self.body.append('\\let\\SphinxLiteralBlockLabel\empty\n')
raise nodes.SkipNode raise nodes.SkipNode
def depart_literal_block(self, node): def depart_literal_block(self, node):
@ -1991,14 +2000,17 @@ class LaTeXTranslator(nodes.NodeVisitor):
for id in self.pop_hyperlink_ids('code-block'): for id in self.pop_hyperlink_ids('code-block'):
ids += self.hypertarget(id, anchor=False) ids += self.hypertarget(id, anchor=False)
if node['ids']: if node['ids']:
ids += self.hypertarget(node['ids'][0]) # suppress with anchor=False \phantomsection insertion
self.body.append('\n') ids += self.hypertarget(node['ids'][0], anchor=False)
self.context.append(ids + '\n') # define label for use in caption.
if ids:
self.body.append('\n\\def\\SphinxLiteralBlockLabel{' + ids + '}\n')
def depart_container(self, node): def depart_container(self, node):
if node.get('literal_block'): if node.get('literal_block'):
self.in_container_literal_block -= 1 self.in_container_literal_block -= 1
self.body.append(self.context.pop()) self.body.append('\\let\\SphinxVerbatimTitle\\empty\n')
self.body.append('\\let\\SphinxLiteralBlockLabel\\empty\n')
def visit_decoration(self, node): def visit_decoration(self, node):
pass pass

View File

@ -1,5 +1,13 @@
Dedent Caption
====== =======
References
----------
See :numref:`caption *test* rb` and :numref:`caption **test** py`.
See :ref:`Ruby <name *test* rb>` and :ref:`Python <name **test** py>`.
Code blocks Code blocks
----------- -----------
@ -19,3 +27,26 @@ Literal Include
:language: python :language: python
:caption: caption **test** py :caption: caption **test** py
:lines: 10-11 :lines: 10-11
Named Code blocks
-----------------
.. code-block:: ruby
:name: name *test* rb
:caption: caption *test* rbnamed
def ruby?
false
end
Named Literal Include
---------------------
.. literalinclude:: literal.inc
:language: python
:name: name **test** py
:caption: caption **test** pynamed
:lines: 10-11

View File

@ -2,3 +2,5 @@
master_doc = 'index' master_doc = 'index'
exclude_patterns = ['_build'] exclude_patterns = ['_build']
numfig = True

View File

@ -0,0 +1,28 @@
Named Blocks
============
References to named blocks
--------------------------
See :ref:`the ruby code <some ruby code>` and
also :ref:`the python code <some python code>`.
Named Code block
----------------
.. code-block:: ruby
:name: some ruby code
def ruby?
false
end
Named Literal Include
---------------------
.. literalinclude:: literal.inc
:language: python
:name: some python code

View File

@ -54,6 +54,7 @@ def test_code_block_caption_html(app, status, warning):
app.builder.build(['caption']) app.builder.build(['caption'])
html = (app.outdir / 'caption.html').text(encoding='utf-8') html = (app.outdir / 'caption.html').text(encoding='utf-8')
caption = (u'<div class="code-block-caption">' caption = (u'<div class="code-block-caption">'
u'<span class="caption-number">Listing 1 </span>'
u'<span class="caption-text">caption <em>test</em> rb' u'<span class="caption-text">caption <em>test</em> rb'
u'</span><a class="headerlink" href="#caption-test-rb" ' u'</span><a class="headerlink" href="#caption-test-rb" '
u'title="Permalink to this code">\xb6</a></div>') u'title="Permalink to this code">\xb6</a></div>')
@ -65,7 +66,28 @@ def test_code_block_caption_latex(app, status, warning):
app.builder.build_all() app.builder.build_all()
latex = (app.outdir / 'Python.tex').text(encoding='utf-8') latex = (app.outdir / 'Python.tex').text(encoding='utf-8')
caption = '\\SphinxSetupCaptionForVerbatim{literal-block}{caption \\emph{test} rb}' caption = '\\SphinxSetupCaptionForVerbatim{literal-block}{caption \\emph{test} rb}'
label = '\\def\\SphinxLiteralBlockLabel{\\label{caption:caption-test-rb}}'
link = '\hyperref[caption:caption-test-rb]' \
'{Listing \\ref{caption:caption-test-rb}}'
assert caption in latex assert caption in latex
assert label in latex
assert link in latex
@with_app('latex', testroot='directive-code')
def test_code_block_namedlink_latex(app, status, warning):
app.builder.build_all()
latex = (app.outdir / 'Python.tex').text(encoding='utf-8')
label1 = '\def\SphinxLiteralBlockLabel{\label{caption:name-test-rb}}'
link1 = '\\hyperref[caption:name\\string-test\\string-rb]'\
'{\\crossref{\\DUrole{std,std-ref}{Ruby}}'
label2 = '\def\SphinxLiteralBlockLabel{\label{namedblocks:some-ruby-code}}'
link2 = '\\hyperref[namedblocks:some\\string-ruby\\string-code]'\
'{\\crossref{\\DUrole{std,std-ref}{the ruby code}}}'
assert label1 in latex
assert link1 in latex
assert label2 in latex
assert link2 in latex
@with_app('xml', testroot='directive-code') @with_app('xml', testroot='directive-code')
@ -219,6 +241,7 @@ def test_literalinclude_caption_html(app, status, warning):
app.builder.build('index') app.builder.build('index')
html = (app.outdir / 'caption.html').text(encoding='utf-8') html = (app.outdir / 'caption.html').text(encoding='utf-8')
caption = (u'<div class="code-block-caption">' caption = (u'<div class="code-block-caption">'
u'<span class="caption-number">Listing 2 </span>'
u'<span class="caption-text">caption <strong>test</strong> py' u'<span class="caption-text">caption <strong>test</strong> py'
u'</span><a class="headerlink" href="#caption-test-py" ' u'</span><a class="headerlink" href="#caption-test-py" '
u'title="Permalink to this code">\xb6</a></div>') u'title="Permalink to this code">\xb6</a></div>')
@ -230,7 +253,28 @@ def test_literalinclude_caption_latex(app, status, warning):
app.builder.build('index') app.builder.build('index')
latex = (app.outdir / 'Python.tex').text(encoding='utf-8') latex = (app.outdir / 'Python.tex').text(encoding='utf-8')
caption = '\\SphinxSetupCaptionForVerbatim{literal-block}{caption \\textbf{test} py}' caption = '\\SphinxSetupCaptionForVerbatim{literal-block}{caption \\textbf{test} py}'
label = '\\def\\SphinxLiteralBlockLabel{\\label{caption:caption-test-py}}'
link = '\hyperref[caption:caption-test-py]' \
'{Listing \\ref{caption:caption-test-py}}'
assert caption in latex assert caption in latex
assert label in latex
assert link in latex
@with_app('latex', testroot='directive-code')
def test_literalinclude_namedlink_latex(app, status, warning):
app.builder.build('index')
latex = (app.outdir / 'Python.tex').text(encoding='utf-8')
label1 = '\def\SphinxLiteralBlockLabel{\label{caption:name-test-py}}'
link1 = '\\hyperref[caption:name\\string-test\\string-py]'\
'{\\crossref{\\DUrole{std,std-ref}{Python}}'
label2 = '\def\SphinxLiteralBlockLabel{\label{namedblocks:some-python-code}}'
link2 = '\\hyperref[namedblocks:some\\string-python\\string-code]'\
'{\\crossref{\\DUrole{std,std-ref}{the python code}}}'
assert label1 in latex
assert link1 in latex
assert label2 in latex
assert link2 in latex
@with_app('xml', testroot='directive-code') @with_app('xml', testroot='directive-code')