##// END OF EJS Templates
progress: flush stderr after clearing...
progress: flush stderr after clearing On python3, ui.stderr is buffered, it seems, so we need to flush it to actually get the progress bar off the screen. This is important since ui.write() will call into progbar.clear() if it thinks there's a progress bar on the screen, with the intent that the next thing it outputs is at the beginning of the line (instead of at the end of the progress bar line). Without the flush, we buffer up the clearing of the screen, and we get some really weird/corrupt output. Differential Revision: https://phab.mercurial-scm.org/D7784

File last commit:

r44165:e006f09e default
r44485:4e0a6d15 stable
Show More
graphmod.py
516 lines | 16.7 KiB | text/x-python | PythonLexer
Dirkjan Ochtman
add graph page to hgweb
r6691 # Revision graph generator for Mercurial
#
# Copyright 2008 Dirkjan Ochtman <dirkjan@ochtman.nl>
# Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod/graphlog: make dag walks carry data as type, payload
r8840 """supports walking the history as DAGs suitable for graphical output
The most basic format we use is that of::
(id, type, data, [parentids])
The node and parent ids are arbitrary integers which identify a node in the
context of the graph returned. Type is a constant specifying the node type.
Data depends on type.
"""
Gregory Szorc
graphmod: use absolute_import
r25951 from __future__ import absolute_import
Peter Arrenbrecht
graphmod/graphlog: make dag walks carry data as type, payload
r8840
Gregory Szorc
graphmod: use absolute_import
r25951 from .node import nullrev
Laurent Charignon
revset: remove grandparent by using reachableroots...
r26003 from . import (
Yuya Nishihara
dagop: split module hosting DAG-related algorithms from revset...
r32903 dagop,
Gregory Szorc
global: use pycompat.xrange()...
r38806 pycompat,
Yuya Nishihara
revset: import set classes directly from smartset module...
r31023 smartset,
Laurent Charignon
revset: remove grandparent by using reachableroots...
r26003 util,
)
Gregory Szorc
graphmod: use absolute_import
r25951
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 CHANGESET = b'C'
PARENT = b'P'
GRANDPARENT = b'G'
MISSINGPARENT = b'M'
Martijn Pieters
graphmod: allow edges to end early...
r28601 # Style of line to draw. None signals a line that ends and is removed at this
Martijn Pieters
graphmod: partial edge styling...
r29134 # point. A number prefix means only the last N characters of the current block
# will use that style, the rest will use the PARENT style. Add a - sign
# (so making N negative) and all but the first N characters use that style.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 EDGES = {PARENT: b'|', GRANDPARENT: b':', MISSINGPARENT: None}
Dirkjan Ochtman
add graph page to hgweb
r6691
Augie Fackler
formatting: blacken the codebase...
r43346
Alexander Solovyov
graphmod: use revsets internally...
r14042 def dagwalker(repo, revs):
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 """cset DAG generator yielding (id, CHANGESET, ctx, [parentinfo]) tuples
Alexander Solovyov
graphmod: use revsets internally...
r14042
This generator function walks through revisions (which should be ordered
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 from bigger to lower). It returns a tuple for each node.
Each parentinfo entry is a tuple with (edgetype, parentid), where edgetype
is one of PARENT, GRANDPARENT or MISSINGPARENT. The node and parent ids
are arbitrary integers which identify a node in the context of the graph
Alexander Solovyov
graphmod: use revsets internally...
r14042 returned.
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376
Peter Arrenbrecht
graphmod/graphlog: move log walks to graphmod
r8836 """
Alexander Solovyov
graphmod: use revsets internally...
r14042 gpcache = {}
Idan Kamara
graphmod: restore generator nature of dagwalker...
r14087 for rev in revs:
ctx = repo[rev]
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 # partition into parents in the rev set and missing parents, then
# augment the lists with markers, to inform graph drawing code about
# what kind of edge to draw between nodes.
pset = set(p.rev() for p in ctx.parents() if p.rev() in revs)
Augie Fackler
formatting: blacken the codebase...
r43346 mpars = [
p.rev()
for p in ctx.parents()
if p.rev() != nullrev and p.rev() not in pset
]
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 parents = [(PARENT, p) for p in sorted(pset)]
Alexander Solovyov
graphmod: use revsets internally...
r14042
for mpar in mpars:
Patrick Mezard
graphmod: correctly emit nodes with more than 2 predecessors...
r14131 gp = gpcache.get(mpar)
Alexander Solovyov
graphmod: use revsets internally...
r14042 if gp is None:
Yuya Nishihara
graphmod: compute slow revset query once prior to reachableroots (issue4782)...
r26187 # precompute slow query as we know reachableroots() goes
# through all revs (issue4782)
Yuya Nishihara
revset: import set classes directly from smartset module...
r31023 if not isinstance(revs, smartset.baseset):
revs = smartset.baseset(revs)
Augie Fackler
formatting: blacken the codebase...
r43346 gp = gpcache[mpar] = sorted(
set(dagop.reachableroots(repo, revs, [mpar]))
)
Patrick Mezard
graphmod: correctly emit nodes with more than 2 predecessors...
r14131 if not gp:
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 parents.append((MISSINGPARENT, mpar))
pset.add(mpar)
Patrick Mezard
graphmod: correctly emit nodes with more than 2 predecessors...
r14131 else:
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 parents.extend((GRANDPARENT, g) for g in gp if g not in pset)
pset.update(gp)
Alexander Solovyov
graphmod: use revsets internally...
r14042
Idan Kamara
graphmod: restore generator nature of dagwalker...
r14087 yield (ctx.rev(), CHANGESET, ctx, parents)
Peter Arrenbrecht
graphmod/graphlog: move log walks to graphmod
r8836
Augie Fackler
formatting: blacken the codebase...
r43346
Peter Arrenbrecht
graphmod/graphlog: extract nodelistwalk
r8837 def nodes(repo, nodes):
Peter Arrenbrecht
graphmod/graphlog: make dag walks carry data as type, payload
r8840 """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
This generator function walks the given nodes. It only returns parents
that are in nodes, too.
"""
Peter Arrenbrecht
graphmod/graphlog: extract nodelistwalk
r8837 include = set(nodes)
for node in nodes:
ctx = repo[node]
Augie Fackler
formatting: blacken the codebase...
r43346 parents = set(
(PARENT, p.rev()) for p in ctx.parents() if p.node() in include
)
Peter Arrenbrecht
graphmod/graphlog: make dag walks carry data as type, payload
r8840 yield (ctx.rev(), CHANGESET, ctx, sorted(parents))
Peter Arrenbrecht
graphmod/graphlog: extract nodelistwalk
r8837
Augie Fackler
formatting: blacken the codebase...
r43346
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129 def colored(dag, repo):
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 """annotates a DAG with colored edge information
For each DAG node this function emits tuples::
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 (id, type, data, (col, color), [(col, nextcol, color)])
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 with the following new elements:
Peter Arrenbrecht
graphmod: code cleanup and doc fix
r8835 - Tuple (col, color) with column and color index for the current node
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 - A list of tuples indicating the edges between the current node and its
parents.
Dirkjan Ochtman
add graph page to hgweb
r6691 """
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 seen = []
Dirkjan Ochtman
add graph page to hgweb
r6691 colors = {}
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 newcolor = 1
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129 config = {}
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for key, val in repo.ui.configitems(b'graph'):
if b'.' in key:
branch, setting = key.rsplit(b'.', 1)
Matt Mackall
graphmod: rewrite graph config validation...
r16131 # Validation
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if setting == b"width" and val.isdigit():
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 config.setdefault(branch, {})[setting] = int(val)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif setting == b"color" and val.isalnum():
Matt Mackall
graphmod: rewrite graph config validation...
r16131 config.setdefault(branch, {})[setting] = val
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129
Matt Mackall
graphmod: add config cache...
r16132 if config:
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 getconf = util.lrucachefunc(
Augie Fackler
formatting: blacken the codebase...
r43346 lambda rev: config.get(repo[rev].branch(), {})
)
Matt Mackall
graphmod: add config cache...
r16132 else:
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 getconf = lambda rev: {}
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 for (cur, type, data, parents) in dag:
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 # Compute seen and next
if cur not in seen:
Augie Fackler
formatting: blacken the codebase...
r43346 seen.append(cur) # new head
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 colors[cur] = newcolor
newcolor += 1
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 col = seen.index(cur)
color = colors.pop(cur)
next = seen[:]
Dirkjan Ochtman
add graph page to hgweb
r6691
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 # Add parents to next
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 addparents = [p for pt, p in parents if p not in next]
Augie Fackler
formatting: blacken the codebase...
r43346 next[col : col + 1] = addparents
Dirkjan Ochtman
add graph page to hgweb
r6691
# Set colors for the parents
for i, p in enumerate(addparents):
if not i:
colors[p] = color
else:
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 colors[p] = newcolor
newcolor += 1
Dirkjan Ochtman
add graph page to hgweb
r6691
# Add edges to the graph
edges = []
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 for ecol, eid in enumerate(seen):
if eid in next:
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 bconf = getconf(eid)
Augie Fackler
formatting: blacken the codebase...
r43346 edges.append(
(
ecol,
next.index(eid),
colors[eid],
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 bconf.get(b'width', -1),
bconf.get(b'color', b''),
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 elif eid == cur:
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 for ptype, p in parents:
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 bconf = getconf(p)
Augie Fackler
formatting: blacken the codebase...
r43346 edges.append(
(
ecol,
next.index(p),
color,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 bconf.get(b'width', -1),
bconf.get(b'color', b''),
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Dirkjan Ochtman
add graph page to hgweb
r6691
# Yield and move on
Peter Arrenbrecht
graphmod/webcommands: use generic DAG walks...
r8842 yield (cur, type, data, (col, color), edges)
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 seen = next
Alexander Solovyov
graphmod: use revsets internally...
r14042
Augie Fackler
formatting: blacken the codebase...
r43346
Danny Hooper
log: add a "graphwidth" template variable...
r33860 def asciiedges(type, char, state, rev, parents):
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 """adds edge info to changelog DAG walk suitable for ascii()"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 seen = state[b'seen']
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 if rev not in seen:
seen.append(rev)
nodeidx = seen.index(rev)
knownparents = []
newparents = []
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 for ptype, parent in parents:
Yuya Nishihara
graphlog: draw multiple edges towards null node (issue5440)...
r31552 if parent == rev:
# self reference (should only be seen in null rev)
continue
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 if parent in seen:
knownparents.append(parent)
else:
newparents.append(parent)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state[b'edges'][parent] = state[b'styles'].get(ptype, b'|')
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
ncols = len(seen)
Danny Hooper
log: add a "graphwidth" template variable...
r33860 width = 1 + ncols * 2
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 nextseen = seen[:]
Augie Fackler
formatting: blacken the codebase...
r43346 nextseen[nodeidx : nodeidx + 1] = newparents
Yuya Nishihara
graphlog: draw multiple edges towards null node (issue5440)...
r31552 edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
Martijn Pieters
graphmod: fix seen state handling for > 2 parents (issue5174)...
r28998 seen[:] = nextseen
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 while len(newparents) > 2:
# ascii() only knows how to add or remove a single column between two
# calls. Nodes with more than two parents break this constraint so we
# introduce intermediate expansion lines to grow the active node list
# slowly.
edges.append((nodeidx, nodeidx))
edges.append((nodeidx, nodeidx + 1))
nmorecols = 1
Danny Hooper
log: add a "graphwidth" template variable...
r33860 width += 2
yield (type, char, width, (nodeidx, edges, ncols, nmorecols))
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 char = b'\\'
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 nodeidx += 1
ncols += 1
edges = []
del newparents[0]
if len(newparents) > 0:
edges.append((nodeidx, nodeidx))
if len(newparents) > 1:
edges.append((nodeidx, nodeidx + 1))
nmorecols = len(nextseen) - ncols
Danny Hooper
log: add a "graphwidth" template variable...
r33860 if nmorecols > 0:
width += 2
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 # remove current node from edge characters, no longer needed
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state[b'edges'].pop(rev, None)
Danny Hooper
log: add a "graphwidth" template variable...
r33860 yield (type, char, width, (nodeidx, edges, ncols, nmorecols))
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 def _fixlongrightedges(edges):
for (i, (start, end)) in enumerate(edges):
if end > start:
edges[i] = (start, end + 1)
Augie Fackler
formatting: blacken the codebase...
r43346
def _getnodelineedgestail(echars, idx, pidx, ncols, coldiff, pdiff, fix_tail):
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 if fix_tail and coldiff == pdiff and coldiff != 0:
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 # Still going in the same non-vertical direction.
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 if coldiff == -1:
start = max(idx + 1, pidx)
Augie Fackler
formatting: blacken the codebase...
r43346 tail = echars[idx * 2 : (start - 1) * 2]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 tail.extend([b"/", b" "] * (ncols - start))
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 return tail
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return [b"\\", b" "] * (ncols - idx - 1)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 else:
Augie Fackler
formatting: blacken the codebase...
r43346 remainder = ncols - idx - 1
return echars[-(remainder * 2) :] if remainder > 0 else []
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 def _drawedges(echars, edges, nodeline, interline):
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 for (start, end) in edges:
if start == end + 1:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 interline[2 * end + 1] = b"/"
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 elif start == end - 1:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 interline[2 * start + 1] = b"\\"
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 elif start == end:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 interline[2 * start] = echars[2 * start]
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 else:
if 2 * end >= len(nodeline):
continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 nodeline[2 * end] = b"+"
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 if start > end:
(start, end) = (end, start)
for i in range(2 * start + 1, 2 * end):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if nodeline[i] != b"+":
nodeline[i] = b"-"
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 def _getpaddingline(echars, idx, ncols, edges):
# all edges up to the current node
Augie Fackler
formatting: blacken the codebase...
r43346 line = echars[: idx * 2]
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 # an edge for the current node, if there is one
if (idx, idx - 1) in edges or (idx, idx) in edges:
# (idx, idx - 1) (idx, idx)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 # | | | | | | | |
# +---o | | o---+
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 # | | X | | X | |
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 # | |/ / | |/ /
# | | | | | |
Augie Fackler
formatting: blacken the codebase...
r43346 line.extend(echars[idx * 2 : (idx + 1) * 2])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 line.extend([b' ', b' '])
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 # all edges to the right of the current node
remainder = ncols - idx - 1
if remainder > 0:
Augie Fackler
formatting: blacken the codebase...
r43346 line.extend(echars[-(remainder * 2) :])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 return line
Augie Fackler
formatting: blacken the codebase...
r43346
Kyle Lippincott
log: respect graphshorten on terminal nodes (collapsing o-~ to just o~)...
r39322 def _drawendinglines(lines, extra, edgemap, seen, state):
Martijn Pieters
graphmod: allow edges to end early...
r28601 """Draw ending lines for missing parent edges
None indicates an edge that ends at between this node and the next
Replace with a short line ending in ~ and add / lines to any edges to
the right.
"""
if None not in edgemap.values():
return
# Check for more edges to the right of our ending edges.
# We need enough space to draw adjustment lines for these.
edgechars = extra[::2]
while edgechars and edgechars[-1] is None:
edgechars.pop()
shift_size = max((edgechars.count(None) * 2) - 1, 0)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 minlines = 3 if not state[b'graphshorten'] else 2
Kyle Lippincott
log: respect graphshorten on terminal nodes (collapsing o-~ to just o~)...
r39322 while len(lines) < minlines + shift_size:
Martijn Pieters
graphmod: allow edges to end early...
r28601 lines.append(extra[:])
if shift_size:
empties = []
toshift = []
first_empty = extra.index(None)
for i, c in enumerate(extra[first_empty::2], first_empty // 2):
if c is None:
empties.append(i * 2)
else:
toshift.append(i * 2)
targets = list(range(first_empty, first_empty + len(toshift) * 2, 2))
positions = toshift[:]
for line in lines[-shift_size:]:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 line[first_empty:] = [b' '] * (len(line) - first_empty)
Martijn Pieters
graphmod: allow edges to end early...
r28601 for i in range(len(positions)):
pos = positions[i] - 1
positions[i] = max(pos, targets[i])
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 line[pos] = b'/' if pos > targets[i] else extra[toshift[i]]
Martijn Pieters
graphmod: allow edges to end early...
r28601
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 map = {1: b'|', 2: b'~'} if not state[b'graphshorten'] else {1: b'~'}
Martijn Pieters
graphmod: allow edges to end early...
r28601 for i, line in enumerate(lines):
if None not in line:
continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 line[:] = [c or map.get(i, b' ') for c in line]
Martijn Pieters
graphmod: allow edges to end early...
r28601
# remove edges that ended
remove = [p for p, c in edgemap.items() if c is None]
for parent in remove:
del edgemap[parent]
seen.remove(parent)
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 def asciistate():
"""returns the initial value for the "state" argument to ascii()"""
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 return {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'seen': [],
b'edges': {},
b'lastcoldiff': 0,
b'lastindex': 0,
b'styles': EDGES.copy(),
b'graphshorten': False,
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 }
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
Augie Fackler
formatting: blacken the codebase...
r43346
John Stiles
graph: add outputgraph() function, called by ascii() to print...
r38167 def outputgraph(ui, graph):
"""outputs an ASCII graph of a DAG
this is a helper function for 'ascii' below.
takes the following arguments:
- ui to write to
- graph data: list of { graph nodes/edges, text }
this function can be monkey-patched by extensions to alter graph display
without needing to mimic all of the edge-fixup logic in ascii()
"""
for (ln, logstr) in graph:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.write((ln + logstr).rstrip() + b"\n")
John Stiles
graph: add outputgraph() function, called by ascii() to print...
r38167
Augie Fackler
formatting: blacken the codebase...
r43346
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 def ascii(ui, state, type, char, text, coldata):
"""prints an ASCII graph of the DAG
takes the following arguments (one call per node in the graph):
- ui to write to
- Somewhere to keep the needed state in (init to asciistate())
- Column of the current node in the set of ongoing edges.
- Type indicator of node data, usually 'C' for changesets.
- Payload: (char, lines):
- Character to use as node's symbol.
- List of lines to display as the node's text.
- Edges; a list of (col, next_col) indicating the edges between
the current node and its parents.
- Number of columns (ongoing edges) in the current revision.
- The difference between the number of columns (ongoing edges)
in the next revision and the number of columns (ongoing edges)
in the current revision. That is: -1 means one column removed;
0 means no columns added or removed; 1 means one column added.
"""
idx, edges, ncols, coldiff = coldata
assert -2 < coldiff < 2
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 edgemap, seen = state[b'edges'], state[b'seen']
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 # Be tolerant of history issues; make sure we have at least ncols + coldiff
# elements to work with. See test-glog.t for broken history test cases.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 echars = [c for p in seen for c in (edgemap.get(p, b'|'), b' ')]
echars.extend((b'|', b' ') * max(ncols + coldiff - len(seen), 0))
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 if coldiff == -1:
# Transform
#
# | | | | | |
# o | | into o---+
# |X / |/ /
# | | | |
_fixlongrightedges(edges)
# add_padding_line says whether to rewrite
#
# | | | | | | | |
# | o---+ into | o---+
# | / / | | | # <--- padding line
# o | | | / /
# o | |
Augie Fackler
formatting: blacken the codebase...
r43346 add_padding_line = (
len(text) > 2 and coldiff == -1 and [x for (x, y) in edges if x + 1 < y]
)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# fix_nodeline_tail says whether to rewrite
#
# | | o | | | | o | |
# | | |/ / | | |/ /
# | o | | into | o / / # <--- fixed nodeline tail
# | |/ / | |/ /
# o | | o | |
fix_nodeline_tail = len(text) <= 2 and not add_padding_line
# nodeline is the line containing the node character (typically o)
Augie Fackler
formatting: blacken the codebase...
r43346 nodeline = echars[: idx * 2]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 nodeline.extend([char, b" "])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
nodeline.extend(
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 _getnodelineedgestail(
Augie Fackler
formatting: blacken the codebase...
r43346 echars,
idx,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state[b'lastindex'],
Augie Fackler
formatting: blacken the codebase...
r43346 ncols,
coldiff,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state[b'lastcoldiff'],
Augie Fackler
formatting: blacken the codebase...
r43346 fix_nodeline_tail,
)
)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# shift_interline is the line containing the non-vertical
# edges between this entry and the next
Augie Fackler
formatting: blacken the codebase...
r43346 shift_interline = echars[: idx * 2]
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(2 + coldiff):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 shift_interline.append(b' ')
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 count = ncols - idx - 1
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 if coldiff == -1:
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(count):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 shift_interline.extend([b'/', b' '])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 elif coldiff == 0:
Augie Fackler
formatting: blacken the codebase...
r43346 shift_interline.extend(echars[(idx + 1) * 2 : ncols * 2])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 else:
Gregory Szorc
global: use pycompat.xrange()...
r38806 for i in pycompat.xrange(count):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 shift_interline.extend([b'\\', b' '])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# draw edges from the current node to its parents
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 _drawedges(echars, edges, nodeline, shift_interline)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# lines is the list of all graph lines to print
lines = [nodeline]
if add_padding_line:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 lines.append(_getpaddingline(echars, idx, ncols, edges))
santiagopim
graphmod: shorten graph...
r28891
# If 'graphshorten' config, only draw shift_interline
# when there is any non vertical flow in graph.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if state[b'graphshorten']:
Gregory Szorc
graphmod: use raw string...
r41677 if any(c in br'\/' for c in shift_interline if c):
santiagopim
graphmod: shorten graph...
r28891 lines.append(shift_interline)
# Else, no 'graphshorten' config so draw shift_interline.
else:
lines.append(shift_interline)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# make sure that there are as many graph lines as there are
# log strings
Augie Fackler
formatting: blacken the codebase...
r43346 extra_interline = echars[: (ncols + coldiff) * 2]
Martijn Pieters
graphmod: allow edges to end early...
r28601 if len(lines) < len(text):
while len(lines) < len(text):
lines.append(extra_interline[:])
Kyle Lippincott
log: respect graphshorten on terminal nodes (collapsing o-~ to just o~)...
r39322 _drawendinglines(lines, extra_interline, edgemap, seen, state)
Martijn Pieters
graphmod: allow edges to end early...
r28601
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 while len(text) < len(lines):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 text.append(b"")
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# print lines
indentation_level = max(ncols, ncols + coldiff)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 lines = [
b"%-*s " % (2 * indentation_level, b"".join(line)) for line in lines
]
John Stiles
graph: add outputgraph() function, called by ascii() to print...
r38167 outputgraph(ui, zip(lines, text))
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
# ... and start over
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 state[b'lastcoldiff'] = coldiff
state[b'lastindex'] = idx