comparison mercurial/graphmod.py @ 43776:faa8a59f4a06

graphlog: change state dict to attr struct This should help static analysis.
author Yuya Nishihara <yuya@tcha.org>
date Thu, 21 Nov 2019 22:43:01 +0900
parents 687b865b95ad
children 9d2b2df2c2ba
comparison
equal deleted inserted replaced
43775:b5f183eedd98 43776:faa8a59f4a06
18 """ 18 """
19 19
20 from __future__ import absolute_import 20 from __future__ import absolute_import
21 21
22 from .node import nullrev 22 from .node import nullrev
23 from .thirdparty import attr
23 from . import ( 24 from . import (
24 dagop, 25 dagop,
25 pycompat, 26 pycompat,
26 smartset, 27 smartset,
27 util, 28 util,
190 seen = next 191 seen = next
191 192
192 193
193 def asciiedges(type, char, state, rev, parents): 194 def asciiedges(type, char, state, rev, parents):
194 """adds edge info to changelog DAG walk suitable for ascii()""" 195 """adds edge info to changelog DAG walk suitable for ascii()"""
195 seen = state[b'seen'] 196 seen = state.seen
196 if rev not in seen: 197 if rev not in seen:
197 seen.append(rev) 198 seen.append(rev)
198 nodeidx = seen.index(rev) 199 nodeidx = seen.index(rev)
199 200
200 knownparents = [] 201 knownparents = []
205 continue 206 continue
206 if parent in seen: 207 if parent in seen:
207 knownparents.append(parent) 208 knownparents.append(parent)
208 else: 209 else:
209 newparents.append(parent) 210 newparents.append(parent)
210 state[b'edges'][parent] = state[b'styles'].get(ptype, b'|') 211 state.edges[parent] = state.styles.get(ptype, b'|')
211 212
212 ncols = len(seen) 213 ncols = len(seen)
213 width = 1 + ncols * 2 214 width = 1 + ncols * 2
214 nextseen = seen[:] 215 nextseen = seen[:]
215 nextseen[nodeidx : nodeidx + 1] = newparents 216 nextseen[nodeidx : nodeidx + 1] = newparents
238 edges.append((nodeidx, nodeidx + 1)) 239 edges.append((nodeidx, nodeidx + 1))
239 nmorecols = len(nextseen) - ncols 240 nmorecols = len(nextseen) - ncols
240 if nmorecols > 0: 241 if nmorecols > 0:
241 width += 2 242 width += 2
242 # remove current node from edge characters, no longer needed 243 # remove current node from edge characters, no longer needed
243 state[b'edges'].pop(rev, None) 244 state.edges.pop(rev, None)
244 yield (type, char, width, (nodeidx, edges, ncols, nmorecols)) 245 yield (type, char, width, (nodeidx, edges, ncols, nmorecols))
245 246
246 247
247 def _fixlongrightedges(edges): 248 def _fixlongrightedges(edges):
248 for (i, (start, end)) in enumerate(edges): 249 for (i, (start, end)) in enumerate(edges):
320 # We need enough space to draw adjustment lines for these. 321 # We need enough space to draw adjustment lines for these.
321 edgechars = extra[::2] 322 edgechars = extra[::2]
322 while edgechars and edgechars[-1] is None: 323 while edgechars and edgechars[-1] is None:
323 edgechars.pop() 324 edgechars.pop()
324 shift_size = max((edgechars.count(None) * 2) - 1, 0) 325 shift_size = max((edgechars.count(None) * 2) - 1, 0)
325 minlines = 3 if not state[b'graphshorten'] else 2 326 minlines = 3 if not state.graphshorten else 2
326 while len(lines) < minlines + shift_size: 327 while len(lines) < minlines + shift_size:
327 lines.append(extra[:]) 328 lines.append(extra[:])
328 329
329 if shift_size: 330 if shift_size:
330 empties = [] 331 empties = []
342 for i in range(len(positions)): 343 for i in range(len(positions)):
343 pos = positions[i] - 1 344 pos = positions[i] - 1
344 positions[i] = max(pos, targets[i]) 345 positions[i] = max(pos, targets[i])
345 line[pos] = b'/' if pos > targets[i] else extra[toshift[i]] 346 line[pos] = b'/' if pos > targets[i] else extra[toshift[i]]
346 347
347 map = {1: b'|', 2: b'~'} if not state[b'graphshorten'] else {1: b'~'} 348 map = {1: b'|', 2: b'~'} if not state.graphshorten else {1: b'~'}
348 for i, line in enumerate(lines): 349 for i, line in enumerate(lines):
349 if None not in line: 350 if None not in line:
350 continue 351 continue
351 line[:] = [c or map.get(i, b' ') for c in line] 352 line[:] = [c or map.get(i, b' ') for c in line]
352 353
355 for parent in remove: 356 for parent in remove:
356 del edgemap[parent] 357 del edgemap[parent]
357 seen.remove(parent) 358 seen.remove(parent)
358 359
359 360
360 def asciistate(): 361 @attr.s
361 """returns the initial value for the "state" argument to ascii()""" 362 class asciistate(object):
362 return { 363 """State of ascii() graph rendering"""
363 b'seen': [], 364
364 b'edges': {}, 365 seen = attr.ib(init=False, default=attr.Factory(list))
365 b'lastcoldiff': 0, 366 edges = attr.ib(init=False, default=attr.Factory(dict))
366 b'lastindex': 0, 367 lastcoldiff = attr.ib(init=False, default=0)
367 b'styles': EDGES.copy(), 368 lastindex = attr.ib(init=False, default=0)
368 b'graphshorten': False, 369 styles = attr.ib(init=False, default=attr.Factory(EDGES.copy))
369 } 370 graphshorten = attr.ib(init=False, default=False)
370 371
371 372
372 def outputgraph(ui, graph): 373 def outputgraph(ui, graph):
373 """outputs an ASCII graph of a DAG 374 """outputs an ASCII graph of a DAG
374 375
407 0 means no columns added or removed; 1 means one column added. 408 0 means no columns added or removed; 1 means one column added.
408 """ 409 """
409 idx, edges, ncols, coldiff = coldata 410 idx, edges, ncols, coldiff = coldata
410 assert -2 < coldiff < 2 411 assert -2 < coldiff < 2
411 412
412 edgemap, seen = state[b'edges'], state[b'seen'] 413 edgemap, seen = state.edges, state.seen
413 # Be tolerant of history issues; make sure we have at least ncols + coldiff 414 # 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. 415 # 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, b'|'), b' ')] 416 echars = [c for p in seen for c in (edgemap.get(p, b'|'), b' ')]
416 echars.extend((b'|', b' ') * max(ncols + coldiff - len(seen), 0)) 417 echars.extend((b'|', b' ') * max(ncols + coldiff - len(seen), 0))
417 418
450 451
451 nodeline.extend( 452 nodeline.extend(
452 _getnodelineedgestail( 453 _getnodelineedgestail(
453 echars, 454 echars,
454 idx, 455 idx,
455 state[b'lastindex'], 456 state.lastindex,
456 ncols, 457 ncols,
457 coldiff, 458 coldiff,
458 state[b'lastcoldiff'], 459 state.lastcoldiff,
459 fix_nodeline_tail, 460 fix_nodeline_tail,
460 ) 461 )
461 ) 462 )
462 463
463 # shift_interline is the line containing the non-vertical 464 # shift_interline is the line containing the non-vertical
483 if add_padding_line: 484 if add_padding_line:
484 lines.append(_getpaddingline(echars, idx, ncols, edges)) 485 lines.append(_getpaddingline(echars, idx, ncols, edges))
485 486
486 # If 'graphshorten' config, only draw shift_interline 487 # If 'graphshorten' config, only draw shift_interline
487 # when there is any non vertical flow in graph. 488 # when there is any non vertical flow in graph.
488 if state[b'graphshorten']: 489 if state.graphshorten:
489 if any(c in br'\/' for c in shift_interline if c): 490 if any(c in br'\/' for c in shift_interline if c):
490 lines.append(shift_interline) 491 lines.append(shift_interline)
491 # Else, no 'graphshorten' config so draw shift_interline. 492 # Else, no 'graphshorten' config so draw shift_interline.
492 else: 493 else:
493 lines.append(shift_interline) 494 lines.append(shift_interline)
510 b"%-*s " % (2 * indentation_level, b"".join(line)) for line in lines 511 b"%-*s " % (2 * indentation_level, b"".join(line)) for line in lines
511 ] 512 ]
512 outputgraph(ui, zip(lines, text)) 513 outputgraph(ui, zip(lines, text))
513 514
514 # ... and start over 515 # ... and start over
515 state[b'lastcoldiff'] = coldiff 516 state.lastcoldiff = coldiff
516 state[b'lastindex'] = idx 517 state.lastindex = idx