138 l = getlist(x) |
138 l = getlist(x) |
139 if len(l) < min or len(l) > max: |
139 if len(l) < min or len(l) > max: |
140 raise error.ParseError(err) |
140 raise error.ParseError(err) |
141 return l |
141 return l |
142 |
142 |
143 def getset(mctx, x): |
143 def getmatch(mctx, x): |
144 if not x: |
144 if not x: |
145 raise error.ParseError(_("missing argument")) |
145 raise error.ParseError(_("missing argument")) |
146 return methods[x[0]](mctx, *x[1:]) |
146 return methods[x[0]](mctx, *x[1:]) |
147 |
147 |
148 def stringset(mctx, x): |
148 def stringmatch(mctx, x): |
149 m = mctx.matcher([x]) |
149 return mctx.matcher([x]) |
150 return [f for f in mctx.subset if m(f)] |
150 |
151 |
151 def kindpatmatch(mctx, x, y): |
152 def kindpatset(mctx, x, y): |
152 return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds, |
153 return stringset(mctx, _getkindpat(x, y, matchmod.allpatternkinds, |
153 _("pattern must be a string"))) |
154 _("pattern must be a string"))) |
154 |
155 |
155 def andmatch(mctx, x, y): |
156 def andset(mctx, x, y): |
156 xm = getmatch(mctx, x) |
157 xl = set(getset(mctx, x)) |
157 ym = getmatch(mctx, y) |
158 yl = getset(mctx, y) |
158 return matchmod.intersectmatchers(xm, ym) |
159 return [f for f in yl if f in xl] |
159 |
160 |
160 def ormatch(mctx, x, y): |
161 def orset(mctx, x, y): |
161 xm = getmatch(mctx, x) |
162 # needs optimizing |
162 ym = getmatch(mctx, y) |
163 xl = getset(mctx, x) |
163 return matchmod.unionmatcher([xm, ym]) |
164 yl = getset(mctx, y) |
164 |
165 return xl + [f for f in yl if f not in xl] |
165 def notmatch(mctx, x): |
166 |
166 m = getmatch(mctx, x) |
167 def notset(mctx, x): |
167 return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m)) |
168 s = set(getset(mctx, x)) |
168 |
169 return [r for r in mctx.subset if r not in s] |
169 def minusmatch(mctx, x, y): |
170 |
170 xm = getmatch(mctx, x) |
171 def minusset(mctx, x, y): |
171 ym = getmatch(mctx, y) |
172 xl = getset(mctx, x) |
172 return matchmod.differencematcher(xm, ym) |
173 yl = set(getset(mctx, y)) |
173 |
174 return [f for f in xl if f not in yl] |
174 def negatematch(mctx, x): |
175 |
|
176 def negateset(mctx, x): |
|
177 raise error.ParseError(_("can't use negate operator in this context")) |
175 raise error.ParseError(_("can't use negate operator in this context")) |
178 |
176 |
179 def listset(mctx, a, b): |
177 def listmatch(mctx, x, y): |
180 raise error.ParseError(_("can't use a list in this context"), |
178 raise error.ParseError(_("can't use a list in this context"), |
181 hint=_('see hg help "filesets.x or y"')) |
179 hint=_('see hg help "filesets.x or y"')) |
182 |
180 |
183 def func(mctx, a, b): |
181 def func(mctx, a, b): |
184 funcname = getsymbol(a) |
182 funcname = getsymbol(a) |
215 """File that is modified according to :hg:`status`. |
213 """File that is modified according to :hg:`status`. |
216 """ |
214 """ |
217 # i18n: "modified" is a keyword |
215 # i18n: "modified" is a keyword |
218 getargs(x, 0, 0, _("modified takes no arguments")) |
216 getargs(x, 0, 0, _("modified takes no arguments")) |
219 s = set(mctx.status().modified) |
217 s = set(mctx.status().modified) |
220 return [f for f in mctx.subset if f in s] |
218 return mctx.predicate(s.__contains__, predrepr='modified') |
221 |
219 |
222 @predicate('added()', callstatus=True) |
220 @predicate('added()', callstatus=True) |
223 def added(mctx, x): |
221 def added(mctx, x): |
224 """File that is added according to :hg:`status`. |
222 """File that is added according to :hg:`status`. |
225 """ |
223 """ |
226 # i18n: "added" is a keyword |
224 # i18n: "added" is a keyword |
227 getargs(x, 0, 0, _("added takes no arguments")) |
225 getargs(x, 0, 0, _("added takes no arguments")) |
228 s = set(mctx.status().added) |
226 s = set(mctx.status().added) |
229 return [f for f in mctx.subset if f in s] |
227 return mctx.predicate(s.__contains__, predrepr='added') |
230 |
228 |
231 @predicate('removed()', callstatus=True) |
229 @predicate('removed()', callstatus=True) |
232 def removed(mctx, x): |
230 def removed(mctx, x): |
233 """File that is removed according to :hg:`status`. |
231 """File that is removed according to :hg:`status`. |
234 """ |
232 """ |
235 # i18n: "removed" is a keyword |
233 # i18n: "removed" is a keyword |
236 getargs(x, 0, 0, _("removed takes no arguments")) |
234 getargs(x, 0, 0, _("removed takes no arguments")) |
237 s = set(mctx.status().removed) |
235 s = set(mctx.status().removed) |
238 return [f for f in mctx.subset if f in s] |
236 return mctx.predicate(s.__contains__, predrepr='removed') |
239 |
237 |
240 @predicate('deleted()', callstatus=True) |
238 @predicate('deleted()', callstatus=True) |
241 def deleted(mctx, x): |
239 def deleted(mctx, x): |
242 """Alias for ``missing()``. |
240 """Alias for ``missing()``. |
243 """ |
241 """ |
244 # i18n: "deleted" is a keyword |
242 # i18n: "deleted" is a keyword |
245 getargs(x, 0, 0, _("deleted takes no arguments")) |
243 getargs(x, 0, 0, _("deleted takes no arguments")) |
246 s = set(mctx.status().deleted) |
244 s = set(mctx.status().deleted) |
247 return [f for f in mctx.subset if f in s] |
245 return mctx.predicate(s.__contains__, predrepr='deleted') |
248 |
246 |
249 @predicate('missing()', callstatus=True) |
247 @predicate('missing()', callstatus=True) |
250 def missing(mctx, x): |
248 def missing(mctx, x): |
251 """File that is missing according to :hg:`status`. |
249 """File that is missing according to :hg:`status`. |
252 """ |
250 """ |
253 # i18n: "missing" is a keyword |
251 # i18n: "missing" is a keyword |
254 getargs(x, 0, 0, _("missing takes no arguments")) |
252 getargs(x, 0, 0, _("missing takes no arguments")) |
255 s = set(mctx.status().deleted) |
253 s = set(mctx.status().deleted) |
256 return [f for f in mctx.subset if f in s] |
254 return mctx.predicate(s.__contains__, predrepr='deleted') |
257 |
255 |
258 @predicate('unknown()', callstatus=True) |
256 @predicate('unknown()', callstatus=True) |
259 def unknown(mctx, x): |
257 def unknown(mctx, x): |
260 """File that is unknown according to :hg:`status`. These files will only be |
258 """File that is unknown according to :hg:`status`.""" |
261 considered if this predicate is used. |
|
262 """ |
|
263 # i18n: "unknown" is a keyword |
259 # i18n: "unknown" is a keyword |
264 getargs(x, 0, 0, _("unknown takes no arguments")) |
260 getargs(x, 0, 0, _("unknown takes no arguments")) |
265 s = set(mctx.status().unknown) |
261 s = set(mctx.status().unknown) |
266 return [f for f in mctx.subset if f in s] |
262 return mctx.predicate(s.__contains__, predrepr='unknown') |
267 |
263 |
268 @predicate('ignored()', callstatus=True) |
264 @predicate('ignored()', callstatus=True) |
269 def ignored(mctx, x): |
265 def ignored(mctx, x): |
270 """File that is ignored according to :hg:`status`. These files will only be |
266 """File that is ignored according to :hg:`status`.""" |
271 considered if this predicate is used. |
|
272 """ |
|
273 # i18n: "ignored" is a keyword |
267 # i18n: "ignored" is a keyword |
274 getargs(x, 0, 0, _("ignored takes no arguments")) |
268 getargs(x, 0, 0, _("ignored takes no arguments")) |
275 s = set(mctx.status().ignored) |
269 s = set(mctx.status().ignored) |
276 return [f for f in mctx.subset if f in s] |
270 return mctx.predicate(s.__contains__, predrepr='ignored') |
277 |
271 |
278 @predicate('clean()', callstatus=True) |
272 @predicate('clean()', callstatus=True) |
279 def clean(mctx, x): |
273 def clean(mctx, x): |
280 """File that is clean according to :hg:`status`. |
274 """File that is clean according to :hg:`status`. |
281 """ |
275 """ |
282 # i18n: "clean" is a keyword |
276 # i18n: "clean" is a keyword |
283 getargs(x, 0, 0, _("clean takes no arguments")) |
277 getargs(x, 0, 0, _("clean takes no arguments")) |
284 s = set(mctx.status().clean) |
278 s = set(mctx.status().clean) |
285 return [f for f in mctx.subset if f in s] |
279 return mctx.predicate(s.__contains__, predrepr='clean') |
286 |
280 |
287 @predicate('tracked()') |
281 @predicate('tracked()') |
288 def tracked(mctx, x): |
282 def tracked(mctx, x): |
289 """File that is under Mercurial control.""" |
283 """File that is under Mercurial control.""" |
290 # i18n: "tracked" is a keyword |
284 # i18n: "tracked" is a keyword |
291 getargs(x, 0, 0, _("tracked takes no arguments")) |
285 getargs(x, 0, 0, _("tracked takes no arguments")) |
292 return [f for f in mctx.subset if f in mctx.ctx] |
286 return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked') |
293 |
287 |
294 @predicate('binary()', callexisting=True) |
288 @predicate('binary()', callexisting=True) |
295 def binary(mctx, x): |
289 def binary(mctx, x): |
296 """File that appears to be binary (contains NUL bytes). |
290 """File that appears to be binary (contains NUL bytes). |
297 """ |
291 """ |
298 # i18n: "binary" is a keyword |
292 # i18n: "binary" is a keyword |
299 getargs(x, 0, 0, _("binary takes no arguments")) |
293 getargs(x, 0, 0, _("binary takes no arguments")) |
300 return [f for f in mctx.existing() if mctx.ctx[f].isbinary()] |
294 return mctx.fpredicate(lambda fctx: fctx.isbinary(), |
|
295 predrepr='binary', cache=True) |
301 |
296 |
302 @predicate('exec()', callexisting=True) |
297 @predicate('exec()', callexisting=True) |
303 def exec_(mctx, x): |
298 def exec_(mctx, x): |
304 """File that is marked as executable. |
299 """File that is marked as executable. |
305 """ |
300 """ |
306 # i18n: "exec" is a keyword |
301 # i18n: "exec" is a keyword |
307 getargs(x, 0, 0, _("exec takes no arguments")) |
302 getargs(x, 0, 0, _("exec takes no arguments")) |
308 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x'] |
303 ctx = mctx.ctx |
|
304 return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec') |
309 |
305 |
310 @predicate('symlink()', callexisting=True) |
306 @predicate('symlink()', callexisting=True) |
311 def symlink(mctx, x): |
307 def symlink(mctx, x): |
312 """File that is marked as a symlink. |
308 """File that is marked as a symlink. |
313 """ |
309 """ |
314 # i18n: "symlink" is a keyword |
310 # i18n: "symlink" is a keyword |
315 getargs(x, 0, 0, _("symlink takes no arguments")) |
311 getargs(x, 0, 0, _("symlink takes no arguments")) |
316 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l'] |
312 ctx = mctx.ctx |
|
313 return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink') |
317 |
314 |
318 @predicate('resolved()') |
315 @predicate('resolved()') |
319 def resolved(mctx, x): |
316 def resolved(mctx, x): |
320 """File that is marked resolved according to :hg:`resolve -l`. |
317 """File that is marked resolved according to :hg:`resolve -l`. |
321 """ |
318 """ |
322 # i18n: "resolved" is a keyword |
319 # i18n: "resolved" is a keyword |
323 getargs(x, 0, 0, _("resolved takes no arguments")) |
320 getargs(x, 0, 0, _("resolved takes no arguments")) |
324 if mctx.ctx.rev() is not None: |
321 if mctx.ctx.rev() is not None: |
325 return [] |
322 return mctx.never() |
326 ms = merge.mergestate.read(mctx.ctx.repo()) |
323 ms = merge.mergestate.read(mctx.ctx.repo()) |
327 return [f for f in mctx.subset if f in ms and ms[f] == 'r'] |
324 return mctx.predicate(lambda f: f in ms and ms[f] == 'r', |
|
325 predrepr='resolved') |
328 |
326 |
329 @predicate('unresolved()') |
327 @predicate('unresolved()') |
330 def unresolved(mctx, x): |
328 def unresolved(mctx, x): |
331 """File that is marked unresolved according to :hg:`resolve -l`. |
329 """File that is marked unresolved according to :hg:`resolve -l`. |
332 """ |
330 """ |
333 # i18n: "unresolved" is a keyword |
331 # i18n: "unresolved" is a keyword |
334 getargs(x, 0, 0, _("unresolved takes no arguments")) |
332 getargs(x, 0, 0, _("unresolved takes no arguments")) |
335 if mctx.ctx.rev() is not None: |
333 if mctx.ctx.rev() is not None: |
336 return [] |
334 return mctx.never() |
337 ms = merge.mergestate.read(mctx.ctx.repo()) |
335 ms = merge.mergestate.read(mctx.ctx.repo()) |
338 return [f for f in mctx.subset if f in ms and ms[f] == 'u'] |
336 return mctx.predicate(lambda f: f in ms and ms[f] == 'u', |
|
337 predrepr='unresolved') |
339 |
338 |
340 @predicate('hgignore()') |
339 @predicate('hgignore()') |
341 def hgignore(mctx, x): |
340 def hgignore(mctx, x): |
342 """File that matches the active .hgignore pattern. |
341 """File that matches the active .hgignore pattern. |
343 """ |
342 """ |
344 # i18n: "hgignore" is a keyword |
343 # i18n: "hgignore" is a keyword |
345 getargs(x, 0, 0, _("hgignore takes no arguments")) |
344 getargs(x, 0, 0, _("hgignore takes no arguments")) |
346 ignore = mctx.ctx.repo().dirstate._ignore |
345 return mctx.ctx.repo().dirstate._ignore |
347 return [f for f in mctx.subset if ignore(f)] |
|
348 |
346 |
349 @predicate('portable()') |
347 @predicate('portable()') |
350 def portable(mctx, x): |
348 def portable(mctx, x): |
351 """File that has a portable name. (This doesn't include filenames with case |
349 """File that has a portable name. (This doesn't include filenames with case |
352 collisions.) |
350 collisions.) |
353 """ |
351 """ |
354 # i18n: "portable" is a keyword |
352 # i18n: "portable" is a keyword |
355 getargs(x, 0, 0, _("portable takes no arguments")) |
353 getargs(x, 0, 0, _("portable takes no arguments")) |
356 checkwinfilename = util.checkwinfilename |
354 return mctx.predicate(lambda f: util.checkwinfilename(f) is None, |
357 return [f for f in mctx.subset if checkwinfilename(f) is None] |
355 predrepr='portable') |
358 |
356 |
359 @predicate('grep(regex)', callexisting=True) |
357 @predicate('grep(regex)', callexisting=True) |
360 def grep(mctx, x): |
358 def grep(mctx, x): |
361 """File contains the given regular expression. |
359 """File contains the given regular expression. |
362 """ |
360 """ |
524 reverr = _("second argument to status must be a revision") |
519 reverr = _("second argument to status must be a revision") |
525 revspec = getstring(r, reverr) |
520 revspec = getstring(r, reverr) |
526 if not revspec: |
521 if not revspec: |
527 raise error.ParseError(reverr) |
522 raise error.ParseError(reverr) |
528 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec]) |
523 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec]) |
529 return getset(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x) |
524 return getmatch(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x) |
530 |
525 |
531 @predicate('subrepo([pattern])') |
526 @predicate('subrepo([pattern])') |
532 def subrepo(mctx, x): |
527 def subrepo(mctx, x): |
533 """Subrepositories whose paths match the given pattern. |
528 """Subrepositories whose paths match the given pattern. |
534 """ |
529 """ |
535 # i18n: "subrepo" is a keyword |
530 # i18n: "subrepo" is a keyword |
536 getargs(x, 0, 1, _("subrepo takes at most one argument")) |
531 getargs(x, 0, 1, _("subrepo takes at most one argument")) |
537 ctx = mctx.ctx |
532 ctx = mctx.ctx |
538 sstate = sorted(ctx.substate) |
533 sstate = ctx.substate |
539 if x: |
534 if x: |
540 pat = getpattern(x, matchmod.allpatternkinds, |
535 pat = getpattern(x, matchmod.allpatternkinds, |
541 # i18n: "subrepo" is a keyword |
536 # i18n: "subrepo" is a keyword |
542 _("subrepo requires a pattern or no arguments")) |
537 _("subrepo requires a pattern or no arguments")) |
543 fast = not matchmod.patkind(pat) |
538 fast = not matchmod.patkind(pat) |
544 if fast: |
539 if fast: |
545 def m(s): |
540 def m(s): |
546 return (s == pat) |
541 return (s == pat) |
547 else: |
542 else: |
548 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx) |
543 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx) |
549 return [sub for sub in sstate if m(sub)] |
544 return mctx.predicate(lambda f: f in sstate and m(f), |
|
545 predrepr=('subrepo(%r)', pat)) |
550 else: |
546 else: |
551 return [sub for sub in sstate] |
547 return mctx.predicate(sstate.__contains__, predrepr='subrepo') |
552 |
548 |
553 methods = { |
549 methods = { |
554 'string': stringset, |
550 'string': stringmatch, |
555 'symbol': stringset, |
551 'symbol': stringmatch, |
556 'kindpat': kindpatset, |
552 'kindpat': kindpatmatch, |
557 'and': andset, |
553 'and': andmatch, |
558 'or': orset, |
554 'or': ormatch, |
559 'minus': minusset, |
555 'minus': minusmatch, |
560 'negate': negateset, |
556 'negate': negatematch, |
561 'list': listset, |
557 'list': listmatch, |
562 'group': getset, |
558 'group': getmatch, |
563 'not': notset, |
559 'not': notmatch, |
564 'func': func, |
560 'func': func, |
565 } |
561 } |
566 |
562 |
567 class matchctx(object): |
563 class matchctx(object): |
568 def __init__(self, ctx, subset, status=None, badfn=None): |
564 def __init__(self, ctx, subset, status=None, badfn=None): |