1
0

diff.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import difflib
  2. import itertools
  3. # one line diff functions.
  4. def one_line_diff_str(before,after,mx=15,pre=2):
  5. """
  6. Return a summary of the differences between two strings, concatenated.
  7. Parameters:
  8. before - string before.
  9. after - after string.
  10. mx - the max number of strings.
  11. pre - number of characters to show before diff (context)
  12. Returns a string no longer than 'mx'.
  13. """
  14. old = one_line_diff(before,after)
  15. result = ''
  16. firstEl = True
  17. # TODO instead of using +addition+ and -subtraction- it'd be nice to be able
  18. # to highlight the change w/o requiring the +/- chars.
  19. for v in old:
  20. # if the first element doesn't have a change, then don't include it.
  21. v = escape_returns(v)
  22. if firstEl:
  23. firstEl = False
  24. # add in pre character context:
  25. if not (v.startswith('+') or v.startswith('-')) and result == '':
  26. v = v[-pre:]
  27. # when we're going to be bigger than our max limit, lets ensure that the
  28. # trailing +/- appears in the text:
  29. if len(result) + len(v) > mx:
  30. if v.startswith('+') or v.startswith('-'):
  31. result += v[:mx - len(result) - 1]
  32. result += v[0]
  33. break
  34. result += v
  35. return result
  36. def escape_returns(result):
  37. return result.replace('\n','\\n').replace('\r','\\r').replace('\t','\\t')
  38. def one_line_diff(before, after):
  39. """
  40. Return a summary of the differences between two arbitrary strings.
  41. Returns a list of strings, summarizing all the changes.
  42. """
  43. a, b, result = [], [], []
  44. for line in itertools.chain(itertools.islice(
  45. difflib.unified_diff(before.splitlines(),
  46. after.splitlines()), 2, None), ['@@']):
  47. if line.startswith('@@'):
  48. result.extend(one_line_diff_raw('\n'.join(a), '\n'.join(b)))
  49. a, b = [], []
  50. continue
  51. if not line.startswith('+'):
  52. a.append(line[1:])
  53. if not line.startswith('-'):
  54. b.append(line[1:])
  55. if after.endswith('\n') and not before.endswith('\n'):
  56. if result:
  57. result[-1] = result[-1][:-1] + '\n+'
  58. else:
  59. result = ['+\n+']
  60. return result
  61. def one_line_diff_raw(before,after):
  62. s = difflib.SequenceMatcher(None,before,after)
  63. results = []
  64. for tag, i1, i2, j1, j2 in s.get_opcodes():
  65. #print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" % (tag, i1, i2, before[i1:i2], j1, j2, after[j1:j2]))
  66. if tag == 'equal':
  67. _append_result(results,{
  68. 'equal': after[j1:j2]
  69. })
  70. if tag == 'insert':
  71. _append_result(results,{
  72. 'plus': after[j1:j2]
  73. })
  74. elif tag == 'delete':
  75. _append_result(results,{
  76. 'minus': before[i1:i2]
  77. })
  78. elif tag == 'replace':
  79. _append_result(results,{
  80. 'minus': before[j1:j2],
  81. 'plus': after[j1:j2]
  82. })
  83. final_results = []
  84. # finally, create a human readable string of information.
  85. for v in results:
  86. if 'minus' in v and 'plus' in v and len(v['minus']) > 0 and len(v['plus']) > 0:
  87. final_results.append("-%s-+%s+"% (v['minus'],v['plus']))
  88. elif 'minus' in v and len(v['minus']) > 0:
  89. final_results.append("-%s-"% (v['minus']))
  90. elif 'plus' in v and len(v['plus']) > 0:
  91. final_results.append("+%s+"% (v['plus']))
  92. elif 'equal' in v:
  93. final_results.append("%s"% (v['equal']))
  94. return final_results
  95. def _append_result(results,val):
  96. results.append(val)