Fix #2030: automatic dedent support in code-block directive

This commit is contained in:
Takeshi KOMIYA 2021-01-12 23:34:29 +09:00
parent e314789f4f
commit fddc42847f
4 changed files with 24 additions and 5 deletions

View File

@ -30,6 +30,8 @@ Features added
* #6241: mathjax: Include mathjax.js only on the document using equations * #6241: mathjax: Include mathjax.js only on the document using equations
* #8132: Add :confval:`project_copyright` as an alias of :confval:`copyright` * #8132: Add :confval:`project_copyright` as an alias of :confval:`copyright`
* #207: Now :confval:`highlight_language` supports multiple languages * #207: Now :confval:`highlight_language` supports multiple languages
* #2030: :rst:dir:`code-block` and :rst:dir:`literalinclude` supports automatic
dedent via no-argument ``:dedent:`` option
Bugs fixed Bugs fixed
---------- ----------

View File

@ -572,9 +572,11 @@ __ http://pygments.org/docs/lexers
.. versionadded:: 1.3 .. versionadded:: 1.3
.. rst:directive:option:: dedent: number .. rst:directive:option:: dedent: number
:type: number :type: number or no value
Strip indentation characters from the code block. For example:: Strip indentation characters from the code block. When number given,
leading N characters are removed. When no argument given, leading spaces
are removed via :func:`textwrap.dedent()`. For example::
.. code-block:: ruby .. code-block:: ruby
:dedent: 4 :dedent: 4
@ -582,6 +584,8 @@ __ http://pygments.org/docs/lexers
some ruby code some ruby code
.. versionadded:: 1.3 .. versionadded:: 1.3
.. versionchanged:: 3.5
Support automatic dedent.
.. rst:directive:option:: force .. rst:directive:option:: force
:type: no value :type: no value
@ -742,6 +746,9 @@ __ http://pygments.org/docs/lexers
.. versionchanged:: 2.1 .. versionchanged:: 2.1
Added the ``force`` option. Added the ``force`` option.
.. versionchanged:: 3.5
Support automatic dedent.
.. _glossary-directive: .. _glossary-directive:
Glossary Glossary

View File

@ -7,6 +7,7 @@
""" """
import sys import sys
import textwrap
import warnings import warnings
from difflib import unified_diff from difflib import unified_diff
from typing import Any, Dict, List, Tuple from typing import Any, Dict, List, Tuple
@ -19,6 +20,7 @@ from docutils.statemachine import StringList
from sphinx import addnodes from sphinx import addnodes
from sphinx.config import Config from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.directives import optional_int
from sphinx.locale import __ from sphinx.locale import __
from sphinx.util import logging, parselinenos from sphinx.util import logging, parselinenos
from sphinx.util.docutils import SphinxDirective from sphinx.util.docutils import SphinxDirective
@ -68,7 +70,7 @@ class HighlightLang(Highlight):
def dedent_lines(lines: List[str], dedent: int, location: Tuple[str, int] = None) -> List[str]: def dedent_lines(lines: List[str], dedent: int, location: Tuple[str, int] = None) -> List[str]:
if not dedent: if not dedent:
return lines return textwrap.dedent(''.join(lines)).splitlines(True)
if any(s[:dedent].strip() for s in lines): if any(s[:dedent].strip() for s in lines):
logger.warning(__('non-whitespace stripped by dedent'), location=location) logger.warning(__('non-whitespace stripped by dedent'), location=location)
@ -117,7 +119,7 @@ class CodeBlock(SphinxDirective):
option_spec = { option_spec = {
'force': directives.flag, 'force': directives.flag,
'linenos': directives.flag, 'linenos': directives.flag,
'dedent': int, 'dedent': optional_int,
'lineno-start': int, 'lineno-start': int,
'emphasize-lines': directives.unchanged_required, 'emphasize-lines': directives.unchanged_required,
'caption': directives.unchanged_required, 'caption': directives.unchanged_required,
@ -391,7 +393,7 @@ class LiteralInclude(SphinxDirective):
optional_arguments = 0 optional_arguments = 0
final_argument_whitespace = True final_argument_whitespace = True
option_spec = { option_spec = {
'dedent': int, 'dedent': optional_int,
'linenos': directives.flag, 'linenos': directives.flag,
'lineno-start': int, 'lineno-start': int,
'lineno-match': directives.flag, 'lineno-match': directives.flag,

View File

@ -250,6 +250,14 @@ def test_LiteralIncludeReader_dedent(literal_inc_path):
" pass\n" " pass\n"
"\n") "\n")
# dedent: None
options = {'lines': '9-11', 'dedent': None}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("def baz():\n"
" pass\n"
"\n")
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows") @pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_tabwidth(testroot): def test_LiteralIncludeReader_tabwidth(testroot):