PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/pack/general/start/vim-mundo/autoload/mundo/graphlog.py

https://bitbucket.org/lilydjwg/dotvim
Python | 248 lines | 225 code | 12 blank | 11 comment | 19 complexity | a7009ae8d586bdd30f7e186140901853 MD5 | raw file
  1. import time
  2. import mundo.util as util
  3. # Mercurial's graphlog code -------------------------------------------------------
  4. def asciiedges(seen, rev, parents):
  5. """adds edge info to changelog DAG walk suitable for ascii()"""
  6. if rev not in seen:
  7. seen.append(rev)
  8. nodeidx = seen.index(rev)
  9. knownparents = []
  10. newparents = []
  11. for parent in parents:
  12. if parent in seen:
  13. knownparents.append(parent)
  14. else:
  15. newparents.append(parent)
  16. ncols = len(seen)
  17. seen[nodeidx:nodeidx + 1] = newparents
  18. edges = [(nodeidx, seen.index(p)) for p in knownparents]
  19. if len(newparents) > 0:
  20. edges.append((nodeidx, nodeidx))
  21. if len(newparents) > 1:
  22. edges.append((nodeidx, nodeidx + 1))
  23. nmorecols = len(seen) - ncols
  24. return nodeidx, edges, ncols, nmorecols
  25. def get_nodeline_edges_tail(
  26. node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
  27. if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
  28. # Still going in the same non-vertical direction.
  29. if n_columns_diff == -1:
  30. start = max(node_index + 1, p_node_index)
  31. tail = ["|", " "] * (start - node_index - 1)
  32. tail.extend(["/", " "] * (n_columns - start))
  33. return tail
  34. else:
  35. return ["\\", " "] * (n_columns - node_index - 1)
  36. else:
  37. return ["|", " "] * (n_columns - node_index - 1)
  38. def draw_edges(edges, nodeline, interline):
  39. for (start, end) in edges:
  40. if start == end + 1:
  41. interline[2 * end + 1] = "/"
  42. elif start == end - 1:
  43. interline[2 * start + 1] = "\\"
  44. elif start == end:
  45. interline[2 * start] = "|"
  46. else:
  47. nodeline[2 * end] = "+"
  48. if start > end:
  49. (start, end) = (end, start)
  50. for i in range(2 * start + 1, 2 * end):
  51. if nodeline[i] != "+":
  52. nodeline[i] = "-"
  53. def fix_long_right_edges(edges):
  54. for (i, (start, end)) in enumerate(edges):
  55. if end > start:
  56. edges[i] = (start, end + 1)
  57. def ascii(state, type, char, text, coldata, verbose):
  58. """prints an ASCII graph of the DAG
  59. takes the following arguments (one call per node in the graph):
  60. - Somewhere to keep the needed state in (init to asciistate())
  61. - Column of the current node in the set of ongoing edges.
  62. - Type indicator of node data == ASCIIDATA.
  63. - Payload: (char, lines):
  64. - Character to use as node's symbol.
  65. - List of lines to display as the node's text.
  66. - Edges; a list of (col, next_col) indicating the edges between
  67. the current node and its parents.
  68. - Number of columns (ongoing edges) in the current revision.
  69. - The difference between the number of columns (ongoing edges)
  70. in the next revision and the number of columns (ongoing edges)
  71. in the current revision. That is: -1 means one column removed;
  72. 0 means no columns added or removed; 1 means one column added.
  73. - Verbosity: if enabled then the graph prints an extra '|'
  74. between each line of information.
  75. Returns a string representing the output.
  76. """
  77. idx, edges, ncols, coldiff = coldata
  78. assert -2 < coldiff < 2
  79. if coldiff == -1:
  80. # Transform
  81. #
  82. # | | | | | |
  83. # o | | into o---+
  84. # |X / |/ /
  85. # | | | |
  86. fix_long_right_edges(edges)
  87. # fix_nodeline_tail says whether to rewrite
  88. #
  89. # | | o | | | | o | |
  90. # | | |/ / | | |/ /
  91. # | o | | into | o / / # <--- fixed nodeline tail
  92. # | |/ / | |/ /
  93. # o | | o | |
  94. fix_nodeline_tail = len(text) <= 2
  95. # nodeline is the line containing the node character (typically o)
  96. nodeline = ["|", " "] * idx
  97. nodeline.extend([char, " "])
  98. nodeline.extend(
  99. get_nodeline_edges_tail(idx, state[1], ncols, coldiff,
  100. state[0], fix_nodeline_tail))
  101. # shift_interline is the line containing the non-vertical
  102. # edges between this entry and the next
  103. shift_interline = ["|", " "] * idx
  104. if coldiff == -1:
  105. n_spaces = 1
  106. edge_ch = "/"
  107. elif coldiff == 0:
  108. n_spaces = 2
  109. edge_ch = "|"
  110. else:
  111. n_spaces = 3
  112. edge_ch = "\\"
  113. shift_interline.extend(n_spaces * [" "])
  114. shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
  115. # draw edges from the current node to its parents
  116. draw_edges(edges, nodeline, shift_interline)
  117. # lines is the list of all graph lines to print
  118. lines = [nodeline]
  119. lines.append(shift_interline)
  120. # make sure that there are as many graph lines as there are
  121. # log strings
  122. if any("/" in s for s in lines) or verbose:
  123. while len(text) < len(lines):
  124. text.append('')
  125. if len(lines) < len(text):
  126. extra_interline = ["|", " "] * (ncols + coldiff)
  127. while len(lines) < len(text):
  128. lines.append(extra_interline)
  129. indentation_level = max(ncols, ncols + coldiff)
  130. result = []
  131. for (line, logstr) in zip(lines, text):
  132. graph = "%-*s" % (2 * indentation_level, "".join(line))
  133. if not graph.isspace():
  134. result.append([graph, logstr])
  135. # ... and start over
  136. state[0] = coldiff
  137. state[1] = idx
  138. return result
  139. def generate(verbose, num_header_lines, first_visible_line, last_visible_line, inline_graph, nodesData):
  140. """
  141. Generate an array of the graph, and text describing the node of the graph.
  142. """
  143. seen, state = [], [0, 0]
  144. result = []
  145. current = nodesData.current()
  146. nodes, nmap = nodesData.make_nodes()
  147. for node in nodes:
  148. node.children = [n for n in nodes if n.parent == node]
  149. def walk_nodes(nodes):
  150. for node in nodes:
  151. if node.parent:
  152. yield (node, [node.parent])
  153. else:
  154. yield (node, [])
  155. dag = sorted(nodes, key=lambda n: int(n.n), reverse=True)
  156. dag = walk_nodes(dag)
  157. line_number = num_header_lines
  158. for idx, part in list(enumerate(dag)):
  159. node, parents = part
  160. if node.time:
  161. age_label = age(int(node.time))
  162. else:
  163. age_label = 'Original'
  164. line = '[%s] %s' % (node.n, age_label)
  165. if node.n == current:
  166. char = '@'
  167. elif node.saved:
  168. char = 'w'
  169. else:
  170. char = 'o'
  171. show_inine_diff = inline_graph and line_number >= first_visible_line and line_number <= last_visible_line
  172. preview_diff = nodesData.preview_diff(node.parent, node, False, show_inine_diff)
  173. line = '[%s] %-10s %s' % (node.n, age_label, preview_diff)
  174. new_lines = ascii(state, 'C', char, [line], asciiedges(seen, node, parents), verbose)
  175. line_number += len(new_lines)
  176. result.extend(new_lines)
  177. util._undo_to(current)
  178. return result
  179. # Mercurial age function -----------------------------------------------------------
  180. agescales = [("yr", 3600 * 24 * 365),
  181. ("mon", 3600 * 24 * 30),
  182. ("wk", 3600 * 24 * 7),
  183. ("dy", 3600 * 24),
  184. ("hr", 3600),
  185. ("min", 60)]
  186. def age(ts):
  187. '''turn a timestamp into an age string.'''
  188. def plural(t, c):
  189. if c == 1:
  190. return t
  191. return t + "s"
  192. def fmt(t, c):
  193. return "%d %s" % (int(c), plural(t, c))
  194. now = time.time()
  195. then = ts
  196. if then > now:
  197. return 'in the future'
  198. delta = max(1, int(now - then))
  199. if delta > agescales[0][1] * 2:
  200. return time.strftime('%Y-%m-%d', time.gmtime(float(ts)))
  201. for t, s in agescales:
  202. n = delta // s
  203. if n >= 2 or s == 1:
  204. return '%s ago' % fmt(t, n)
  205. return "<1 min ago"