diff options
Diffstat (limited to 'Lib/textwrap.py')
-rw-r--r-- | Lib/textwrap.py | 73 |
1 files changed, 66 insertions, 7 deletions
diff --git a/Lib/textwrap.py b/Lib/textwrap.py index 7024d4d..2489180 100644 --- a/Lib/textwrap.py +++ b/Lib/textwrap.py @@ -7,7 +7,7 @@ import re -__all__ = ['TextWrapper', 'wrap', 'fill', 'dedent', 'indent'] +__all__ = ['TextWrapper', 'wrap', 'fill', 'dedent', 'indent', 'shorten'] # Hardcode the recognized whitespace characters to the US-ASCII # whitespace characters. The main reason for doing this is that in @@ -62,6 +62,10 @@ class TextWrapper: compound words. drop_whitespace (default: true) Drop leading and trailing whitespace from lines. + max_lines (default: None) + Truncate wrapped lines. + placeholder (default: ' [...]') + Append to the last line of truncated text. """ unicode_whitespace_trans = {} @@ -104,7 +108,10 @@ class TextWrapper: break_long_words=True, drop_whitespace=True, break_on_hyphens=True, - tabsize=8): + tabsize=8, + *, + max_lines=None, + placeholder=' [...]'): self.width = width self.initial_indent = initial_indent self.subsequent_indent = subsequent_indent @@ -115,6 +122,8 @@ class TextWrapper: self.drop_whitespace = drop_whitespace self.break_on_hyphens = break_on_hyphens self.tabsize = tabsize + self.max_lines = max_lines + self.placeholder = placeholder # -- Private methods ----------------------------------------------- @@ -223,6 +232,13 @@ class TextWrapper: lines = [] 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. @@ -265,18 +281,47 @@ class TextWrapper: # 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] - # Convert current line back to a string and store it in list - # of all lines (return value). if cur_line: - lines.append(indent + ''.join(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). + lines.append(indent + ''.join(cur_line)) + else: + while cur_line: + if (cur_line[-1].strip() and + cur_len + len(self.placeholder) <= width): + cur_line.append(self.placeholder) + lines.append(indent + ''.join(cur_line)) + break + 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 + break + lines.append(indent + self.placeholder.lstrip()) + break return lines + def _split_chunks(self, text): + text = self._munge_whitespace(text) + return self._split(text) # -- Public interface ---------------------------------------------- @@ -289,8 +334,7 @@ class TextWrapper: and all other whitespace characters (including newline) are converted to space. """ - text = self._munge_whitespace(text) - chunks = self._split(text) + chunks = self._split_chunks(text) if self.fix_sentence_endings: self._fix_sentence_endings(chunks) return self._wrap_chunks(chunks) @@ -332,6 +376,21 @@ def fill(text, width=70, **kwargs): w = TextWrapper(width=width, **kwargs) return w.fill(text) +def shorten(text, width, **kwargs): + """Collapse and truncate the given text to fit in the given width. + + The text first has its whitespace collapsed. If it then fits in + the *width*, it is returned as is. Otherwise, as many words + as possible are joined and then the placeholder is appended:: + + >>> textwrap.shorten("Hello world!", width=12) + 'Hello world!' + >>> textwrap.shorten("Hello world!", width=11) + 'Hello [...]' + """ + w = TextWrapper(width=width, max_lines=1, **kwargs) + return w.fill(' '.join(text.strip().split())) + # -- Loosely related functionality ------------------------------------- |