#3998: Add optional section numbering in plain text output

Controlled by new config values: text_add_secnumbers and
text_secnumber_suffix.
This commit is contained in:
Matthew Woodcraft 2017-11-05 22:47:57 +00:00
parent f46c91b652
commit 6b15c9c1c7
9 changed files with 105 additions and 1 deletions

View File

@ -67,6 +67,7 @@ Other contributors, listed alphabetically, are:
* Barry Warsaw -- setup command improvements
* Sebastian Wiesner -- image handling, distutils support
* Michael Wilson -- Intersphinx HTTP basic auth support
* Matthew Woodcraft -- text output improvements
* Joel Wurtz -- cellspanning support in LaTeX
* Hong Xu -- svg support in imgmath extension and various bug fixes
* Stephen Finucane -- setup command improvements and documentation

View File

@ -43,6 +43,8 @@ Features added
* #4168: improve zh search with jieba
* HTML themes can set up default sidebars through ``theme.conf``
* #3160: html: Use ``<kdb>`` to represent ``:kbd:`` role
* #3998: text: Add new config values :confval:`text_add_secnumbers` and
:confval:`text_secnumber_suffix`
Features removed

View File

@ -1959,6 +1959,20 @@ These options influence text output.
.. versionadded:: 1.1
.. confval:: text_add_secnumbers
A boolean that decides whether section numbers are included in text output.
Default is ``False``.
.. versionadded:: 1.7
.. confval:: text_secnumber_suffix
Suffix for section numbers in text output. Default: ``". "``. Set to ``" "``
to suppress the final dot on section numbers.
.. versionadded:: 1.7
.. _man-options:

View File

@ -39,7 +39,8 @@ class TextBuilder(Builder):
def init(self):
# type: () -> None
pass
# section numbers for headings in the currently visited document
self.secnumbers = {} # type: Dict[unicode, Tuple[int, ...]]
def get_outdated_docs(self):
# type: () -> Iterator[unicode]
@ -72,6 +73,7 @@ class TextBuilder(Builder):
def write_doc(self, docname, doctree):
# type: (unicode, nodes.Node) -> None
self.current_docname = docname
self.secnumbers = self.env.toc_secnumbers.get(docname, {})
destination = StringOutput(encoding='utf-8')
self.writer.write(doctree, destination)
outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
@ -93,6 +95,8 @@ def setup(app):
app.add_config_value('text_sectionchars', '*=-~"+`', 'env')
app.add_config_value('text_newlines', 'unix', 'env')
app.add_config_value('text_add_secnumbers', False, 'env')
app.add_config_value('text_secnumber_suffix', '. ', 'env')
return {
'version': 'builtin',

View File

@ -183,6 +183,8 @@ class TextTranslator(nodes.NodeVisitor):
else:
self.nl = '\n'
self.sectionchars = builder.config.text_sectionchars
self.add_secnumbers = builder.config.text_add_secnumbers
self.secnumber_suffix = builder.config.text_secnumber_suffix
self.states = [[]] # type: List[List[Tuple[int, Union[unicode, List[unicode]]]]]
self.stateindent = [0]
self.list_counter = [] # type: List[int]
@ -307,6 +309,17 @@ class TextTranslator(nodes.NodeVisitor):
raise nodes.SkipNode
self.new_state(0)
def get_section_number_string(self, node):
# type: (nodes.Node) -> unicode
if isinstance(node.parent, nodes.section):
anchorname = '#' + node.parent['ids'][0]
numbers = self.builder.secnumbers.get(anchorname)
if numbers is None:
numbers = self.builder.secnumbers.get('')
if numbers is not None:
return '.'.join(map(str, numbers)) + self.secnumber_suffix
return ''
def depart_title(self, node):
# type: (nodes.Node) -> None
if isinstance(node.parent, nodes.section):
@ -315,6 +328,8 @@ class TextTranslator(nodes.NodeVisitor):
char = '^'
text = None # type: unicode
text = ''.join(x[1] for x in self.states.pop() if x[0] == -1) # type: ignore
if self.add_secnumbers:
text = self.get_section_number_string(node) + text
self.stateindent.pop()
title = ['', text, '%s' % (char * column_width(text)), ''] # type: List[unicode]
if len(self.states) == 2 and len(self.states[-1]) == 0:

View File

@ -1,5 +1,8 @@
.. toctree::
:numbered:
doc1
doc2
maxwidth
lineblock
nonascii_title

View File

@ -0,0 +1,2 @@
Section A
=========

View File

@ -0,0 +1,9 @@
Section B
=========
Sub Ba
------
Sub Bb
------

View File

@ -110,3 +110,57 @@ def test_list_items_in_admonition(app, status, warning):
assert lines[2] == " * item 1"
assert lines[3] == ""
assert lines[4] == " * item 2"
@with_text_app()
def test_secnums(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'doc2.txt').text(encoding='utf8')
expect = (
"Section B\n"
"*********\n"
"\n"
"\n"
"Sub Ba\n"
"======\n"
"\n"
"\n"
"Sub Bb\n"
"======\n"
)
assert result == expect
app.config.text_add_secnumbers = True
app.builder.build_all()
result = (app.outdir / 'doc2.txt').text(encoding='utf8')
expect = (
"2. Section B\n"
"************\n"
"\n"
"\n"
"2.1. Sub Ba\n"
"===========\n"
"\n"
"\n"
"2.2. Sub Bb\n"
"===========\n"
)
assert result == expect
app.config.text_secnumber_suffix = " "
app.builder.build_all()
result = (app.outdir / 'doc2.txt').text(encoding='utf8')
expect = (
"2 Section B\n"
"***********\n"
"\n"
"\n"
"2.1 Sub Ba\n"
"==========\n"
"\n"
"\n"
"2.2 Sub Bb\n"
"==========\n"
)
assert result == expect