|
|
@ -12,103 +12,106 @@ class TextWrapper(textwrap.TextWrapper): |
|
|
|
|
|
|
|
def _wrap_chunks(self, chunks): |
|
|
|
lines = [] |
|
|
|
"""Override the mother class method. |
|
|
|
|
|
|
|
Most of that method is directly copied from the original class, except |
|
|
|
for the part with use_hyphenator. |
|
|
|
""" |
|
|
|
if self.width <= 0: |
|
|
|
raise ValueError("invalid width %r (must be > 0)" % self.width) |
|
|
|
if self.max_lines is not None: |
|
|
|
if self.max_lines > 1: |
|
|
|
indent = self.subsequent_indent |
|
|
|
else: |
|
|
|
indent = self.initial_indent |
|
|
|
if len(indent) + len(self.placeholder.lstrip()) > self.width: |
|
|
|
raise ValueError("placeholder too large for max width") |
|
|
|
|
|
|
|
# Arrange in reverse order so items can be efficiently popped |
|
|
|
# from a stack of chucks. |
|
|
|
chunks.reverse() |
|
|
|
|
|
|
|
# Start the list of chunks that will make up the current line. |
|
|
|
# cur_len is just the length of all the chunks in cur_line. |
|
|
|
cur_line = [] |
|
|
|
cur_len = 0 |
|
|
|
|
|
|
|
# Figure out which static string will prefix this line. |
|
|
|
if lines: |
|
|
|
indent = self.subsequent_indent |
|
|
|
if (chunks): |
|
|
|
"""Override the mother class method. |
|
|
|
|
|
|
|
Most of that method is directly copied from the original class, except |
|
|
|
for the part with use_hyphenator. |
|
|
|
""" |
|
|
|
if self.width <= 0: |
|
|
|
raise ValueError("invalid width %r (must be > 0)" % self.width) |
|
|
|
if self.max_lines is not None: |
|
|
|
if self.max_lines > 1: |
|
|
|
indent = self.subsequent_indent |
|
|
|
else: |
|
|
|
indent = self.initial_indent |
|
|
|
if len(indent) + len(self.placeholder.lstrip()) > self.width: |
|
|
|
raise ValueError("placeholder too large for max width") |
|
|
|
|
|
|
|
# Arrange in reverse order so items can be efficiently popped |
|
|
|
# from a stack of chucks. |
|
|
|
chunks.reverse() |
|
|
|
|
|
|
|
# Start the list of chunks that will make up the current line. |
|
|
|
# cur_len is just the length of all the chunks in cur_line. |
|
|
|
cur_line = [] |
|
|
|
cur_len = 0 |
|
|
|
|
|
|
|
# Figure out which static string will prefix this line. |
|
|
|
if lines: |
|
|
|
indent = self.subsequent_indent |
|
|
|
else: |
|
|
|
indent = self.initial_indent |
|
|
|
|
|
|
|
# Maximum width for this line. |
|
|
|
width = self.width - len(indent) |
|
|
|
|
|
|
|
# First chunk on line is whitespace -- drop it, unless this |
|
|
|
# is the very beginning of the text (ie. no lines started yet). |
|
|
|
if self.drop_whitespace and chunks[-1].strip() == '' and lines: |
|
|
|
del chunks[-1] |
|
|
|
|
|
|
|
while chunks: |
|
|
|
l = len(chunks[-1]) |
|
|
|
|
|
|
|
# Can at least squeeze this chunk onto the current line. |
|
|
|
if cur_len + l <= width: |
|
|
|
cur_line.append(chunks.pop()) |
|
|
|
cur_len += l |
|
|
|
|
|
|
|
# Nope, this line is full. |
|
|
|
# But try hyphenation. |
|
|
|
else: |
|
|
|
if self.use_hyphenator and (width - cur_len >= 2): |
|
|
|
hyphenated_chunk = self.use_hyphenator.wrap(chunks[-1], width - cur_len) |
|
|
|
if hyphenated_chunk: |
|
|
|
cur_line.append(hyphenated_chunk[0]) |
|
|
|
chunks[-1] = hyphenated_chunk[1] |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
# The current line is full, and the next chunk is too big to |
|
|
|
# fit on *any* line (not just this one). |
|
|
|
if chunks and len(chunks[-1]) > width: |
|
|
|
self._handle_long_word(chunks, cur_line, cur_len, width) |
|
|
|
cur_len = sum(map(len, cur_line)) |
|
|
|
|
|
|
|
# If the last chunk on this line is all whitespace, drop it. |
|
|
|
if self.drop_whitespace and cur_line and cur_line[-1].strip() == '': |
|
|
|
cur_len -= len(cur_line[-1]) |
|
|
|
del cur_line[-1] |
|
|
|
|
|
|
|
if cur_line: |
|
|
|
if (self.max_lines is None or |
|
|
|
len(lines) + 1 < self.max_lines or |
|
|
|
(not chunks or |
|
|
|
self.drop_whitespace and |
|
|
|
len(chunks) == 1 and |
|
|
|
not chunks[0].strip()) and cur_len <= width): |
|
|
|
# Convert current line back to a string and store it in |
|
|
|
# list of all lines (return value). |
|
|
|
return indent + ''.join(cur_line), ''.join(reversed(chunks)) |
|
|
|
else: |
|
|
|
while cur_line: |
|
|
|
if (cur_line[-1].strip() and |
|
|
|
cur_len + len(self.placeholder) <= width): |
|
|
|
cur_line.append(self.placeholder) |
|
|
|
|
|
|
|
return indent + ''.join(cur_line), ''.join(reversed(chunks)) |
|
|
|
|
|
|
|
cur_len -= len(cur_line[-1]) |
|
|
|
del cur_line[-1] |
|
|
|
else: |
|
|
|
if lines: |
|
|
|
prev_line = lines[-1].rstrip() |
|
|
|
if (len(prev_line) + len(self.placeholder) <= |
|
|
|
self.width): |
|
|
|
lines[-1] = prev_line + self.placeholder |
|
|
|
|
|
|
|
return indent + self.placeholder.lstrip(), ''.join(reversed(chunks)) |
|
|
|
else: |
|
|
|
indent = self.initial_indent |
|
|
|
|
|
|
|
# Maximum width for this line. |
|
|
|
width = self.width - len(indent) |
|
|
|
|
|
|
|
# First chunk on line is whitespace -- drop it, unless this |
|
|
|
# is the very beginning of the text (ie. no lines started yet). |
|
|
|
if self.drop_whitespace and chunks[-1].strip() == '' and lines: |
|
|
|
del chunks[-1] |
|
|
|
|
|
|
|
while chunks: |
|
|
|
l = len(chunks[-1]) |
|
|
|
|
|
|
|
# Can at least squeeze this chunk onto the current line. |
|
|
|
if cur_len + l <= width: |
|
|
|
cur_line.append(chunks.pop()) |
|
|
|
cur_len += l |
|
|
|
|
|
|
|
# Nope, this line is full. |
|
|
|
# But try hyphenation. |
|
|
|
else: |
|
|
|
if self.use_hyphenator and (width - cur_len >= 2): |
|
|
|
hyphenated_chunk = self.use_hyphenator.wrap(chunks[-1], width - cur_len) |
|
|
|
if hyphenated_chunk: |
|
|
|
cur_line.append(hyphenated_chunk[0]) |
|
|
|
chunks[-1] = hyphenated_chunk[1] |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
# The current line is full, and the next chunk is too big to |
|
|
|
# fit on *any* line (not just this one). |
|
|
|
if chunks and len(chunks[-1]) > width: |
|
|
|
self._handle_long_word(chunks, cur_line, cur_len, width) |
|
|
|
cur_len = sum(map(len, cur_line)) |
|
|
|
|
|
|
|
# If the last chunk on this line is all whitespace, drop it. |
|
|
|
if self.drop_whitespace and cur_line and cur_line[-1].strip() == '': |
|
|
|
cur_len -= len(cur_line[-1]) |
|
|
|
del cur_line[-1] |
|
|
|
|
|
|
|
if cur_line: |
|
|
|
if (self.max_lines is None or |
|
|
|
len(lines) + 1 < self.max_lines or |
|
|
|
(not chunks or |
|
|
|
self.drop_whitespace and |
|
|
|
len(chunks) == 1 and |
|
|
|
not chunks[0].strip()) and cur_len <= width): |
|
|
|
# Convert current line back to a string and store it in |
|
|
|
# list of all lines (return value). |
|
|
|
return indent + ''.join(cur_line), ''.join(reversed(chunks)) |
|
|
|
else: |
|
|
|
while cur_line: |
|
|
|
if (cur_line[-1].strip() and |
|
|
|
cur_len + len(self.placeholder) <= width): |
|
|
|
cur_line.append(self.placeholder) |
|
|
|
|
|
|
|
return indent + ''.join(cur_line), ''.join(reversed(chunks)) |
|
|
|
|
|
|
|
cur_len -= len(cur_line[-1]) |
|
|
|
del cur_line[-1] |
|
|
|
else: |
|
|
|
if lines: |
|
|
|
prev_line = lines[-1].rstrip() |
|
|
|
if (len(prev_line) + len(self.placeholder) <= |
|
|
|
self.width): |
|
|
|
lines[-1] = prev_line + self.placeholder |
|
|
|
|
|
|
|
return indent + self.placeholder.lstrip(), ''.join(reversed(chunks)) |
|
|
|
|
|
|
|
return '', [] |
|
|
|
|
|
|
|
def wrap_single_line (text, width=70, **kwargs): |
|
|
|
w = TextWrapper(width=width, **kwargs) |
|
|
|
return w.wrap(text) |
|
|
|