diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -1603,6 +1603,10 @@ def debugfileset(ui, repo, expr): if ui.verbose: tree = fileset.parse(expr)[0] ui.note(tree, "\n") + matcher = lambda x: scmutil.match(repo, x, default='glob') + + for f in fileset.getfileset(repo[None], matcher, expr): + ui.write("%s\n" % f) @command('debugfsinfo', [], _('[PATH]')) def debugfsinfo(ui, path = "."): diff --git a/mercurial/fileset.py b/mercurial/fileset.py --- a/mercurial/fileset.py +++ b/mercurial/fileset.py @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import parser, error +import parser, error, match from i18n import _ elements = { @@ -27,6 +27,8 @@ elements = { keywords = set(['and', 'or', 'not']) +globchars = ".*{}[]?/\\" + def tokenize(program): pos, l = 0, len(program) while pos < l: @@ -56,13 +58,13 @@ def tokenize(program): pos += 1 else: raise error.ParseError(_("unterminated string"), s) - elif c.isalnum() or c in '.*{}[]?' or ord(c) > 127: + elif c.isalnum() or c in globchars or ord(c) > 127: # gather up a symbol/keyword s = pos pos += 1 while pos < l: # find end of symbol d = program[pos] - if not (d.isalnum() or d in ".*{}[]?," or ord(d) > 127): + if not (d.isalnum() or d in globchars or ord(d) > 127): break pos += 1 sym = program[s:pos] @@ -78,3 +80,63 @@ def tokenize(program): parse = parser.parser(tokenize, elements).parse +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] + +def listset(mctx, a, b): + raise error.ParseError(_("can't use a list in this context")) + +methods = { + 'string': stringset, + 'symbol': stringset, + 'and': andset, + 'or': orset, + 'list': listset, + 'group': getset, + 'not': notset +} + +class matchctx(object): + def __init__(self, ctx, matchfn, subset=None): + self.ctx = ctx + self.matchfn = matchfn + self.subset = subset + if subset is None: + self.subset = ctx.walk(matchfn([])) # optimize this later + def matcher(self, pattern): + return self.matchfn(pattern) + def filter(self, files): + return [f for f in files if f in self.subset] + def narrow(self, files): + return matchctx(self.ctx, self.matchfn, + self.filter(files)) + +def getfileset(ctx, matchfn, expr): + tree, pos = parse(expr) + if (pos != len(expr)): + raise error.ParseError("invalid token", pos) + return getset(matchctx(ctx, matchfn), tree)