comparison mercurial/graphmod.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children faa8a59f4a06
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
25 pycompat, 25 pycompat,
26 smartset, 26 smartset,
27 util, 27 util,
28 ) 28 )
29 29
30 CHANGESET = 'C' 30 CHANGESET = b'C'
31 PARENT = 'P' 31 PARENT = b'P'
32 GRANDPARENT = 'G' 32 GRANDPARENT = b'G'
33 MISSINGPARENT = 'M' 33 MISSINGPARENT = b'M'
34 # Style of line to draw. None signals a line that ends and is removed at this 34 # Style of line to draw. None signals a line that ends and is removed at this
35 # point. A number prefix means only the last N characters of the current block 35 # point. A number prefix means only the last N characters of the current block
36 # will use that style, the rest will use the PARENT style. Add a - sign 36 # will use that style, the rest will use the PARENT style. Add a - sign
37 # (so making N negative) and all but the first N characters use that style. 37 # (so making N negative) and all but the first N characters use that style.
38 EDGES = {PARENT: '|', GRANDPARENT: ':', MISSINGPARENT: None} 38 EDGES = {PARENT: b'|', GRANDPARENT: b':', MISSINGPARENT: None}
39 39
40 40
41 def dagwalker(repo, revs): 41 def dagwalker(repo, revs):
42 """cset DAG generator yielding (id, CHANGESET, ctx, [parentinfo]) tuples 42 """cset DAG generator yielding (id, CHANGESET, ctx, [parentinfo]) tuples
43 43
116 seen = [] 116 seen = []
117 colors = {} 117 colors = {}
118 newcolor = 1 118 newcolor = 1
119 config = {} 119 config = {}
120 120
121 for key, val in repo.ui.configitems('graph'): 121 for key, val in repo.ui.configitems(b'graph'):
122 if '.' in key: 122 if b'.' in key:
123 branch, setting = key.rsplit('.', 1) 123 branch, setting = key.rsplit(b'.', 1)
124 # Validation 124 # Validation
125 if setting == "width" and val.isdigit(): 125 if setting == b"width" and val.isdigit():
126 config.setdefault(branch, {})[setting] = int(val) 126 config.setdefault(branch, {})[setting] = int(val)
127 elif setting == "color" and val.isalnum(): 127 elif setting == b"color" and val.isalnum():
128 config.setdefault(branch, {})[setting] = val 128 config.setdefault(branch, {})[setting] = val
129 129
130 if config: 130 if config:
131 getconf = util.lrucachefunc( 131 getconf = util.lrucachefunc(
132 lambda rev: config.get(repo[rev].branch(), {}) 132 lambda rev: config.get(repo[rev].branch(), {})
166 edges.append( 166 edges.append(
167 ( 167 (
168 ecol, 168 ecol,
169 next.index(eid), 169 next.index(eid),
170 colors[eid], 170 colors[eid],
171 bconf.get('width', -1), 171 bconf.get(b'width', -1),
172 bconf.get('color', ''), 172 bconf.get(b'color', b''),
173 ) 173 )
174 ) 174 )
175 elif eid == cur: 175 elif eid == cur:
176 for ptype, p in parents: 176 for ptype, p in parents:
177 bconf = getconf(p) 177 bconf = getconf(p)
178 edges.append( 178 edges.append(
179 ( 179 (
180 ecol, 180 ecol,
181 next.index(p), 181 next.index(p),
182 color, 182 color,
183 bconf.get('width', -1), 183 bconf.get(b'width', -1),
184 bconf.get('color', ''), 184 bconf.get(b'color', b''),
185 ) 185 )
186 ) 186 )
187 187
188 # Yield and move on 188 # Yield and move on
189 yield (cur, type, data, (col, color), edges) 189 yield (cur, type, data, (col, color), edges)
190 seen = next 190 seen = next
191 191
192 192
193 def asciiedges(type, char, state, rev, parents): 193 def asciiedges(type, char, state, rev, parents):
194 """adds edge info to changelog DAG walk suitable for ascii()""" 194 """adds edge info to changelog DAG walk suitable for ascii()"""
195 seen = state['seen'] 195 seen = state[b'seen']
196 if rev not in seen: 196 if rev not in seen:
197 seen.append(rev) 197 seen.append(rev)
198 nodeidx = seen.index(rev) 198 nodeidx = seen.index(rev)
199 199
200 knownparents = [] 200 knownparents = []
205 continue 205 continue
206 if parent in seen: 206 if parent in seen:
207 knownparents.append(parent) 207 knownparents.append(parent)
208 else: 208 else:
209 newparents.append(parent) 209 newparents.append(parent)
210 state['edges'][parent] = state['styles'].get(ptype, '|') 210 state[b'edges'][parent] = state[b'styles'].get(ptype, b'|')
211 211
212 ncols = len(seen) 212 ncols = len(seen)
213 width = 1 + ncols * 2 213 width = 1 + ncols * 2
214 nextseen = seen[:] 214 nextseen = seen[:]
215 nextseen[nodeidx : nodeidx + 1] = newparents 215 nextseen[nodeidx : nodeidx + 1] = newparents
224 edges.append((nodeidx, nodeidx)) 224 edges.append((nodeidx, nodeidx))
225 edges.append((nodeidx, nodeidx + 1)) 225 edges.append((nodeidx, nodeidx + 1))
226 nmorecols = 1 226 nmorecols = 1
227 width += 2 227 width += 2
228 yield (type, char, width, (nodeidx, edges, ncols, nmorecols)) 228 yield (type, char, width, (nodeidx, edges, ncols, nmorecols))
229 char = '\\' 229 char = b'\\'
230 nodeidx += 1 230 nodeidx += 1
231 ncols += 1 231 ncols += 1
232 edges = [] 232 edges = []
233 del newparents[0] 233 del newparents[0]
234 234
238 edges.append((nodeidx, nodeidx + 1)) 238 edges.append((nodeidx, nodeidx + 1))
239 nmorecols = len(nextseen) - ncols 239 nmorecols = len(nextseen) - ncols
240 if nmorecols > 0: 240 if nmorecols > 0:
241 width += 2 241 width += 2
242 # remove current node from edge characters, no longer needed 242 # remove current node from edge characters, no longer needed
243 state['edges'].pop(rev, None) 243 state[b'edges'].pop(rev, None)
244 yield (type, char, width, (nodeidx, edges, ncols, nmorecols)) 244 yield (type, char, width, (nodeidx, edges, ncols, nmorecols))
245 245
246 246
247 def _fixlongrightedges(edges): 247 def _fixlongrightedges(edges):
248 for (i, (start, end)) in enumerate(edges): 248 for (i, (start, end)) in enumerate(edges):
254 if fix_tail and coldiff == pdiff and coldiff != 0: 254 if fix_tail and coldiff == pdiff and coldiff != 0:
255 # Still going in the same non-vertical direction. 255 # Still going in the same non-vertical direction.
256 if coldiff == -1: 256 if coldiff == -1:
257 start = max(idx + 1, pidx) 257 start = max(idx + 1, pidx)
258 tail = echars[idx * 2 : (start - 1) * 2] 258 tail = echars[idx * 2 : (start - 1) * 2]
259 tail.extend(["/", " "] * (ncols - start)) 259 tail.extend([b"/", b" "] * (ncols - start))
260 return tail 260 return tail
261 else: 261 else:
262 return ["\\", " "] * (ncols - idx - 1) 262 return [b"\\", b" "] * (ncols - idx - 1)
263 else: 263 else:
264 remainder = ncols - idx - 1 264 remainder = ncols - idx - 1
265 return echars[-(remainder * 2) :] if remainder > 0 else [] 265 return echars[-(remainder * 2) :] if remainder > 0 else []
266 266
267 267
268 def _drawedges(echars, edges, nodeline, interline): 268 def _drawedges(echars, edges, nodeline, interline):
269 for (start, end) in edges: 269 for (start, end) in edges:
270 if start == end + 1: 270 if start == end + 1:
271 interline[2 * end + 1] = "/" 271 interline[2 * end + 1] = b"/"
272 elif start == end - 1: 272 elif start == end - 1:
273 interline[2 * start + 1] = "\\" 273 interline[2 * start + 1] = b"\\"
274 elif start == end: 274 elif start == end:
275 interline[2 * start] = echars[2 * start] 275 interline[2 * start] = echars[2 * start]
276 else: 276 else:
277 if 2 * end >= len(nodeline): 277 if 2 * end >= len(nodeline):
278 continue 278 continue
279 nodeline[2 * end] = "+" 279 nodeline[2 * end] = b"+"
280 if start > end: 280 if start > end:
281 (start, end) = (end, start) 281 (start, end) = (end, start)
282 for i in range(2 * start + 1, 2 * end): 282 for i in range(2 * start + 1, 2 * end):
283 if nodeline[i] != "+": 283 if nodeline[i] != b"+":
284 nodeline[i] = "-" 284 nodeline[i] = b"-"
285 285
286 286
287 def _getpaddingline(echars, idx, ncols, edges): 287 def _getpaddingline(echars, idx, ncols, edges):
288 # all edges up to the current node 288 # all edges up to the current node
289 line = echars[: idx * 2] 289 line = echars[: idx * 2]
295 # | | X | | X | | 295 # | | X | | X | |
296 # | |/ / | |/ / 296 # | |/ / | |/ /
297 # | | | | | | 297 # | | | | | |
298 line.extend(echars[idx * 2 : (idx + 1) * 2]) 298 line.extend(echars[idx * 2 : (idx + 1) * 2])
299 else: 299 else:
300 line.extend([' ', ' ']) 300 line.extend([b' ', b' '])
301 # all edges to the right of the current node 301 # all edges to the right of the current node
302 remainder = ncols - idx - 1 302 remainder = ncols - idx - 1
303 if remainder > 0: 303 if remainder > 0:
304 line.extend(echars[-(remainder * 2) :]) 304 line.extend(echars[-(remainder * 2) :])
305 return line 305 return line
320 # We need enough space to draw adjustment lines for these. 320 # We need enough space to draw adjustment lines for these.
321 edgechars = extra[::2] 321 edgechars = extra[::2]
322 while edgechars and edgechars[-1] is None: 322 while edgechars and edgechars[-1] is None:
323 edgechars.pop() 323 edgechars.pop()
324 shift_size = max((edgechars.count(None) * 2) - 1, 0) 324 shift_size = max((edgechars.count(None) * 2) - 1, 0)
325 minlines = 3 if not state['graphshorten'] else 2 325 minlines = 3 if not state[b'graphshorten'] else 2
326 while len(lines) < minlines + shift_size: 326 while len(lines) < minlines + shift_size:
327 lines.append(extra[:]) 327 lines.append(extra[:])
328 328
329 if shift_size: 329 if shift_size:
330 empties = [] 330 empties = []
336 else: 336 else:
337 toshift.append(i * 2) 337 toshift.append(i * 2)
338 targets = list(range(first_empty, first_empty + len(toshift) * 2, 2)) 338 targets = list(range(first_empty, first_empty + len(toshift) * 2, 2))
339 positions = toshift[:] 339 positions = toshift[:]
340 for line in lines[-shift_size:]: 340 for line in lines[-shift_size:]:
341 line[first_empty:] = [' '] * (len(line) - first_empty) 341 line[first_empty:] = [b' '] * (len(line) - first_empty)
342 for i in range(len(positions)): 342 for i in range(len(positions)):
343 pos = positions[i] - 1 343 pos = positions[i] - 1
344 positions[i] = max(pos, targets[i]) 344 positions[i] = max(pos, targets[i])
345 line[pos] = '/' if pos > targets[i] else extra[toshift[i]] 345 line[pos] = b'/' if pos > targets[i] else extra[toshift[i]]
346 346
347 map = {1: '|', 2: '~'} if not state['graphshorten'] else {1: '~'} 347 map = {1: b'|', 2: b'~'} if not state[b'graphshorten'] else {1: b'~'}
348 for i, line in enumerate(lines): 348 for i, line in enumerate(lines):
349 if None not in line: 349 if None not in line:
350 continue 350 continue
351 line[:] = [c or map.get(i, ' ') for c in line] 351 line[:] = [c or map.get(i, b' ') for c in line]
352 352
353 # remove edges that ended 353 # remove edges that ended
354 remove = [p for p, c in edgemap.items() if c is None] 354 remove = [p for p, c in edgemap.items() if c is None]
355 for parent in remove: 355 for parent in remove:
356 del edgemap[parent] 356 del edgemap[parent]
358 358
359 359
360 def asciistate(): 360 def asciistate():
361 """returns the initial value for the "state" argument to ascii()""" 361 """returns the initial value for the "state" argument to ascii()"""
362 return { 362 return {
363 'seen': [], 363 b'seen': [],
364 'edges': {}, 364 b'edges': {},
365 'lastcoldiff': 0, 365 b'lastcoldiff': 0,
366 'lastindex': 0, 366 b'lastindex': 0,
367 'styles': EDGES.copy(), 367 b'styles': EDGES.copy(),
368 'graphshorten': False, 368 b'graphshorten': False,
369 } 369 }
370 370
371 371
372 def outputgraph(ui, graph): 372 def outputgraph(ui, graph):
373 """outputs an ASCII graph of a DAG 373 """outputs an ASCII graph of a DAG
381 381
382 this function can be monkey-patched by extensions to alter graph display 382 this function can be monkey-patched by extensions to alter graph display
383 without needing to mimic all of the edge-fixup logic in ascii() 383 without needing to mimic all of the edge-fixup logic in ascii()
384 """ 384 """
385 for (ln, logstr) in graph: 385 for (ln, logstr) in graph:
386 ui.write((ln + logstr).rstrip() + "\n") 386 ui.write((ln + logstr).rstrip() + b"\n")
387 387
388 388
389 def ascii(ui, state, type, char, text, coldata): 389 def ascii(ui, state, type, char, text, coldata):
390 """prints an ASCII graph of the DAG 390 """prints an ASCII graph of the DAG
391 391
407 0 means no columns added or removed; 1 means one column added. 407 0 means no columns added or removed; 1 means one column added.
408 """ 408 """
409 idx, edges, ncols, coldiff = coldata 409 idx, edges, ncols, coldiff = coldata
410 assert -2 < coldiff < 2 410 assert -2 < coldiff < 2
411 411
412 edgemap, seen = state['edges'], state['seen'] 412 edgemap, seen = state[b'edges'], state[b'seen']
413 # Be tolerant of history issues; make sure we have at least ncols + coldiff 413 # Be tolerant of history issues; make sure we have at least ncols + coldiff
414 # elements to work with. See test-glog.t for broken history test cases. 414 # elements to work with. See test-glog.t for broken history test cases.
415 echars = [c for p in seen for c in (edgemap.get(p, '|'), ' ')] 415 echars = [c for p in seen for c in (edgemap.get(p, b'|'), b' ')]
416 echars.extend(('|', ' ') * max(ncols + coldiff - len(seen), 0)) 416 echars.extend((b'|', b' ') * max(ncols + coldiff - len(seen), 0))
417 417
418 if coldiff == -1: 418 if coldiff == -1:
419 # Transform 419 # Transform
420 # 420 #
421 # | | | | | | 421 # | | | | | |
444 # o | | o | | 444 # o | | o | |
445 fix_nodeline_tail = len(text) <= 2 and not add_padding_line 445 fix_nodeline_tail = len(text) <= 2 and not add_padding_line
446 446
447 # nodeline is the line containing the node character (typically o) 447 # nodeline is the line containing the node character (typically o)
448 nodeline = echars[: idx * 2] 448 nodeline = echars[: idx * 2]
449 nodeline.extend([char, " "]) 449 nodeline.extend([char, b" "])
450 450
451 nodeline.extend( 451 nodeline.extend(
452 _getnodelineedgestail( 452 _getnodelineedgestail(
453 echars, 453 echars,
454 idx, 454 idx,
455 state['lastindex'], 455 state[b'lastindex'],
456 ncols, 456 ncols,
457 coldiff, 457 coldiff,
458 state['lastcoldiff'], 458 state[b'lastcoldiff'],
459 fix_nodeline_tail, 459 fix_nodeline_tail,
460 ) 460 )
461 ) 461 )
462 462
463 # shift_interline is the line containing the non-vertical 463 # shift_interline is the line containing the non-vertical
464 # edges between this entry and the next 464 # edges between this entry and the next
465 shift_interline = echars[: idx * 2] 465 shift_interline = echars[: idx * 2]
466 for i in pycompat.xrange(2 + coldiff): 466 for i in pycompat.xrange(2 + coldiff):
467 shift_interline.append(' ') 467 shift_interline.append(b' ')
468 count = ncols - idx - 1 468 count = ncols - idx - 1
469 if coldiff == -1: 469 if coldiff == -1:
470 for i in pycompat.xrange(count): 470 for i in pycompat.xrange(count):
471 shift_interline.extend(['/', ' ']) 471 shift_interline.extend([b'/', b' '])
472 elif coldiff == 0: 472 elif coldiff == 0:
473 shift_interline.extend(echars[(idx + 1) * 2 : ncols * 2]) 473 shift_interline.extend(echars[(idx + 1) * 2 : ncols * 2])
474 else: 474 else:
475 for i in pycompat.xrange(count): 475 for i in pycompat.xrange(count):
476 shift_interline.extend(['\\', ' ']) 476 shift_interline.extend([b'\\', b' '])
477 477
478 # draw edges from the current node to its parents 478 # draw edges from the current node to its parents
479 _drawedges(echars, edges, nodeline, shift_interline) 479 _drawedges(echars, edges, nodeline, shift_interline)
480 480
481 # lines is the list of all graph lines to print 481 # lines is the list of all graph lines to print
483 if add_padding_line: 483 if add_padding_line:
484 lines.append(_getpaddingline(echars, idx, ncols, edges)) 484 lines.append(_getpaddingline(echars, idx, ncols, edges))
485 485
486 # If 'graphshorten' config, only draw shift_interline 486 # If 'graphshorten' config, only draw shift_interline
487 # when there is any non vertical flow in graph. 487 # when there is any non vertical flow in graph.
488 if state['graphshorten']: 488 if state[b'graphshorten']:
489 if any(c in br'\/' for c in shift_interline if c): 489 if any(c in br'\/' for c in shift_interline if c):
490 lines.append(shift_interline) 490 lines.append(shift_interline)
491 # Else, no 'graphshorten' config so draw shift_interline. 491 # Else, no 'graphshorten' config so draw shift_interline.
492 else: 492 else:
493 lines.append(shift_interline) 493 lines.append(shift_interline)
500 lines.append(extra_interline[:]) 500 lines.append(extra_interline[:])
501 501
502 _drawendinglines(lines, extra_interline, edgemap, seen, state) 502 _drawendinglines(lines, extra_interline, edgemap, seen, state)
503 503
504 while len(text) < len(lines): 504 while len(text) < len(lines):
505 text.append("") 505 text.append(b"")
506 506
507 # print lines 507 # print lines
508 indentation_level = max(ncols, ncols + coldiff) 508 indentation_level = max(ncols, ncols + coldiff)
509 lines = ["%-*s " % (2 * indentation_level, "".join(line)) for line in lines] 509 lines = [
510 b"%-*s " % (2 * indentation_level, b"".join(line)) for line in lines
511 ]
510 outputgraph(ui, zip(lines, text)) 512 outputgraph(ui, zip(lines, text))
511 513
512 # ... and start over 514 # ... and start over
513 state['lastcoldiff'] = coldiff 515 state[b'lastcoldiff'] = coldiff
514 state['lastindex'] = idx 516 state[b'lastindex'] = idx