@@ -61,6 +61,8 @@ class TextWrapper:
6161 Truncate wrapped lines.
6262 placeholder (default: ' [...]')
6363 Append to the last line of truncated text.
64+ ignore_ansi_escape (default: false)
65+ Ignore ANSI escape sequences when computing lengths of lines.
6466 """
6567
6668 unicode_whitespace_trans = dict .fromkeys (map (ord , _whitespace ), ord (' ' ))
@@ -109,6 +111,8 @@ class TextWrapper:
109111 r'[\"\']?' # optional end-of-quote
110112 r'\z' ) # end of chunk
111113
114+ ansi_escape_re = re .compile (r'\x1b\[[0-9;]*m' )
115+
112116 def __init__ (self ,
113117 width = 70 ,
114118 initial_indent = "" ,
@@ -122,7 +126,8 @@ def __init__(self,
122126 tabsize = 8 ,
123127 * ,
124128 max_lines = None ,
125- placeholder = ' [...]' ):
129+ placeholder = ' [...]' ,
130+ ignore_ansi_escape = False ):
126131 self .width = width
127132 self .initial_indent = initial_indent
128133 self .subsequent_indent = subsequent_indent
@@ -135,6 +140,7 @@ def __init__(self,
135140 self .tabsize = tabsize
136141 self .max_lines = max_lines
137142 self .placeholder = placeholder
143+ self .ignore_ansi_escape = ignore_ansi_escape
138144
139145
140146 # -- Private methods -----------------------------------------------
@@ -235,6 +241,10 @@ def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
235241 # cur_len will be zero, so the next line will be entirely
236242 # devoted to the long word that we can't handle right now.
237243
244+ def _str_len_without_ansi_escape_codes (self , s ):
245+ """Return the length of string s without ANSI escape codes."""
246+ return len (self .ansi_escape_re .sub (s , "" ))
247+
238248 def _wrap_chunks (self , chunks ):
239249 """_wrap_chunks(chunks : [string]) -> [string]
240250
@@ -259,6 +269,11 @@ def _wrap_chunks(self, chunks):
259269 if len (indent ) + len (self .placeholder .lstrip ()) > self .width :
260270 raise ValueError ("placeholder too large for max width" )
261271
272+ if self .ignore_ansi_escape :
273+ _str_len = self ._str_len_without_ansi_escape_codes
274+ else :
275+ _str_len = len
276+
262277 # Arrange in reverse order so items can be efficiently popped
263278 # from a stack of chucks.
264279 chunks .reverse ()
@@ -285,7 +300,7 @@ def _wrap_chunks(self, chunks):
285300 del chunks [- 1 ]
286301
287302 while chunks :
288- l = len (chunks [- 1 ])
303+ l = _str_len (chunks [- 1 ])
289304
290305 # Can at least squeeze this chunk onto the current line.
291306 if cur_len + l <= width :
0 commit comments