##// END OF EJS Templates
graphlog: extract ascii drawing code into graphmod
Patrick Mezard -
r17179:0849d725 default
parent child Browse files
Show More
@@ -21,209 +21,6 b' cmdtable = {}'
21 command = cmdutil.command(cmdtable)
21 command = cmdutil.command(cmdtable)
22 testedwith = 'internal'
22 testedwith = 'internal'
23
23
24 def asciiedges(type, char, lines, seen, rev, parents):
25 """adds edge info to changelog DAG walk suitable for ascii()"""
26 if rev not in seen:
27 seen.append(rev)
28 nodeidx = seen.index(rev)
29
30 knownparents = []
31 newparents = []
32 for parent in parents:
33 if parent in seen:
34 knownparents.append(parent)
35 else:
36 newparents.append(parent)
37
38 ncols = len(seen)
39 nextseen = seen[:]
40 nextseen[nodeidx:nodeidx + 1] = newparents
41 edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
42
43 while len(newparents) > 2:
44 # ascii() only knows how to add or remove a single column between two
45 # calls. Nodes with more than two parents break this constraint so we
46 # introduce intermediate expansion lines to grow the active node list
47 # slowly.
48 edges.append((nodeidx, nodeidx))
49 edges.append((nodeidx, nodeidx + 1))
50 nmorecols = 1
51 yield (type, char, lines, (nodeidx, edges, ncols, nmorecols))
52 char = '\\'
53 lines = []
54 nodeidx += 1
55 ncols += 1
56 edges = []
57 del newparents[0]
58
59 if len(newparents) > 0:
60 edges.append((nodeidx, nodeidx))
61 if len(newparents) > 1:
62 edges.append((nodeidx, nodeidx + 1))
63 nmorecols = len(nextseen) - ncols
64 seen[:] = nextseen
65 yield (type, char, lines, (nodeidx, edges, ncols, nmorecols))
66
67 def _fixlongrightedges(edges):
68 for (i, (start, end)) in enumerate(edges):
69 if end > start:
70 edges[i] = (start, end + 1)
71
72 def _getnodelineedgestail(
73 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
74 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
75 # Still going in the same non-vertical direction.
76 if n_columns_diff == -1:
77 start = max(node_index + 1, p_node_index)
78 tail = ["|", " "] * (start - node_index - 1)
79 tail.extend(["/", " "] * (n_columns - start))
80 return tail
81 else:
82 return ["\\", " "] * (n_columns - node_index - 1)
83 else:
84 return ["|", " "] * (n_columns - node_index - 1)
85
86 def _drawedges(edges, nodeline, interline):
87 for (start, end) in edges:
88 if start == end + 1:
89 interline[2 * end + 1] = "/"
90 elif start == end - 1:
91 interline[2 * start + 1] = "\\"
92 elif start == end:
93 interline[2 * start] = "|"
94 else:
95 if 2 * end >= len(nodeline):
96 continue
97 nodeline[2 * end] = "+"
98 if start > end:
99 (start, end) = (end, start)
100 for i in range(2 * start + 1, 2 * end):
101 if nodeline[i] != "+":
102 nodeline[i] = "-"
103
104 def _getpaddingline(ni, n_columns, edges):
105 line = []
106 line.extend(["|", " "] * ni)
107 if (ni, ni - 1) in edges or (ni, ni) in edges:
108 # (ni, ni - 1) (ni, ni)
109 # | | | | | | | |
110 # +---o | | o---+
111 # | | c | | c | |
112 # | |/ / | |/ /
113 # | | | | | |
114 c = "|"
115 else:
116 c = " "
117 line.extend([c, " "])
118 line.extend(["|", " "] * (n_columns - ni - 1))
119 return line
120
121 def asciistate():
122 """returns the initial value for the "state" argument to ascii()"""
123 return [0, 0]
124
125 def ascii(ui, state, type, char, text, coldata):
126 """prints an ASCII graph of the DAG
127
128 takes the following arguments (one call per node in the graph):
129
130 - ui to write to
131 - Somewhere to keep the needed state in (init to asciistate())
132 - Column of the current node in the set of ongoing edges.
133 - Type indicator of node data, usually 'C' for changesets.
134 - Payload: (char, lines):
135 - Character to use as node's symbol.
136 - List of lines to display as the node's text.
137 - Edges; a list of (col, next_col) indicating the edges between
138 the current node and its parents.
139 - Number of columns (ongoing edges) in the current revision.
140 - The difference between the number of columns (ongoing edges)
141 in the next revision and the number of columns (ongoing edges)
142 in the current revision. That is: -1 means one column removed;
143 0 means no columns added or removed; 1 means one column added.
144 """
145
146 idx, edges, ncols, coldiff = coldata
147 assert -2 < coldiff < 2
148 if coldiff == -1:
149 # Transform
150 #
151 # | | | | | |
152 # o | | into o---+
153 # |X / |/ /
154 # | | | |
155 _fixlongrightedges(edges)
156
157 # add_padding_line says whether to rewrite
158 #
159 # | | | | | | | |
160 # | o---+ into | o---+
161 # | / / | | | # <--- padding line
162 # o | | | / /
163 # o | |
164 add_padding_line = (len(text) > 2 and coldiff == -1 and
165 [x for (x, y) in edges if x + 1 < y])
166
167 # fix_nodeline_tail says whether to rewrite
168 #
169 # | | o | | | | o | |
170 # | | |/ / | | |/ /
171 # | o | | into | o / / # <--- fixed nodeline tail
172 # | |/ / | |/ /
173 # o | | o | |
174 fix_nodeline_tail = len(text) <= 2 and not add_padding_line
175
176 # nodeline is the line containing the node character (typically o)
177 nodeline = ["|", " "] * idx
178 nodeline.extend([char, " "])
179
180 nodeline.extend(
181 _getnodelineedgestail(idx, state[1], ncols, coldiff,
182 state[0], fix_nodeline_tail))
183
184 # shift_interline is the line containing the non-vertical
185 # edges between this entry and the next
186 shift_interline = ["|", " "] * idx
187 if coldiff == -1:
188 n_spaces = 1
189 edge_ch = "/"
190 elif coldiff == 0:
191 n_spaces = 2
192 edge_ch = "|"
193 else:
194 n_spaces = 3
195 edge_ch = "\\"
196 shift_interline.extend(n_spaces * [" "])
197 shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
198
199 # draw edges from the current node to its parents
200 _drawedges(edges, nodeline, shift_interline)
201
202 # lines is the list of all graph lines to print
203 lines = [nodeline]
204 if add_padding_line:
205 lines.append(_getpaddingline(idx, ncols, edges))
206 lines.append(shift_interline)
207
208 # make sure that there are as many graph lines as there are
209 # log strings
210 while len(text) < len(lines):
211 text.append("")
212 if len(lines) < len(text):
213 extra_interline = ["|", " "] * (ncols + coldiff)
214 while len(lines) < len(text):
215 lines.append(extra_interline)
216
217 # print lines
218 indentation_level = max(ncols, ncols + coldiff)
219 for (line, logstr) in zip(lines, text):
220 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
221 ui.write(ln.rstrip() + '\n')
222
223 # ... and start over
224 state[0] = coldiff
225 state[1] = idx
226
227 def _checkunsupportedflags(pats, opts):
24 def _checkunsupportedflags(pats, opts):
228 for op in ["newest_first"]:
25 for op in ["newest_first"]:
229 if op in opts and opts[op]:
26 if op in opts and opts[op]:
@@ -441,7 +238,7 b' def getlogrevs(repo, pats, opts):'
441
238
442 def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None,
239 def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None,
443 filematcher=None):
240 filematcher=None):
444 seen, state = [], asciistate()
241 seen, state = [], graphmod.asciistate()
445 for rev, type, ctx, parents in dag:
242 for rev, type, ctx, parents in dag:
446 char = 'o'
243 char = 'o'
447 if ctx.node() in showparents:
244 if ctx.node() in showparents:
@@ -465,7 +262,7 b' def generate(ui, dag, displayer, showpar'
465 displayer.flush(rev)
262 displayer.flush(rev)
466 edges = edgefn(type, char, lines, seen, rev, parents)
263 edges = edgefn(type, char, lines, seen, rev, parents)
467 for type, char, lines, coldata in edges:
264 for type, char, lines, coldata in edges:
468 ascii(ui, state, type, char, lines, coldata)
265 graphmod.ascii(ui, state, type, char, lines, coldata)
469 displayer.close()
266 displayer.close()
470
267
471 @command('glog',
268 @command('glog',
@@ -516,8 +313,8 b' def graphlog(ui, repo, *pats, **opts):'
516 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
313 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
517 displayer = show_changeset(ui, repo, opts, buffered=True)
314 displayer = show_changeset(ui, repo, opts, buffered=True)
518 showparents = [ctx.node() for ctx in repo[None].parents()]
315 showparents = [ctx.node() for ctx in repo[None].parents()]
519 generate(ui, revdag, displayer, showparents, asciiedges, getrenamed,
316 generate(ui, revdag, displayer, showparents, graphmod.asciiedges,
520 filematcher)
317 getrenamed, filematcher)
521
318
522 def graphrevs(repo, nodes, opts):
319 def graphrevs(repo, nodes, opts):
523 limit = cmdutil.loglimit(opts)
320 limit = cmdutil.loglimit(opts)
@@ -544,7 +341,7 b' def goutgoing(ui, repo, dest=None, **opt'
544 revdag = graphrevs(repo, o, opts)
341 revdag = graphrevs(repo, o, opts)
545 displayer = show_changeset(ui, repo, opts, buffered=True)
342 displayer = show_changeset(ui, repo, opts, buffered=True)
546 showparents = [ctx.node() for ctx in repo[None].parents()]
343 showparents = [ctx.node() for ctx in repo[None].parents()]
547 generate(ui, revdag, displayer, showparents, asciiedges)
344 generate(ui, revdag, displayer, showparents, graphmod.asciiedges)
548
345
549 def gincoming(ui, repo, source="default", **opts):
346 def gincoming(ui, repo, source="default", **opts):
550 """show the incoming changesets alongside an ASCII revision graph
347 """show the incoming changesets alongside an ASCII revision graph
@@ -562,7 +359,7 b' def gincoming(ui, repo, source="default"'
562 def display(other, chlist, displayer):
359 def display(other, chlist, displayer):
563 revdag = graphrevs(other, chlist, opts)
360 revdag = graphrevs(other, chlist, opts)
564 showparents = [ctx.node() for ctx in repo[None].parents()]
361 showparents = [ctx.node() for ctx in repo[None].parents()]
565 generate(ui, revdag, displayer, showparents, asciiedges)
362 generate(ui, revdag, displayer, showparents, graphmod.asciiedges)
566
363
567 hg._incoming(display, subreporecurse, ui, repo, source, opts, buffered=True)
364 hg._incoming(display, subreporecurse, ui, repo, source, opts, buffered=True)
568
365
@@ -163,3 +163,206 b' def grandparent(cl, lowestrev, roots, he'
163 pending.update([p for p in cl.parentrevs(r)])
163 pending.update([p for p in cl.parentrevs(r)])
164 seen.add(r)
164 seen.add(r)
165 return sorted(kept)
165 return sorted(kept)
166
167 def asciiedges(type, char, lines, seen, rev, parents):
168 """adds edge info to changelog DAG walk suitable for ascii()"""
169 if rev not in seen:
170 seen.append(rev)
171 nodeidx = seen.index(rev)
172
173 knownparents = []
174 newparents = []
175 for parent in parents:
176 if parent in seen:
177 knownparents.append(parent)
178 else:
179 newparents.append(parent)
180
181 ncols = len(seen)
182 nextseen = seen[:]
183 nextseen[nodeidx:nodeidx + 1] = newparents
184 edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
185
186 while len(newparents) > 2:
187 # ascii() only knows how to add or remove a single column between two
188 # calls. Nodes with more than two parents break this constraint so we
189 # introduce intermediate expansion lines to grow the active node list
190 # slowly.
191 edges.append((nodeidx, nodeidx))
192 edges.append((nodeidx, nodeidx + 1))
193 nmorecols = 1
194 yield (type, char, lines, (nodeidx, edges, ncols, nmorecols))
195 char = '\\'
196 lines = []
197 nodeidx += 1
198 ncols += 1
199 edges = []
200 del newparents[0]
201
202 if len(newparents) > 0:
203 edges.append((nodeidx, nodeidx))
204 if len(newparents) > 1:
205 edges.append((nodeidx, nodeidx + 1))
206 nmorecols = len(nextseen) - ncols
207 seen[:] = nextseen
208 yield (type, char, lines, (nodeidx, edges, ncols, nmorecols))
209
210 def _fixlongrightedges(edges):
211 for (i, (start, end)) in enumerate(edges):
212 if end > start:
213 edges[i] = (start, end + 1)
214
215 def _getnodelineedgestail(
216 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
217 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
218 # Still going in the same non-vertical direction.
219 if n_columns_diff == -1:
220 start = max(node_index + 1, p_node_index)
221 tail = ["|", " "] * (start - node_index - 1)
222 tail.extend(["/", " "] * (n_columns - start))
223 return tail
224 else:
225 return ["\\", " "] * (n_columns - node_index - 1)
226 else:
227 return ["|", " "] * (n_columns - node_index - 1)
228
229 def _drawedges(edges, nodeline, interline):
230 for (start, end) in edges:
231 if start == end + 1:
232 interline[2 * end + 1] = "/"
233 elif start == end - 1:
234 interline[2 * start + 1] = "\\"
235 elif start == end:
236 interline[2 * start] = "|"
237 else:
238 if 2 * end >= len(nodeline):
239 continue
240 nodeline[2 * end] = "+"
241 if start > end:
242 (start, end) = (end, start)
243 for i in range(2 * start + 1, 2 * end):
244 if nodeline[i] != "+":
245 nodeline[i] = "-"
246
247 def _getpaddingline(ni, n_columns, edges):
248 line = []
249 line.extend(["|", " "] * ni)
250 if (ni, ni - 1) in edges or (ni, ni) in edges:
251 # (ni, ni - 1) (ni, ni)
252 # | | | | | | | |
253 # +---o | | o---+
254 # | | c | | c | |
255 # | |/ / | |/ /
256 # | | | | | |
257 c = "|"
258 else:
259 c = " "
260 line.extend([c, " "])
261 line.extend(["|", " "] * (n_columns - ni - 1))
262 return line
263
264 def asciistate():
265 """returns the initial value for the "state" argument to ascii()"""
266 return [0, 0]
267
268 def ascii(ui, state, type, char, text, coldata):
269 """prints an ASCII graph of the DAG
270
271 takes the following arguments (one call per node in the graph):
272
273 - ui to write to
274 - Somewhere to keep the needed state in (init to asciistate())
275 - Column of the current node in the set of ongoing edges.
276 - Type indicator of node data, usually 'C' for changesets.
277 - Payload: (char, lines):
278 - Character to use as node's symbol.
279 - List of lines to display as the node's text.
280 - Edges; a list of (col, next_col) indicating the edges between
281 the current node and its parents.
282 - Number of columns (ongoing edges) in the current revision.
283 - The difference between the number of columns (ongoing edges)
284 in the next revision and the number of columns (ongoing edges)
285 in the current revision. That is: -1 means one column removed;
286 0 means no columns added or removed; 1 means one column added.
287 """
288
289 idx, edges, ncols, coldiff = coldata
290 assert -2 < coldiff < 2
291 if coldiff == -1:
292 # Transform
293 #
294 # | | | | | |
295 # o | | into o---+
296 # |X / |/ /
297 # | | | |
298 _fixlongrightedges(edges)
299
300 # add_padding_line says whether to rewrite
301 #
302 # | | | | | | | |
303 # | o---+ into | o---+
304 # | / / | | | # <--- padding line
305 # o | | | / /
306 # o | |
307 add_padding_line = (len(text) > 2 and coldiff == -1 and
308 [x for (x, y) in edges if x + 1 < y])
309
310 # fix_nodeline_tail says whether to rewrite
311 #
312 # | | o | | | | o | |
313 # | | |/ / | | |/ /
314 # | o | | into | o / / # <--- fixed nodeline tail
315 # | |/ / | |/ /
316 # o | | o | |
317 fix_nodeline_tail = len(text) <= 2 and not add_padding_line
318
319 # nodeline is the line containing the node character (typically o)
320 nodeline = ["|", " "] * idx
321 nodeline.extend([char, " "])
322
323 nodeline.extend(
324 _getnodelineedgestail(idx, state[1], ncols, coldiff,
325 state[0], fix_nodeline_tail))
326
327 # shift_interline is the line containing the non-vertical
328 # edges between this entry and the next
329 shift_interline = ["|", " "] * idx
330 if coldiff == -1:
331 n_spaces = 1
332 edge_ch = "/"
333 elif coldiff == 0:
334 n_spaces = 2
335 edge_ch = "|"
336 else:
337 n_spaces = 3
338 edge_ch = "\\"
339 shift_interline.extend(n_spaces * [" "])
340 shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
341
342 # draw edges from the current node to its parents
343 _drawedges(edges, nodeline, shift_interline)
344
345 # lines is the list of all graph lines to print
346 lines = [nodeline]
347 if add_padding_line:
348 lines.append(_getpaddingline(idx, ncols, edges))
349 lines.append(shift_interline)
350
351 # make sure that there are as many graph lines as there are
352 # log strings
353 while len(text) < len(lines):
354 text.append("")
355 if len(lines) < len(text):
356 extra_interline = ["|", " "] * (ncols + coldiff)
357 while len(lines) < len(text):
358 lines.append(extra_interline)
359
360 # print lines
361 indentation_level = max(ncols, ncols + coldiff)
362 for (line, logstr) in zip(lines, text):
363 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
364 ui.write(ln.rstrip() + '\n')
365
366 # ... and start over
367 state[0] = coldiff
368 state[1] = idx
General Comments 0
You need to be logged in to leave comments. Login now