##// END OF EJS Templates
wireproto: separate commands tables for version 1 and 2 commands...
wireproto: separate commands tables for version 1 and 2 commands We can't easily reuse existing command handlers for version 2 commands because the response types will be different. e.g. many commands return nodes encoded as hex. Our new wire protocol is binary safe, so we'll wish to encode nodes as binary. We /could/ teach each command handler to look at the protocol handler and change behavior based on the version in use. However, this would make logic a bit unwieldy over time and would make it harder to design a unified protocol handler interface. I think it's better to create a clean break between version 1 and version 2 of commands on the server. What I imagine happening is we will have separate @wireprotocommand functions for each protocol generation. Those functions will parse the request, dispatch to a common function to process it, then generate the response in its own, transport-specific manner. This commit establishes a separate table for tracking version 1 commands from version 2 commands. The HTTP server pieces have been updated to use this new table. Most commands are marked as both version 1 and version 2, so there is little practical impact to this change. A side-effect of this change is we now rely on transport registration in wireprototypes.TRANSPORTS and certain properties of the protocol interface. So a test had to be updated to conform. Differential Revision: https://phab.mercurial-scm.org/D2982

File last commit:

r36198:34e85044 default
r37311:45b39c69 default
Show More
graphmod.py
478 lines | 16.4 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,
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 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)
Yuya Nishihara
dagop: split module hosting DAG-related algorithms from revset...
r32903 gp = gpcache[mpar] = sorted(set(dagop.reachableroots(
Martijn Pieters
graphmod: augment the graph to include more information about the edges...
r28376 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
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()"""
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)
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[:]
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))
Patrick Mezard
graphlog: extract ascii drawing code into graphmod
r17179 char = '\\'
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
state['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
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:
Pulkit Goyal
py3: use list of bytes rather than bytestring while extending bytes into lists...
r32160 line.extend([' ', ' '])
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:
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]
Pulkit Goyal
py3: use list of bytes rather than bytestring while extending bytes into lists...
r32160 for i in xrange(2 + coldiff):
shift_interline.append(' ')
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:
Pulkit Goyal
py3: use list of bytes rather than bytestring while extending bytes into lists...
r32160 for i in xrange(count):
shift_interline.extend(['/', ' '])
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:
Pulkit Goyal
py3: use list of bytes rather than bytestring while extending bytes into lists...
r32160 for i in xrange(count):
shift_interline.extend(['\\', ' '])
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.
Pulkit Goyal
py3: slice over bytes to prevent getting ascii values...
r36198 parent = state['styles'][PARENT][-1:]
Martijn Pieters
graphmod: partial edge styling...
r29134 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):
Pulkit Goyal
py3: slice over bytes to prevent getting ascii values...
r36198 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