| 
				
			 | 
			
			
				@@ -70,11 +70,8 @@ VCS_INFO = { 
			 | 
		
	
		
			
			| 
				70
			 | 
			
				70
			 | 
			
			
				 } 
			 | 
		
	
		
			
			| 
				71
			 | 
			
				71
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				72
			 | 
			
				72
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				73
			 | 
			
				
			 | 
			
			
				-def ansi_code(color): 
			 | 
		
	
		
			
			| 
				74
			 | 
			
				
			 | 
			
			
				-    return COLORS.get(color, '') 
			 | 
		
	
		
			
			| 
				75
			 | 
			
				
			 | 
			
			
				- 
			 | 
		
	
		
			
			| 
				76
			 | 
			
				73
			 | 
			
			
				 def colorize(text, start_color, end_color='reset'): 
			 | 
		
	
		
			
			| 
				77
			 | 
			
				
			 | 
			
			
				-    return ansi_code(start_color) + text + ansi_code(end_color) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				74
			 | 
			
			
				+    return COLORS[start_color] + text + COLORS[end_color] 
			 | 
		
	
		
			
			| 
				78
			 | 
			
				75
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				79
			 | 
			
				76
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				80
			 | 
			
				77
			 | 
			
			
				 class Hunk(object): 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -217,27 +214,38 @@ class Diff(object): 
			 | 
		
	
		
			
			| 
				217
			 | 
			
				214
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				218
			 | 
			
				215
			 | 
			
			
				     def markup_side_by_side(self, width): 
			 | 
		
	
		
			
			| 
				219
			 | 
			
				216
			 | 
			
			
				         """Returns a generator""" 
			 | 
		
	
		
			
			| 
				
			 | 
			
				217
			 | 
			
			
				+        wrap_char = colorize('>', 'lightmagenta') 
			 | 
		
	
		
			
			| 
				220
			 | 
			
				218
			 | 
			
			
				         def _normalize(line): 
			 | 
		
	
		
			
			| 
				221
			 | 
			
				219
			 | 
			
			
				             return line.replace('\t', ' '*8).replace('\n', '').replace('\r', '') 
			 | 
		
	
		
			
			| 
				222
			 | 
			
				220
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				223
			 | 
			
				
			 | 
			
			
				-        def _fit_width(markup, width, pad=False): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				221
			 | 
			
			
				+        def _fit_with_marker(text, markup_fn, width, pad=False): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				222
			 | 
			
			
				+            """Wrap or pad input pure text, then markup""" 
			 | 
		
	
		
			
			| 
				
			 | 
			
				223
			 | 
			
			
				+            if len(text) > width: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				224
			 | 
			
			
				+                return markup_fn(text[:width-1]) + wrap_char 
			 | 
		
	
		
			
			| 
				
			 | 
			
				225
			 | 
			
			
				+            elif pad: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				226
			 | 
			
			
				+                pad_len = width - len(text) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				227
			 | 
			
			
				+                return '%s%*s' % (markup_fn(text), pad_len, '') 
			 | 
		
	
		
			
			| 
				
			 | 
			
				228
			 | 
			
			
				+            else: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				229
			 | 
			
			
				+                return markup_fn(text) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				230
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				231
			 | 
			
			
				+        def _fit_markup(markup, width, pad=False): 
			 | 
		
	
		
			
			| 
				224
			 | 
			
				232
			 | 
			
			
				             """Fit input markup to given width, pad or wrap accordingly, str len 
			 | 
		
	
		
			
			| 
				225
			 | 
			
				
			 | 
			
			
				-            does not count correctly if line contains ansi color code.  Only 
			 | 
		
	
		
			
			| 
				
			 | 
			
				233
			 | 
			
			
				+            does not count correctly if string contains ansi color code.  Only 
			 | 
		
	
		
			
			| 
				226
			 | 
			
				234
			 | 
			
			
				             left side need to set `pad` 
			 | 
		
	
		
			
			| 
				227
			 | 
			
				235
			 | 
			
			
				             """ 
			 | 
		
	
		
			
			| 
				228
			 | 
			
				236
			 | 
			
			
				             out = [] 
			 | 
		
	
		
			
			| 
				229
			 | 
			
				237
			 | 
			
			
				             count = 0 
			 | 
		
	
		
			
			| 
				230
			 | 
			
				238
			 | 
			
			
				             ansi_color_regex = r'\x1b\[(1;)?\d{1,2}m' 
			 | 
		
	
		
			
			| 
				231
			 | 
			
				
			 | 
			
			
				-            patt = re.compile('^(%s)(.*)' % ansi_color_regex) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				239
			 | 
			
			
				+            patt = re.compile('^((%s)+)(.*)' % ansi_color_regex) 
			 | 
		
	
		
			
			| 
				232
			 | 
			
				240
			 | 
			
			
				             repl = re.compile(ansi_color_regex) 
			 | 
		
	
		
			
			| 
				233
			 | 
			
				241
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				234
			 | 
			
				242
			 | 
			
			
				             while markup and count < width: 
			 | 
		
	
		
			
			| 
				235
			 | 
			
				243
			 | 
			
			
				                 if patt.match(markup): 
			 | 
		
	
		
			
			| 
				236
			 | 
			
				
			 | 
			
			
				-                    # Extract the ansi color code seq to target output and 
			 | 
		
	
		
			
			| 
				237
			 | 
			
				
			 | 
			
			
				-                    # remove the seq from input markup, no update on counter  
			 | 
		
	
		
			
			| 
				
			 | 
			
				244
			 | 
			
			
				+                    # Extract longest ansi color code seq to target output and 
			 | 
		
	
		
			
			| 
				
			 | 
			
				245
			 | 
			
			
				+                    # remove the seq from input markup, no update on counter 
			 | 
		
	
		
			
			| 
				238
			 | 
			
				246
			 | 
			
			
				                     # 
			 | 
		
	
		
			
			| 
				239
			 | 
			
				247
			 | 
			
			
				                     out.append(patt.sub(r'\1', markup)) 
			 | 
		
	
		
			
			| 
				240
			 | 
			
				
			 | 
			
			
				-                    markup = patt.sub(r'\3', markup) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				248
			 | 
			
			
				+                    markup = patt.sub(r'\4', markup) 
			 | 
		
	
		
			
			| 
				241
			 | 
			
				249
			 | 
			
			
				                 else: 
			 | 
		
	
		
			
			| 
				242
			 | 
			
				250
			 | 
			
			
				                     # FIXME: utf-8 wchar might break the rule here, e.g. 
			 | 
		
	
		
			
			| 
				243
			 | 
			
				251
			 | 
			
			
				                     # u'\u554a' takes double width of a single letter, also this 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -250,7 +258,7 @@ class Diff(object): 
			 | 
		
	
		
			
			| 
				250
			 | 
			
				258
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				251
			 | 
			
				259
			 | 
			
			
				             if count == width and repl.sub('', markup): 
			 | 
		
	
		
			
			| 
				252
			 | 
			
				260
			 | 
			
			
				                 # Was stripped: output fulfil and still has ascii in markup 
			 | 
		
	
		
			
			| 
				253
			 | 
			
				
			 | 
			
			
				-                out[-1] = ansi_code('reset') + colorize('>', 'lightmagenta') 
			 | 
		
	
		
			
			| 
				
			 | 
			
				261
			 | 
			
			
				+                out[-1] = COLORS['reset'] + wrap_char 
			 | 
		
	
		
			
			| 
				254
			 | 
			
				262
			 | 
			
			
				             elif count < width and pad: 
			 | 
		
	
		
			
			| 
				255
			 | 
			
				263
			 | 
			
			
				                 pad_len = width - count 
			 | 
		
	
		
			
			| 
				256
			 | 
			
				264
			 | 
			
			
				                 out.append('%*s' % (pad_len, '')) 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -260,6 +268,7 @@ class Diff(object): 
			 | 
		
	
		
			
			| 
				260
			 | 
			
				268
			 | 
			
			
				         # Setup line width and number width 
			 | 
		
	
		
			
			| 
				261
			 | 
			
				269
			 | 
			
			
				         if width <= 0: 
			 | 
		
	
		
			
			| 
				262
			 | 
			
				270
			 | 
			
			
				             width = 80 
			 | 
		
	
		
			
			| 
				
			 | 
			
				271
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				263
			 | 
			
				272
			 | 
			
			
				         (start, offset) = self._hunks[-1].get_old_addr() 
			 | 
		
	
		
			
			| 
				264
			 | 
			
				273
			 | 
			
			
				         max1 = start + offset - 1 
			 | 
		
	
		
			
			| 
				265
			 | 
			
				274
			 | 
			
			
				         (start, offset) = self._hunks[-1].get_new_addr() 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -267,7 +276,7 @@ class Diff(object): 
			 | 
		
	
		
			
			| 
				267
			 | 
			
				276
			 | 
			
			
				         num_width = max(len(str(max1)), len(str(max2))) 
			 | 
		
	
		
			
			| 
				268
			 | 
			
				277
			 | 
			
			
				         left_num_fmt = colorize('%%(left_num)%ds' % num_width, 'yellow') 
			 | 
		
	
		
			
			| 
				269
			 | 
			
				278
			 | 
			
			
				         right_num_fmt = colorize('%%(right_num)%ds' % num_width, 'yellow') 
			 | 
		
	
		
			
			| 
				270
			 | 
			
				
			 | 
			
			
				-        line_fmt = left_num_fmt + ' %(left)s ' + ansi_code('reset') + \ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				279
			 | 
			
			
				+        line_fmt = left_num_fmt + ' %(left)s ' + COLORS['reset'] + \ 
			 | 
		
	
		
			
			| 
				271
			 | 
			
				280
			 | 
			
			
				                 right_num_fmt + ' %(right)s\n' 
			 | 
		
	
		
			
			| 
				272
			 | 
			
				281
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				273
			 | 
			
				282
			 | 
			
			
				         # yield header, old path and new path 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -299,17 +308,17 @@ class Diff(object): 
			 | 
		
	
		
			
			| 
				299
			 | 
			
				308
			 | 
			
			
				                     if not old[0]: 
			 | 
		
	
		
			
			| 
				300
			 | 
			
				309
			 | 
			
			
				                         left = '%*s' % (width, ' ') 
			 | 
		
	
		
			
			| 
				301
			 | 
			
				310
			 | 
			
			
				                         right = right.lstrip('\x00+').rstrip('\x01') 
			 | 
		
	
		
			
			| 
				302
			 | 
			
				
			 | 
			
			
				-                        right = _fit_width(self._markup_new(right), width) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				311
			 | 
			
			
				+                        right = _fit_with_marker(right, self._markup_new, width) 
			 | 
		
	
		
			
			| 
				303
			 | 
			
				312
			 | 
			
			
				                     elif not new[0]: 
			 | 
		
	
		
			
			| 
				304
			 | 
			
				313
			 | 
			
			
				                         left = left.lstrip('\x00-').rstrip('\x01') 
			 | 
		
	
		
			
			| 
				305
			 | 
			
				
			 | 
			
			
				-                        left = _fit_width(self._markup_old(left), width) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				314
			 | 
			
			
				+                        left = _fit_with_marker(left, self._markup_old, width) 
			 | 
		
	
		
			
			| 
				306
			 | 
			
				315
			 | 
			
			
				                         right = '' 
			 | 
		
	
		
			
			| 
				307
			 | 
			
				316
			 | 
			
			
				                     else: 
			 | 
		
	
		
			
			| 
				308
			 | 
			
				
			 | 
			
			
				-                        left = _fit_width(self._markup_old_mix(left), width, 1) 
			 | 
		
	
		
			
			| 
				309
			 | 
			
				
			 | 
			
			
				-                        right = _fit_width(self._markup_new_mix(right), width) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				317
			 | 
			
			
				+                        left = _fit_markup(self._markup_old_mix(left), width, 1) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				318
			 | 
			
			
				+                        right = _fit_markup(self._markup_new_mix(right), width) 
			 | 
		
	
		
			
			| 
				310
			 | 
			
				319
			 | 
			
			
				                 else: 
			 | 
		
	
		
			
			| 
				311
			 | 
			
				
			 | 
			
			
				-                    left = _fit_width(self._markup_common(left), width, 1) 
			 | 
		
	
		
			
			| 
				312
			 | 
			
				
			 | 
			
			
				-                    right = _fit_width(self._markup_common(right), width) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				320
			 | 
			
			
				+                    left = _fit_with_marker(left, self._markup_common, width, 1) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				321
			 | 
			
			
				+                    right = _fit_with_marker(right, self._markup_common, width) 
			 | 
		
	
		
			
			| 
				313
			 | 
			
				322
			 | 
			
			
				                 yield line_fmt % { 
			 | 
		
	
		
			
			| 
				314
			 | 
			
				323
			 | 
			
			
				                     'left_num': left_num, 
			 | 
		
	
		
			
			| 
				315
			 | 
			
				324
			 | 
			
			
				                     'left': left, 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -342,10 +351,10 @@ class Diff(object): 
			 | 
		
	
		
			
			| 
				342
			 | 
			
				351
			 | 
			
			
				         return colorize(line, 'lightgreen') 
			 | 
		
	
		
			
			| 
				343
			 | 
			
				352
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				344
			 | 
			
				353
			 | 
			
			
				     def _markup_mix(self, line, base_color): 
			 | 
		
	
		
			
			| 
				345
			 | 
			
				
			 | 
			
			
				-        del_code = ansi_code('reverse') + ansi_code(base_color) 
			 | 
		
	
		
			
			| 
				346
			 | 
			
				
			 | 
			
			
				-        add_code = ansi_code('reverse') + ansi_code(base_color) 
			 | 
		
	
		
			
			| 
				347
			 | 
			
				
			 | 
			
			
				-        chg_code = ansi_code('underline') + ansi_code(base_color) 
			 | 
		
	
		
			
			| 
				348
			 | 
			
				
			 | 
			
			
				-        rst_code = ansi_code('reset') + ansi_code(base_color) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				354
			 | 
			
			
				+        del_code = COLORS['reverse'] + COLORS[base_color] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				355
			 | 
			
			
				+        add_code = COLORS['reverse'] + COLORS[base_color] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				356
			 | 
			
			
				+        chg_code = COLORS['underline'] + COLORS[base_color] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				357
			 | 
			
			
				+        rst_code = COLORS['reset'] + COLORS[base_color] 
			 | 
		
	
		
			
			| 
				349
			 | 
			
				358
			 | 
			
			
				         line = line.replace('\x00-', del_code) 
			 | 
		
	
		
			
			| 
				350
			 | 
			
				359
			 | 
			
			
				         line = line.replace('\x00+', add_code) 
			 | 
		
	
		
			
			| 
				351
			 | 
			
				360
			 | 
			
			
				         line = line.replace('\x00^', chg_code) 
			 |