Merge pull request #6132 from tk0miya/2155_code_directive

Closes #2155: Support ``code`` directive
This commit is contained in:
Takeshi KOMIYA 2019-03-10 12:22:47 +09:00 committed by GitHub
commit 63c05f2b6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 177 additions and 34 deletions

View File

@ -30,10 +30,13 @@ Bugs fixed
* #6147: classes attribute of ``citation_reference`` node is lost
* AssertionError is raised when custom ``citation_reference`` node having
classes attribute refers missing citation (refs: #6147)
* #2155: Support ``code`` directive
Testing
--------
* Add a helper function: ``sphinx.testing.restructuredtext.parse()``
Release 2.0.0 beta1 (in development)
====================================

View File

@ -19,18 +19,6 @@ from sphinx.util import docutils
from sphinx.util.docfields import DocFieldTransformer
from sphinx.util.docutils import SphinxDirective
# import all directives sphinx provides
from sphinx.directives.code import ( # noqa
Highlight, CodeBlock, LiteralInclude
)
from sphinx.directives.other import ( # noqa
TocTree, Author, Index, VersionChange, SeeAlso,
TabularColumns, Centered, Acks, HList, Only, Include, Class
)
from sphinx.directives.patches import ( # noqa
Figure, Meta
)
if False:
# For type annotation
from typing import Any, Dict # NOQA
@ -44,6 +32,19 @@ nl_escape_re = re.compile(r'\\\n')
strip_backslash_re = re.compile(r'\\(.)')
def optional_int(argument):
"""
Check for an integer argument or None value; raise ``ValueError`` if not.
"""
if argument is None:
return None
else:
value = int(argument)
if value < 0:
raise ValueError('negative value; must be positive or zero')
return value
class ObjectDescription(SphinxDirective):
"""
Directive to describe a class, function or similar object. Not used
@ -241,6 +242,18 @@ class DefaultDomain(SphinxDirective):
self.env.temp_data['default_domain'] = self.env.domains.get(domain_name)
return []
# import all directives sphinx provides (for compatibility)
from sphinx.directives.code import ( # noqa
Highlight, CodeBlock, LiteralInclude
)
from sphinx.directives.other import ( # noqa
TocTree, Author, Index, VersionChange, SeeAlso,
TabularColumns, Centered, Acks, HList, Only, Include, Class
)
from sphinx.directives.patches import ( # noqa
Figure, Meta
)
def setup(app):
# type: (Sphinx) -> Dict[str, Any]

View File

@ -14,6 +14,7 @@ from docutils.parsers.rst import directives
from docutils.parsers.rst.directives import images, html, tables
from sphinx import addnodes
from sphinx.directives import optional_int
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
@ -110,6 +111,52 @@ class ListTable(tables.ListTable):
return title, message
class Code(SphinxDirective):
"""Parse and mark up content of a code block.
This is compatible with docutils' :rst:dir:`code` directive.
"""
optional_arguments = 1
option_spec = {
'class': directives.class_option,
'name': directives.unchanged,
'number-lines': optional_int,
}
has_content = True
def run(self):
# type: () -> List[nodes.Node]
self.assert_has_content()
code = '\n'.join(self.content)
node = nodes.literal_block(code, code,
classes=self.options.get('classes', []),
highlight_args={})
self.add_name(node)
set_source_info(self, node)
if self.arguments:
# highlight language specified
node['language'] = self.arguments[0]
node['force_highlighting'] = True
else:
# no highlight language specified. Then this directive refers the current
# highlight setting via ``highlight`` directive or ``highlight_language``
# configuration.
node['language'] = self.env.temp_data.get('highlight_language',
self.config.highlight_language)
node['force_highlighting'] = False
if 'number-lines' in self.options:
node['linenos'] = True
# if number given, treat as lineno-start.
if self.options['number-lines']:
node['highlight_args']['linenostart'] = self.options['number-lines']
return [node]
class MathDirective(SphinxDirective):
has_content = True
required_arguments = 0
@ -174,6 +221,7 @@ def setup(app):
directives.register_directive('table', RSTTable)
directives.register_directive('csv-table', CSVTable)
directives.register_directive('list-table', ListTable)
directives.register_directive('code', Code)
directives.register_directive('math', MathDirective)
return {

View File

@ -0,0 +1,38 @@
"""
sphinx.testing.restructuredtext
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from os import path
from docutils.core import publish_doctree
from sphinx.io import SphinxStandaloneReader
from sphinx.parsers import RSTParser
from sphinx.util.docutils import sphinx_domains
if False:
# For type annotation
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
def parse(app, text, docname='index'):
# type: (Sphinx, str, str) -> nodes.document
"""Parse a string as reStructuredText with Sphinx application."""
try:
app.env.temp_data['docname'] = docname
parser = RSTParser()
parser.set_application(app)
with sphinx_domains(app.env):
return publish_doctree(text, path.join(app.srcdir, docname + '.rst'),
reader=SphinxStandaloneReader(app),
parser=parser,
settings_overrides={'env': app.env,
'gettext_compact': True})
finally:
app.env.temp_data.pop('docname', None)

View File

@ -10,25 +10,12 @@
import pytest
from docutils import nodes
from docutils.core import publish_doctree
from sphinx import addnodes
from sphinx.io import SphinxStandaloneReader
from sphinx.parsers import RSTParser
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
def parse(app, docname, text):
app.env.temp_data['docname'] = docname
parser = RSTParser()
parser.set_application(app)
return publish_doctree(text, app.srcdir / docname + '.rst',
reader=SphinxStandaloneReader(app),
parser=parser,
settings_overrides={'env': app.env,
'gettext_compact': True})
@pytest.mark.sphinx(testroot='toctree-glob')
def test_toctree(app):
text = (".. toctree::\n"
@ -38,7 +25,7 @@ def test_toctree(app):
" baz\n")
app.env.find_files(app.config, app.builder)
doctree = parse(app, 'index', text)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'foo'), (None, 'bar/index'), (None, 'baz')],
@ -55,7 +42,7 @@ def test_relative_toctree(app):
" ../quux\n")
app.env.find_files(app.config, app.builder)
doctree = parse(app, 'bar/index', text)
doctree = restructuredtext.parse(app, text, 'bar/index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'bar/bar_1'), (None, 'bar/bar_2'), (None, 'bar/bar_3'),
@ -72,7 +59,7 @@ def test_toctree_urls_and_titles(app):
" The BAR <bar/index>\n")
app.env.find_files(app.config, app.builder)
doctree = parse(app, 'index', text)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[('Sphinx', 'https://www.sphinx-doc.org/'),
@ -89,7 +76,7 @@ def test_toctree_glob(app):
" *\n")
app.env.find_files(app.config, app.builder)
doctree = parse(app, 'index', text)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'baz'), (None, 'foo'), (None, 'quux')],
@ -103,7 +90,7 @@ def test_toctree_glob(app):
" *\n")
app.env.find_files(app.config, app.builder)
doctree = parse(app, 'index', text)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'foo'), (None, 'baz'), (None, 'quux')],
@ -117,7 +104,7 @@ def test_toctree_glob(app):
" foo\n")
app.env.find_files(app.config, app.builder)
doctree = parse(app, 'index', text)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'baz'), (None, 'foo'), (None, 'quux'), (None, 'foo')],
@ -132,7 +119,7 @@ def test_toctree_glob_and_url(app):
" https://example.com/?q=sphinx\n")
app.env.find_files(app.config, app.builder)
doctree = parse(app, 'index', text)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'https://example.com/?q=sphinx')],
@ -147,7 +134,7 @@ def test_toctree_twice(app):
" foo\n")
app.env.find_files(app.config, app.builder)
doctree = parse(app, 'index', text)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'foo'), (None, 'foo')],

View File

@ -0,0 +1,54 @@
"""
test_directive_patch
~~~~~~~~~~~~~~~~~~~
Test the patched directives.
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from docutils import nodes
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
def test_code_directive(app):
# normal case
text = ('.. code::\n'
'\n'
' print("hello world")\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
assert_node(doctree[0], language="default", highlight_args={})
# with language
text = ('.. code:: python\n'
'\n'
' print("hello world")\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
assert_node(doctree[0], language="python", highlight_args={})
# :number-lines: option
text = ('.. code:: python\n'
' :number-lines:\n'
'\n'
' print("hello world")\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
assert_node(doctree[0], language="python", linenos=True, highlight_args={})
# :number-lines: option
text = ('.. code:: python\n'
' :number-lines: 5\n'
'\n'
' print("hello world")\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
assert_node(doctree[0], language="python", linenos=True, highlight_args={'linenostart': 5})