# http://bugs.python.org/issue12806, http://bugs.python.org/file22977/argparse_formatter.py """ In http://bugs.python.org/issue22029 argparse CSS white-space: like control for individual text blocks I propose a set of `str` subclasses that can be used to define the wrapping style of individual text blocks. The idea is adapted from the HTML '
' tag, and the CSS white-space: option.

`argparse.WhitespaceStyle` is a cover class that defines various utility
methods, including `_str_format` which handles all of the `%` formatting.
The individual subclasses implement their own version of `_split_lines` and
`_fill_text`.  I chose a standard set of classes based on the CSS
white-space options:

Normal() - full white space compression and wrapping.  This is the default
   default of text in `argparse`.
Pre() - preformatting, the same as the `Raw` formatters
NoWrap() - Pre plus whitespace compression
PreLine()
PreWrap()

In `HelpFormatter`, `_split_lines`, `_fill_lines`, `_str_format` delegate
the action to text's own methods.  Plain text is handled as `Normal()`.

I also defined a `WSList` class.  This is a list of Style class objects.
It has the same API as the Style classes, iterating over the items.

Where possible these methods try to return an object of the same type as
self.
------------------
Here I demonstrate two ways that these classes could be used to implement
a hybrid formatter.

The first is a simple adaptation of the `PareML` formatter from
`paraformatter.py`.  It shows how a custom style class could be defined.

The second is defines a `preformat` function, which converts the text
block into a `WSList`, a list of style text objects.  The wrappable
paragraphs are `Normal()`, the preformatted indented lines are `Pre()`.
Blank lines are `Pre(' ')`.

I've explored writing a `Hanging` class, which performs a hanging indent
on list item sentences.


"""

import argparse
import re
import textwrap
import os, shutil
os.environ['COLUMNS'] = str(shutil.get_terminal_size().columns)

from paraformatter import _para_reformat
class ParaML(argparse.WhitespaceStyle):
    """ adapted from http://bugs.python.org/file28091/paraformatter.py

    """
    _whitespace_matcher = re.compile(r'\s+')
    def _split_lines(self, width):
        lines =  _para_reformat(self, self, width, multiline=True)
        return self.copy_class(lines)

    def _fill_text(self, width, indent):
        lines =_para_reformat(self, self, width, indent, multiline=True)
        text = '\n'.join(lines)
        return self.copy_class(text)

from argparse import Normal, Pre, WSList

def preformat(text, multiline=True, keepblank=True, para_style=Normal, indent_style=Pre):
    """Convert a multiline text block into wrappable lines and preformated indented blocks
    Returns a WSList of style objects
    """
    text = textwrap.dedent(text)
    _whitespace_matcher = re.compile(r'\s+')
    new_lines = list()
    main_indent = len(re.match(r'( *)',text).group(1))
    if main_indent: print('indent', main_indent)
    def blocker (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 .
                    yield True, ' '.join(block)  #  of text if any, for wrapping.
                    block = list()
                yield False, line               # And now yield our no-wrap 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
                    yield True, line            #  for wrapping.
        if block:                           # Yield any text block left over.
            yield (True, ' '.join(block))

    for wrap, line in blocker(text):
        if wrap:
            new_lines.append(para_style(line))
        else:
            # The line was a no-wrap one so leave the formatting alone.
            line = line[main_indent:]
            if len(line)==0:
                if keepblank:
                    line = ' ' # ' ' preserves a blank line between paragraphs
                    new_lines.append(Pre(line))
                # else - don't append anything
            else:
                new_lines.append(indent_style(line))

    return WSList(new_lines)

if __name__ == '__main__':

    parser = argparse.ArgumentParser(prog='ParaML',
                                     description=ParaML('''\
        This description help text will have this first long line wrapped to
        fit the target window size so that your text remains flexible.

            1. But lines such as
            2. this that that are indented beyond the first line's indent,
            3. are reproduced verbatim, with no wrapping.
               or other formatting applied.

        The ParagraphFormatterML class will treat consecutive lines of
        text as a single block to rewrap.  So there is no need to end lines
        with backslashes to create a single long logical line.

        As with docstrings, the leading space to the text block is ignored.'''))
    parser.print_help()

    print('---------------------\n')

    parser = argparse.ArgumentParser(prog='Preformat',
                                     description=preformat('''\
        This description help text will have this first long line wrapped to
        fit the target window size so that your text remains flexible.

            1. But lines such as
            2. this that that are indented beyond the first line's indent,
            3. are reproduced verbatim, with no wrapping.
               or other formatting applied.

        The ParagraphFormatterML class will treat consecutive lines of
        text as a single block to rewrap.  So there is no need to end lines
        with backslashes to create a single long logical line.

        As with docstrings, the leading space to the text block is ignored.'''))

    parser.add_argument('--example', help=preformat('''\
        This argument's help text will have this first long line wrapped to
        fit the target window size so that your text remains flexible.

            1. But lines such as
            2. this that that are indented beyond the first line's indent,
            3. are reproduced verbatim, with no wrapping.
               or other formatting applied.

        The ParagraphFormatterML class will treat consecutive lines of
        text as a single block to rewrap.  So there is no need to end lines
        with backslashes to create a single long logical line.

        As with docstrings, the leading space to the text block is ignored.'''))
    parser.print_help()