namespaces.py
244 lines
| 8.1 KiB
| text/x-python
|
PythonLexer
/ mercurial / namespaces.py
Matt Harbison
|
r52756 | from __future__ import annotations | ||
Gregory Szorc
|
r25961 | from .i18n import _ | ||
from . import ( | ||||
Yuya Nishihara
|
r36610 | registrar, | ||
Gregory Szorc
|
r25961 | templatekw, | ||
util, | ||||
) | ||||
Sean Farley
|
r23553 | |||
Augie Fackler
|
r43346 | |||
Sean Farley
|
r23555 | def tolist(val): | ||
""" | ||||
a convenience method to return an empty list instead of None | ||||
""" | ||||
if val is None: | ||||
return [] | ||||
else: | ||||
return [val] | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r49801 | class namespaces: | ||
Sean Farley
|
r23718 | """provides an interface to register and operate on multiple namespaces. See | ||
the namespace class below for details on the namespace object. | ||||
Sean Farley
|
r23553 | |||
""" | ||||
_names_version = 0 | ||||
Ryan McElroy
|
r23561 | def __init__(self): | ||
Sean Farley
|
r23553 | self._names = util.sortdict() | ||
Yuya Nishihara
|
r35213 | columns = templatekw.getlogcolumns() | ||
Sean Farley
|
r23554 | |||
Sean Farley
|
r23558 | # we need current mercurial named objects (bookmarks, tags, and | ||
# branches) to be initialized somewhere, so that place is here | ||||
Sean Farley
|
r23873 | bmknames = lambda repo: repo._bookmarks.keys() | ||
bmknamemap = lambda repo, name: tolist(repo._bookmarks.get(name)) | ||||
timeless
|
r28567 | bmknodemap = lambda repo, node: repo.nodebookmarks(node) | ||
Augie Fackler
|
r43346 | n = namespace( | ||
Augie Fackler
|
r43347 | b"bookmarks", | ||
templatename=b"bookmark", | ||||
logfmt=columns[b'bookmark'], | ||||
Augie Fackler
|
r43346 | listnames=bmknames, | ||
namemap=bmknamemap, | ||||
nodemap=bmknodemap, | ||||
builtin=True, | ||||
) | ||||
Sean Farley
|
r23717 | self.addnamespace(n) | ||
Sean Farley
|
r23562 | |||
Sean Farley
|
r23873 | tagnames = lambda repo: [t for t, n in repo.tagslist()] | ||
tagnamemap = lambda repo, name: tolist(repo._tagscache.tags.get(name)) | ||||
timeless
|
r28567 | tagnodemap = lambda repo, node: repo.nodetags(node) | ||
Augie Fackler
|
r43346 | n = namespace( | ||
Augie Fackler
|
r43347 | b"tags", | ||
templatename=b"tag", | ||||
logfmt=columns[b'tag'], | ||||
Augie Fackler
|
r43346 | listnames=tagnames, | ||
namemap=tagnamemap, | ||||
nodemap=tagnodemap, | ||||
Augie Fackler
|
r43347 | deprecated={b'tip'}, | ||
Augie Fackler
|
r43346 | builtin=True, | ||
) | ||||
Sean Farley
|
r23717 | self.addnamespace(n) | ||
Sean Farley
|
r23558 | |||
Sean Farley
|
r23873 | bnames = lambda repo: repo.branchmap().keys() | ||
bnamemap = lambda repo, name: tolist(repo.branchtip(name, True)) | ||||
bnodemap = lambda repo, node: [repo[node].branch()] | ||||
Augie Fackler
|
r43346 | n = namespace( | ||
Augie Fackler
|
r43347 | b"branches", | ||
templatename=b"branch", | ||||
logfmt=columns[b'branch'], | ||||
Augie Fackler
|
r43346 | listnames=bnames, | ||
namemap=bnamemap, | ||||
nodemap=bnodemap, | ||||
builtin=True, | ||||
) | ||||
Sean Farley
|
r23717 | self.addnamespace(n) | ||
Sean Farley
|
r23563 | |||
Sean Farley
|
r23736 | def __getitem__(self, namespace): | ||
"""returns the namespace object""" | ||||
return self._names[namespace] | ||||
Sean Farley
|
r23761 | def __iter__(self): | ||
return self._names.__iter__() | ||||
Yuya Nishihara
|
r45222 | def get(self, namespace, default=None): | ||
return self._names.get(namespace, default) | ||||
Augie Fackler
|
r32550 | def items(self): | ||
Gregory Szorc
|
r49768 | return self._names.items() | ||
Sean Farley
|
r23761 | |||
Augie Fackler
|
r32550 | iteritems = items | ||
Sean Farley
|
r23717 | def addnamespace(self, namespace, order=None): | ||
Sean Farley
|
r23718 | """register a namespace | ||
Sean Farley
|
r23554 | |||
namespace: the name to be registered (in plural form) | ||||
order: optional argument to specify the order of namespaces | ||||
(e.g. 'branches' should be listed before 'bookmarks') | ||||
Sean Farley
|
r23718 | |||
Sean Farley
|
r23554 | """ | ||
if order is not None: | ||||
Sean Farley
|
r23717 | self._names.insert(order, namespace.name, namespace) | ||
Sean Farley
|
r23554 | else: | ||
Sean Farley
|
r23717 | self._names[namespace.name] = namespace | ||
Sean Farley
|
r23559 | |||
Sean Farley
|
r23610 | # we only generate a template keyword if one does not already exist | ||
Sean Farley
|
r23717 | if namespace.name not in templatekw.keywords: | ||
Yuya Nishihara
|
r36610 | templatekeyword = registrar.templatekeyword(templatekw.keywords) | ||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r43347 | @templatekeyword(namespace.name, requires={b'repo', b'ctx'}) | ||
Yuya Nishihara
|
r36611 | def generatekw(context, mapping): | ||
return templatekw.shownames(context, mapping, namespace.name) | ||||
Sean Farley
|
r23610 | |||
Ryan McElroy
|
r23561 | def singlenode(self, repo, name): | ||
Sean Farley
|
r23559 | """ | ||
Martin von Zweigbergk
|
r38505 | Return the 'best' node for the given name. What's best is defined | ||
by the namespace's singlenode() function. The first match returned by | ||||
a namespace in the defined precedence order is used. | ||||
Sean Farley
|
r23559 | |||
Raises a KeyError if there is no such node. | ||||
""" | ||||
Gregory Szorc
|
r49768 | for ns, v in self._names.items(): | ||
Martin von Zweigbergk
|
r38505 | n = v.singlenode(repo, name) | ||
Sean Farley
|
r23559 | if n: | ||
Martin von Zweigbergk
|
r38505 | return n | ||
Augie Fackler
|
r43347 | raise KeyError(_(b'no such name: %s') % name) | ||
Sean Farley
|
r23606 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r49801 | class namespace: | ||
Sean Farley
|
r23715 | """provides an interface to a namespace | ||
Namespaces are basically 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 mapping of names to nodes, and a mapping from | ||||
nodes to names. Each mapping returns a list. | ||||
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. | ||||
This namespace object will define the properties we need: | ||||
'name': the namespace (plural form) | ||||
'templatename': name to use for templating (usually the singular form | ||||
of the plural namespace name) | ||||
Sean Farley
|
r23760 | 'listnames': list of all names in the namespace (usually the keys of a | ||
dictionary) | ||||
Sean Farley
|
r23715 | 'namemap': function that takes a name and returns a list of nodes | ||
'nodemap': function that takes a node and returns a list of names | ||||
FUJIWARA Katsunori
|
r24151 | 'deprecated': set of names to be masked for ordinary use | ||
Gregory Szorc
|
r33048 | 'builtin': bool indicating if this namespace is supported by core | ||
Mercurial. | ||||
Sean Farley
|
r23715 | """ | ||
Augie Fackler
|
r43346 | def __init__( | ||
self, | ||||
name, | ||||
templatename=None, | ||||
logname=None, | ||||
colorname=None, | ||||
logfmt=None, | ||||
listnames=None, | ||||
namemap=None, | ||||
nodemap=None, | ||||
deprecated=None, | ||||
builtin=False, | ||||
singlenode=None, | ||||
): | ||||
Sean Farley
|
r23715 | """create a namespace | ||
name: the namespace to be registered (in plural form) | ||||
Sean Farley
|
r23872 | templatename: the name to use for templating | ||
Sean Farley
|
r23874 | logname: the name to use for log output; if not specified templatename | ||
is used | ||||
Sean Farley
|
r23875 | colorname: the name to use for colored log output; if not specified | ||
logname is used | ||||
Mads Kiilerich
|
r24180 | logfmt: the format to use for (i18n-ed) log output; if not specified | ||
FUJIWARA Katsunori
|
r23967 | it is composed from logname | ||
Sean Farley
|
r23760 | listnames: function to list all names | ||
timeless
|
r28567 | namemap: function that inputs a name, output node(s) | ||
nodemap: function that inputs a node, output name(s) | ||||
FUJIWARA Katsunori
|
r24151 | deprecated: set of names to be masked for ordinary use | ||
Gregory Szorc
|
r33048 | builtin: whether namespace is implemented by core Mercurial | ||
Martin von Zweigbergk
|
r38505 | singlenode: function that inputs a name, output best node (or None) | ||
Sean Farley
|
r23715 | """ | ||
self.name = name | ||||
self.templatename = templatename | ||||
Sean Farley
|
r23874 | self.logname = logname | ||
Sean Farley
|
r23875 | self.colorname = colorname | ||
FUJIWARA Katsunori
|
r23967 | self.logfmt = logfmt | ||
Sean Farley
|
r23760 | self.listnames = listnames | ||
Sean Farley
|
r23715 | self.namemap = namemap | ||
self.nodemap = nodemap | ||||
Martin von Zweigbergk
|
r38505 | if singlenode: | ||
self.singlenode = singlenode | ||||
Sean Farley
|
r23716 | |||
Sean Farley
|
r23874 | # if logname is not specified, use the template name as backup | ||
if self.logname is None: | ||||
self.logname = self.templatename | ||||
Sean Farley
|
r23875 | # if colorname is not specified, just use the logname as a backup | ||
if self.colorname is None: | ||||
self.colorname = self.logname | ||||
FUJIWARA Katsunori
|
r23967 | # if logfmt is not specified, compose it from logname as backup | ||
if self.logfmt is None: | ||||
# i18n: column positioning for "hg log" | ||||
Augie Fackler
|
r43347 | self.logfmt = (b"%s:" % self.logname).ljust(13) + b"%s\n" | ||
FUJIWARA Katsunori
|
r23967 | |||
FUJIWARA Katsunori
|
r24151 | if deprecated is None: | ||
self.deprecated = set() | ||||
else: | ||||
self.deprecated = deprecated | ||||
Gregory Szorc
|
r33048 | self.builtin = builtin | ||
Sean Farley
|
r23716 | def names(self, repo, node): | ||
"""method that returns a (sorted) list of names in a namespace that | ||||
match a given node""" | ||||
return sorted(self.nodemap(repo, node)) | ||||
Sean Farley
|
r23774 | |||
def nodes(self, repo, name): | ||||
"""method that returns a list of nodes in a namespace that | ||||
match a given name. | ||||
""" | ||||
return sorted(self.namemap(repo, name)) | ||||
Martin von Zweigbergk
|
r38505 | |||
def singlenode(self, repo, name): | ||||
"""returns the best node for the given name | ||||
By default, the best node is the node from nodes() with the highest | ||||
revision number. It can be overriden by the namespace.""" | ||||
n = self.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] | ||||
return None | ||||