1*cda5da8dSAndroid Build Coastguard Worker"""Text wrapping and filling. 2*cda5da8dSAndroid Build Coastguard Worker""" 3*cda5da8dSAndroid Build Coastguard Worker 4*cda5da8dSAndroid Build Coastguard Worker# Copyright (C) 1999-2001 Gregory P. Ward. 5*cda5da8dSAndroid Build Coastguard Worker# Copyright (C) 2002, 2003 Python Software Foundation. 6*cda5da8dSAndroid Build Coastguard Worker# Written by Greg Ward <[email protected]> 7*cda5da8dSAndroid Build Coastguard Worker 8*cda5da8dSAndroid Build Coastguard Workerimport re 9*cda5da8dSAndroid Build Coastguard Worker 10*cda5da8dSAndroid Build Coastguard Worker__all__ = ['TextWrapper', 'wrap', 'fill', 'dedent', 'indent', 'shorten'] 11*cda5da8dSAndroid Build Coastguard Worker 12*cda5da8dSAndroid Build Coastguard Worker# Hardcode the recognized whitespace characters to the US-ASCII 13*cda5da8dSAndroid Build Coastguard Worker# whitespace characters. The main reason for doing this is that 14*cda5da8dSAndroid Build Coastguard Worker# some Unicode spaces (like \u00a0) are non-breaking whitespaces. 15*cda5da8dSAndroid Build Coastguard Worker_whitespace = '\t\n\x0b\x0c\r ' 16*cda5da8dSAndroid Build Coastguard Worker 17*cda5da8dSAndroid Build Coastguard Workerclass TextWrapper: 18*cda5da8dSAndroid Build Coastguard Worker """ 19*cda5da8dSAndroid Build Coastguard Worker Object for wrapping/filling text. The public interface consists of 20*cda5da8dSAndroid Build Coastguard Worker the wrap() and fill() methods; the other methods are just there for 21*cda5da8dSAndroid Build Coastguard Worker subclasses to override in order to tweak the default behaviour. 22*cda5da8dSAndroid Build Coastguard Worker If you want to completely replace the main wrapping algorithm, 23*cda5da8dSAndroid Build Coastguard Worker you'll probably have to override _wrap_chunks(). 24*cda5da8dSAndroid Build Coastguard Worker 25*cda5da8dSAndroid Build Coastguard Worker Several instance attributes control various aspects of wrapping: 26*cda5da8dSAndroid Build Coastguard Worker width (default: 70) 27*cda5da8dSAndroid Build Coastguard Worker the maximum width of wrapped lines (unless break_long_words 28*cda5da8dSAndroid Build Coastguard Worker is false) 29*cda5da8dSAndroid Build Coastguard Worker initial_indent (default: "") 30*cda5da8dSAndroid Build Coastguard Worker string that will be prepended to the first line of wrapped 31*cda5da8dSAndroid Build Coastguard Worker output. Counts towards the line's width. 32*cda5da8dSAndroid Build Coastguard Worker subsequent_indent (default: "") 33*cda5da8dSAndroid Build Coastguard Worker string that will be prepended to all lines save the first 34*cda5da8dSAndroid Build Coastguard Worker of wrapped output; also counts towards each line's width. 35*cda5da8dSAndroid Build Coastguard Worker expand_tabs (default: true) 36*cda5da8dSAndroid Build Coastguard Worker Expand tabs in input text to spaces before further processing. 37*cda5da8dSAndroid Build Coastguard Worker Each tab will become 0 .. 'tabsize' spaces, depending on its position 38*cda5da8dSAndroid Build Coastguard Worker in its line. If false, each tab is treated as a single character. 39*cda5da8dSAndroid Build Coastguard Worker tabsize (default: 8) 40*cda5da8dSAndroid Build Coastguard Worker Expand tabs in input text to 0 .. 'tabsize' spaces, unless 41*cda5da8dSAndroid Build Coastguard Worker 'expand_tabs' is false. 42*cda5da8dSAndroid Build Coastguard Worker replace_whitespace (default: true) 43*cda5da8dSAndroid Build Coastguard Worker Replace all whitespace characters in the input text by spaces 44*cda5da8dSAndroid Build Coastguard Worker after tab expansion. Note that if expand_tabs is false and 45*cda5da8dSAndroid Build Coastguard Worker replace_whitespace is true, every tab will be converted to a 46*cda5da8dSAndroid Build Coastguard Worker single space! 47*cda5da8dSAndroid Build Coastguard Worker fix_sentence_endings (default: false) 48*cda5da8dSAndroid Build Coastguard Worker Ensure that sentence-ending punctuation is always followed 49*cda5da8dSAndroid Build Coastguard Worker by two spaces. Off by default because the algorithm is 50*cda5da8dSAndroid Build Coastguard Worker (unavoidably) imperfect. 51*cda5da8dSAndroid Build Coastguard Worker break_long_words (default: true) 52*cda5da8dSAndroid Build Coastguard Worker Break words longer than 'width'. If false, those words will not 53*cda5da8dSAndroid Build Coastguard Worker be broken, and some lines might be longer than 'width'. 54*cda5da8dSAndroid Build Coastguard Worker break_on_hyphens (default: true) 55*cda5da8dSAndroid Build Coastguard Worker Allow breaking hyphenated words. If true, wrapping will occur 56*cda5da8dSAndroid Build Coastguard Worker preferably on whitespaces and right after hyphens part of 57*cda5da8dSAndroid Build Coastguard Worker compound words. 58*cda5da8dSAndroid Build Coastguard Worker drop_whitespace (default: true) 59*cda5da8dSAndroid Build Coastguard Worker Drop leading and trailing whitespace from lines. 60*cda5da8dSAndroid Build Coastguard Worker max_lines (default: None) 61*cda5da8dSAndroid Build Coastguard Worker Truncate wrapped lines. 62*cda5da8dSAndroid Build Coastguard Worker placeholder (default: ' [...]') 63*cda5da8dSAndroid Build Coastguard Worker Append to the last line of truncated text. 64*cda5da8dSAndroid Build Coastguard Worker """ 65*cda5da8dSAndroid Build Coastguard Worker 66*cda5da8dSAndroid Build Coastguard Worker unicode_whitespace_trans = dict.fromkeys(map(ord, _whitespace), ord(' ')) 67*cda5da8dSAndroid Build Coastguard Worker 68*cda5da8dSAndroid Build Coastguard Worker # This funky little regex is just the trick for splitting 69*cda5da8dSAndroid Build Coastguard Worker # text up into word-wrappable chunks. E.g. 70*cda5da8dSAndroid Build Coastguard Worker # "Hello there -- you goof-ball, use the -b option!" 71*cda5da8dSAndroid Build Coastguard Worker # splits into 72*cda5da8dSAndroid Build Coastguard Worker # Hello/ /there/ /--/ /you/ /goof-/ball,/ /use/ /the/ /-b/ /option! 73*cda5da8dSAndroid Build Coastguard Worker # (after stripping out empty strings). 74*cda5da8dSAndroid Build Coastguard Worker word_punct = r'[\w!"\'&.,?]' 75*cda5da8dSAndroid Build Coastguard Worker letter = r'[^\d\W]' 76*cda5da8dSAndroid Build Coastguard Worker whitespace = r'[%s]' % re.escape(_whitespace) 77*cda5da8dSAndroid Build Coastguard Worker nowhitespace = '[^' + whitespace[1:] 78*cda5da8dSAndroid Build Coastguard Worker wordsep_re = re.compile(r''' 79*cda5da8dSAndroid Build Coastguard Worker ( # any whitespace 80*cda5da8dSAndroid Build Coastguard Worker %(ws)s+ 81*cda5da8dSAndroid Build Coastguard Worker | # em-dash between words 82*cda5da8dSAndroid Build Coastguard Worker (?<=%(wp)s) -{2,} (?=\w) 83*cda5da8dSAndroid Build Coastguard Worker | # word, possibly hyphenated 84*cda5da8dSAndroid Build Coastguard Worker %(nws)s+? (?: 85*cda5da8dSAndroid Build Coastguard Worker # hyphenated word 86*cda5da8dSAndroid Build Coastguard Worker -(?: (?<=%(lt)s{2}-) | (?<=%(lt)s-%(lt)s-)) 87*cda5da8dSAndroid Build Coastguard Worker (?= %(lt)s -? %(lt)s) 88*cda5da8dSAndroid Build Coastguard Worker | # end of word 89*cda5da8dSAndroid Build Coastguard Worker (?=%(ws)s|\Z) 90*cda5da8dSAndroid Build Coastguard Worker | # em-dash 91*cda5da8dSAndroid Build Coastguard Worker (?<=%(wp)s) (?=-{2,}\w) 92*cda5da8dSAndroid Build Coastguard Worker ) 93*cda5da8dSAndroid Build Coastguard Worker )''' % {'wp': word_punct, 'lt': letter, 94*cda5da8dSAndroid Build Coastguard Worker 'ws': whitespace, 'nws': nowhitespace}, 95*cda5da8dSAndroid Build Coastguard Worker re.VERBOSE) 96*cda5da8dSAndroid Build Coastguard Worker del word_punct, letter, nowhitespace 97*cda5da8dSAndroid Build Coastguard Worker 98*cda5da8dSAndroid Build Coastguard Worker # This less funky little regex just split on recognized spaces. E.g. 99*cda5da8dSAndroid Build Coastguard Worker # "Hello there -- you goof-ball, use the -b option!" 100*cda5da8dSAndroid Build Coastguard Worker # splits into 101*cda5da8dSAndroid Build Coastguard Worker # Hello/ /there/ /--/ /you/ /goof-ball,/ /use/ /the/ /-b/ /option!/ 102*cda5da8dSAndroid Build Coastguard Worker wordsep_simple_re = re.compile(r'(%s+)' % whitespace) 103*cda5da8dSAndroid Build Coastguard Worker del whitespace 104*cda5da8dSAndroid Build Coastguard Worker 105*cda5da8dSAndroid Build Coastguard Worker # XXX this is not locale- or charset-aware -- string.lowercase 106*cda5da8dSAndroid Build Coastguard Worker # is US-ASCII only (and therefore English-only) 107*cda5da8dSAndroid Build Coastguard Worker sentence_end_re = re.compile(r'[a-z]' # lowercase letter 108*cda5da8dSAndroid Build Coastguard Worker r'[\.\!\?]' # sentence-ending punct. 109*cda5da8dSAndroid Build Coastguard Worker r'[\"\']?' # optional end-of-quote 110*cda5da8dSAndroid Build Coastguard Worker r'\Z') # end of chunk 111*cda5da8dSAndroid Build Coastguard Worker 112*cda5da8dSAndroid Build Coastguard Worker def __init__(self, 113*cda5da8dSAndroid Build Coastguard Worker width=70, 114*cda5da8dSAndroid Build Coastguard Worker initial_indent="", 115*cda5da8dSAndroid Build Coastguard Worker subsequent_indent="", 116*cda5da8dSAndroid Build Coastguard Worker expand_tabs=True, 117*cda5da8dSAndroid Build Coastguard Worker replace_whitespace=True, 118*cda5da8dSAndroid Build Coastguard Worker fix_sentence_endings=False, 119*cda5da8dSAndroid Build Coastguard Worker break_long_words=True, 120*cda5da8dSAndroid Build Coastguard Worker drop_whitespace=True, 121*cda5da8dSAndroid Build Coastguard Worker break_on_hyphens=True, 122*cda5da8dSAndroid Build Coastguard Worker tabsize=8, 123*cda5da8dSAndroid Build Coastguard Worker *, 124*cda5da8dSAndroid Build Coastguard Worker max_lines=None, 125*cda5da8dSAndroid Build Coastguard Worker placeholder=' [...]'): 126*cda5da8dSAndroid Build Coastguard Worker self.width = width 127*cda5da8dSAndroid Build Coastguard Worker self.initial_indent = initial_indent 128*cda5da8dSAndroid Build Coastguard Worker self.subsequent_indent = subsequent_indent 129*cda5da8dSAndroid Build Coastguard Worker self.expand_tabs = expand_tabs 130*cda5da8dSAndroid Build Coastguard Worker self.replace_whitespace = replace_whitespace 131*cda5da8dSAndroid Build Coastguard Worker self.fix_sentence_endings = fix_sentence_endings 132*cda5da8dSAndroid Build Coastguard Worker self.break_long_words = break_long_words 133*cda5da8dSAndroid Build Coastguard Worker self.drop_whitespace = drop_whitespace 134*cda5da8dSAndroid Build Coastguard Worker self.break_on_hyphens = break_on_hyphens 135*cda5da8dSAndroid Build Coastguard Worker self.tabsize = tabsize 136*cda5da8dSAndroid Build Coastguard Worker self.max_lines = max_lines 137*cda5da8dSAndroid Build Coastguard Worker self.placeholder = placeholder 138*cda5da8dSAndroid Build Coastguard Worker 139*cda5da8dSAndroid Build Coastguard Worker 140*cda5da8dSAndroid Build Coastguard Worker # -- Private methods ----------------------------------------------- 141*cda5da8dSAndroid Build Coastguard Worker # (possibly useful for subclasses to override) 142*cda5da8dSAndroid Build Coastguard Worker 143*cda5da8dSAndroid Build Coastguard Worker def _munge_whitespace(self, text): 144*cda5da8dSAndroid Build Coastguard Worker """_munge_whitespace(text : string) -> string 145*cda5da8dSAndroid Build Coastguard Worker 146*cda5da8dSAndroid Build Coastguard Worker Munge whitespace in text: expand tabs and convert all other 147*cda5da8dSAndroid Build Coastguard Worker whitespace characters to spaces. Eg. " foo\\tbar\\n\\nbaz" 148*cda5da8dSAndroid Build Coastguard Worker becomes " foo bar baz". 149*cda5da8dSAndroid Build Coastguard Worker """ 150*cda5da8dSAndroid Build Coastguard Worker if self.expand_tabs: 151*cda5da8dSAndroid Build Coastguard Worker text = text.expandtabs(self.tabsize) 152*cda5da8dSAndroid Build Coastguard Worker if self.replace_whitespace: 153*cda5da8dSAndroid Build Coastguard Worker text = text.translate(self.unicode_whitespace_trans) 154*cda5da8dSAndroid Build Coastguard Worker return text 155*cda5da8dSAndroid Build Coastguard Worker 156*cda5da8dSAndroid Build Coastguard Worker 157*cda5da8dSAndroid Build Coastguard Worker def _split(self, text): 158*cda5da8dSAndroid Build Coastguard Worker """_split(text : string) -> [string] 159*cda5da8dSAndroid Build Coastguard Worker 160*cda5da8dSAndroid Build Coastguard Worker Split the text to wrap into indivisible chunks. Chunks are 161*cda5da8dSAndroid Build Coastguard Worker not quite the same as words; see _wrap_chunks() for full 162*cda5da8dSAndroid Build Coastguard Worker details. As an example, the text 163*cda5da8dSAndroid Build Coastguard Worker Look, goof-ball -- use the -b option! 164*cda5da8dSAndroid Build Coastguard Worker breaks into the following chunks: 165*cda5da8dSAndroid Build Coastguard Worker 'Look,', ' ', 'goof-', 'ball', ' ', '--', ' ', 166*cda5da8dSAndroid Build Coastguard Worker 'use', ' ', 'the', ' ', '-b', ' ', 'option!' 167*cda5da8dSAndroid Build Coastguard Worker if break_on_hyphens is True, or in: 168*cda5da8dSAndroid Build Coastguard Worker 'Look,', ' ', 'goof-ball', ' ', '--', ' ', 169*cda5da8dSAndroid Build Coastguard Worker 'use', ' ', 'the', ' ', '-b', ' ', option!' 170*cda5da8dSAndroid Build Coastguard Worker otherwise. 171*cda5da8dSAndroid Build Coastguard Worker """ 172*cda5da8dSAndroid Build Coastguard Worker if self.break_on_hyphens is True: 173*cda5da8dSAndroid Build Coastguard Worker chunks = self.wordsep_re.split(text) 174*cda5da8dSAndroid Build Coastguard Worker else: 175*cda5da8dSAndroid Build Coastguard Worker chunks = self.wordsep_simple_re.split(text) 176*cda5da8dSAndroid Build Coastguard Worker chunks = [c for c in chunks if c] 177*cda5da8dSAndroid Build Coastguard Worker return chunks 178*cda5da8dSAndroid Build Coastguard Worker 179*cda5da8dSAndroid Build Coastguard Worker def _fix_sentence_endings(self, chunks): 180*cda5da8dSAndroid Build Coastguard Worker """_fix_sentence_endings(chunks : [string]) 181*cda5da8dSAndroid Build Coastguard Worker 182*cda5da8dSAndroid Build Coastguard Worker Correct for sentence endings buried in 'chunks'. Eg. when the 183*cda5da8dSAndroid Build Coastguard Worker original text contains "... foo.\\nBar ...", munge_whitespace() 184*cda5da8dSAndroid Build Coastguard Worker and split() will convert that to [..., "foo.", " ", "Bar", ...] 185*cda5da8dSAndroid Build Coastguard Worker which has one too few spaces; this method simply changes the one 186*cda5da8dSAndroid Build Coastguard Worker space to two. 187*cda5da8dSAndroid Build Coastguard Worker """ 188*cda5da8dSAndroid Build Coastguard Worker i = 0 189*cda5da8dSAndroid Build Coastguard Worker patsearch = self.sentence_end_re.search 190*cda5da8dSAndroid Build Coastguard Worker while i < len(chunks)-1: 191*cda5da8dSAndroid Build Coastguard Worker if chunks[i+1] == " " and patsearch(chunks[i]): 192*cda5da8dSAndroid Build Coastguard Worker chunks[i+1] = " " 193*cda5da8dSAndroid Build Coastguard Worker i += 2 194*cda5da8dSAndroid Build Coastguard Worker else: 195*cda5da8dSAndroid Build Coastguard Worker i += 1 196*cda5da8dSAndroid Build Coastguard Worker 197*cda5da8dSAndroid Build Coastguard Worker def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): 198*cda5da8dSAndroid Build Coastguard Worker """_handle_long_word(chunks : [string], 199*cda5da8dSAndroid Build Coastguard Worker cur_line : [string], 200*cda5da8dSAndroid Build Coastguard Worker cur_len : int, width : int) 201*cda5da8dSAndroid Build Coastguard Worker 202*cda5da8dSAndroid Build Coastguard Worker Handle a chunk of text (most likely a word, not whitespace) that 203*cda5da8dSAndroid Build Coastguard Worker is too long to fit in any line. 204*cda5da8dSAndroid Build Coastguard Worker """ 205*cda5da8dSAndroid Build Coastguard Worker # Figure out when indent is larger than the specified width, and make 206*cda5da8dSAndroid Build Coastguard Worker # sure at least one character is stripped off on every pass 207*cda5da8dSAndroid Build Coastguard Worker if width < 1: 208*cda5da8dSAndroid Build Coastguard Worker space_left = 1 209*cda5da8dSAndroid Build Coastguard Worker else: 210*cda5da8dSAndroid Build Coastguard Worker space_left = width - cur_len 211*cda5da8dSAndroid Build Coastguard Worker 212*cda5da8dSAndroid Build Coastguard Worker # If we're allowed to break long words, then do so: put as much 213*cda5da8dSAndroid Build Coastguard Worker # of the next chunk onto the current line as will fit. 214*cda5da8dSAndroid Build Coastguard Worker if self.break_long_words: 215*cda5da8dSAndroid Build Coastguard Worker end = space_left 216*cda5da8dSAndroid Build Coastguard Worker chunk = reversed_chunks[-1] 217*cda5da8dSAndroid Build Coastguard Worker if self.break_on_hyphens and len(chunk) > space_left: 218*cda5da8dSAndroid Build Coastguard Worker # break after last hyphen, but only if there are 219*cda5da8dSAndroid Build Coastguard Worker # non-hyphens before it 220*cda5da8dSAndroid Build Coastguard Worker hyphen = chunk.rfind('-', 0, space_left) 221*cda5da8dSAndroid Build Coastguard Worker if hyphen > 0 and any(c != '-' for c in chunk[:hyphen]): 222*cda5da8dSAndroid Build Coastguard Worker end = hyphen + 1 223*cda5da8dSAndroid Build Coastguard Worker cur_line.append(chunk[:end]) 224*cda5da8dSAndroid Build Coastguard Worker reversed_chunks[-1] = chunk[end:] 225*cda5da8dSAndroid Build Coastguard Worker 226*cda5da8dSAndroid Build Coastguard Worker # Otherwise, we have to preserve the long word intact. Only add 227*cda5da8dSAndroid Build Coastguard Worker # it to the current line if there's nothing already there -- 228*cda5da8dSAndroid Build Coastguard Worker # that minimizes how much we violate the width constraint. 229*cda5da8dSAndroid Build Coastguard Worker elif not cur_line: 230*cda5da8dSAndroid Build Coastguard Worker cur_line.append(reversed_chunks.pop()) 231*cda5da8dSAndroid Build Coastguard Worker 232*cda5da8dSAndroid Build Coastguard Worker # If we're not allowed to break long words, and there's already 233*cda5da8dSAndroid Build Coastguard Worker # text on the current line, do nothing. Next time through the 234*cda5da8dSAndroid Build Coastguard Worker # main loop of _wrap_chunks(), we'll wind up here again, but 235*cda5da8dSAndroid Build Coastguard Worker # cur_len will be zero, so the next line will be entirely 236*cda5da8dSAndroid Build Coastguard Worker # devoted to the long word that we can't handle right now. 237*cda5da8dSAndroid Build Coastguard Worker 238*cda5da8dSAndroid Build Coastguard Worker def _wrap_chunks(self, chunks): 239*cda5da8dSAndroid Build Coastguard Worker """_wrap_chunks(chunks : [string]) -> [string] 240*cda5da8dSAndroid Build Coastguard Worker 241*cda5da8dSAndroid Build Coastguard Worker Wrap a sequence of text chunks and return a list of lines of 242*cda5da8dSAndroid Build Coastguard Worker length 'self.width' or less. (If 'break_long_words' is false, 243*cda5da8dSAndroid Build Coastguard Worker some lines may be longer than this.) Chunks correspond roughly 244*cda5da8dSAndroid Build Coastguard Worker to words and the whitespace between them: each chunk is 245*cda5da8dSAndroid Build Coastguard Worker indivisible (modulo 'break_long_words'), but a line break can 246*cda5da8dSAndroid Build Coastguard Worker come between any two chunks. Chunks should not have internal 247*cda5da8dSAndroid Build Coastguard Worker whitespace; ie. a chunk is either all whitespace or a "word". 248*cda5da8dSAndroid Build Coastguard Worker Whitespace chunks will be removed from the beginning and end of 249*cda5da8dSAndroid Build Coastguard Worker lines, but apart from that whitespace is preserved. 250*cda5da8dSAndroid Build Coastguard Worker """ 251*cda5da8dSAndroid Build Coastguard Worker lines = [] 252*cda5da8dSAndroid Build Coastguard Worker if self.width <= 0: 253*cda5da8dSAndroid Build Coastguard Worker raise ValueError("invalid width %r (must be > 0)" % self.width) 254*cda5da8dSAndroid Build Coastguard Worker if self.max_lines is not None: 255*cda5da8dSAndroid Build Coastguard Worker if self.max_lines > 1: 256*cda5da8dSAndroid Build Coastguard Worker indent = self.subsequent_indent 257*cda5da8dSAndroid Build Coastguard Worker else: 258*cda5da8dSAndroid Build Coastguard Worker indent = self.initial_indent 259*cda5da8dSAndroid Build Coastguard Worker if len(indent) + len(self.placeholder.lstrip()) > self.width: 260*cda5da8dSAndroid Build Coastguard Worker raise ValueError("placeholder too large for max width") 261*cda5da8dSAndroid Build Coastguard Worker 262*cda5da8dSAndroid Build Coastguard Worker # Arrange in reverse order so items can be efficiently popped 263*cda5da8dSAndroid Build Coastguard Worker # from a stack of chucks. 264*cda5da8dSAndroid Build Coastguard Worker chunks.reverse() 265*cda5da8dSAndroid Build Coastguard Worker 266*cda5da8dSAndroid Build Coastguard Worker while chunks: 267*cda5da8dSAndroid Build Coastguard Worker 268*cda5da8dSAndroid Build Coastguard Worker # Start the list of chunks that will make up the current line. 269*cda5da8dSAndroid Build Coastguard Worker # cur_len is just the length of all the chunks in cur_line. 270*cda5da8dSAndroid Build Coastguard Worker cur_line = [] 271*cda5da8dSAndroid Build Coastguard Worker cur_len = 0 272*cda5da8dSAndroid Build Coastguard Worker 273*cda5da8dSAndroid Build Coastguard Worker # Figure out which static string will prefix this line. 274*cda5da8dSAndroid Build Coastguard Worker if lines: 275*cda5da8dSAndroid Build Coastguard Worker indent = self.subsequent_indent 276*cda5da8dSAndroid Build Coastguard Worker else: 277*cda5da8dSAndroid Build Coastguard Worker indent = self.initial_indent 278*cda5da8dSAndroid Build Coastguard Worker 279*cda5da8dSAndroid Build Coastguard Worker # Maximum width for this line. 280*cda5da8dSAndroid Build Coastguard Worker width = self.width - len(indent) 281*cda5da8dSAndroid Build Coastguard Worker 282*cda5da8dSAndroid Build Coastguard Worker # First chunk on line is whitespace -- drop it, unless this 283*cda5da8dSAndroid Build Coastguard Worker # is the very beginning of the text (ie. no lines started yet). 284*cda5da8dSAndroid Build Coastguard Worker if self.drop_whitespace and chunks[-1].strip() == '' and lines: 285*cda5da8dSAndroid Build Coastguard Worker del chunks[-1] 286*cda5da8dSAndroid Build Coastguard Worker 287*cda5da8dSAndroid Build Coastguard Worker while chunks: 288*cda5da8dSAndroid Build Coastguard Worker l = len(chunks[-1]) 289*cda5da8dSAndroid Build Coastguard Worker 290*cda5da8dSAndroid Build Coastguard Worker # Can at least squeeze this chunk onto the current line. 291*cda5da8dSAndroid Build Coastguard Worker if cur_len + l <= width: 292*cda5da8dSAndroid Build Coastguard Worker cur_line.append(chunks.pop()) 293*cda5da8dSAndroid Build Coastguard Worker cur_len += l 294*cda5da8dSAndroid Build Coastguard Worker 295*cda5da8dSAndroid Build Coastguard Worker # Nope, this line is full. 296*cda5da8dSAndroid Build Coastguard Worker else: 297*cda5da8dSAndroid Build Coastguard Worker break 298*cda5da8dSAndroid Build Coastguard Worker 299*cda5da8dSAndroid Build Coastguard Worker # The current line is full, and the next chunk is too big to 300*cda5da8dSAndroid Build Coastguard Worker # fit on *any* line (not just this one). 301*cda5da8dSAndroid Build Coastguard Worker if chunks and len(chunks[-1]) > width: 302*cda5da8dSAndroid Build Coastguard Worker self._handle_long_word(chunks, cur_line, cur_len, width) 303*cda5da8dSAndroid Build Coastguard Worker cur_len = sum(map(len, cur_line)) 304*cda5da8dSAndroid Build Coastguard Worker 305*cda5da8dSAndroid Build Coastguard Worker # If the last chunk on this line is all whitespace, drop it. 306*cda5da8dSAndroid Build Coastguard Worker if self.drop_whitespace and cur_line and cur_line[-1].strip() == '': 307*cda5da8dSAndroid Build Coastguard Worker cur_len -= len(cur_line[-1]) 308*cda5da8dSAndroid Build Coastguard Worker del cur_line[-1] 309*cda5da8dSAndroid Build Coastguard Worker 310*cda5da8dSAndroid Build Coastguard Worker if cur_line: 311*cda5da8dSAndroid Build Coastguard Worker if (self.max_lines is None or 312*cda5da8dSAndroid Build Coastguard Worker len(lines) + 1 < self.max_lines or 313*cda5da8dSAndroid Build Coastguard Worker (not chunks or 314*cda5da8dSAndroid Build Coastguard Worker self.drop_whitespace and 315*cda5da8dSAndroid Build Coastguard Worker len(chunks) == 1 and 316*cda5da8dSAndroid Build Coastguard Worker not chunks[0].strip()) and cur_len <= width): 317*cda5da8dSAndroid Build Coastguard Worker # Convert current line back to a string and store it in 318*cda5da8dSAndroid Build Coastguard Worker # list of all lines (return value). 319*cda5da8dSAndroid Build Coastguard Worker lines.append(indent + ''.join(cur_line)) 320*cda5da8dSAndroid Build Coastguard Worker else: 321*cda5da8dSAndroid Build Coastguard Worker while cur_line: 322*cda5da8dSAndroid Build Coastguard Worker if (cur_line[-1].strip() and 323*cda5da8dSAndroid Build Coastguard Worker cur_len + len(self.placeholder) <= width): 324*cda5da8dSAndroid Build Coastguard Worker cur_line.append(self.placeholder) 325*cda5da8dSAndroid Build Coastguard Worker lines.append(indent + ''.join(cur_line)) 326*cda5da8dSAndroid Build Coastguard Worker break 327*cda5da8dSAndroid Build Coastguard Worker cur_len -= len(cur_line[-1]) 328*cda5da8dSAndroid Build Coastguard Worker del cur_line[-1] 329*cda5da8dSAndroid Build Coastguard Worker else: 330*cda5da8dSAndroid Build Coastguard Worker if lines: 331*cda5da8dSAndroid Build Coastguard Worker prev_line = lines[-1].rstrip() 332*cda5da8dSAndroid Build Coastguard Worker if (len(prev_line) + len(self.placeholder) <= 333*cda5da8dSAndroid Build Coastguard Worker self.width): 334*cda5da8dSAndroid Build Coastguard Worker lines[-1] = prev_line + self.placeholder 335*cda5da8dSAndroid Build Coastguard Worker break 336*cda5da8dSAndroid Build Coastguard Worker lines.append(indent + self.placeholder.lstrip()) 337*cda5da8dSAndroid Build Coastguard Worker break 338*cda5da8dSAndroid Build Coastguard Worker 339*cda5da8dSAndroid Build Coastguard Worker return lines 340*cda5da8dSAndroid Build Coastguard Worker 341*cda5da8dSAndroid Build Coastguard Worker def _split_chunks(self, text): 342*cda5da8dSAndroid Build Coastguard Worker text = self._munge_whitespace(text) 343*cda5da8dSAndroid Build Coastguard Worker return self._split(text) 344*cda5da8dSAndroid Build Coastguard Worker 345*cda5da8dSAndroid Build Coastguard Worker # -- Public interface ---------------------------------------------- 346*cda5da8dSAndroid Build Coastguard Worker 347*cda5da8dSAndroid Build Coastguard Worker def wrap(self, text): 348*cda5da8dSAndroid Build Coastguard Worker """wrap(text : string) -> [string] 349*cda5da8dSAndroid Build Coastguard Worker 350*cda5da8dSAndroid Build Coastguard Worker Reformat the single paragraph in 'text' so it fits in lines of 351*cda5da8dSAndroid Build Coastguard Worker no more than 'self.width' columns, and return a list of wrapped 352*cda5da8dSAndroid Build Coastguard Worker lines. Tabs in 'text' are expanded with string.expandtabs(), 353*cda5da8dSAndroid Build Coastguard Worker and all other whitespace characters (including newline) are 354*cda5da8dSAndroid Build Coastguard Worker converted to space. 355*cda5da8dSAndroid Build Coastguard Worker """ 356*cda5da8dSAndroid Build Coastguard Worker chunks = self._split_chunks(text) 357*cda5da8dSAndroid Build Coastguard Worker if self.fix_sentence_endings: 358*cda5da8dSAndroid Build Coastguard Worker self._fix_sentence_endings(chunks) 359*cda5da8dSAndroid Build Coastguard Worker return self._wrap_chunks(chunks) 360*cda5da8dSAndroid Build Coastguard Worker 361*cda5da8dSAndroid Build Coastguard Worker def fill(self, text): 362*cda5da8dSAndroid Build Coastguard Worker """fill(text : string) -> string 363*cda5da8dSAndroid Build Coastguard Worker 364*cda5da8dSAndroid Build Coastguard Worker Reformat the single paragraph in 'text' to fit in lines of no 365*cda5da8dSAndroid Build Coastguard Worker more than 'self.width' columns, and return a new string 366*cda5da8dSAndroid Build Coastguard Worker containing the entire wrapped paragraph. 367*cda5da8dSAndroid Build Coastguard Worker """ 368*cda5da8dSAndroid Build Coastguard Worker return "\n".join(self.wrap(text)) 369*cda5da8dSAndroid Build Coastguard Worker 370*cda5da8dSAndroid Build Coastguard Worker 371*cda5da8dSAndroid Build Coastguard Worker# -- Convenience interface --------------------------------------------- 372*cda5da8dSAndroid Build Coastguard Worker 373*cda5da8dSAndroid Build Coastguard Workerdef wrap(text, width=70, **kwargs): 374*cda5da8dSAndroid Build Coastguard Worker """Wrap a single paragraph of text, returning a list of wrapped lines. 375*cda5da8dSAndroid Build Coastguard Worker 376*cda5da8dSAndroid Build Coastguard Worker Reformat the single paragraph in 'text' so it fits in lines of no 377*cda5da8dSAndroid Build Coastguard Worker more than 'width' columns, and return a list of wrapped lines. By 378*cda5da8dSAndroid Build Coastguard Worker default, tabs in 'text' are expanded with string.expandtabs(), and 379*cda5da8dSAndroid Build Coastguard Worker all other whitespace characters (including newline) are converted to 380*cda5da8dSAndroid Build Coastguard Worker space. See TextWrapper class for available keyword args to customize 381*cda5da8dSAndroid Build Coastguard Worker wrapping behaviour. 382*cda5da8dSAndroid Build Coastguard Worker """ 383*cda5da8dSAndroid Build Coastguard Worker w = TextWrapper(width=width, **kwargs) 384*cda5da8dSAndroid Build Coastguard Worker return w.wrap(text) 385*cda5da8dSAndroid Build Coastguard Worker 386*cda5da8dSAndroid Build Coastguard Workerdef fill(text, width=70, **kwargs): 387*cda5da8dSAndroid Build Coastguard Worker """Fill a single paragraph of text, returning a new string. 388*cda5da8dSAndroid Build Coastguard Worker 389*cda5da8dSAndroid Build Coastguard Worker Reformat the single paragraph in 'text' to fit in lines of no more 390*cda5da8dSAndroid Build Coastguard Worker than 'width' columns, and return a new string containing the entire 391*cda5da8dSAndroid Build Coastguard Worker wrapped paragraph. As with wrap(), tabs are expanded and other 392*cda5da8dSAndroid Build Coastguard Worker whitespace characters converted to space. See TextWrapper class for 393*cda5da8dSAndroid Build Coastguard Worker available keyword args to customize wrapping behaviour. 394*cda5da8dSAndroid Build Coastguard Worker """ 395*cda5da8dSAndroid Build Coastguard Worker w = TextWrapper(width=width, **kwargs) 396*cda5da8dSAndroid Build Coastguard Worker return w.fill(text) 397*cda5da8dSAndroid Build Coastguard Worker 398*cda5da8dSAndroid Build Coastguard Workerdef shorten(text, width, **kwargs): 399*cda5da8dSAndroid Build Coastguard Worker """Collapse and truncate the given text to fit in the given width. 400*cda5da8dSAndroid Build Coastguard Worker 401*cda5da8dSAndroid Build Coastguard Worker The text first has its whitespace collapsed. If it then fits in 402*cda5da8dSAndroid Build Coastguard Worker the *width*, it is returned as is. Otherwise, as many words 403*cda5da8dSAndroid Build Coastguard Worker as possible are joined and then the placeholder is appended:: 404*cda5da8dSAndroid Build Coastguard Worker 405*cda5da8dSAndroid Build Coastguard Worker >>> textwrap.shorten("Hello world!", width=12) 406*cda5da8dSAndroid Build Coastguard Worker 'Hello world!' 407*cda5da8dSAndroid Build Coastguard Worker >>> textwrap.shorten("Hello world!", width=11) 408*cda5da8dSAndroid Build Coastguard Worker 'Hello [...]' 409*cda5da8dSAndroid Build Coastguard Worker """ 410*cda5da8dSAndroid Build Coastguard Worker w = TextWrapper(width=width, max_lines=1, **kwargs) 411*cda5da8dSAndroid Build Coastguard Worker return w.fill(' '.join(text.strip().split())) 412*cda5da8dSAndroid Build Coastguard Worker 413*cda5da8dSAndroid Build Coastguard Worker 414*cda5da8dSAndroid Build Coastguard Worker# -- Loosely related functionality ------------------------------------- 415*cda5da8dSAndroid Build Coastguard Worker 416*cda5da8dSAndroid Build Coastguard Worker_whitespace_only_re = re.compile('^[ \t]+$', re.MULTILINE) 417*cda5da8dSAndroid Build Coastguard Worker_leading_whitespace_re = re.compile('(^[ \t]*)(?:[^ \t\n])', re.MULTILINE) 418*cda5da8dSAndroid Build Coastguard Worker 419*cda5da8dSAndroid Build Coastguard Workerdef dedent(text): 420*cda5da8dSAndroid Build Coastguard Worker """Remove any common leading whitespace from every line in `text`. 421*cda5da8dSAndroid Build Coastguard Worker 422*cda5da8dSAndroid Build Coastguard Worker This can be used to make triple-quoted strings line up with the left 423*cda5da8dSAndroid Build Coastguard Worker edge of the display, while still presenting them in the source code 424*cda5da8dSAndroid Build Coastguard Worker in indented form. 425*cda5da8dSAndroid Build Coastguard Worker 426*cda5da8dSAndroid Build Coastguard Worker Note that tabs and spaces are both treated as whitespace, but they 427*cda5da8dSAndroid Build Coastguard Worker are not equal: the lines " hello" and "\\thello" are 428*cda5da8dSAndroid Build Coastguard Worker considered to have no common leading whitespace. 429*cda5da8dSAndroid Build Coastguard Worker 430*cda5da8dSAndroid Build Coastguard Worker Entirely blank lines are normalized to a newline character. 431*cda5da8dSAndroid Build Coastguard Worker """ 432*cda5da8dSAndroid Build Coastguard Worker # Look for the longest leading string of spaces and tabs common to 433*cda5da8dSAndroid Build Coastguard Worker # all lines. 434*cda5da8dSAndroid Build Coastguard Worker margin = None 435*cda5da8dSAndroid Build Coastguard Worker text = _whitespace_only_re.sub('', text) 436*cda5da8dSAndroid Build Coastguard Worker indents = _leading_whitespace_re.findall(text) 437*cda5da8dSAndroid Build Coastguard Worker for indent in indents: 438*cda5da8dSAndroid Build Coastguard Worker if margin is None: 439*cda5da8dSAndroid Build Coastguard Worker margin = indent 440*cda5da8dSAndroid Build Coastguard Worker 441*cda5da8dSAndroid Build Coastguard Worker # Current line more deeply indented than previous winner: 442*cda5da8dSAndroid Build Coastguard Worker # no change (previous winner is still on top). 443*cda5da8dSAndroid Build Coastguard Worker elif indent.startswith(margin): 444*cda5da8dSAndroid Build Coastguard Worker pass 445*cda5da8dSAndroid Build Coastguard Worker 446*cda5da8dSAndroid Build Coastguard Worker # Current line consistent with and no deeper than previous winner: 447*cda5da8dSAndroid Build Coastguard Worker # it's the new winner. 448*cda5da8dSAndroid Build Coastguard Worker elif margin.startswith(indent): 449*cda5da8dSAndroid Build Coastguard Worker margin = indent 450*cda5da8dSAndroid Build Coastguard Worker 451*cda5da8dSAndroid Build Coastguard Worker # Find the largest common whitespace between current line and previous 452*cda5da8dSAndroid Build Coastguard Worker # winner. 453*cda5da8dSAndroid Build Coastguard Worker else: 454*cda5da8dSAndroid Build Coastguard Worker for i, (x, y) in enumerate(zip(margin, indent)): 455*cda5da8dSAndroid Build Coastguard Worker if x != y: 456*cda5da8dSAndroid Build Coastguard Worker margin = margin[:i] 457*cda5da8dSAndroid Build Coastguard Worker break 458*cda5da8dSAndroid Build Coastguard Worker 459*cda5da8dSAndroid Build Coastguard Worker # sanity check (testing/debugging only) 460*cda5da8dSAndroid Build Coastguard Worker if 0 and margin: 461*cda5da8dSAndroid Build Coastguard Worker for line in text.split("\n"): 462*cda5da8dSAndroid Build Coastguard Worker assert not line or line.startswith(margin), \ 463*cda5da8dSAndroid Build Coastguard Worker "line = %r, margin = %r" % (line, margin) 464*cda5da8dSAndroid Build Coastguard Worker 465*cda5da8dSAndroid Build Coastguard Worker if margin: 466*cda5da8dSAndroid Build Coastguard Worker text = re.sub(r'(?m)^' + margin, '', text) 467*cda5da8dSAndroid Build Coastguard Worker return text 468*cda5da8dSAndroid Build Coastguard Worker 469*cda5da8dSAndroid Build Coastguard Worker 470*cda5da8dSAndroid Build Coastguard Workerdef indent(text, prefix, predicate=None): 471*cda5da8dSAndroid Build Coastguard Worker """Adds 'prefix' to the beginning of selected lines in 'text'. 472*cda5da8dSAndroid Build Coastguard Worker 473*cda5da8dSAndroid Build Coastguard Worker If 'predicate' is provided, 'prefix' will only be added to the lines 474*cda5da8dSAndroid Build Coastguard Worker where 'predicate(line)' is True. If 'predicate' is not provided, 475*cda5da8dSAndroid Build Coastguard Worker it will default to adding 'prefix' to all non-empty lines that do not 476*cda5da8dSAndroid Build Coastguard Worker consist solely of whitespace characters. 477*cda5da8dSAndroid Build Coastguard Worker """ 478*cda5da8dSAndroid Build Coastguard Worker if predicate is None: 479*cda5da8dSAndroid Build Coastguard Worker def predicate(line): 480*cda5da8dSAndroid Build Coastguard Worker return line.strip() 481*cda5da8dSAndroid Build Coastguard Worker 482*cda5da8dSAndroid Build Coastguard Worker def prefixed_lines(): 483*cda5da8dSAndroid Build Coastguard Worker for line in text.splitlines(True): 484*cda5da8dSAndroid Build Coastguard Worker yield (prefix + line if predicate(line) else line) 485*cda5da8dSAndroid Build Coastguard Worker return ''.join(prefixed_lines()) 486*cda5da8dSAndroid Build Coastguard Worker 487*cda5da8dSAndroid Build Coastguard Worker 488*cda5da8dSAndroid Build Coastguard Workerif __name__ == "__main__": 489*cda5da8dSAndroid Build Coastguard Worker #print dedent("\tfoo\n\tbar") 490*cda5da8dSAndroid Build Coastguard Worker #print dedent(" \thello there\n \t how are you?") 491*cda5da8dSAndroid Build Coastguard Worker print(dedent("Hello there.\n This is indented.")) 492