fileset: use decorator to mark a predicate as "status caller"

Before this patch, predicates calling 'matchctx.status()' are listed
up by immediate list value in 'getfileset()'.

This prevents 3rd party extensions from adding specific predicate
calling 'matchctx.status()'.

This uses decorator to mark a predicate as "status caller".

This can also localize changes for adding (or removing) a "status
caller" predicate function in source code.
This commit is contained in:
FUJIWARA Katsunori 2015-12-21 22:31:16 +09:00
parent a8588195a6
commit c965a4dbaa

View File

@ -137,11 +137,17 @@ def listset(mctx, a, b):
# x - argument in tree form # x - argument in tree form
symbols = {} symbols = {}
def predicate(decl): # filesets using matchctx.status()
_statuscallers = []
def predicate(decl, callstatus=False):
"""Return a decorator for fileset predicate function """Return a decorator for fileset predicate function
'decl' argument is the declaration (including argument list like 'decl' argument is the declaration (including argument list like
'adds(pattern)') or the name (for internal use only) of predicate. 'adds(pattern)') or the name (for internal use only) of predicate.
Optional 'callstatus' argument indicates whether predicate implies
'matchctx.status()' at runtime or not (False, by default).
""" """
def decorator(func): def decorator(func):
i = decl.find('(') i = decl.find('(')
@ -150,12 +156,14 @@ def predicate(decl):
else: else:
name = decl name = decl
symbols[name] = func symbols[name] = func
if callstatus:
_statuscallers.append(name)
if func.__doc__: if func.__doc__:
func.__doc__ = "``%s``\n %s" % (decl, func.__doc__.strip()) func.__doc__ = "``%s``\n %s" % (decl, func.__doc__.strip())
return func return func
return decorator return decorator
@predicate('modified()') @predicate('modified()', callstatus=True)
def modified(mctx, x): def modified(mctx, x):
"""File that is modified according to :hg:`status`. """File that is modified according to :hg:`status`.
""" """
@ -164,7 +172,7 @@ def modified(mctx, x):
s = mctx.status().modified s = mctx.status().modified
return [f for f in mctx.subset if f in s] return [f for f in mctx.subset if f in s]
@predicate('added()') @predicate('added()', callstatus=True)
def added(mctx, x): def added(mctx, x):
"""File that is added according to :hg:`status`. """File that is added according to :hg:`status`.
""" """
@ -173,7 +181,7 @@ def added(mctx, x):
s = mctx.status().added s = mctx.status().added
return [f for f in mctx.subset if f in s] return [f for f in mctx.subset if f in s]
@predicate('removed()') @predicate('removed()', callstatus=True)
def removed(mctx, x): def removed(mctx, x):
"""File that is removed according to :hg:`status`. """File that is removed according to :hg:`status`.
""" """
@ -182,7 +190,7 @@ def removed(mctx, x):
s = mctx.status().removed s = mctx.status().removed
return [f for f in mctx.subset if f in s] return [f for f in mctx.subset if f in s]
@predicate('deleted()') @predicate('deleted()', callstatus=True)
def deleted(mctx, x): def deleted(mctx, x):
"""Alias for ``missing()``. """Alias for ``missing()``.
""" """
@ -191,7 +199,7 @@ def deleted(mctx, x):
s = mctx.status().deleted s = mctx.status().deleted
return [f for f in mctx.subset if f in s] return [f for f in mctx.subset if f in s]
@predicate('missing()') @predicate('missing()', callstatus=True)
def missing(mctx, x): def missing(mctx, x):
"""File that is missing according to :hg:`status`. """File that is missing according to :hg:`status`.
""" """
@ -200,7 +208,7 @@ def missing(mctx, x):
s = mctx.status().deleted s = mctx.status().deleted
return [f for f in mctx.subset if f in s] return [f for f in mctx.subset if f in s]
@predicate('unknown()') @predicate('unknown()', callstatus=True)
def unknown(mctx, x): def unknown(mctx, x):
"""File that is unknown according to :hg:`status`. These files will only be """File that is unknown according to :hg:`status`. These files will only be
considered if this predicate is used. considered if this predicate is used.
@ -210,7 +218,7 @@ def unknown(mctx, x):
s = mctx.status().unknown s = mctx.status().unknown
return [f for f in mctx.subset if f in s] return [f for f in mctx.subset if f in s]
@predicate('ignored()') @predicate('ignored()', callstatus=True)
def ignored(mctx, x): def ignored(mctx, x):
"""File that is ignored according to :hg:`status`. These files will only be """File that is ignored according to :hg:`status`. These files will only be
considered if this predicate is used. considered if this predicate is used.
@ -220,7 +228,7 @@ def ignored(mctx, x):
s = mctx.status().ignored s = mctx.status().ignored
return [f for f in mctx.subset if f in s] return [f for f in mctx.subset if f in s]
@predicate('clean()') @predicate('clean()', callstatus=True)
def clean(mctx, x): def clean(mctx, x):
"""File that is clean according to :hg:`status`. """File that is clean according to :hg:`status`.
""" """
@ -523,8 +531,7 @@ def getfileset(ctx, expr):
tree = parse(expr) tree = parse(expr)
# do we need status info? # do we need status info?
if (_intree(['modified', 'added', 'removed', 'deleted', if (_intree(_statuscallers, tree) or
'missing', 'unknown', 'ignored', 'clean'], tree) or
# Using matchctx.existing() on a workingctx requires us to check # Using matchctx.existing() on a workingctx requires us to check
# for deleted files. # for deleted files.
(ctx.rev() is None and _intree(_existingcallers, tree))): (ctx.rev() is None and _intree(_existingcallers, tree))):