11from multidiff .Ansi import Ansi
22import binascii
33import html
4+ import textwrap
5+ import re
46
57class Render ():
6- def __init__ (self , encoder = 'hexdump' , color = 'ansi' ):
8+ def __init__ (self , encoder = 'hexdump' , color = 'ansi' , bytes = 16 , width = None ):
79 '''Configure the output encoding and coloring method of this rendering object'''
810 if color == 'ansi' :
911 self .highligther = ansi_colored
@@ -16,17 +18,22 @@ def __init__(self, encoder='hexdump', color='ansi'):
1618 self .encoder = HexEncoder
1719 elif encoder == 'utf8' :
1820 self .encoder = Utf8Encoder
19-
21+
22+ self .width = width
23+ self .bytes = bytes
24+
2025 def render (self , model , diff ):
2126 '''Render the diff in the given model into a UTF-8 String'''
2227 result = self .encoder (self .highligther )
2328 obj = model .objects [diff .target ]
2429 for op in diff .opcodes :
2530 data = obj .data [op [3 ]:op [4 ]]
2631 if type (data ) == bytes :
27- result .append (data , op [0 ])
32+ result .append (data , op [0 ], self . width , self . bytes )
2833 elif type (data ) == str :
29- result .append (bytes (data , "utf8" ), op [0 ])
34+ result .append (bytes (data , "utf8" ), op [0 ], self .width , self .bytes )
35+ if self .bytes != 16 :
36+ return result .reformat (result .final (), int (self .bytes ))
3037 return result .final ()
3138
3239 def dumps (self , model ):
@@ -42,9 +49,12 @@ def __init__(self, highligther):
4249 self .highligther = highligther
4350 self .output = ''
4451
45- def append (self , data , color ):
52+ def append (self , data , color , width = None , bytes = 16 ):
4653 self .output += self .highligther (str (data , 'utf8' ), color )
47-
54+ if width :
55+ if len (self .output ) > int (width ):
56+ self .output = textwrap .fill (self .output , int (width ))
57+
4858 def final (self ):
4959 return self .output
5060
@@ -53,10 +63,14 @@ class HexEncoder():
5363 def __init__ (self , highligther ):
5464 self .highligther = highligther
5565 self .output = ''
56- def append (self , data , color ):
66+
67+ def append (self , data , color , width = None , bytes = 16 ):
5768 data = str (binascii .hexlify (data ),'utf8' )
5869 self .output += self .highligther (data , color )
59-
70+ if width :
71+ if len (self .output ) > int (width ):
72+ self .output = textwrap .fill (self .output , int (width ))
73+
6074 def final (self ):
6175 return self .output
6276
@@ -71,21 +85,21 @@ def __init__(self, highligther):
7185 self .skipspace = False
7286 self .asciirow = ''
7387
74- def append (self , data , color ):
88+ def append (self , data , color , width = None , bytes = 16 ):
7589 if len (data ) == 0 :
76- self ._append (data , color )
90+ self ._append (data , color , width )
7791 while len (data ) > 0 :
7892 if self .rowlen == 16 :
7993 self ._newrow ()
80- consumed = self ._append (data [:16 - self .rowlen ], color )
94+ consumed = self ._append (data [:16 - self .rowlen ], color , width )
8195 data = data [consumed :]
8296
83- def _append (self , data , color ):
97+ def _append (self , data , color , width ):
8498 if len (data ) == 0 :
8599 #in the case of highlightig a deletion in a target or an
86100 #addition in the source, print a highlighted space and mark
87101 #it skippanble for the next append
88- hexs = ' '
102+ hexs = ' '
89103 self .skipspace = True
90104 else :
91105 self ._add_hex_space ()
@@ -101,7 +115,10 @@ def _append(self, data, color):
101115 asciis += '.'
102116 self .asciirow += self .highligther (asciis , color )
103117
104- self .hexrow += self .highligther (hexs , color )
118+ self .hexrow += self .highligther (hexs , color )
119+ if width :
120+ if len (self .hexrow ) > int (width ):
121+ self .hexrow = textwrap .fill (self .hexrow , int (width ))
105122 self .rowlen += len (data )
106123 return len (data )
107124
@@ -121,14 +138,42 @@ def _add_hex_space(self):
121138 self .skipspace = False
122139 else :
123140 self .hexrow += ' '
124-
141+
125142
126143 def final (self ):
127144 self .hexrow += 3 * (16 - self .rowlen ) * ' '
128145 self .asciirow += (16 - self .rowlen ) * ' '
129146 self ._newrow ()
130147 return self .body
131148
149+ def reformat (self , body , n = 16 ):
150+ instring = ''
151+ foo = body .split ('\n ' )
152+ for line in foo :
153+ line .rstrip ()
154+ line = line [line .find (':' )+ 1 :line .find ('|' )]
155+ instring += line
156+ instring += '\n '
157+ outstring = ''
158+ # Remove line numbers and newlines.
159+ clean_string = instring .replace (r'\d+:|\n' , '' )
160+ # Split on spaces that are not in tags.
161+ elements = re .split (r'\s+(?![^<]+>)' , clean_string )
162+ # Omit first tag so that everything else can be chunked by n.
163+ clean_elements = elements [1 :]
164+ # Chunk by n.
165+ chunks = [' ' .join (clean_elements [i :i + n ])
166+ for i in range (0 , len (clean_elements ), n )]
167+ # Concatenate the chunks as a line in outstring, with a line number.
168+ for i , chunk in enumerate (chunks ):
169+ line = '{:06x}' .format (i * 16 )
170+ if i == 0 :
171+ outstring += '{}:{} {}\n ' .format (line , elements [0 ], chunk )
172+ else :
173+ outstring += '{}: {}\n ' .format (line , chunk )
174+ outstring .rstrip ()
175+ return outstring
176+
132177def ansi_colored (string , op ):
133178 if op == 'equal' :
134179 return string
0 commit comments