Source code for tacl.command.formatters
import argparse
import re
import textwrap
[docs]class ParagraphFormatter (argparse.ArgumentDefaultsHelpFormatter):
"""argparse formatter to maintain paragraph breaks in text, while
wrapping those blocks.
Code minimally adapted from the patch at
http://bugs.python.org/file28091, authored by rurpy2.
"""
def _split_lines(self, text, width):
return self._para_reformat(text, width, multiline=True)
def _fill_text(self, text, width, indent):
lines = self._para_reformat(text, width, indent, True)
return '\n'.join(lines)
def _para_reformat(self, text, width, indent='', multiline=False):
new_lines = list()
main_indent = len(re.match(r'( *)', text).group(1))
def blocker(text):
"""On each call yields 2-tuple consisting of a boolean and
the next block of text from 'text'. A block is either a
single line, or a group of contiguous lines. The former
is returned when not in multiline mode, the text in the
line was indented beyond the indentation of the first
line, or it was a blank line (the latter two jointly
referred to as "no-wrap" lines). A block of concatenated
text lines up to the next no-wrap line is returned when
in multiline mode. The boolean value indicates whether
text wrapping should be done on the returned text."""
block = list()
for line in text.splitlines():
line_indent = len(re.match(r'( *)', line).group(1))
isindented = line_indent - main_indent > 0
isblank = re.match(r'\s*$', line)
if isblank or isindented:
# A no-wrap line.
if block:
# Yield previously accumulated block of text
# if any, for wrapping.
yield True, ''.join(block)
block = list()
# And now yield our no-wrap line.
yield False, line
else:
# We have a regular text line.
if multiline:
# In multiline mode accumulate it.
block.append(line)
else:
# Not in multiline mode, yield it for
# wrapping.
yield True, line
if block:
# Yield any text block left over.
yield (True, ''.join(block))
for wrap, line in blocker(text):
if wrap:
# We have either a single line or a group of
# concatented lines. Either way, we treat them as a
# block of text and wrap them (after reducing multiple
# whitespace to just single space characters).
line = self._whitespace_matcher.sub(' ', line).strip()
# Textwrap will do all the hard work for us.
new_lines.extend(textwrap.wrap(text=line, width=width,
initial_indent=indent,
subsequent_indent=indent))
else:
# The line was a no-wrap one so leave the formatting
# alone.
new_lines.append(line[main_indent:])
return new_lines