from i18n import _
from mercurial import util
import templatekw
def tolist(val):
"""
a convenience method to return an empty list instead of None
"""
if val is None:
return []
else:
return [val]
class namespaces(object):
"""
provides an interface to register a generic many-to-many mapping between
some (namespaced) names and nodes. The goal here is to control the
pollution of jamming things into tags or bookmarks (in extension-land) and
to simplify internal bits of mercurial: log output, tab completion, etc.
More precisely, we define a list of names (the namespace), a mapping of
names to nodes, and a mapping from nodes to names. Each mapping
returns a list of nodes.
Furthermore, each name mapping will be passed a name to lookup which might
not be in its domain. In this case, each method should return an empty list
and not raise an error.
We'll have a dictionary '_names' where each key is a namespace and
its value is a dictionary of functions:
'templatename': name to use for templating (usually the singular form
of the plural namespace name)
'namemap': function that takes a name and returns a list of nodes
'nodemap': function that takes a node and returns a list of names
"""
_names_version = 0
def __init__(self):
self._names = util.sortdict()
addns = self.addnamespace
# we need current mercurial named objects (bookmarks, tags, and
# branches) to be initialized somewhere, so that place is here
addns("bookmarks", "bookmark",
lambda repo, name: tolist(repo._bookmarks.get(name)),
lambda repo, name: repo.nodebookmarks(name))
addns("tags", "tag",
lambda repo, name: tolist(repo._tagscache.tags.get(name)),
lambda repo, name: repo.nodetags(name))
addns("branches", "branch",
lambda repo, name: tolist(repo.branchtip(name)),
lambda repo, node: [repo[node].branch()])
def addnamespace(self, namespace, templatename, namemap, nodemap,
order=None):
"""
register a namespace
namespace: the name to be registered (in plural form)
templatename: the name to use for templating
namemap: function that inputs a node, output name(s)
nodemap: function that inputs a name, output node(s)
order: optional argument to specify the order of namespaces
(e.g. 'branches' should be listed before 'bookmarks')
"""
val = {'templatename': templatename,
'namemap': namemap,
'nodemap': nodemap}
if order is not None:
self._names.insert(order, namespace, val)
else:
self._names[namespace] = val
# we only generate a template keyword if one does not already exist
if namespace not in templatekw.keywords:
def generatekw(**args):
return templatekw.shownames(namespace, **args)
templatekw.keywords[namespace] = generatekw
def singlenode(self, repo, name):
"""
Return the 'best' node for the given name. Best means the first node
in the first nonempty list returned by a name-to-nodes mapping function
in the defined precedence order.
Raises a KeyError if there is no such node.
"""
for ns, v in self._names.iteritems():
n = v['namemap'](repo, name)
if n:
# return max revision number
if len(n) > 1:
cl = repo.changelog
maxrev = max(cl.rev(node) for node in n)
return cl.node(maxrev)
return n[0]
raise KeyError(_('no such name: %s') % name)
def templatename(self, namespace):
"""method that returns the template name of a namespace"""
return self._names[namespace]['templatename']
def names(self, repo, namespace, node):
"""method that returns a (sorted) list of names in a namespace that
match a given node"""
return sorted(self._names[namespace]['nodemap'](repo, node))