mercurial/fileset.py
changeset 43076 2372284d9457
parent 41676 0531dff73d0b
child 43077 687b865b95ad
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
    19     pycompat,
    19     pycompat,
    20     registrar,
    20     registrar,
    21     scmutil,
    21     scmutil,
    22     util,
    22     util,
    23 )
    23 )
    24 from .utils import (
    24 from .utils import stringutil
    25     stringutil,
       
    26 )
       
    27 
    25 
    28 # common weight constants
    26 # common weight constants
    29 _WEIGHT_CHECK_FILENAME = filesetlang.WEIGHT_CHECK_FILENAME
    27 _WEIGHT_CHECK_FILENAME = filesetlang.WEIGHT_CHECK_FILENAME
    30 _WEIGHT_READ_CONTENTS = filesetlang.WEIGHT_READ_CONTENTS
    28 _WEIGHT_READ_CONTENTS = filesetlang.WEIGHT_READ_CONTENTS
    31 _WEIGHT_STATUS = filesetlang.WEIGHT_STATUS
    29 _WEIGHT_STATUS = filesetlang.WEIGHT_STATUS
    36 getstring = filesetlang.getstring
    34 getstring = filesetlang.getstring
    37 _getkindpat = filesetlang.getkindpat
    35 _getkindpat = filesetlang.getkindpat
    38 getpattern = filesetlang.getpattern
    36 getpattern = filesetlang.getpattern
    39 getargs = filesetlang.getargs
    37 getargs = filesetlang.getargs
    40 
    38 
       
    39 
    41 def getmatch(mctx, x):
    40 def getmatch(mctx, x):
    42     if not x:
    41     if not x:
    43         raise error.ParseError(_("missing argument"))
    42         raise error.ParseError(_("missing argument"))
    44     return methods[x[0]](mctx, *x[1:])
    43     return methods[x[0]](mctx, *x[1:])
    45 
    44 
       
    45 
    46 def getmatchwithstatus(mctx, x, hint):
    46 def getmatchwithstatus(mctx, x, hint):
    47     keys = set(getstring(hint, 'status hint must be a string').split())
    47     keys = set(getstring(hint, 'status hint must be a string').split())
    48     return getmatch(mctx.withstatus(keys), x)
    48     return getmatch(mctx.withstatus(keys), x)
    49 
    49 
       
    50 
    50 def stringmatch(mctx, x):
    51 def stringmatch(mctx, x):
    51     return mctx.matcher([x])
    52     return mctx.matcher([x])
    52 
    53 
       
    54 
    53 def kindpatmatch(mctx, x, y):
    55 def kindpatmatch(mctx, x, y):
    54     return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
    56     return stringmatch(
    55                                          _("pattern must be a string")))
    57         mctx,
       
    58         _getkindpat(
       
    59             x, y, matchmod.allpatternkinds, _("pattern must be a string")
       
    60         ),
       
    61     )
       
    62 
    56 
    63 
    57 def patternsmatch(mctx, *xs):
    64 def patternsmatch(mctx, *xs):
    58     allkinds = matchmod.allpatternkinds
    65     allkinds = matchmod.allpatternkinds
    59     patterns = [getpattern(x, allkinds, _("pattern must be a string"))
    66     patterns = [
    60                 for x in xs]
    67         getpattern(x, allkinds, _("pattern must be a string")) for x in xs
       
    68     ]
    61     return mctx.matcher(patterns)
    69     return mctx.matcher(patterns)
       
    70 
    62 
    71 
    63 def andmatch(mctx, x, y):
    72 def andmatch(mctx, x, y):
    64     xm = getmatch(mctx, x)
    73     xm = getmatch(mctx, x)
    65     ym = getmatch(mctx.narrowed(xm), y)
    74     ym = getmatch(mctx.narrowed(xm), y)
    66     return matchmod.intersectmatchers(xm, ym)
    75     return matchmod.intersectmatchers(xm, ym)
    67 
    76 
       
    77 
    68 def ormatch(mctx, *xs):
    78 def ormatch(mctx, *xs):
    69     ms = [getmatch(mctx, x) for x in xs]
    79     ms = [getmatch(mctx, x) for x in xs]
    70     return matchmod.unionmatcher(ms)
    80     return matchmod.unionmatcher(ms)
    71 
    81 
       
    82 
    72 def notmatch(mctx, x):
    83 def notmatch(mctx, x):
    73     m = getmatch(mctx, x)
    84     m = getmatch(mctx, x)
    74     return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m))
    85     return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m))
       
    86 
    75 
    87 
    76 def minusmatch(mctx, x, y):
    88 def minusmatch(mctx, x, y):
    77     xm = getmatch(mctx, x)
    89     xm = getmatch(mctx, x)
    78     ym = getmatch(mctx.narrowed(xm), y)
    90     ym = getmatch(mctx.narrowed(xm), y)
    79     return matchmod.differencematcher(xm, ym)
    91     return matchmod.differencematcher(xm, ym)
    80 
    92 
       
    93 
    81 def listmatch(mctx, *xs):
    94 def listmatch(mctx, *xs):
    82     raise error.ParseError(_("can't use a list in this context"),
    95     raise error.ParseError(
    83                            hint=_('see \'hg help "filesets.x or y"\''))
    96         _("can't use a list in this context"),
       
    97         hint=_('see \'hg help "filesets.x or y"\''),
       
    98     )
       
    99 
    84 
   100 
    85 def func(mctx, a, b):
   101 def func(mctx, a, b):
    86     funcname = getsymbol(a)
   102     funcname = getsymbol(a)
    87     if funcname in symbols:
   103     if funcname in symbols:
    88         return symbols[funcname](mctx, b)
   104         return symbols[funcname](mctx, b)
    89 
   105 
    90     keep = lambda fn: getattr(fn, '__doc__', None) is not None
   106     keep = lambda fn: getattr(fn, '__doc__', None) is not None
    91 
   107 
    92     syms = [s for (s, fn) in symbols.items() if keep(fn)]
   108     syms = [s for (s, fn) in symbols.items() if keep(fn)]
    93     raise error.UnknownIdentifier(funcname, syms)
   109     raise error.UnknownIdentifier(funcname, syms)
       
   110 
    94 
   111 
    95 # symbols are callable like:
   112 # symbols are callable like:
    96 #  fun(mctx, x)
   113 #  fun(mctx, x)
    97 # with:
   114 # with:
    98 #  mctx - current matchctx instance
   115 #  mctx - current matchctx instance
    99 #  x - argument in tree form
   116 #  x - argument in tree form
   100 symbols = filesetlang.symbols
   117 symbols = filesetlang.symbols
   101 
   118 
   102 predicate = registrar.filesetpredicate(symbols)
   119 predicate = registrar.filesetpredicate(symbols)
   103 
   120 
       
   121 
   104 @predicate('modified()', callstatus=True, weight=_WEIGHT_STATUS)
   122 @predicate('modified()', callstatus=True, weight=_WEIGHT_STATUS)
   105 def modified(mctx, x):
   123 def modified(mctx, x):
   106     """File that is modified according to :hg:`status`.
   124     """File that is modified according to :hg:`status`.
   107     """
   125     """
   108     # i18n: "modified" is a keyword
   126     # i18n: "modified" is a keyword
   109     getargs(x, 0, 0, _("modified takes no arguments"))
   127     getargs(x, 0, 0, _("modified takes no arguments"))
   110     s = set(mctx.status().modified)
   128     s = set(mctx.status().modified)
   111     return mctx.predicate(s.__contains__, predrepr='modified')
   129     return mctx.predicate(s.__contains__, predrepr='modified')
   112 
   130 
       
   131 
   113 @predicate('added()', callstatus=True, weight=_WEIGHT_STATUS)
   132 @predicate('added()', callstatus=True, weight=_WEIGHT_STATUS)
   114 def added(mctx, x):
   133 def added(mctx, x):
   115     """File that is added according to :hg:`status`.
   134     """File that is added according to :hg:`status`.
   116     """
   135     """
   117     # i18n: "added" is a keyword
   136     # i18n: "added" is a keyword
   118     getargs(x, 0, 0, _("added takes no arguments"))
   137     getargs(x, 0, 0, _("added takes no arguments"))
   119     s = set(mctx.status().added)
   138     s = set(mctx.status().added)
   120     return mctx.predicate(s.__contains__, predrepr='added')
   139     return mctx.predicate(s.__contains__, predrepr='added')
   121 
   140 
       
   141 
   122 @predicate('removed()', callstatus=True, weight=_WEIGHT_STATUS)
   142 @predicate('removed()', callstatus=True, weight=_WEIGHT_STATUS)
   123 def removed(mctx, x):
   143 def removed(mctx, x):
   124     """File that is removed according to :hg:`status`.
   144     """File that is removed according to :hg:`status`.
   125     """
   145     """
   126     # i18n: "removed" is a keyword
   146     # i18n: "removed" is a keyword
   127     getargs(x, 0, 0, _("removed takes no arguments"))
   147     getargs(x, 0, 0, _("removed takes no arguments"))
   128     s = set(mctx.status().removed)
   148     s = set(mctx.status().removed)
   129     return mctx.predicate(s.__contains__, predrepr='removed')
   149     return mctx.predicate(s.__contains__, predrepr='removed')
   130 
   150 
       
   151 
   131 @predicate('deleted()', callstatus=True, weight=_WEIGHT_STATUS)
   152 @predicate('deleted()', callstatus=True, weight=_WEIGHT_STATUS)
   132 def deleted(mctx, x):
   153 def deleted(mctx, x):
   133     """Alias for ``missing()``.
   154     """Alias for ``missing()``.
   134     """
   155     """
   135     # i18n: "deleted" is a keyword
   156     # i18n: "deleted" is a keyword
   136     getargs(x, 0, 0, _("deleted takes no arguments"))
   157     getargs(x, 0, 0, _("deleted takes no arguments"))
   137     s = set(mctx.status().deleted)
   158     s = set(mctx.status().deleted)
   138     return mctx.predicate(s.__contains__, predrepr='deleted')
   159     return mctx.predicate(s.__contains__, predrepr='deleted')
   139 
   160 
       
   161 
   140 @predicate('missing()', callstatus=True, weight=_WEIGHT_STATUS)
   162 @predicate('missing()', callstatus=True, weight=_WEIGHT_STATUS)
   141 def missing(mctx, x):
   163 def missing(mctx, x):
   142     """File that is missing according to :hg:`status`.
   164     """File that is missing according to :hg:`status`.
   143     """
   165     """
   144     # i18n: "missing" is a keyword
   166     # i18n: "missing" is a keyword
   145     getargs(x, 0, 0, _("missing takes no arguments"))
   167     getargs(x, 0, 0, _("missing takes no arguments"))
   146     s = set(mctx.status().deleted)
   168     s = set(mctx.status().deleted)
   147     return mctx.predicate(s.__contains__, predrepr='deleted')
   169     return mctx.predicate(s.__contains__, predrepr='deleted')
       
   170 
   148 
   171 
   149 @predicate('unknown()', callstatus=True, weight=_WEIGHT_STATUS_THOROUGH)
   172 @predicate('unknown()', callstatus=True, weight=_WEIGHT_STATUS_THOROUGH)
   150 def unknown(mctx, x):
   173 def unknown(mctx, x):
   151     """File that is unknown according to :hg:`status`."""
   174     """File that is unknown according to :hg:`status`."""
   152     # i18n: "unknown" is a keyword
   175     # i18n: "unknown" is a keyword
   153     getargs(x, 0, 0, _("unknown takes no arguments"))
   176     getargs(x, 0, 0, _("unknown takes no arguments"))
   154     s = set(mctx.status().unknown)
   177     s = set(mctx.status().unknown)
   155     return mctx.predicate(s.__contains__, predrepr='unknown')
   178     return mctx.predicate(s.__contains__, predrepr='unknown')
   156 
   179 
       
   180 
   157 @predicate('ignored()', callstatus=True, weight=_WEIGHT_STATUS_THOROUGH)
   181 @predicate('ignored()', callstatus=True, weight=_WEIGHT_STATUS_THOROUGH)
   158 def ignored(mctx, x):
   182 def ignored(mctx, x):
   159     """File that is ignored according to :hg:`status`."""
   183     """File that is ignored according to :hg:`status`."""
   160     # i18n: "ignored" is a keyword
   184     # i18n: "ignored" is a keyword
   161     getargs(x, 0, 0, _("ignored takes no arguments"))
   185     getargs(x, 0, 0, _("ignored takes no arguments"))
   162     s = set(mctx.status().ignored)
   186     s = set(mctx.status().ignored)
   163     return mctx.predicate(s.__contains__, predrepr='ignored')
   187     return mctx.predicate(s.__contains__, predrepr='ignored')
   164 
   188 
       
   189 
   165 @predicate('clean()', callstatus=True, weight=_WEIGHT_STATUS)
   190 @predicate('clean()', callstatus=True, weight=_WEIGHT_STATUS)
   166 def clean(mctx, x):
   191 def clean(mctx, x):
   167     """File that is clean according to :hg:`status`.
   192     """File that is clean according to :hg:`status`.
   168     """
   193     """
   169     # i18n: "clean" is a keyword
   194     # i18n: "clean" is a keyword
   170     getargs(x, 0, 0, _("clean takes no arguments"))
   195     getargs(x, 0, 0, _("clean takes no arguments"))
   171     s = set(mctx.status().clean)
   196     s = set(mctx.status().clean)
   172     return mctx.predicate(s.__contains__, predrepr='clean')
   197     return mctx.predicate(s.__contains__, predrepr='clean')
       
   198 
   173 
   199 
   174 @predicate('tracked()')
   200 @predicate('tracked()')
   175 def tracked(mctx, x):
   201 def tracked(mctx, x):
   176     """File that is under Mercurial control."""
   202     """File that is under Mercurial control."""
   177     # i18n: "tracked" is a keyword
   203     # i18n: "tracked" is a keyword
   178     getargs(x, 0, 0, _("tracked takes no arguments"))
   204     getargs(x, 0, 0, _("tracked takes no arguments"))
   179     return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked')
   205     return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked')
   180 
   206 
       
   207 
   181 @predicate('binary()', weight=_WEIGHT_READ_CONTENTS)
   208 @predicate('binary()', weight=_WEIGHT_READ_CONTENTS)
   182 def binary(mctx, x):
   209 def binary(mctx, x):
   183     """File that appears to be binary (contains NUL bytes).
   210     """File that appears to be binary (contains NUL bytes).
   184     """
   211     """
   185     # i18n: "binary" is a keyword
   212     # i18n: "binary" is a keyword
   186     getargs(x, 0, 0, _("binary takes no arguments"))
   213     getargs(x, 0, 0, _("binary takes no arguments"))
   187     return mctx.fpredicate(lambda fctx: fctx.isbinary(),
   214     return mctx.fpredicate(
   188                            predrepr='binary', cache=True)
   215         lambda fctx: fctx.isbinary(), predrepr='binary', cache=True
       
   216     )
       
   217 
   189 
   218 
   190 @predicate('exec()')
   219 @predicate('exec()')
   191 def exec_(mctx, x):
   220 def exec_(mctx, x):
   192     """File that is marked as executable.
   221     """File that is marked as executable.
   193     """
   222     """
   194     # i18n: "exec" is a keyword
   223     # i18n: "exec" is a keyword
   195     getargs(x, 0, 0, _("exec takes no arguments"))
   224     getargs(x, 0, 0, _("exec takes no arguments"))
   196     ctx = mctx.ctx
   225     ctx = mctx.ctx
   197     return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec')
   226     return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec')
   198 
   227 
       
   228 
   199 @predicate('symlink()')
   229 @predicate('symlink()')
   200 def symlink(mctx, x):
   230 def symlink(mctx, x):
   201     """File that is marked as a symlink.
   231     """File that is marked as a symlink.
   202     """
   232     """
   203     # i18n: "symlink" is a keyword
   233     # i18n: "symlink" is a keyword
   204     getargs(x, 0, 0, _("symlink takes no arguments"))
   234     getargs(x, 0, 0, _("symlink takes no arguments"))
   205     ctx = mctx.ctx
   235     ctx = mctx.ctx
   206     return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink')
   236     return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink')
       
   237 
   207 
   238 
   208 @predicate('resolved()', weight=_WEIGHT_STATUS)
   239 @predicate('resolved()', weight=_WEIGHT_STATUS)
   209 def resolved(mctx, x):
   240 def resolved(mctx, x):
   210     """File that is marked resolved according to :hg:`resolve -l`.
   241     """File that is marked resolved according to :hg:`resolve -l`.
   211     """
   242     """
   212     # i18n: "resolved" is a keyword
   243     # i18n: "resolved" is a keyword
   213     getargs(x, 0, 0, _("resolved takes no arguments"))
   244     getargs(x, 0, 0, _("resolved takes no arguments"))
   214     if mctx.ctx.rev() is not None:
   245     if mctx.ctx.rev() is not None:
   215         return mctx.never()
   246         return mctx.never()
   216     ms = merge.mergestate.read(mctx.ctx.repo())
   247     ms = merge.mergestate.read(mctx.ctx.repo())
   217     return mctx.predicate(lambda f: f in ms and ms[f] == 'r',
   248     return mctx.predicate(
   218                           predrepr='resolved')
   249         lambda f: f in ms and ms[f] == 'r', predrepr='resolved'
       
   250     )
       
   251 
   219 
   252 
   220 @predicate('unresolved()', weight=_WEIGHT_STATUS)
   253 @predicate('unresolved()', weight=_WEIGHT_STATUS)
   221 def unresolved(mctx, x):
   254 def unresolved(mctx, x):
   222     """File that is marked unresolved according to :hg:`resolve -l`.
   255     """File that is marked unresolved according to :hg:`resolve -l`.
   223     """
   256     """
   224     # i18n: "unresolved" is a keyword
   257     # i18n: "unresolved" is a keyword
   225     getargs(x, 0, 0, _("unresolved takes no arguments"))
   258     getargs(x, 0, 0, _("unresolved takes no arguments"))
   226     if mctx.ctx.rev() is not None:
   259     if mctx.ctx.rev() is not None:
   227         return mctx.never()
   260         return mctx.never()
   228     ms = merge.mergestate.read(mctx.ctx.repo())
   261     ms = merge.mergestate.read(mctx.ctx.repo())
   229     return mctx.predicate(lambda f: f in ms and ms[f] == 'u',
   262     return mctx.predicate(
   230                           predrepr='unresolved')
   263         lambda f: f in ms and ms[f] == 'u', predrepr='unresolved'
       
   264     )
       
   265 
   231 
   266 
   232 @predicate('hgignore()', weight=_WEIGHT_STATUS)
   267 @predicate('hgignore()', weight=_WEIGHT_STATUS)
   233 def hgignore(mctx, x):
   268 def hgignore(mctx, x):
   234     """File that matches the active .hgignore pattern.
   269     """File that matches the active .hgignore pattern.
   235     """
   270     """
   236     # i18n: "hgignore" is a keyword
   271     # i18n: "hgignore" is a keyword
   237     getargs(x, 0, 0, _("hgignore takes no arguments"))
   272     getargs(x, 0, 0, _("hgignore takes no arguments"))
   238     return mctx.ctx.repo().dirstate._ignore
   273     return mctx.ctx.repo().dirstate._ignore
       
   274 
   239 
   275 
   240 @predicate('portable()', weight=_WEIGHT_CHECK_FILENAME)
   276 @predicate('portable()', weight=_WEIGHT_CHECK_FILENAME)
   241 def portable(mctx, x):
   277 def portable(mctx, x):
   242     """File that has a portable name. (This doesn't include filenames with case
   278     """File that has a portable name. (This doesn't include filenames with case
   243     collisions.)
   279     collisions.)
   244     """
   280     """
   245     # i18n: "portable" is a keyword
   281     # i18n: "portable" is a keyword
   246     getargs(x, 0, 0, _("portable takes no arguments"))
   282     getargs(x, 0, 0, _("portable takes no arguments"))
   247     return mctx.predicate(lambda f: util.checkwinfilename(f) is None,
   283     return mctx.predicate(
   248                           predrepr='portable')
   284         lambda f: util.checkwinfilename(f) is None, predrepr='portable'
       
   285     )
       
   286 
   249 
   287 
   250 @predicate('grep(regex)', weight=_WEIGHT_READ_CONTENTS)
   288 @predicate('grep(regex)', weight=_WEIGHT_READ_CONTENTS)
   251 def grep(mctx, x):
   289 def grep(mctx, x):
   252     """File contains the given regular expression.
   290     """File contains the given regular expression.
   253     """
   291     """
   254     try:
   292     try:
   255         # i18n: "grep" is a keyword
   293         # i18n: "grep" is a keyword
   256         r = re.compile(getstring(x, _("grep requires a pattern")))
   294         r = re.compile(getstring(x, _("grep requires a pattern")))
   257     except re.error as e:
   295     except re.error as e:
   258         raise error.ParseError(_('invalid match pattern: %s') %
   296         raise error.ParseError(
   259                                stringutil.forcebytestr(e))
   297             _('invalid match pattern: %s') % stringutil.forcebytestr(e)
   260     return mctx.fpredicate(lambda fctx: r.search(fctx.data()),
   298         )
   261                            predrepr=('grep(%r)', r.pattern), cache=True)
   299     return mctx.fpredicate(
       
   300         lambda fctx: r.search(fctx.data()),
       
   301         predrepr=('grep(%r)', r.pattern),
       
   302         cache=True,
       
   303     )
       
   304 
   262 
   305 
   263 def _sizetomax(s):
   306 def _sizetomax(s):
   264     try:
   307     try:
   265         s = s.strip().lower()
   308         s = s.strip().lower()
   266         for k, v in util._sizeunits:
   309         for k, v in util._sizeunits:
   267             if s.endswith(k):
   310             if s.endswith(k):
   268                 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
   311                 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
   269                 n = s[:-len(k)]
   312                 n = s[: -len(k)]
   270                 inc = 1.0
   313                 inc = 1.0
   271                 if "." in n:
   314                 if "." in n:
   272                     inc /= 10 ** len(n.split(".")[1])
   315                     inc /= 10 ** len(n.split(".")[1])
   273                 return int((float(n) + inc) * v) - 1
   316                 return int((float(n) + inc) * v) - 1
   274         # no extension, this is a precise value
   317         # no extension, this is a precise value
   275         return int(s)
   318         return int(s)
   276     except ValueError:
   319     except ValueError:
   277         raise error.ParseError(_("couldn't parse size: %s") % s)
   320         raise error.ParseError(_("couldn't parse size: %s") % s)
   278 
   321 
       
   322 
   279 def sizematcher(expr):
   323 def sizematcher(expr):
   280     """Return a function(size) -> bool from the ``size()`` expression"""
   324     """Return a function(size) -> bool from the ``size()`` expression"""
   281     expr = expr.strip()
   325     expr = expr.strip()
   282     if '-' in expr: # do we have a range?
   326     if '-' in expr:  # do we have a range?
   283         a, b = expr.split('-', 1)
   327         a, b = expr.split('-', 1)
   284         a = util.sizetoint(a)
   328         a = util.sizetoint(a)
   285         b = util.sizetoint(b)
   329         b = util.sizetoint(b)
   286         return lambda x: x >= a and x <= b
   330         return lambda x: x >= a and x <= b
   287     elif expr.startswith("<="):
   331     elif expr.startswith("<="):
   299     else:
   343     else:
   300         a = util.sizetoint(expr)
   344         a = util.sizetoint(expr)
   301         b = _sizetomax(expr)
   345         b = _sizetomax(expr)
   302         return lambda x: x >= a and x <= b
   346         return lambda x: x >= a and x <= b
   303 
   347 
       
   348 
   304 @predicate('size(expression)', weight=_WEIGHT_STATUS)
   349 @predicate('size(expression)', weight=_WEIGHT_STATUS)
   305 def size(mctx, x):
   350 def size(mctx, x):
   306     """File size matches the given expression. Examples:
   351     """File size matches the given expression. Examples:
   307 
   352 
   308     - size('1k') - files from 1024 to 2047 bytes
   353     - size('1k') - files from 1024 to 2047 bytes
   311     - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
   356     - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
   312     """
   357     """
   313     # i18n: "size" is a keyword
   358     # i18n: "size" is a keyword
   314     expr = getstring(x, _("size requires an expression"))
   359     expr = getstring(x, _("size requires an expression"))
   315     m = sizematcher(expr)
   360     m = sizematcher(expr)
   316     return mctx.fpredicate(lambda fctx: m(fctx.size()),
   361     return mctx.fpredicate(
   317                            predrepr=('size(%r)', expr), cache=True)
   362         lambda fctx: m(fctx.size()), predrepr=('size(%r)', expr), cache=True
       
   363     )
       
   364 
   318 
   365 
   319 @predicate('encoding(name)', weight=_WEIGHT_READ_CONTENTS)
   366 @predicate('encoding(name)', weight=_WEIGHT_READ_CONTENTS)
   320 def encoding(mctx, x):
   367 def encoding(mctx, x):
   321     """File can be successfully decoded with the given character
   368     """File can be successfully decoded with the given character
   322     encoding. May not be useful for encodings other than ASCII and
   369     encoding. May not be useful for encodings other than ASCII and
   335             raise error.Abort(_("unknown encoding '%s'") % enc)
   382             raise error.Abort(_("unknown encoding '%s'") % enc)
   336         except UnicodeDecodeError:
   383         except UnicodeDecodeError:
   337             return False
   384             return False
   338 
   385 
   339     return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True)
   386     return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True)
       
   387 
   340 
   388 
   341 @predicate('eol(style)', weight=_WEIGHT_READ_CONTENTS)
   389 @predicate('eol(style)', weight=_WEIGHT_READ_CONTENTS)
   342 def eol(mctx, x):
   390 def eol(mctx, x):
   343     """File contains newlines of the given style (dos, unix, mac). Binary
   391     """File contains newlines of the given style (dos, unix, mac). Binary
   344     files are excluded, files with mixed line endings match multiple
   392     files are excluded, files with mixed line endings match multiple
   357         elif enc == 'unix' and re.search('(?<!\r)\n', d):
   405         elif enc == 'unix' and re.search('(?<!\r)\n', d):
   358             return True
   406             return True
   359         elif enc == 'mac' and re.search('\r(?!\n)', d):
   407         elif enc == 'mac' and re.search('\r(?!\n)', d):
   360             return True
   408             return True
   361         return False
   409         return False
       
   410 
   362     return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True)
   411     return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True)
       
   412 
   363 
   413 
   364 @predicate('copied()')
   414 @predicate('copied()')
   365 def copied(mctx, x):
   415 def copied(mctx, x):
   366     """File that is recorded as being copied.
   416     """File that is recorded as being copied.
   367     """
   417     """
   368     # i18n: "copied" is a keyword
   418     # i18n: "copied" is a keyword
   369     getargs(x, 0, 0, _("copied takes no arguments"))
   419     getargs(x, 0, 0, _("copied takes no arguments"))
       
   420 
   370     def copiedp(fctx):
   421     def copiedp(fctx):
   371         p = fctx.parents()
   422         p = fctx.parents()
   372         return p and p[0].path() != fctx.path()
   423         return p and p[0].path() != fctx.path()
       
   424 
   373     return mctx.fpredicate(copiedp, predrepr='copied', cache=True)
   425     return mctx.fpredicate(copiedp, predrepr='copied', cache=True)
       
   426 
   374 
   427 
   375 @predicate('revs(revs, pattern)', weight=_WEIGHT_STATUS)
   428 @predicate('revs(revs, pattern)', weight=_WEIGHT_STATUS)
   376 def revs(mctx, x):
   429 def revs(mctx, x):
   377     """Evaluate set in the specified revisions. If the revset match multiple
   430     """Evaluate set in the specified revisions. If the revset match multiple
   378     revs, this will return file matching pattern in any of the revision.
   431     revs, this will return file matching pattern in any of the revision.
   392     if not matchers:
   445     if not matchers:
   393         return mctx.never()
   446         return mctx.never()
   394     if len(matchers) == 1:
   447     if len(matchers) == 1:
   395         return matchers[0]
   448         return matchers[0]
   396     return matchmod.unionmatcher(matchers)
   449     return matchmod.unionmatcher(matchers)
       
   450 
   397 
   451 
   398 @predicate('status(base, rev, pattern)', weight=_WEIGHT_STATUS)
   452 @predicate('status(base, rev, pattern)', weight=_WEIGHT_STATUS)
   399 def status(mctx, x):
   453 def status(mctx, x):
   400     """Evaluate predicate using status change between ``base`` and
   454     """Evaluate predicate using status change between ``base`` and
   401     ``rev``. Examples:
   455     ``rev``. Examples:
   416         raise error.ParseError(reverr)
   470         raise error.ParseError(reverr)
   417     basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
   471     basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
   418     mc = mctx.switch(basectx, ctx)
   472     mc = mctx.switch(basectx, ctx)
   419     return getmatch(mc, x)
   473     return getmatch(mc, x)
   420 
   474 
       
   475 
   421 @predicate('subrepo([pattern])')
   476 @predicate('subrepo([pattern])')
   422 def subrepo(mctx, x):
   477 def subrepo(mctx, x):
   423     """Subrepositories whose paths match the given pattern.
   478     """Subrepositories whose paths match the given pattern.
   424     """
   479     """
   425     # i18n: "subrepo" is a keyword
   480     # i18n: "subrepo" is a keyword
   426     getargs(x, 0, 1, _("subrepo takes at most one argument"))
   481     getargs(x, 0, 1, _("subrepo takes at most one argument"))
   427     ctx = mctx.ctx
   482     ctx = mctx.ctx
   428     sstate = ctx.substate
   483     sstate = ctx.substate
   429     if x:
   484     if x:
   430         pat = getpattern(x, matchmod.allpatternkinds,
   485         pat = getpattern(
   431                          # i18n: "subrepo" is a keyword
   486             x,
   432                          _("subrepo requires a pattern or no arguments"))
   487             matchmod.allpatternkinds,
       
   488             # i18n: "subrepo" is a keyword
       
   489             _("subrepo requires a pattern or no arguments"),
       
   490         )
   433         fast = not matchmod.patkind(pat)
   491         fast = not matchmod.patkind(pat)
   434         if fast:
   492         if fast:
       
   493 
   435             def m(s):
   494             def m(s):
   436                 return (s == pat)
   495                 return s == pat
       
   496 
   437         else:
   497         else:
   438             m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
   498             m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
   439         return mctx.predicate(lambda f: f in sstate and m(f),
   499         return mctx.predicate(
   440                               predrepr=('subrepo(%r)', pat))
   500             lambda f: f in sstate and m(f), predrepr=('subrepo(%r)', pat)
       
   501         )
   441     else:
   502     else:
   442         return mctx.predicate(sstate.__contains__, predrepr='subrepo')
   503         return mctx.predicate(sstate.__contains__, predrepr='subrepo')
       
   504 
   443 
   505 
   444 methods = {
   506 methods = {
   445     'withstatus': getmatchwithstatus,
   507     'withstatus': getmatchwithstatus,
   446     'string': stringmatch,
   508     'string': stringmatch,
   447     'symbol': stringmatch,
   509     'symbol': stringmatch,
   453     'list': listmatch,
   515     'list': listmatch,
   454     'not': notmatch,
   516     'not': notmatch,
   455     'func': func,
   517     'func': func,
   456 }
   518 }
   457 
   519 
       
   520 
   458 class matchctx(object):
   521 class matchctx(object):
   459     def __init__(self, basectx, ctx, badfn=None):
   522     def __init__(self, basectx, ctx, badfn=None):
   460         self._basectx = basectx
   523         self._basectx = basectx
   461         self.ctx = ctx
   524         self.ctx = ctx
   462         self._badfn = badfn
   525         self._badfn = badfn
   482         mctx._match = self._match
   545         mctx._match = self._match
   483         mctx._buildstatus(keys)
   546         mctx._buildstatus(keys)
   484         return mctx
   547         return mctx
   485 
   548 
   486     def _buildstatus(self, keys):
   549     def _buildstatus(self, keys):
   487         self._status = self._basectx.status(self.ctx, self._match,
   550         self._status = self._basectx.status(
   488                                             listignored='ignored' in keys,
   551             self.ctx,
   489                                             listclean='clean' in keys,
   552             self._match,
   490                                             listunknown='unknown' in keys)
   553             listignored='ignored' in keys,
       
   554             listclean='clean' in keys,
       
   555             listunknown='unknown' in keys,
       
   556         )
   491 
   557 
   492     def status(self):
   558     def status(self):
   493         return self._status
   559         return self._status
   494 
   560 
   495     def matcher(self, patterns):
   561     def matcher(self, patterns):
   497 
   563 
   498     def predicate(self, predfn, predrepr=None, cache=False):
   564     def predicate(self, predfn, predrepr=None, cache=False):
   499         """Create a matcher to select files by predfn(filename)"""
   565         """Create a matcher to select files by predfn(filename)"""
   500         if cache:
   566         if cache:
   501             predfn = util.cachefunc(predfn)
   567             predfn = util.cachefunc(predfn)
   502         return matchmod.predicatematcher(predfn, predrepr=predrepr,
   568         return matchmod.predicatematcher(
   503                                          badfn=self._badfn)
   569             predfn, predrepr=predrepr, badfn=self._badfn
       
   570         )
   504 
   571 
   505     def fpredicate(self, predfn, predrepr=None, cache=False):
   572     def fpredicate(self, predfn, predrepr=None, cache=False):
   506         """Create a matcher to select files by predfn(fctx) at the current
   573         """Create a matcher to select files by predfn(fctx) at the current
   507         revision
   574         revision
   508 
   575 
   509         Missing files are ignored.
   576         Missing files are ignored.
   510         """
   577         """
   511         ctx = self.ctx
   578         ctx = self.ctx
   512         if ctx.rev() is None:
   579         if ctx.rev() is None:
       
   580 
   513             def fctxpredfn(f):
   581             def fctxpredfn(f):
   514                 try:
   582                 try:
   515                     fctx = ctx[f]
   583                     fctx = ctx[f]
   516                 except error.LookupError:
   584                 except error.LookupError:
   517                     return False
   585                     return False
   521                     return False
   589                     return False
   522                 try:
   590                 try:
   523                     return predfn(fctx)
   591                     return predfn(fctx)
   524                 except (IOError, OSError) as e:
   592                 except (IOError, OSError) as e:
   525                     # open()-ing a directory fails with EACCES on Windows
   593                     # open()-ing a directory fails with EACCES on Windows
   526                     if e.errno in (errno.ENOENT, errno.EACCES, errno.ENOTDIR,
   594                     if e.errno in (
   527                                    errno.EISDIR):
   595                         errno.ENOENT,
       
   596                         errno.EACCES,
       
   597                         errno.ENOTDIR,
       
   598                         errno.EISDIR,
       
   599                     ):
   528                         return False
   600                         return False
   529                     raise
   601                     raise
       
   602 
   530         else:
   603         else:
       
   604 
   531             def fctxpredfn(f):
   605             def fctxpredfn(f):
   532                 try:
   606                 try:
   533                     fctx = ctx[f]
   607                     fctx = ctx[f]
   534                 except error.LookupError:
   608                 except error.LookupError:
   535                     return False
   609                     return False
   536                 return predfn(fctx)
   610                 return predfn(fctx)
       
   611 
   537         return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache)
   612         return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache)
   538 
   613 
   539     def never(self):
   614     def never(self):
   540         """Create a matcher to select nothing"""
   615         """Create a matcher to select nothing"""
   541         return matchmod.never(badfn=self._badfn)
   616         return matchmod.never(badfn=self._badfn)
       
   617 
   542 
   618 
   543 def match(ctx, expr, badfn=None):
   619 def match(ctx, expr, badfn=None):
   544     """Create a matcher for a single fileset expression"""
   620     """Create a matcher for a single fileset expression"""
   545     tree = filesetlang.parse(expr)
   621     tree = filesetlang.parse(expr)
   546     tree = filesetlang.analyze(tree)
   622     tree = filesetlang.analyze(tree)
   553     """Load fileset predicates from specified registrarobj
   629     """Load fileset predicates from specified registrarobj
   554     """
   630     """
   555     for name, func in registrarobj._table.iteritems():
   631     for name, func in registrarobj._table.iteritems():
   556         symbols[name] = func
   632         symbols[name] = func
   557 
   633 
       
   634 
   558 # tell hggettext to extract docstrings from these functions:
   635 # tell hggettext to extract docstrings from these functions:
   559 i18nfunctions = symbols.values()
   636 i18nfunctions = symbols.values()