node.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import diff
  2. import difflib
  3. import itertools
  4. import time
  5. import util
  6. # Python undo tree data structures and functions ----------------------------------
  7. class Node(object):
  8. def __init__(self, n, parent, time, curhead, saved):
  9. self.n = int(n)
  10. self.parent = parent
  11. self.children = []
  12. self.curhead = curhead
  13. self.saved = saved
  14. self.time = time
  15. def __repr__(self):
  16. return "[n=%s,parent=%s,time=%s,curhead=%s,saved=%s]" % \
  17. (self.n,self.parent,self.time,self.curhead,self.saved)
  18. class Nodes(object):
  19. def __init__(self):
  20. self._clear_cache()
  21. def _clear_cache(self):
  22. self.nodes_made = None
  23. self.target_f = None
  24. self.changedtick = None
  25. self.lines = {}
  26. self.clear_oneline_diffs()
  27. def clear_oneline_diffs(self):
  28. self.diffs = {}
  29. self.diff_has_oneline = {}
  30. def _check_version_location(self):
  31. util._goto_window_for_buffer(util.vim().eval('g:mundo_target_n'))
  32. target_f = util.vim().eval('g:mundo_target_f')
  33. if target_f != self.target_f:
  34. self._clear_cache()
  35. def _make_nodes(self,alts, nodes, parent=None):
  36. p = parent
  37. for alt in alts:
  38. if alt:
  39. curhead = 'curhead' in alt
  40. saved = 'save' in alt
  41. node = Node(n=alt['seq'], parent=p, time=alt['time'], curhead=curhead, saved=saved)
  42. nodes.append(node)
  43. if alt.get('alt'):
  44. self._make_nodes(alt['alt'], nodes, p)
  45. p = node
  46. def is_outdated(self):
  47. util._goto_window_for_buffer(util.vim().eval('g:mundo_target_n'))
  48. current_changedtick = util.vim().eval('b:changedtick')
  49. return self.changedtick != current_changedtick
  50. def make_nodes(self):
  51. # If the current changedtick is unchanged, we don't need to do
  52. # anything:
  53. if not self.is_outdated():
  54. return self.nodes_made
  55. self._check_version_location()
  56. target_f = util.vim().eval('g:mundo_target_f')
  57. ut = util.vim().eval('undotree()')
  58. entries = ut['entries']
  59. seq_last = ut['seq_last']
  60. current_changedtick = util.vim().eval('b:changedtick')
  61. root = Node(0, None, False, 0, 0)
  62. nodes = []
  63. # TODO only compute new values (not all values)
  64. self._make_nodes(entries, nodes, root)
  65. nodes.append(root)
  66. nmap = dict((node.n, node) for node in nodes)
  67. # cache values for later use
  68. self.target_f = target_f
  69. self.seq_last = seq_last
  70. self.nodes_made = (nodes, nmap)
  71. self.changedtick = current_changedtick
  72. return self.nodes_made
  73. def current(self):
  74. """ Return the number of the current change. """
  75. self._check_version_location()
  76. nodes, nmap = self.make_nodes()
  77. _curhead_l = list(itertools.dropwhile(lambda n: not n.curhead, nodes))
  78. if _curhead_l:
  79. current = _curhead_l[0].parent.n
  80. else:
  81. current = int(util.vim().eval('changenr()'))
  82. return current
  83. def _fmt_time(self,t):
  84. return time.strftime('%Y-%m-%d %I:%M:%S %p', time.localtime(float(t)))
  85. def _get_lines(self,node):
  86. n = 0
  87. if node:
  88. n = node.n
  89. if n not in self.lines:
  90. util._undo_to(n)
  91. self.lines[n] = util.vim().current.buffer[:]
  92. return self.lines[n]
  93. def change_preview_diff(self,before,after):
  94. self._check_version_location()
  95. key = "%s-%s-cpd"%(before.n,after.n)
  96. if key in self.diffs:
  97. return self.diffs[key]
  98. util._goto_window_for_buffer(util.vim().eval('g:mundo_target_n'))
  99. before_lines = self._get_lines(before)
  100. after_lines = self._get_lines(after)
  101. before_name = str(before.n or 'Original')
  102. before_time = before.time and self._fmt_time(before.time) or ''
  103. after_name = str(after.n or 'Original')
  104. after_time = after.time and self._fmt_time(after.time) or ''
  105. util._undo_to(self.current())
  106. self.diffs[key] = list(difflib.unified_diff(before_lines, after_lines,
  107. before_name, after_name,
  108. before_time, after_time))
  109. return self.diffs[key]
  110. def preview_diff(self, before, after, unified=True, inline=False):
  111. """
  112. Generate a diff comparing two versions of a file.
  113. Parameters:
  114. current - ?
  115. before
  116. after
  117. unified - If True, generate a unified diff
  118. inline - Generate a one line summary line.
  119. """
  120. self._check_version_location()
  121. bn = 0
  122. an = 0
  123. if not after.n: # we're at the original file
  124. pass
  125. elif not before.n: # we're at a pseudo-root state
  126. an = after.n
  127. else:
  128. bn = before.n
  129. an = after.n
  130. key = "%s-%s-pd-%s"%(bn,an,unified)
  131. needs_oneline = inline and key not in self.diff_has_oneline
  132. if key in self.diffs and not needs_oneline:
  133. return self.diffs[key]
  134. if not after.n: # we're at the original file
  135. before_lines = []
  136. after_lines = self._get_lines(None)
  137. before_name = 'n/a'
  138. before_time = ''
  139. after_name = 'Original'
  140. after_time = ''
  141. elif not before.n: # we're at a pseudo-root state
  142. before_lines = self._get_lines(None)
  143. after_lines = self._get_lines(after)
  144. before_name = 'Original'
  145. before_time = ''
  146. after_name = str(after.n)
  147. after_time = self._fmt_time(after.time)
  148. else:
  149. before_lines = self._get_lines(before)
  150. after_lines = self._get_lines(after)
  151. before_name = str(before.n)
  152. before_time = self._fmt_time(before.time)
  153. after_name = str(after.n)
  154. after_time = self._fmt_time(after.time)
  155. if unified:
  156. self.diffs[key] = list(difflib.unified_diff(before_lines, after_lines,
  157. before_name, after_name,
  158. before_time, after_time))
  159. elif inline:
  160. maxwidth = int(util.vim().eval("winwidth(0)"))
  161. self.diffs[key] = diff.one_line_diff_str('\n'.join(before_lines),'\n'.join(after_lines),maxwidth)
  162. self.diff_has_oneline[key] = True
  163. else:
  164. self.diffs[key] = ""
  165. return self.diffs[key]