summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2019-03-10 12:23:15 +0900
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2019-03-10 12:23:15 +0900
commita37e508b3fc7b28cf10bbd51b766ce62fc390c4c (patch)
treea75e561df3cd8b8fb96923f74ddd62e12bbc9f57
parent2606002bb4d0470181460740b608d13d03b84bfe (diff)
parent63c05f2b6d7c5408978bafe8238913d4dd6637b2 (diff)
downloadsphinx-git-a37e508b3fc7b28cf10bbd51b766ce62fc390c4c.tar.gz
Merge branch '2.0'
-rw-r--r--CHANGES3
-rw-r--r--sphinx/directives/__init__.py37
-rw-r--r--sphinx/directives/patches.py48
-rw-r--r--sphinx/testing/restructuredtext.py38
-rw-r--r--tests/test_directive_other.py31
-rw-r--r--tests/test_directive_patch.py54
6 files changed, 177 insertions, 34 deletions
diff --git a/CHANGES b/CHANGES
index 5461f0472..8dfbfb617 100644
--- a/CHANGES
+++ b/CHANGES
@@ -68,10 +68,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)
====================================
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index c639885f6..1f4520541 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -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]
diff --git a/sphinx/directives/patches.py b/sphinx/directives/patches.py
index 40af03e80..ee8014aa6 100644
--- a/sphinx/directives/patches.py
+++ b/sphinx/directives/patches.py
@@ -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 {
diff --git a/sphinx/testing/restructuredtext.py b/sphinx/testing/restructuredtext.py
new file mode 100644
index 000000000..8bf1c041e
--- /dev/null
+++ b/sphinx/testing/restructuredtext.py
@@ -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)
diff --git a/tests/test_directive_other.py b/tests/test_directive_other.py
index cbbebee5c..376b9cd69 100644
--- a/tests/test_directive_other.py
+++ b/tests/test_directive_other.py
@@ -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')],
diff --git a/tests/test_directive_patch.py b/tests/test_directive_patch.py
new file mode 100644
index 000000000..4f61f2d0b
--- /dev/null
+++ b/tests/test_directive_patch.py
@@ -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})