416 edges.append((nodeidx, nodeidx)) |
419 edges.append((nodeidx, nodeidx)) |
417 if len(newparents) > 1: |
420 if len(newparents) > 1: |
418 edges.append((nodeidx, nodeidx + 1)) |
421 edges.append((nodeidx, nodeidx + 1)) |
419 nmorecols = len(nextseen) - ncols |
422 nmorecols = len(nextseen) - ncols |
420 seen[:] = nextseen |
423 seen[:] = nextseen |
|
424 # remove current node from edge characters, no longer needed |
|
425 state['edges'].pop(rev, None) |
421 yield (type, char, lines, (nodeidx, edges, ncols, nmorecols)) |
426 yield (type, char, lines, (nodeidx, edges, ncols, nmorecols)) |
422 |
427 |
423 def _fixlongrightedges(edges): |
428 def _fixlongrightedges(edges): |
424 for (i, (start, end)) in enumerate(edges): |
429 for (i, (start, end)) in enumerate(edges): |
425 if end > start: |
430 if end > start: |
426 edges[i] = (start, end + 1) |
431 edges[i] = (start, end + 1) |
427 |
432 |
428 def _getnodelineedgestail( |
433 def _getnodelineedgestail( |
429 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail): |
434 echars, idx, pidx, ncols, coldiff, pdiff, fix_tail): |
430 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0: |
435 if fix_tail and coldiff == pdiff and coldiff != 0: |
431 # Still going in the same non-vertical direction. |
436 # Still going in the same non-vertical direction. |
432 if n_columns_diff == -1: |
437 if coldiff == -1: |
433 start = max(node_index + 1, p_node_index) |
438 start = max(idx + 1, pidx) |
434 tail = ["|", " "] * (start - node_index - 1) |
439 tail = echars[idx * 2:(start - 1) * 2] |
435 tail.extend(["/", " "] * (n_columns - start)) |
440 tail.extend(["/", " "] * (ncols - start)) |
436 return tail |
441 return tail |
437 else: |
442 else: |
438 return ["\\", " "] * (n_columns - node_index - 1) |
443 return ["\\", " "] * (ncols - idx - 1) |
439 else: |
444 else: |
440 return ["|", " "] * (n_columns - node_index - 1) |
445 remainder = (ncols - idx - 1) |
441 |
446 return echars[-(remainder * 2):] if remainder > 0 else [] |
442 def _drawedges(edges, nodeline, interline): |
447 |
|
448 def _drawedges(echars, edges, nodeline, interline): |
443 for (start, end) in edges: |
449 for (start, end) in edges: |
444 if start == end + 1: |
450 if start == end + 1: |
445 interline[2 * end + 1] = "/" |
451 interline[2 * end + 1] = "/" |
446 elif start == end - 1: |
452 elif start == end - 1: |
447 interline[2 * start + 1] = "\\" |
453 interline[2 * start + 1] = "\\" |
448 elif start == end: |
454 elif start == end: |
449 interline[2 * start] = "|" |
455 interline[2 * start] = echars[2 * start] |
450 else: |
456 else: |
451 if 2 * end >= len(nodeline): |
457 if 2 * end >= len(nodeline): |
452 continue |
458 continue |
453 nodeline[2 * end] = "+" |
459 nodeline[2 * end] = "+" |
454 if start > end: |
460 if start > end: |
455 (start, end) = (end, start) |
461 (start, end) = (end, start) |
456 for i in range(2 * start + 1, 2 * end): |
462 for i in range(2 * start + 1, 2 * end): |
457 if nodeline[i] != "+": |
463 if nodeline[i] != "+": |
458 nodeline[i] = "-" |
464 nodeline[i] = "-" |
459 |
465 |
460 def _getpaddingline(ni, n_columns, edges): |
466 def _getpaddingline(echars, idx, ncols, edges): |
461 line = [] |
467 # all edges up to the current node |
462 line.extend(["|", " "] * ni) |
468 line = echars[:idx * 2] |
463 if (ni, ni - 1) in edges or (ni, ni) in edges: |
469 # an edge for the current node, if there is one |
464 # (ni, ni - 1) (ni, ni) |
470 if (idx, idx - 1) in edges or (idx, idx) in edges: |
|
471 # (idx, idx - 1) (idx, idx) |
465 # | | | | | | | | |
472 # | | | | | | | | |
466 # +---o | | o---+ |
473 # +---o | | o---+ |
467 # | | c | | c | | |
474 # | | X | | X | | |
468 # | |/ / | |/ / |
475 # | |/ / | |/ / |
469 # | | | | | | |
476 # | | | | | | |
470 c = "|" |
477 line.extend(echars[idx * 2:(idx + 1) * 2]) |
471 else: |
478 else: |
472 c = " " |
479 line.extend(' ') |
473 line.extend([c, " "]) |
480 # all edges to the right of the current node |
474 line.extend(["|", " "] * (n_columns - ni - 1)) |
481 remainder = ncols - idx - 1 |
|
482 if remainder > 0: |
|
483 line.extend(echars[-(remainder * 2):]) |
475 return line |
484 return line |
476 |
485 |
477 def asciistate(): |
486 def asciistate(): |
478 """returns the initial value for the "state" argument to ascii()""" |
487 """returns the initial value for the "state" argument to ascii()""" |
479 return {'seen': [], 'lastcoldiff': 0, 'lastindex': 0} |
488 return { |
|
489 'seen': [], |
|
490 'edges': {}, |
|
491 'lastcoldiff': 0, |
|
492 'lastindex': 0, |
|
493 'styles': EDGES.copy(), |
|
494 } |
480 |
495 |
481 def ascii(ui, state, type, char, text, coldata): |
496 def ascii(ui, state, type, char, text, coldata): |
482 """prints an ASCII graph of the DAG |
497 """prints an ASCII graph of the DAG |
483 |
498 |
484 takes the following arguments (one call per node in the graph): |
499 takes the following arguments (one call per node in the graph): |
528 # | |/ / | |/ / |
549 # | |/ / | |/ / |
529 # o | | o | | |
550 # o | | o | | |
530 fix_nodeline_tail = len(text) <= 2 and not add_padding_line |
551 fix_nodeline_tail = len(text) <= 2 and not add_padding_line |
531 |
552 |
532 # nodeline is the line containing the node character (typically o) |
553 # nodeline is the line containing the node character (typically o) |
533 nodeline = ["|", " "] * idx |
554 nodeline = echars[:idx * 2] |
534 nodeline.extend([char, " "]) |
555 nodeline.extend([char, " "]) |
535 |
556 |
536 nodeline.extend( |
557 nodeline.extend( |
537 _getnodelineedgestail(idx, state['lastindex'], ncols, coldiff, |
558 _getnodelineedgestail( |
538 state['lastcoldiff'], fix_nodeline_tail)) |
559 echars, idx, state['lastindex'], ncols, coldiff, |
|
560 state['lastcoldiff'], fix_nodeline_tail)) |
539 |
561 |
540 # shift_interline is the line containing the non-vertical |
562 # shift_interline is the line containing the non-vertical |
541 # edges between this entry and the next |
563 # edges between this entry and the next |
542 shift_interline = ["|", " "] * idx |
564 shift_interline = echars[:idx * 2] |
|
565 shift_interline.extend(' ' * (2 + coldiff)) |
|
566 count = ncols - idx - 1 |
543 if coldiff == -1: |
567 if coldiff == -1: |
544 n_spaces = 1 |
568 shift_interline.extend('/ ' * count) |
545 edge_ch = "/" |
|
546 elif coldiff == 0: |
569 elif coldiff == 0: |
547 n_spaces = 2 |
570 shift_interline.extend(echars[(idx + 1) * 2:ncols * 2]) |
548 edge_ch = "|" |
|
549 else: |
571 else: |
550 n_spaces = 3 |
572 shift_interline.extend(r'\ ' * count) |
551 edge_ch = "\\" |
|
552 shift_interline.extend(n_spaces * [" "]) |
|
553 shift_interline.extend([edge_ch, " "] * (ncols - idx - 1)) |
|
554 |
573 |
555 # draw edges from the current node to its parents |
574 # draw edges from the current node to its parents |
556 _drawedges(edges, nodeline, shift_interline) |
575 _drawedges(echars, edges, nodeline, shift_interline) |
557 |
576 |
558 # lines is the list of all graph lines to print |
577 # lines is the list of all graph lines to print |
559 lines = [nodeline] |
578 lines = [nodeline] |
560 if add_padding_line: |
579 if add_padding_line: |
561 lines.append(_getpaddingline(idx, ncols, edges)) |
580 lines.append(_getpaddingline(echars, idx, ncols, edges)) |
562 lines.append(shift_interline) |
581 lines.append(shift_interline) |
563 |
582 |
564 # make sure that there are as many graph lines as there are |
583 # make sure that there are as many graph lines as there are |
565 # log strings |
584 # log strings |
566 while len(text) < len(lines): |
585 while len(text) < len(lines): |
567 text.append("") |
586 text.append("") |
568 if len(lines) < len(text): |
587 if len(lines) < len(text): |
569 extra_interline = ["|", " "] * (ncols + coldiff) |
588 extra_interline = echars[:(ncols + coldiff) * 2] |
570 while len(lines) < len(text): |
589 while len(lines) < len(text): |
571 lines.append(extra_interline) |
590 lines.append(extra_interline) |
572 |
591 |
573 # print lines |
592 # print lines |
574 indentation_level = max(ncols, ncols + coldiff) |
593 indentation_level = max(ncols, ncols + coldiff) |