##// END OF EJS Templates
dispatch: ignore further SIGPIPE while handling KeyboardInterrupt...
dispatch: ignore further SIGPIPE while handling KeyboardInterrupt I got the following error by running "hg log" and quitting the pager immediately. Any output here may trigger another SIGPIPE, so only thing we can do is to swallow the exception and exit with an error status. Traceback (most recent call last): File "./hg", line 45, in <module> mercurial.dispatch.run() File "mercurial/dispatch.py", line 83, in run status = (dispatch(req) or 0) & 255 File "mercurial/dispatch.py", line 167, in dispatch req.ui.warn(_("interrupted!\n")) File "mercurial/ui.py", line 1224, in warn self.write_err(*msg, **opts) File "mercurial/ui.py", line 790, in write_err self._write_err(*msgs, **opts) File "mercurial/ui.py", line 798, in _write_err self.ferr.write(a) File "mercurial/ui.py", line 129, in _catchterm raise error.SignalInterrupt mercurial.error.SignalInterrupt Perhaps this wasn't visible before de5c9d0e02ea because the original stderr handle was restored very late.

File last commit:

r31552:d0b9e980 default
r32044:cde72a19 default
Show More
graphmod.py
475 lines | 16.3 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 (
revset,
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
Peter Arrenbrecht
graphmod/graphlog: make dag walks carry data as type, payload
r8840 CHANGESET = 'C'
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 PARENT = 'P'
GRANDPARENT = 'G'
MISSINGPARENT = '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.
Martijn Pieters
graphmod: set default edge styles for ascii graphs (BC)...
r28627 EDGES = {PARENT: '|', GRANDPARENT: ':', MISSINGPARENT: None}
Dirkjan Ochtman
add graph page to hgweb
r6691
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 if not revs:
Idan Kamara
graphmod: restore generator nature of dagwalker...
r14087 return
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)
mpars = [p.rev() for p in ctx.parents()
if p.rev() != nullrev and p.rev() not in pset]
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)
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 gp = gpcache[mpar] = sorted(set(revset.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
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]
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 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
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 = {}
for key, val in repo.ui.configitems('graph'):
Matt Mackall
graphmod: rewrite graph config validation...
r16131 if '.' in key:
branch, setting = key.rsplit('.', 1)
# Validation
if setting == "width" and val.isdigit():
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 config.setdefault(branch, {})[setting] = int(val)
Matt Mackall
graphmod: rewrite graph config validation...
r16131 elif setting == "color" and val.isalnum():
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(
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:
seen.append(cur) # new head
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]
Peter Arrenbrecht
graphmod: rename a bunch of vars in graph()
r8841 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)
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129 edges.append((
ecol, next.index(eid), colors[eid],
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 bconf.get('width', -1),
bconf.get('color', '')))
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)
Constantine Linnick
graph: in hgrc specify line width for main branch...
r16129 edges.append((
ecol, next.index(p), color,
Patrick Mezard
hgweb: refactor graph customization javascript...
r16138 bconf.get('width', -1),
bconf.get('color', '')))
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
Martijn Pieters
graphmod: refactor state handling...
r28375 def asciiedges(type, char, lines, state, rev, parents):
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 """adds edge info to changelog DAG walk suitable for ascii()"""
Martijn Pieters
graphmod: refactor state handling...
r28375 seen = state['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)
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 state['edges'][parent] = state['styles'].get(ptype, '|')
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179
ncols = len(seen)
nextseen = seen[:]
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
yield (type, char, lines, (nodeidx, edges, ncols, nmorecols))
char = '\\'
lines = []
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
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 # remove current node from edge characters, no longer needed
state['edges'].pop(rev, None)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 yield (type, char, lines, (nodeidx, edges, ncols, nmorecols))
def _fixlongrightedges(edges):
for (i, (start, end)) in enumerate(edges):
if end > start:
edges[i] = (start, end + 1)
def _getnodelineedgestail(
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 echars, idx, pidx, ncols, coldiff, pdiff, fix_tail):
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)
tail = echars[idx * 2:(start - 1) * 2]
tail.extend(["/", " "] * (ncols - start))
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 return tail
else:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 return ["\\", " "] * (ncols - idx - 1)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 else:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 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:
interline[2 * end + 1] = "/"
elif start == end - 1:
interline[2 * start + 1] = "\\"
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
nodeline[2 * end] = "+"
if start > end:
(start, end) = (end, start)
for i in range(2 * start + 1, 2 * end):
if nodeline[i] != "+":
nodeline[i] = "-"
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
line = echars[:idx * 2]
# 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 # | |/ / | |/ /
# | | | | | |
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 line.extend(echars[idx * 2:(idx + 1) * 2])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 else:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 line.extend(' ')
# all edges to the right of the current node
remainder = ncols - idx - 1
if remainder > 0:
line.extend(echars[-(remainder * 2):])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 return line
Martijn Pieters
graphmod: allow edges to end early...
r28601 def _drawendinglines(lines, extra, edgemap, seen):
"""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)
while len(lines) < 3 + shift_size:
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:]:
line[first_empty:] = [' '] * (len(line) - first_empty)
for i in range(len(positions)):
pos = positions[i] - 1
positions[i] = max(pos, targets[i])
line[pos] = '/' if pos > targets[i] else extra[toshift[i]]
map = {1: '|', 2: '~'}
for i, line in enumerate(lines):
if None not in line:
continue
line[:] = [c or map.get(i, ' ') for c in line]
# 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)
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 {
'seen': [],
'edges': {},
'lastcoldiff': 0,
'lastindex': 0,
'styles': EDGES.copy(),
santiagopim
graphmod: shorten graph...
r28891 'graphshorten': False,
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 }
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
edgemap, seen = state['edges'], state['seen']
# 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.
echars = [c for p in seen for c in (edgemap.get(p, '|'), ' ')]
echars.extend(('|', ' ') * max(ncols + coldiff - len(seen), 0))
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 | |
add_padding_line = (len(text) > 2 and coldiff == -1 and
[x for (x, y) in edges if x + 1 < y])
# 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)
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 nodeline = echars[:idx * 2]
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 nodeline.extend([char, " "])
nodeline.extend(
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 _getnodelineedgestail(
echars, idx, state['lastindex'], ncols, coldiff,
state['lastcoldiff'], 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
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 shift_interline = echars[:idx * 2]
shift_interline.extend(' ' * (2 + coldiff))
count = ncols - idx - 1
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 if coldiff == -1:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 shift_interline.extend('/ ' * count)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 elif coldiff == 0:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 shift_interline.extend(echars[(idx + 1) * 2:ncols * 2])
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 else:
Martijn Pieters
graphmod: allow for different styles for different edge types...
r28600 shift_interline.extend(r'\ ' * count)
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.
if state['graphshorten']:
if any(c in '\/' for c in shift_interline if c):
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
Martijn Pieters
graphmod: allow edges to end early...
r28601 extra_interline = echars[:(ncols + coldiff) * 2]
if len(lines) < len(text):
while len(lines) < len(text):
lines.append(extra_interline[:])
_drawendinglines(lines, extra_interline, edgemap, seen)
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 while len(text) < len(lines):
text.append("")
Martijn Pieters
graphmod: partial edge styling...
r29134 if any(len(char) > 1 for char in edgemap.values()):
# limit drawing an edge to the first or last N lines of the current
# section the rest of the edge is drawn like a parent line.
parent = state['styles'][PARENT][-1]
def _drawgp(char, i):
# should a grandparent character be drawn for this line?
if len(char) < 2:
return True
num = int(char[:-1])
# either skip first num lines or take last num lines, based on sign
return -num <= i if num < 0 else (len(lines) - i) <= num
for i, line in enumerate(lines):
line[:] = [c[-1] if _drawgp(c, i) else parent for c in line]
Martijn Pieters
graphmod: update edgemap in-place...
r29184 edgemap.update(
(e, (c if len(c) < 2 else parent)) for e, c in edgemap.items())
Martijn Pieters
graphmod: partial edge styling...
r29134
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 # print lines
indentation_level = max(ncols, ncols + coldiff)
for (line, logstr) in zip(lines, text):
ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
ui.write(ln.rstrip() + '\n')
# ... and start over
Martijn Pieters
graphmod: refactor state handling...
r28375 state['lastcoldiff'] = coldiff
state['lastindex'] = idx