##// END OF EJS Templates
misc: update year in copyright lines...
misc: update year in copyright lines This patch also makes some expected output lines in tests glob-ed for persistence of them. BTW, files below aren't yet changed in 2017, but this patch also updates copyright of them, because: - mercurial/help/hg.1.txt almost all of "man hg" output comes from online help of hg command, and is already changed in 2017 - mercurial/help/hgignore.5.txt - mercurial/help/hgrc.5 "copyright 2005-201X Matt Mackall" in them mentions about copyright of Mercurial itself

File last commit:

r29987:d532ef15 default
r30907:75149f84 stable
Show More
fileset.py
551 lines | 16.8 KiB | text/x-python | PythonLexer
Matt Mackall
filesets: introduce basic fileset expression parser
r14511 # fileset.py - file set queries for mercurial
#
# Copyright 2010 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Gregory Szorc
fileset: use absolute_import
r25938 from __future__ import absolute_import
Augie Fackler
cleanup: move stdlib imports to their own import statement...
r20034 import re
Gregory Szorc
fileset: use absolute_import
r25938
from .i18n import _
from . import (
error,
merge,
parser,
FUJIWARA Katsunori
fileset: replace predicate by filesetpredicate of registrar (API)...
r28448 registrar,
Gregory Szorc
fileset: use absolute_import
r25938 util,
)
Matt Mackall
filesets: introduce basic fileset expression parser
r14511
elements = {
Yuya Nishihara
parser: separate actions for primary expression and prefix operator...
r25815 # token-type: binding-strength, primary, prefix, infix, suffix
"(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
"-": (5, None, ("negate", 19), ("minus", 5), None),
"not": (10, None, ("not", 10), None, None),
"!": (10, None, ("not", 10), None, None),
"and": (5, None, None, ("and", 5), None),
"&": (5, None, None, ("and", 5), None),
"or": (4, None, None, ("or", 4), None),
"|": (4, None, None, ("or", 4), None),
"+": (4, None, None, ("or", 4), None),
",": (2, None, None, ("list", 2), None),
")": (0, None, None, None, None),
"symbol": (0, "symbol", None, None, None),
"string": (0, "string", None, None, None),
"end": (0, None, None, None, None),
Matt Mackall
filesets: introduce basic fileset expression parser
r14511 }
keywords = set(['and', 'or', 'not'])
Matt Mackall
fileset: handle underbar in symbols...
r19470 globchars = ".*{}[]?/\\_"
Matt Mackall
fileset: basic pattern and boolean support...
r14551
Matt Mackall
filesets: introduce basic fileset expression parser
r14511 def tokenize(program):
pos, l = 0, len(program)
while pos < l:
c = program[pos]
if c.isspace(): # skip inter-token whitespace
pass
elif c in "(),-|&+!": # handle simple operators
yield (c, None, pos)
elif (c in '"\'' or c == 'r' and
program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
if c == 'r':
pos += 1
c = program[pos]
decode = lambda x: x
else:
Yuya Nishihara
fileset: handle error of string unescaping
r26233 decode = parser.unescapestr
Matt Mackall
filesets: introduce basic fileset expression parser
r14511 pos += 1
s = pos
while pos < l: # find closing quote
d = program[pos]
if d == '\\': # skip over escaped characters
pos += 2
continue
if d == c:
yield ('string', decode(program[s:pos]), s)
break
pos += 1
else:
raise error.ParseError(_("unterminated string"), s)
Matt Mackall
fileset: basic pattern and boolean support...
r14551 elif c.isalnum() or c in globchars or ord(c) > 127:
Matt Mackall
fileset: fix long line
r14513 # gather up a symbol/keyword
Matt Mackall
filesets: introduce basic fileset expression parser
r14511 s = pos
pos += 1
while pos < l: # find end of symbol
d = program[pos]
Matt Mackall
fileset: basic pattern and boolean support...
r14551 if not (d.isalnum() or d in globchars or ord(d) > 127):
Matt Mackall
filesets: introduce basic fileset expression parser
r14511 break
pos += 1
sym = program[s:pos]
if sym in keywords: # operator keywords
yield (sym, None, s)
else:
yield ('symbol', sym, s)
pos -= 1
else:
raise error.ParseError(_("syntax error"), pos)
pos += 1
yield ('end', None, pos)
Yuya Nishihara
fileset, revset: do not use global parser object for thread safety...
r20208 def parse(expr):
Yuya Nishihara
parser: accept iterator of tokens instead of tokenizer function and program...
r25654 p = parser.parser(elements)
tree, pos = p.parse(tokenize(expr))
Yuya Nishihara
fileset: move validation of incomplete parsing to parse() function...
r25252 if pos != len(expr):
raise error.ParseError(_("invalid token"), pos)
return tree
Matt Mackall
filesets: introduce basic fileset expression parser
r14511
Matt Mackall
fileset: basic pattern and boolean support...
r14551 def getstring(x, err):
if x and (x[0] == 'string' or x[0] == 'symbol'):
return x[1]
raise error.ParseError(err)
def getset(mctx, x):
if not x:
raise error.ParseError(_("missing argument"))
return methods[x[0]](mctx, *x[1:])
def stringset(mctx, x):
m = mctx.matcher([x])
return [f for f in mctx.subset if m(f)]
def andset(mctx, x, y):
return getset(mctx.narrow(getset(mctx, x)), y)
def orset(mctx, x, y):
# needs optimizing
xl = getset(mctx, x)
yl = getset(mctx, y)
return xl + [f for f in yl if f not in xl]
def notset(mctx, x):
s = set(getset(mctx, x))
return [r for r in mctx.subset if r not in s]
Patrick Mezard
fileset: actually implement 'minusset'...
r17363 def minusset(mctx, x, y):
xl = getset(mctx, x)
yl = set(getset(mctx, y))
return [f for f in xl if f not in yl]
Matt Mackall
fileset: basic pattern and boolean support...
r14551 def listset(mctx, a, b):
timeless
fileset: add hint for list error to use or
r27518 raise error.ParseError(_("can't use a list in this context"),
hint=_('see hg help "filesets.x or y"'))
Matt Mackall
fileset: basic pattern and boolean support...
r14551
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 # symbols are callable like:
# fun(mctx, x)
# with:
# mctx - current matchctx instance
# x - argument in tree form
symbols = {}
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "status caller"...
r27461 # filesets using matchctx.status()
FUJIWARA Katsunori
fileset: use set instead of list to mark predicates for efficiency (API)...
r27463 _statuscallers = set()
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "status caller"...
r27461
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "existing caller"...
r27462 # filesets using matchctx.existing()
FUJIWARA Katsunori
fileset: use set instead of list to mark predicates for efficiency (API)...
r27463 _existingcallers = set()
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "existing caller"...
r27462
FUJIWARA Katsunori
fileset: replace predicate by filesetpredicate of registrar (API)...
r28448 predicate = registrar.filesetpredicate()
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "status caller"...
r27461 @predicate('modified()', callstatus=True)
Matt Mackall
fileset: add support for file status predicates...
r14677 def modified(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is modified according to :hg:`status`.
Matt Mackall
fileset: add some function help text
r14681 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "modified" is a keyword
Matt Mackall
fileset: add support for file status predicates...
r14677 getargs(x, 0, 0, _("modified takes no arguments"))
Martin von Zweigbergk
fileset: access status fields by name rather than index
r22924 s = mctx.status().modified
Matt Mackall
fileset: add support for file status predicates...
r14677 return [f for f in mctx.subset if f in s]
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "status caller"...
r27461 @predicate('added()', callstatus=True)
Matt Mackall
fileset: add support for file status predicates...
r14677 def added(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is added according to :hg:`status`.
Matt Mackall
fileset: add some function help text
r14681 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "added" is a keyword
Matt Mackall
fileset: add support for file status predicates...
r14677 getargs(x, 0, 0, _("added takes no arguments"))
Martin von Zweigbergk
fileset: access status fields by name rather than index
r22924 s = mctx.status().added
Matt Mackall
fileset: add support for file status predicates...
r14677 return [f for f in mctx.subset if f in s]
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "status caller"...
r27461 @predicate('removed()', callstatus=True)
Matt Mackall
fileset: add support for file status predicates...
r14677 def removed(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is removed according to :hg:`status`.
Matt Mackall
fileset: add some function help text
r14681 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "removed" is a keyword
Matt Mackall
fileset: add support for file status predicates...
r14677 getargs(x, 0, 0, _("removed takes no arguments"))
Martin von Zweigbergk
fileset: access status fields by name rather than index
r22924 s = mctx.status().removed
Matt Mackall
fileset: add support for file status predicates...
r14677 return [f for f in mctx.subset if f in s]
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "status caller"...
r27461 @predicate('deleted()', callstatus=True)
Matt Mackall
fileset: add support for file status predicates...
r14677 def deleted(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """Alias for ``missing()``.
Matt Mackall
fileset: add some function help text
r14681 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "deleted" is a keyword
Matt Mackall
fileset: add support for file status predicates...
r14677 getargs(x, 0, 0, _("deleted takes no arguments"))
Martin von Zweigbergk
fileset: access status fields by name rather than index
r22924 s = mctx.status().deleted
Matt Mackall
fileset: add support for file status predicates...
r14677 return [f for f in mctx.subset if f in s]
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "status caller"...
r27461 @predicate('missing()', callstatus=True)
liscju
fileset: add missing() predicate (issue4925)...
r27024 def missing(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is missing according to :hg:`status`.
liscju
fileset: add missing() predicate (issue4925)...
r27024 """
# i18n: "missing" is a keyword
getargs(x, 0, 0, _("missing takes no arguments"))
s = mctx.status().deleted
return [f for f in mctx.subset if f in s]
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "status caller"...
r27461 @predicate('unknown()', callstatus=True)
Matt Mackall
fileset: add support for file status predicates...
r14677 def unknown(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is unknown according to :hg:`status`. These files will only be
Matt Mackall
fileset: add some function help text
r14681 considered if this predicate is used.
"""
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "unknown" is a keyword
Matt Mackall
fileset: add support for file status predicates...
r14677 getargs(x, 0, 0, _("unknown takes no arguments"))
Martin von Zweigbergk
fileset: access status fields by name rather than index
r22924 s = mctx.status().unknown
Matt Mackall
fileset: add support for file status predicates...
r14677 return [f for f in mctx.subset if f in s]
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "status caller"...
r27461 @predicate('ignored()', callstatus=True)
Matt Mackall
fileset: add support for file status predicates...
r14677 def ignored(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is ignored according to :hg:`status`. These files will only be
Matt Mackall
fileset: add some function help text
r14681 considered if this predicate is used.
"""
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "ignored" is a keyword
Matt Mackall
fileset: add support for file status predicates...
r14677 getargs(x, 0, 0, _("ignored takes no arguments"))
Martin von Zweigbergk
fileset: access status fields by name rather than index
r22924 s = mctx.status().ignored
Matt Mackall
fileset: add support for file status predicates...
r14677 return [f for f in mctx.subset if f in s]
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "status caller"...
r27461 @predicate('clean()', callstatus=True)
Matt Mackall
fileset: add support for file status predicates...
r14677 def clean(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is clean according to :hg:`status`.
Matt Mackall
fileset: add some function help text
r14681 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "clean" is a keyword
Matt Mackall
fileset: add support for file status predicates...
r14677 getargs(x, 0, 0, _("clean takes no arguments"))
Martin von Zweigbergk
fileset: access status fields by name rather than index
r22924 s = mctx.status().clean
Matt Mackall
fileset: add support for file status predicates...
r14677 return [f for f in mctx.subset if f in s]
Matt Mackall
fileset: add some basic predicates
r14676 def func(mctx, a, b):
if a[0] == 'symbol' and a[1] in symbols:
FUJIWARA Katsunori
fileset: detect unintentional existing() invocation at runtime...
r27464 funcname = a[1]
enabled = mctx._existingenabled
mctx._existingenabled = funcname in _existingcallers
try:
return symbols[funcname](mctx, b)
finally:
mctx._existingenabled = enabled
Matt Harbison
fileset: don't suggest private or undocumented queries...
r25633
keep = lambda fn: getattr(fn, '__doc__', None) is not None
syms = [s for (s, fn) in symbols.items() if keep(fn)]
raise error.UnknownIdentifier(a[1], syms)
Matt Mackall
fileset: add some basic predicates
r14676
def getlist(x):
if not x:
return []
if x[0] == 'list':
return getlist(x[1]) + [x[2]]
return [x]
def getargs(x, min, max, err):
l = getlist(x)
if len(l) < min or len(l) > max:
raise error.ParseError(err)
return l
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "existing caller"...
r27462 @predicate('binary()', callexisting=True)
Matt Mackall
fileset: add some basic predicates
r14676 def binary(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that appears to be binary (contains NUL bytes).
Matt Mackall
fileset: add some function help text
r14681 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "binary" is a keyword
Matt Mackall
fileset: add some basic predicates
r14676 getargs(x, 0, 0, _("binary takes no arguments"))
Matt Mackall
fileset: don't attempt to check data predicates against removed files
r15963 return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())]
Matt Mackall
fileset: add some basic predicates
r14676
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "existing caller"...
r27462 @predicate('exec()', callexisting=True)
Matt Mackall
fileset: add some basic predicates
r14676 def exec_(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is marked as executable.
Matt Mackall
fileset: add some function help text
r14681 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "exec" is a keyword
Matt Mackall
fileset: add some basic predicates
r14676 getargs(x, 0, 0, _("exec takes no arguments"))
Matt Mackall
fileset: don't attempt to check data predicates against removed files
r15963 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
Matt Mackall
fileset: add some basic predicates
r14676
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "existing caller"...
r27462 @predicate('symlink()', callexisting=True)
Matt Mackall
fileset: add some basic predicates
r14676 def symlink(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is marked as a symlink.
Matt Mackall
fileset: add some function help text
r14681 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "symlink" is a keyword
Matt Mackall
fileset: add some basic predicates
r14676 getargs(x, 0, 0, _("symlink takes no arguments"))
Matt Mackall
fileset: don't attempt to check data predicates against removed files
r15963 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
Matt Mackall
fileset: add some basic predicates
r14676
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 @predicate('resolved()')
Matt Mackall
fileset: add resolved and unresolved predicates
r14679 def resolved(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is marked resolved according to :hg:`resolve -l`.
Matt Mackall
fileset: add some function help text
r14681 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "resolved" is a keyword
Matt Mackall
fileset: add resolved and unresolved predicates
r14679 getargs(x, 0, 0, _("resolved takes no arguments"))
if mctx.ctx.rev() is not None:
return []
Siddharth Agarwal
fileset: switch to mergestate.read()...
r26995 ms = merge.mergestate.read(mctx.ctx.repo())
Matt Mackall
fileset: add resolved and unresolved predicates
r14679 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 @predicate('unresolved()')
Matt Mackall
fileset: add resolved and unresolved predicates
r14679 def unresolved(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is marked unresolved according to :hg:`resolve -l`.
Matt Mackall
fileset: add some function help text
r14681 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "unresolved" is a keyword
Matt Mackall
fileset: add resolved and unresolved predicates
r14679 getargs(x, 0, 0, _("unresolved takes no arguments"))
if mctx.ctx.rev() is not None:
return []
Siddharth Agarwal
fileset: switch to mergestate.read()...
r26995 ms = merge.mergestate.read(mctx.ctx.repo())
Matt Mackall
fileset: add resolved and unresolved predicates
r14679 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 @predicate('hgignore()')
Matt Mackall
fileset: add hgignore
r14680 def hgignore(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that matches the active .hgignore pattern.
Matt Mackall
fileset: add some function help text
r14681 """
FUJIWARA Katsunori
i18n: add i18n comment to error messages of filesets predicates
r23113 # i18n: "hgignore" is a keyword
Matt Mackall
fileset: add hgignore
r14680 getargs(x, 0, 0, _("hgignore takes no arguments"))
Matt Harbison
fileset: replace 'ctx._repo' with 'ctx.repo()'
r24334 ignore = mctx.ctx.repo().dirstate._ignore
Matt Mackall
fileset: add hgignore
r14680 return [f for f in mctx.subset if ignore(f)]
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 @predicate('portable()')
Siddharth Agarwal
fileset: add a fileset for portable filenames...
r24408 def portable(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that has a portable name. (This doesn't include filenames with case
Siddharth Agarwal
fileset: add a fileset for portable filenames...
r24408 collisions.)
"""
# i18n: "portable" is a keyword
getargs(x, 0, 0, _("portable takes no arguments"))
checkwinfilename = util.checkwinfilename
return [f for f in mctx.subset if checkwinfilename(f) is None]
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "existing caller"...
r27462 @predicate('grep(regex)', callexisting=True)
Matt Mackall
fileset: add grep predicate
r14682 def grep(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File contains the given regular expression.
Matt Mackall
fileset: add grep predicate
r14682 """
Patrick Mezard
fileset: do not traceback on invalid grep pattern
r17368 try:
# i18n: "grep" is a keyword
r = re.compile(getstring(x, _("grep requires a pattern")))
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except re.error as e:
Patrick Mezard
fileset: do not traceback on invalid grep pattern
r17368 raise error.ParseError(_('invalid match pattern: %s') % e)
Matt Mackall
fileset: don't attempt to check data predicates against removed files
r15963 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
Matt Mackall
fileset: add grep predicate
r14682
Matt Mackall
fileset: add size() predicate
r14683 def _sizetomax(s):
try:
av6
filesets: ignore unit case in size() predicate for single value...
r25925 s = s.strip().lower()
Bryan O'Sullivan
util: migrate fileset._sizetoint to util.sizetoint...
r19194 for k, v in util._sizeunits:
Matt Mackall
fileset: add size() predicate
r14683 if s.endswith(k):
# max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
n = s[:-len(k)]
inc = 1.0
if "." in n:
inc /= 10 ** len(n.split(".")[1])
return int((float(n) + inc) * v) - 1
# no extension, this is a precise value
return int(s)
except ValueError:
Mads Kiilerich
fileset: use ParseError pos field correctly...
r14716 raise error.ParseError(_("couldn't parse size: %s") % s)
Matt Mackall
fileset: add size() predicate
r14683
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "existing caller"...
r27462 @predicate('size(expression)', callexisting=True)
Matt Mackall
fileset: add size() predicate
r14683 def size(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File size matches the given expression. Examples:
Matt Mackall
fileset: add size() predicate
r14683
timeless
help: clarify quotes are needed for filesets.size expressions
r29987 - size('1k') - files from 1024 to 2047 bytes
- size('< 20k') - files less than 20480 bytes
- size('>= .5MB') - files at least 524288 bytes
- size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
Matt Mackall
fileset: add size() predicate
r14683 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "size" is a keyword
Mads Kiilerich
revset and fileset: fix typos in parser error messages
r14717 expr = getstring(x, _("size requires an expression")).strip()
Matt Mackall
fileset: add size() predicate
r14683 if '-' in expr: # do we have a range?
a, b = expr.split('-', 1)
Bryan O'Sullivan
util: migrate fileset._sizetoint to util.sizetoint...
r19194 a = util.sizetoint(a)
b = util.sizetoint(b)
Matt Mackall
fileset: add size() predicate
r14683 m = lambda x: x >= a and x <= b
elif expr.startswith("<="):
Bryan O'Sullivan
util: migrate fileset._sizetoint to util.sizetoint...
r19194 a = util.sizetoint(expr[2:])
Matt Mackall
fileset: add size() predicate
r14683 m = lambda x: x <= a
elif expr.startswith("<"):
Bryan O'Sullivan
util: migrate fileset._sizetoint to util.sizetoint...
r19194 a = util.sizetoint(expr[1:])
Matt Mackall
fileset: add size() predicate
r14683 m = lambda x: x < a
elif expr.startswith(">="):
Bryan O'Sullivan
util: migrate fileset._sizetoint to util.sizetoint...
r19194 a = util.sizetoint(expr[2:])
Matt Mackall
fileset: add size() predicate
r14683 m = lambda x: x >= a
elif expr.startswith(">"):
Bryan O'Sullivan
util: migrate fileset._sizetoint to util.sizetoint...
r19194 a = util.sizetoint(expr[1:])
Matt Mackall
fileset: add size() predicate
r14683 m = lambda x: x > a
elif expr[0].isdigit or expr[0] == '.':
Bryan O'Sullivan
util: migrate fileset._sizetoint to util.sizetoint...
r19194 a = util.sizetoint(expr)
Matt Mackall
fileset: add size() predicate
r14683 b = _sizetomax(expr)
Thomas Arendsen Hein
fileset: add missing whitespace around operator
r14690 m = lambda x: x >= a and x <= b
Matt Mackall
fileset: add size() predicate
r14683 else:
Mads Kiilerich
fileset: use ParseError pos field correctly...
r14716 raise error.ParseError(_("couldn't parse size: %s") % expr)
Matt Mackall
fileset: add size() predicate
r14683
Matt Mackall
fileset: don't attempt to check data predicates against removed files
r15963 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
Matt Mackall
fileset: add size() predicate
r14683
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "existing caller"...
r27462 @predicate('encoding(name)', callexisting=True)
Matt Mackall
fileset: add encoding() predicate
r14684 def encoding(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File can be successfully decoded with the given character
Matt Mackall
fileset: add encoding() predicate
r14684 encoding. May not be useful for encodings other than ASCII and
UTF-8.
"""
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "encoding" is a keyword
Matt Mackall
fileset: add encoding() predicate
r14684 enc = getstring(x, _("encoding requires an encoding name"))
s = []
Matt Mackall
fileset: don't attempt to check data predicates against removed files
r15963 for f in mctx.existing():
Matt Mackall
fileset: add encoding() predicate
r14684 d = mctx.ctx[f].data()
try:
d.decode(enc)
except LookupError:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("unknown encoding '%s'") % enc)
Matt Mackall
fileset: add encoding() predicate
r14684 except UnicodeDecodeError:
continue
s.append(f)
return s
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "existing caller"...
r27462 @predicate('eol(style)', callexisting=True)
Matt Mackall
filesets: add eol predicate
r18842 def eol(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File contains newlines of the given style (dos, unix, mac). Binary
Matt Mackall
filesets: add eol predicate
r18842 files are excluded, files with mixed line endings match multiple
styles.
"""
Matt Harbison
fileset: fix copy/paste in eol() error message
r28056 # i18n: "eol" is a keyword
enc = getstring(x, _("eol requires a style name"))
Matt Mackall
filesets: add eol predicate
r18842
s = []
for f in mctx.existing():
d = mctx.ctx[f].data()
if util.binary(d):
continue
if (enc == 'dos' or enc == 'win') and '\r\n' in d:
s.append(f)
elif enc == 'unix' and re.search('(?<!\r)\n', d):
s.append(f)
elif enc == 'mac' and re.search('\r(?!\n)', d):
s.append(f)
return s
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 @predicate('copied()')
Matt Mackall
fileset: add copied predicate
r14685 def copied(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """File that is recorded as being copied.
Matt Mackall
fileset: add copied predicate
r14685 """
Wagner Bruna
fileset: add i18n hints for keywords
r14785 # i18n: "copied" is a keyword
Mads Kiilerich
fileset: copied takes no arguments
r14718 getargs(x, 0, 0, _("copied takes no arguments"))
Matt Mackall
fileset: add copied predicate
r14685 s = []
for f in mctx.subset:
p = mctx.ctx[f].parents()
if p and p[0].path() != f:
s.append(f)
return s
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 @predicate('subrepo([pattern])')
Angel Ezquerra
fileset: add "subrepo" fileset symbol...
r16443 def subrepo(mctx, x):
FUJIWARA Katsunori
fileset: use decorator to mark a function as fileset predicate...
r27460 """Subrepositories whose paths match the given pattern.
Angel Ezquerra
fileset: add "subrepo" fileset symbol...
r16443 """
# i18n: "subrepo" is a keyword
getargs(x, 0, 1, _("subrepo takes at most one argument"))
ctx = mctx.ctx
Mads Kiilerich
subrepos: process subrepos in sorted order...
r18364 sstate = sorted(ctx.substate)
Angel Ezquerra
fileset: add "subrepo" fileset symbol...
r16443 if x:
FUJIWARA Katsunori
i18n: add i18n comment to error messages of filesets predicates
r23113 # i18n: "subrepo" is a keyword
Angel Ezquerra
fileset: add "subrepo" fileset symbol...
r16443 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
Gregory Szorc
fileset: use absolute_import
r25938 from . import match as matchmod # avoid circular import issues
Angel Ezquerra
fileset: add "subrepo" fileset symbol...
r16443 fast = not matchmod.patkind(pat)
if fast:
def m(s):
return (s == pat)
else:
Matt Harbison
fileset: replace 'ctx._repo' with 'ctx.repo()'
r24334 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
Angel Ezquerra
fileset: add "subrepo" fileset symbol...
r16443 return [sub for sub in sstate if m(sub)]
else:
return [sub for sub in sstate]
Matt Mackall
fileset: basic pattern and boolean support...
r14551 methods = {
'string': stringset,
'symbol': stringset,
'and': andset,
'or': orset,
Patrick Mezard
fileset: actually implement 'minusset'...
r17363 'minus': minusset,
Matt Mackall
fileset: basic pattern and boolean support...
r14551 'list': listset,
'group': getset,
Matt Mackall
fileset: add some basic predicates
r14676 'not': notset,
'func': func,
Matt Mackall
fileset: basic pattern and boolean support...
r14551 }
class matchctx(object):
Matt Mackall
fileset: add support for file status predicates...
r14677 def __init__(self, ctx, subset=None, status=None):
Matt Mackall
fileset: basic pattern and boolean support...
r14551 self.ctx = ctx
self.subset = subset
Matt Mackall
fileset: add support for file status predicates...
r14677 self._status = status
FUJIWARA Katsunori
fileset: detect unintentional existing() invocation at runtime...
r27464 self._existingenabled = False
Matt Mackall
fileset: add support for file status predicates...
r14677 def status(self):
return self._status
Matt Mackall
fileset: drop matchfn...
r14673 def matcher(self, patterns):
return self.ctx.match(patterns)
Matt Mackall
fileset: basic pattern and boolean support...
r14551 def filter(self, files):
return [f for f in files if f in self.subset]
Matt Mackall
fileset: don't attempt to check data predicates against removed files
r15963 def existing(self):
FUJIWARA Katsunori
fileset: detect unintentional existing() invocation at runtime...
r27464 assert self._existingenabled, 'unexpected existing() invocation'
Patrick Mezard
fileset: exclude deleted files from matchctx.existing()...
r17365 if self._status is not None:
removed = set(self._status[3])
Patrick Mezard
fileset: matchctx.existing() must consider ignored files...
r17367 unknown = set(self._status[4] + self._status[5])
Patrick Mezard
fileset: exclude deleted files from matchctx.existing()...
r17365 else:
removed = set()
Patrick Mezard
fileset: matchctx.existing() must consider unknown files...
r17366 unknown = set()
Patrick Mezard
fileset: exclude deleted files from matchctx.existing()...
r17365 return (f for f in self.subset
Patrick Mezard
fileset: matchctx.existing() must consider unknown files...
r17366 if (f in self.ctx and f not in removed) or f in unknown)
Matt Mackall
fileset: basic pattern and boolean support...
r14551 def narrow(self, files):
Matt Mackall
fileset: add support for file status predicates...
r14677 return matchctx(self.ctx, self.filter(files), self._status)
Matt Mackall
fileset: basic pattern and boolean support...
r14551
Matt Mackall
fileset: prescan parse tree to optimize status usage...
r14678 def _intree(funcs, tree):
if isinstance(tree, tuple):
if tree[0] == 'func' and tree[1][0] == 'symbol':
if tree[1][1] in funcs:
return True
for s in tree[1:]:
if _intree(funcs, s):
return True
return False
Matt Mackall
fileset: drop matchfn...
r14673 def getfileset(ctx, expr):
Yuya Nishihara
fileset: move validation of incomplete parsing to parse() function...
r25252 tree = parse(expr)
Matt Mackall
fileset: prescan parse tree to optimize status usage...
r14678
# do we need status info?
FUJIWARA Katsunori
fileset: use decorator to mark a predicate as "status caller"...
r27461 if (_intree(_statuscallers, tree) or
Patrick Mezard
fileset: exclude deleted files from matchctx.existing()...
r17365 # Using matchctx.existing() on a workingctx requires us to check
# for deleted files.
(ctx.rev() is None and _intree(_existingcallers, tree))):
Matt Mackall
fileset: prescan parse tree to optimize status usage...
r14678 unknown = _intree(['unknown'], tree)
ignored = _intree(['ignored'], tree)
Matt Harbison
fileset: replace 'ctx._repo' with 'ctx.repo()'
r24334 r = ctx.repo()
Matt Mackall
fileset: prescan parse tree to optimize status usage...
r14678 status = r.status(ctx.p1(), ctx,
unknown=unknown, ignored=ignored, clean=True)
subset = []
for c in status:
subset.extend(c)
else:
status = None
Patrick Mezard
fileset: fix generator vs list bug in fast path...
r17371 subset = list(ctx.walk(ctx.match([])))
Matt Mackall
fileset: prescan parse tree to optimize status usage...
r14678
return getset(matchctx(ctx, subset, status), tree)
Matt Mackall
fileset: add some function help text
r14681
Yuya Nishihara
fileset: pretty print syntax tree in debug output
r25255 def prettyformat(tree):
return parser.prettyformat(tree, ('string', 'symbol'))
FUJIWARA Katsunori
registrar: add filesetpredicate to mark a function as fileset predicate...
r28447 def loadpredicate(ui, extname, registrarobj):
"""Load fileset predicates from specified registrarobj
"""
for name, func in registrarobj._table.iteritems():
symbols[name] = func
if func._callstatus:
_statuscallers.add(name)
if func._callexisting:
_existingcallers.add(name)
FUJIWARA Katsunori
fileset: replace predicate by filesetpredicate of registrar (API)...
r28448 # load built-in predicates explicitly to setup _statuscallers/_existingcallers
loadpredicate(None, None, predicate)
Matt Mackall
fileset: add some function help text
r14681 # tell hggettext to extract docstrings from these functions:
i18nfunctions = symbols.values()