##// END OF EJS Templates
fileset: roughly adjust weights of functions...
Yuya Nishihara -
r38866:bfd5def3 default
parent child Browse files
Show More
@@ -1,557 +1,557 b''
1 # fileset.py - file set queries for mercurial
1 # fileset.py - file set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import re
11 import re
12
12
13 from .i18n import _
13 from .i18n import _
14 from . import (
14 from . import (
15 error,
15 error,
16 filesetlang,
16 filesetlang,
17 match as matchmod,
17 match as matchmod,
18 merge,
18 merge,
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 (
25 stringutil,
25 stringutil,
26 )
26 )
27
27
28 # helpers for processing parsed tree
28 # helpers for processing parsed tree
29 getsymbol = filesetlang.getsymbol
29 getsymbol = filesetlang.getsymbol
30 getstring = filesetlang.getstring
30 getstring = filesetlang.getstring
31 _getkindpat = filesetlang.getkindpat
31 _getkindpat = filesetlang.getkindpat
32 getpattern = filesetlang.getpattern
32 getpattern = filesetlang.getpattern
33 getargs = filesetlang.getargs
33 getargs = filesetlang.getargs
34
34
35 def getmatch(mctx, x):
35 def getmatch(mctx, x):
36 if not x:
36 if not x:
37 raise error.ParseError(_("missing argument"))
37 raise error.ParseError(_("missing argument"))
38 return methods[x[0]](mctx, *x[1:])
38 return methods[x[0]](mctx, *x[1:])
39
39
40 def stringmatch(mctx, x):
40 def stringmatch(mctx, x):
41 return mctx.matcher([x])
41 return mctx.matcher([x])
42
42
43 def kindpatmatch(mctx, x, y):
43 def kindpatmatch(mctx, x, y):
44 return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
44 return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
45 _("pattern must be a string")))
45 _("pattern must be a string")))
46
46
47 def andmatch(mctx, x, y):
47 def andmatch(mctx, x, y):
48 xm = getmatch(mctx, x)
48 xm = getmatch(mctx, x)
49 ym = getmatch(mctx, y)
49 ym = getmatch(mctx, y)
50 return matchmod.intersectmatchers(xm, ym)
50 return matchmod.intersectmatchers(xm, ym)
51
51
52 def ormatch(mctx, *xs):
52 def ormatch(mctx, *xs):
53 ms = [getmatch(mctx, x) for x in xs]
53 ms = [getmatch(mctx, x) for x in xs]
54 return matchmod.unionmatcher(ms)
54 return matchmod.unionmatcher(ms)
55
55
56 def notmatch(mctx, x):
56 def notmatch(mctx, x):
57 m = getmatch(mctx, x)
57 m = getmatch(mctx, x)
58 return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m))
58 return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m))
59
59
60 def minusmatch(mctx, x, y):
60 def minusmatch(mctx, x, y):
61 xm = getmatch(mctx, x)
61 xm = getmatch(mctx, x)
62 ym = getmatch(mctx, y)
62 ym = getmatch(mctx, y)
63 return matchmod.differencematcher(xm, ym)
63 return matchmod.differencematcher(xm, ym)
64
64
65 def listmatch(mctx, *xs):
65 def listmatch(mctx, *xs):
66 raise error.ParseError(_("can't use a list in this context"),
66 raise error.ParseError(_("can't use a list in this context"),
67 hint=_('see \'hg help "filesets.x or y"\''))
67 hint=_('see \'hg help "filesets.x or y"\''))
68
68
69 def func(mctx, a, b):
69 def func(mctx, a, b):
70 funcname = getsymbol(a)
70 funcname = getsymbol(a)
71 if funcname in symbols:
71 if funcname in symbols:
72 return symbols[funcname](mctx, b)
72 return symbols[funcname](mctx, b)
73
73
74 keep = lambda fn: getattr(fn, '__doc__', None) is not None
74 keep = lambda fn: getattr(fn, '__doc__', None) is not None
75
75
76 syms = [s for (s, fn) in symbols.items() if keep(fn)]
76 syms = [s for (s, fn) in symbols.items() if keep(fn)]
77 raise error.UnknownIdentifier(funcname, syms)
77 raise error.UnknownIdentifier(funcname, syms)
78
78
79 # symbols are callable like:
79 # symbols are callable like:
80 # fun(mctx, x)
80 # fun(mctx, x)
81 # with:
81 # with:
82 # mctx - current matchctx instance
82 # mctx - current matchctx instance
83 # x - argument in tree form
83 # x - argument in tree form
84 symbols = filesetlang.symbols
84 symbols = filesetlang.symbols
85
85
86 # filesets using matchctx.status()
86 # filesets using matchctx.status()
87 _statuscallers = set()
87 _statuscallers = set()
88
88
89 predicate = registrar.filesetpredicate()
89 predicate = registrar.filesetpredicate()
90
90
91 @predicate('modified()', callstatus=True)
91 @predicate('modified()', callstatus=True, weight=10)
92 def modified(mctx, x):
92 def modified(mctx, x):
93 """File that is modified according to :hg:`status`.
93 """File that is modified according to :hg:`status`.
94 """
94 """
95 # i18n: "modified" is a keyword
95 # i18n: "modified" is a keyword
96 getargs(x, 0, 0, _("modified takes no arguments"))
96 getargs(x, 0, 0, _("modified takes no arguments"))
97 s = set(mctx.status().modified)
97 s = set(mctx.status().modified)
98 return mctx.predicate(s.__contains__, predrepr='modified')
98 return mctx.predicate(s.__contains__, predrepr='modified')
99
99
100 @predicate('added()', callstatus=True)
100 @predicate('added()', callstatus=True, weight=10)
101 def added(mctx, x):
101 def added(mctx, x):
102 """File that is added according to :hg:`status`.
102 """File that is added according to :hg:`status`.
103 """
103 """
104 # i18n: "added" is a keyword
104 # i18n: "added" is a keyword
105 getargs(x, 0, 0, _("added takes no arguments"))
105 getargs(x, 0, 0, _("added takes no arguments"))
106 s = set(mctx.status().added)
106 s = set(mctx.status().added)
107 return mctx.predicate(s.__contains__, predrepr='added')
107 return mctx.predicate(s.__contains__, predrepr='added')
108
108
109 @predicate('removed()', callstatus=True)
109 @predicate('removed()', callstatus=True, weight=10)
110 def removed(mctx, x):
110 def removed(mctx, x):
111 """File that is removed according to :hg:`status`.
111 """File that is removed according to :hg:`status`.
112 """
112 """
113 # i18n: "removed" is a keyword
113 # i18n: "removed" is a keyword
114 getargs(x, 0, 0, _("removed takes no arguments"))
114 getargs(x, 0, 0, _("removed takes no arguments"))
115 s = set(mctx.status().removed)
115 s = set(mctx.status().removed)
116 return mctx.predicate(s.__contains__, predrepr='removed')
116 return mctx.predicate(s.__contains__, predrepr='removed')
117
117
118 @predicate('deleted()', callstatus=True)
118 @predicate('deleted()', callstatus=True, weight=10)
119 def deleted(mctx, x):
119 def deleted(mctx, x):
120 """Alias for ``missing()``.
120 """Alias for ``missing()``.
121 """
121 """
122 # i18n: "deleted" is a keyword
122 # i18n: "deleted" is a keyword
123 getargs(x, 0, 0, _("deleted takes no arguments"))
123 getargs(x, 0, 0, _("deleted takes no arguments"))
124 s = set(mctx.status().deleted)
124 s = set(mctx.status().deleted)
125 return mctx.predicate(s.__contains__, predrepr='deleted')
125 return mctx.predicate(s.__contains__, predrepr='deleted')
126
126
127 @predicate('missing()', callstatus=True)
127 @predicate('missing()', callstatus=True, weight=10)
128 def missing(mctx, x):
128 def missing(mctx, x):
129 """File that is missing according to :hg:`status`.
129 """File that is missing according to :hg:`status`.
130 """
130 """
131 # i18n: "missing" is a keyword
131 # i18n: "missing" is a keyword
132 getargs(x, 0, 0, _("missing takes no arguments"))
132 getargs(x, 0, 0, _("missing takes no arguments"))
133 s = set(mctx.status().deleted)
133 s = set(mctx.status().deleted)
134 return mctx.predicate(s.__contains__, predrepr='deleted')
134 return mctx.predicate(s.__contains__, predrepr='deleted')
135
135
136 @predicate('unknown()', callstatus=True)
136 @predicate('unknown()', callstatus=True, weight=50)
137 def unknown(mctx, x):
137 def unknown(mctx, x):
138 """File that is unknown according to :hg:`status`."""
138 """File that is unknown according to :hg:`status`."""
139 # i18n: "unknown" is a keyword
139 # i18n: "unknown" is a keyword
140 getargs(x, 0, 0, _("unknown takes no arguments"))
140 getargs(x, 0, 0, _("unknown takes no arguments"))
141 s = set(mctx.status().unknown)
141 s = set(mctx.status().unknown)
142 return mctx.predicate(s.__contains__, predrepr='unknown')
142 return mctx.predicate(s.__contains__, predrepr='unknown')
143
143
144 @predicate('ignored()', callstatus=True)
144 @predicate('ignored()', callstatus=True, weight=50)
145 def ignored(mctx, x):
145 def ignored(mctx, x):
146 """File that is ignored according to :hg:`status`."""
146 """File that is ignored according to :hg:`status`."""
147 # i18n: "ignored" is a keyword
147 # i18n: "ignored" is a keyword
148 getargs(x, 0, 0, _("ignored takes no arguments"))
148 getargs(x, 0, 0, _("ignored takes no arguments"))
149 s = set(mctx.status().ignored)
149 s = set(mctx.status().ignored)
150 return mctx.predicate(s.__contains__, predrepr='ignored')
150 return mctx.predicate(s.__contains__, predrepr='ignored')
151
151
152 @predicate('clean()', callstatus=True)
152 @predicate('clean()', callstatus=True, weight=10)
153 def clean(mctx, x):
153 def clean(mctx, x):
154 """File that is clean according to :hg:`status`.
154 """File that is clean according to :hg:`status`.
155 """
155 """
156 # i18n: "clean" is a keyword
156 # i18n: "clean" is a keyword
157 getargs(x, 0, 0, _("clean takes no arguments"))
157 getargs(x, 0, 0, _("clean takes no arguments"))
158 s = set(mctx.status().clean)
158 s = set(mctx.status().clean)
159 return mctx.predicate(s.__contains__, predrepr='clean')
159 return mctx.predicate(s.__contains__, predrepr='clean')
160
160
161 @predicate('tracked()')
161 @predicate('tracked()')
162 def tracked(mctx, x):
162 def tracked(mctx, x):
163 """File that is under Mercurial control."""
163 """File that is under Mercurial control."""
164 # i18n: "tracked" is a keyword
164 # i18n: "tracked" is a keyword
165 getargs(x, 0, 0, _("tracked takes no arguments"))
165 getargs(x, 0, 0, _("tracked takes no arguments"))
166 return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked')
166 return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked')
167
167
168 @predicate('binary()')
168 @predicate('binary()', weight=30)
169 def binary(mctx, x):
169 def binary(mctx, x):
170 """File that appears to be binary (contains NUL bytes).
170 """File that appears to be binary (contains NUL bytes).
171 """
171 """
172 # i18n: "binary" is a keyword
172 # i18n: "binary" is a keyword
173 getargs(x, 0, 0, _("binary takes no arguments"))
173 getargs(x, 0, 0, _("binary takes no arguments"))
174 return mctx.fpredicate(lambda fctx: fctx.isbinary(),
174 return mctx.fpredicate(lambda fctx: fctx.isbinary(),
175 predrepr='binary', cache=True)
175 predrepr='binary', cache=True)
176
176
177 @predicate('exec()')
177 @predicate('exec()')
178 def exec_(mctx, x):
178 def exec_(mctx, x):
179 """File that is marked as executable.
179 """File that is marked as executable.
180 """
180 """
181 # i18n: "exec" is a keyword
181 # i18n: "exec" is a keyword
182 getargs(x, 0, 0, _("exec takes no arguments"))
182 getargs(x, 0, 0, _("exec takes no arguments"))
183 ctx = mctx.ctx
183 ctx = mctx.ctx
184 return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec')
184 return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec')
185
185
186 @predicate('symlink()')
186 @predicate('symlink()')
187 def symlink(mctx, x):
187 def symlink(mctx, x):
188 """File that is marked as a symlink.
188 """File that is marked as a symlink.
189 """
189 """
190 # i18n: "symlink" is a keyword
190 # i18n: "symlink" is a keyword
191 getargs(x, 0, 0, _("symlink takes no arguments"))
191 getargs(x, 0, 0, _("symlink takes no arguments"))
192 ctx = mctx.ctx
192 ctx = mctx.ctx
193 return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink')
193 return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink')
194
194
195 @predicate('resolved()')
195 @predicate('resolved()', weight=10)
196 def resolved(mctx, x):
196 def resolved(mctx, x):
197 """File that is marked resolved according to :hg:`resolve -l`.
197 """File that is marked resolved according to :hg:`resolve -l`.
198 """
198 """
199 # i18n: "resolved" is a keyword
199 # i18n: "resolved" is a keyword
200 getargs(x, 0, 0, _("resolved takes no arguments"))
200 getargs(x, 0, 0, _("resolved takes no arguments"))
201 if mctx.ctx.rev() is not None:
201 if mctx.ctx.rev() is not None:
202 return mctx.never()
202 return mctx.never()
203 ms = merge.mergestate.read(mctx.ctx.repo())
203 ms = merge.mergestate.read(mctx.ctx.repo())
204 return mctx.predicate(lambda f: f in ms and ms[f] == 'r',
204 return mctx.predicate(lambda f: f in ms and ms[f] == 'r',
205 predrepr='resolved')
205 predrepr='resolved')
206
206
207 @predicate('unresolved()')
207 @predicate('unresolved()', weight=10)
208 def unresolved(mctx, x):
208 def unresolved(mctx, x):
209 """File that is marked unresolved according to :hg:`resolve -l`.
209 """File that is marked unresolved according to :hg:`resolve -l`.
210 """
210 """
211 # i18n: "unresolved" is a keyword
211 # i18n: "unresolved" is a keyword
212 getargs(x, 0, 0, _("unresolved takes no arguments"))
212 getargs(x, 0, 0, _("unresolved takes no arguments"))
213 if mctx.ctx.rev() is not None:
213 if mctx.ctx.rev() is not None:
214 return mctx.never()
214 return mctx.never()
215 ms = merge.mergestate.read(mctx.ctx.repo())
215 ms = merge.mergestate.read(mctx.ctx.repo())
216 return mctx.predicate(lambda f: f in ms and ms[f] == 'u',
216 return mctx.predicate(lambda f: f in ms and ms[f] == 'u',
217 predrepr='unresolved')
217 predrepr='unresolved')
218
218
219 @predicate('hgignore()')
219 @predicate('hgignore()', weight=10)
220 def hgignore(mctx, x):
220 def hgignore(mctx, x):
221 """File that matches the active .hgignore pattern.
221 """File that matches the active .hgignore pattern.
222 """
222 """
223 # i18n: "hgignore" is a keyword
223 # i18n: "hgignore" is a keyword
224 getargs(x, 0, 0, _("hgignore takes no arguments"))
224 getargs(x, 0, 0, _("hgignore takes no arguments"))
225 return mctx.ctx.repo().dirstate._ignore
225 return mctx.ctx.repo().dirstate._ignore
226
226
227 @predicate('portable()')
227 @predicate('portable()', weight=0.5)
228 def portable(mctx, x):
228 def portable(mctx, x):
229 """File that has a portable name. (This doesn't include filenames with case
229 """File that has a portable name. (This doesn't include filenames with case
230 collisions.)
230 collisions.)
231 """
231 """
232 # i18n: "portable" is a keyword
232 # i18n: "portable" is a keyword
233 getargs(x, 0, 0, _("portable takes no arguments"))
233 getargs(x, 0, 0, _("portable takes no arguments"))
234 return mctx.predicate(lambda f: util.checkwinfilename(f) is None,
234 return mctx.predicate(lambda f: util.checkwinfilename(f) is None,
235 predrepr='portable')
235 predrepr='portable')
236
236
237 @predicate('grep(regex)')
237 @predicate('grep(regex)', weight=30)
238 def grep(mctx, x):
238 def grep(mctx, x):
239 """File contains the given regular expression.
239 """File contains the given regular expression.
240 """
240 """
241 try:
241 try:
242 # i18n: "grep" is a keyword
242 # i18n: "grep" is a keyword
243 r = re.compile(getstring(x, _("grep requires a pattern")))
243 r = re.compile(getstring(x, _("grep requires a pattern")))
244 except re.error as e:
244 except re.error as e:
245 raise error.ParseError(_('invalid match pattern: %s') %
245 raise error.ParseError(_('invalid match pattern: %s') %
246 stringutil.forcebytestr(e))
246 stringutil.forcebytestr(e))
247 return mctx.fpredicate(lambda fctx: r.search(fctx.data()),
247 return mctx.fpredicate(lambda fctx: r.search(fctx.data()),
248 predrepr=('grep(%r)', r.pattern), cache=True)
248 predrepr=('grep(%r)', r.pattern), cache=True)
249
249
250 def _sizetomax(s):
250 def _sizetomax(s):
251 try:
251 try:
252 s = s.strip().lower()
252 s = s.strip().lower()
253 for k, v in util._sizeunits:
253 for k, v in util._sizeunits:
254 if s.endswith(k):
254 if s.endswith(k):
255 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
255 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
256 n = s[:-len(k)]
256 n = s[:-len(k)]
257 inc = 1.0
257 inc = 1.0
258 if "." in n:
258 if "." in n:
259 inc /= 10 ** len(n.split(".")[1])
259 inc /= 10 ** len(n.split(".")[1])
260 return int((float(n) + inc) * v) - 1
260 return int((float(n) + inc) * v) - 1
261 # no extension, this is a precise value
261 # no extension, this is a precise value
262 return int(s)
262 return int(s)
263 except ValueError:
263 except ValueError:
264 raise error.ParseError(_("couldn't parse size: %s") % s)
264 raise error.ParseError(_("couldn't parse size: %s") % s)
265
265
266 def sizematcher(expr):
266 def sizematcher(expr):
267 """Return a function(size) -> bool from the ``size()`` expression"""
267 """Return a function(size) -> bool from the ``size()`` expression"""
268 expr = expr.strip()
268 expr = expr.strip()
269 if '-' in expr: # do we have a range?
269 if '-' in expr: # do we have a range?
270 a, b = expr.split('-', 1)
270 a, b = expr.split('-', 1)
271 a = util.sizetoint(a)
271 a = util.sizetoint(a)
272 b = util.sizetoint(b)
272 b = util.sizetoint(b)
273 return lambda x: x >= a and x <= b
273 return lambda x: x >= a and x <= b
274 elif expr.startswith("<="):
274 elif expr.startswith("<="):
275 a = util.sizetoint(expr[2:])
275 a = util.sizetoint(expr[2:])
276 return lambda x: x <= a
276 return lambda x: x <= a
277 elif expr.startswith("<"):
277 elif expr.startswith("<"):
278 a = util.sizetoint(expr[1:])
278 a = util.sizetoint(expr[1:])
279 return lambda x: x < a
279 return lambda x: x < a
280 elif expr.startswith(">="):
280 elif expr.startswith(">="):
281 a = util.sizetoint(expr[2:])
281 a = util.sizetoint(expr[2:])
282 return lambda x: x >= a
282 return lambda x: x >= a
283 elif expr.startswith(">"):
283 elif expr.startswith(">"):
284 a = util.sizetoint(expr[1:])
284 a = util.sizetoint(expr[1:])
285 return lambda x: x > a
285 return lambda x: x > a
286 else:
286 else:
287 a = util.sizetoint(expr)
287 a = util.sizetoint(expr)
288 b = _sizetomax(expr)
288 b = _sizetomax(expr)
289 return lambda x: x >= a and x <= b
289 return lambda x: x >= a and x <= b
290
290
291 @predicate('size(expression)')
291 @predicate('size(expression)', weight=10)
292 def size(mctx, x):
292 def size(mctx, x):
293 """File size matches the given expression. Examples:
293 """File size matches the given expression. Examples:
294
294
295 - size('1k') - files from 1024 to 2047 bytes
295 - size('1k') - files from 1024 to 2047 bytes
296 - size('< 20k') - files less than 20480 bytes
296 - size('< 20k') - files less than 20480 bytes
297 - size('>= .5MB') - files at least 524288 bytes
297 - size('>= .5MB') - files at least 524288 bytes
298 - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
298 - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
299 """
299 """
300 # i18n: "size" is a keyword
300 # i18n: "size" is a keyword
301 expr = getstring(x, _("size requires an expression"))
301 expr = getstring(x, _("size requires an expression"))
302 m = sizematcher(expr)
302 m = sizematcher(expr)
303 return mctx.fpredicate(lambda fctx: m(fctx.size()),
303 return mctx.fpredicate(lambda fctx: m(fctx.size()),
304 predrepr=('size(%r)', expr), cache=True)
304 predrepr=('size(%r)', expr), cache=True)
305
305
306 @predicate('encoding(name)')
306 @predicate('encoding(name)', weight=30)
307 def encoding(mctx, x):
307 def encoding(mctx, x):
308 """File can be successfully decoded with the given character
308 """File can be successfully decoded with the given character
309 encoding. May not be useful for encodings other than ASCII and
309 encoding. May not be useful for encodings other than ASCII and
310 UTF-8.
310 UTF-8.
311 """
311 """
312
312
313 # i18n: "encoding" is a keyword
313 # i18n: "encoding" is a keyword
314 enc = getstring(x, _("encoding requires an encoding name"))
314 enc = getstring(x, _("encoding requires an encoding name"))
315
315
316 def encp(fctx):
316 def encp(fctx):
317 d = fctx.data()
317 d = fctx.data()
318 try:
318 try:
319 d.decode(pycompat.sysstr(enc))
319 d.decode(pycompat.sysstr(enc))
320 return True
320 return True
321 except LookupError:
321 except LookupError:
322 raise error.Abort(_("unknown encoding '%s'") % enc)
322 raise error.Abort(_("unknown encoding '%s'") % enc)
323 except UnicodeDecodeError:
323 except UnicodeDecodeError:
324 return False
324 return False
325
325
326 return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True)
326 return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True)
327
327
328 @predicate('eol(style)')
328 @predicate('eol(style)', weight=30)
329 def eol(mctx, x):
329 def eol(mctx, x):
330 """File contains newlines of the given style (dos, unix, mac). Binary
330 """File contains newlines of the given style (dos, unix, mac). Binary
331 files are excluded, files with mixed line endings match multiple
331 files are excluded, files with mixed line endings match multiple
332 styles.
332 styles.
333 """
333 """
334
334
335 # i18n: "eol" is a keyword
335 # i18n: "eol" is a keyword
336 enc = getstring(x, _("eol requires a style name"))
336 enc = getstring(x, _("eol requires a style name"))
337
337
338 def eolp(fctx):
338 def eolp(fctx):
339 if fctx.isbinary():
339 if fctx.isbinary():
340 return False
340 return False
341 d = fctx.data()
341 d = fctx.data()
342 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
342 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
343 return True
343 return True
344 elif enc == 'unix' and re.search('(?<!\r)\n', d):
344 elif enc == 'unix' and re.search('(?<!\r)\n', d):
345 return True
345 return True
346 elif enc == 'mac' and re.search('\r(?!\n)', d):
346 elif enc == 'mac' and re.search('\r(?!\n)', d):
347 return True
347 return True
348 return False
348 return False
349 return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True)
349 return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True)
350
350
351 @predicate('copied()')
351 @predicate('copied()')
352 def copied(mctx, x):
352 def copied(mctx, x):
353 """File that is recorded as being copied.
353 """File that is recorded as being copied.
354 """
354 """
355 # i18n: "copied" is a keyword
355 # i18n: "copied" is a keyword
356 getargs(x, 0, 0, _("copied takes no arguments"))
356 getargs(x, 0, 0, _("copied takes no arguments"))
357 def copiedp(fctx):
357 def copiedp(fctx):
358 p = fctx.parents()
358 p = fctx.parents()
359 return p and p[0].path() != fctx.path()
359 return p and p[0].path() != fctx.path()
360 return mctx.fpredicate(copiedp, predrepr='copied', cache=True)
360 return mctx.fpredicate(copiedp, predrepr='copied', cache=True)
361
361
362 @predicate('revs(revs, pattern)')
362 @predicate('revs(revs, pattern)', weight=10)
363 def revs(mctx, x):
363 def revs(mctx, x):
364 """Evaluate set in the specified revisions. If the revset match multiple
364 """Evaluate set in the specified revisions. If the revset match multiple
365 revs, this will return file matching pattern in any of the revision.
365 revs, this will return file matching pattern in any of the revision.
366 """
366 """
367 # i18n: "revs" is a keyword
367 # i18n: "revs" is a keyword
368 r, x = getargs(x, 2, 2, _("revs takes two arguments"))
368 r, x = getargs(x, 2, 2, _("revs takes two arguments"))
369 # i18n: "revs" is a keyword
369 # i18n: "revs" is a keyword
370 revspec = getstring(r, _("first argument to revs must be a revision"))
370 revspec = getstring(r, _("first argument to revs must be a revision"))
371 repo = mctx.ctx.repo()
371 repo = mctx.ctx.repo()
372 revs = scmutil.revrange(repo, [revspec])
372 revs = scmutil.revrange(repo, [revspec])
373
373
374 matchers = []
374 matchers = []
375 for r in revs:
375 for r in revs:
376 ctx = repo[r]
376 ctx = repo[r]
377 matchers.append(getmatch(mctx.switch(ctx, _buildstatus(ctx, x)), x))
377 matchers.append(getmatch(mctx.switch(ctx, _buildstatus(ctx, x)), x))
378 if not matchers:
378 if not matchers:
379 return mctx.never()
379 return mctx.never()
380 if len(matchers) == 1:
380 if len(matchers) == 1:
381 return matchers[0]
381 return matchers[0]
382 return matchmod.unionmatcher(matchers)
382 return matchmod.unionmatcher(matchers)
383
383
384 @predicate('status(base, rev, pattern)')
384 @predicate('status(base, rev, pattern)', weight=10)
385 def status(mctx, x):
385 def status(mctx, x):
386 """Evaluate predicate using status change between ``base`` and
386 """Evaluate predicate using status change between ``base`` and
387 ``rev``. Examples:
387 ``rev``. Examples:
388
388
389 - ``status(3, 7, added())`` - matches files added from "3" to "7"
389 - ``status(3, 7, added())`` - matches files added from "3" to "7"
390 """
390 """
391 repo = mctx.ctx.repo()
391 repo = mctx.ctx.repo()
392 # i18n: "status" is a keyword
392 # i18n: "status" is a keyword
393 b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
393 b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
394 # i18n: "status" is a keyword
394 # i18n: "status" is a keyword
395 baseerr = _("first argument to status must be a revision")
395 baseerr = _("first argument to status must be a revision")
396 baserevspec = getstring(b, baseerr)
396 baserevspec = getstring(b, baseerr)
397 if not baserevspec:
397 if not baserevspec:
398 raise error.ParseError(baseerr)
398 raise error.ParseError(baseerr)
399 reverr = _("second argument to status must be a revision")
399 reverr = _("second argument to status must be a revision")
400 revspec = getstring(r, reverr)
400 revspec = getstring(r, reverr)
401 if not revspec:
401 if not revspec:
402 raise error.ParseError(reverr)
402 raise error.ParseError(reverr)
403 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
403 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
404 return getmatch(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
404 return getmatch(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
405
405
406 @predicate('subrepo([pattern])')
406 @predicate('subrepo([pattern])')
407 def subrepo(mctx, x):
407 def subrepo(mctx, x):
408 """Subrepositories whose paths match the given pattern.
408 """Subrepositories whose paths match the given pattern.
409 """
409 """
410 # i18n: "subrepo" is a keyword
410 # i18n: "subrepo" is a keyword
411 getargs(x, 0, 1, _("subrepo takes at most one argument"))
411 getargs(x, 0, 1, _("subrepo takes at most one argument"))
412 ctx = mctx.ctx
412 ctx = mctx.ctx
413 sstate = ctx.substate
413 sstate = ctx.substate
414 if x:
414 if x:
415 pat = getpattern(x, matchmod.allpatternkinds,
415 pat = getpattern(x, matchmod.allpatternkinds,
416 # i18n: "subrepo" is a keyword
416 # i18n: "subrepo" is a keyword
417 _("subrepo requires a pattern or no arguments"))
417 _("subrepo requires a pattern or no arguments"))
418 fast = not matchmod.patkind(pat)
418 fast = not matchmod.patkind(pat)
419 if fast:
419 if fast:
420 def m(s):
420 def m(s):
421 return (s == pat)
421 return (s == pat)
422 else:
422 else:
423 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
423 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
424 return mctx.predicate(lambda f: f in sstate and m(f),
424 return mctx.predicate(lambda f: f in sstate and m(f),
425 predrepr=('subrepo(%r)', pat))
425 predrepr=('subrepo(%r)', pat))
426 else:
426 else:
427 return mctx.predicate(sstate.__contains__, predrepr='subrepo')
427 return mctx.predicate(sstate.__contains__, predrepr='subrepo')
428
428
429 methods = {
429 methods = {
430 'string': stringmatch,
430 'string': stringmatch,
431 'symbol': stringmatch,
431 'symbol': stringmatch,
432 'kindpat': kindpatmatch,
432 'kindpat': kindpatmatch,
433 'and': andmatch,
433 'and': andmatch,
434 'or': ormatch,
434 'or': ormatch,
435 'minus': minusmatch,
435 'minus': minusmatch,
436 'list': listmatch,
436 'list': listmatch,
437 'not': notmatch,
437 'not': notmatch,
438 'func': func,
438 'func': func,
439 }
439 }
440
440
441 class matchctx(object):
441 class matchctx(object):
442 def __init__(self, ctx, status=None, badfn=None):
442 def __init__(self, ctx, status=None, badfn=None):
443 self.ctx = ctx
443 self.ctx = ctx
444 self._status = status
444 self._status = status
445 self._badfn = badfn
445 self._badfn = badfn
446
446
447 def status(self):
447 def status(self):
448 return self._status
448 return self._status
449
449
450 def matcher(self, patterns):
450 def matcher(self, patterns):
451 return self.ctx.match(patterns, badfn=self._badfn)
451 return self.ctx.match(patterns, badfn=self._badfn)
452
452
453 def predicate(self, predfn, predrepr=None, cache=False):
453 def predicate(self, predfn, predrepr=None, cache=False):
454 """Create a matcher to select files by predfn(filename)"""
454 """Create a matcher to select files by predfn(filename)"""
455 if cache:
455 if cache:
456 predfn = util.cachefunc(predfn)
456 predfn = util.cachefunc(predfn)
457 repo = self.ctx.repo()
457 repo = self.ctx.repo()
458 return matchmod.predicatematcher(repo.root, repo.getcwd(), predfn,
458 return matchmod.predicatematcher(repo.root, repo.getcwd(), predfn,
459 predrepr=predrepr, badfn=self._badfn)
459 predrepr=predrepr, badfn=self._badfn)
460
460
461 def fpredicate(self, predfn, predrepr=None, cache=False):
461 def fpredicate(self, predfn, predrepr=None, cache=False):
462 """Create a matcher to select files by predfn(fctx) at the current
462 """Create a matcher to select files by predfn(fctx) at the current
463 revision
463 revision
464
464
465 Missing files are ignored.
465 Missing files are ignored.
466 """
466 """
467 ctx = self.ctx
467 ctx = self.ctx
468 if ctx.rev() is None:
468 if ctx.rev() is None:
469 def fctxpredfn(f):
469 def fctxpredfn(f):
470 try:
470 try:
471 fctx = ctx[f]
471 fctx = ctx[f]
472 except error.LookupError:
472 except error.LookupError:
473 return False
473 return False
474 try:
474 try:
475 fctx.audit()
475 fctx.audit()
476 except error.Abort:
476 except error.Abort:
477 return False
477 return False
478 try:
478 try:
479 return predfn(fctx)
479 return predfn(fctx)
480 except (IOError, OSError) as e:
480 except (IOError, OSError) as e:
481 # open()-ing a directory fails with EACCES on Windows
481 # open()-ing a directory fails with EACCES on Windows
482 if e.errno in (errno.ENOENT, errno.EACCES, errno.ENOTDIR,
482 if e.errno in (errno.ENOENT, errno.EACCES, errno.ENOTDIR,
483 errno.EISDIR):
483 errno.EISDIR):
484 return False
484 return False
485 raise
485 raise
486 else:
486 else:
487 def fctxpredfn(f):
487 def fctxpredfn(f):
488 try:
488 try:
489 fctx = ctx[f]
489 fctx = ctx[f]
490 except error.LookupError:
490 except error.LookupError:
491 return False
491 return False
492 return predfn(fctx)
492 return predfn(fctx)
493 return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache)
493 return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache)
494
494
495 def never(self):
495 def never(self):
496 """Create a matcher to select nothing"""
496 """Create a matcher to select nothing"""
497 repo = self.ctx.repo()
497 repo = self.ctx.repo()
498 return matchmod.nevermatcher(repo.root, repo.getcwd(),
498 return matchmod.nevermatcher(repo.root, repo.getcwd(),
499 badfn=self._badfn)
499 badfn=self._badfn)
500
500
501 def switch(self, ctx, status=None):
501 def switch(self, ctx, status=None):
502 return matchctx(ctx, status, self._badfn)
502 return matchctx(ctx, status, self._badfn)
503
503
504 # filesets using matchctx.switch()
504 # filesets using matchctx.switch()
505 _switchcallers = [
505 _switchcallers = [
506 'revs',
506 'revs',
507 'status',
507 'status',
508 ]
508 ]
509
509
510 def _intree(funcs, tree):
510 def _intree(funcs, tree):
511 if isinstance(tree, tuple):
511 if isinstance(tree, tuple):
512 if tree[0] == 'func' and tree[1][0] == 'symbol':
512 if tree[0] == 'func' and tree[1][0] == 'symbol':
513 if tree[1][1] in funcs:
513 if tree[1][1] in funcs:
514 return True
514 return True
515 if tree[1][1] in _switchcallers:
515 if tree[1][1] in _switchcallers:
516 # arguments won't be evaluated in the current context
516 # arguments won't be evaluated in the current context
517 return False
517 return False
518 for s in tree[1:]:
518 for s in tree[1:]:
519 if _intree(funcs, s):
519 if _intree(funcs, s):
520 return True
520 return True
521 return False
521 return False
522
522
523 def match(ctx, expr, badfn=None):
523 def match(ctx, expr, badfn=None):
524 """Create a matcher for a single fileset expression"""
524 """Create a matcher for a single fileset expression"""
525 tree = filesetlang.parse(expr)
525 tree = filesetlang.parse(expr)
526 tree = filesetlang.analyze(tree)
526 tree = filesetlang.analyze(tree)
527 tree = filesetlang.optimize(tree)
527 tree = filesetlang.optimize(tree)
528 mctx = matchctx(ctx, _buildstatus(ctx, tree), badfn=badfn)
528 mctx = matchctx(ctx, _buildstatus(ctx, tree), badfn=badfn)
529 return getmatch(mctx, tree)
529 return getmatch(mctx, tree)
530
530
531 def _buildstatus(ctx, tree, basectx=None):
531 def _buildstatus(ctx, tree, basectx=None):
532 # do we need status info?
532 # do we need status info?
533
533
534 if _intree(_statuscallers, tree):
534 if _intree(_statuscallers, tree):
535 unknown = _intree(['unknown'], tree)
535 unknown = _intree(['unknown'], tree)
536 ignored = _intree(['ignored'], tree)
536 ignored = _intree(['ignored'], tree)
537
537
538 if basectx is None:
538 if basectx is None:
539 basectx = ctx.p1()
539 basectx = ctx.p1()
540 return basectx.status(ctx, listunknown=unknown, listignored=ignored,
540 return basectx.status(ctx, listunknown=unknown, listignored=ignored,
541 listclean=True)
541 listclean=True)
542 else:
542 else:
543 return None
543 return None
544
544
545 def loadpredicate(ui, extname, registrarobj):
545 def loadpredicate(ui, extname, registrarobj):
546 """Load fileset predicates from specified registrarobj
546 """Load fileset predicates from specified registrarobj
547 """
547 """
548 for name, func in registrarobj._table.iteritems():
548 for name, func in registrarobj._table.iteritems():
549 symbols[name] = func
549 symbols[name] = func
550 if func._callstatus:
550 if func._callstatus:
551 _statuscallers.add(name)
551 _statuscallers.add(name)
552
552
553 # load built-in predicates explicitly to setup _statuscallers
553 # load built-in predicates explicitly to setup _statuscallers
554 loadpredicate(None, None, predicate)
554 loadpredicate(None, None, predicate)
555
555
556 # tell hggettext to extract docstrings from these functions:
556 # tell hggettext to extract docstrings from these functions:
557 i18nfunctions = symbols.values()
557 i18nfunctions = symbols.values()
@@ -1,443 +1,452 b''
1 # registrar.py - utilities to register function for specific purpose
1 # registrar.py - utilities to register function for specific purpose
2 #
2 #
3 # Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> and others
3 # Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from . import (
10 from . import (
11 configitems,
11 configitems,
12 error,
12 error,
13 pycompat,
13 pycompat,
14 util,
14 util,
15 )
15 )
16
16
17 # unlike the other registered items, config options are neither functions or
17 # unlike the other registered items, config options are neither functions or
18 # classes. Registering the option is just small function call.
18 # classes. Registering the option is just small function call.
19 #
19 #
20 # We still add the official API to the registrar module for consistency with
20 # We still add the official API to the registrar module for consistency with
21 # the other items extensions want might to register.
21 # the other items extensions want might to register.
22 configitem = configitems.getitemregister
22 configitem = configitems.getitemregister
23
23
24 class _funcregistrarbase(object):
24 class _funcregistrarbase(object):
25 """Base of decorator to register a function for specific purpose
25 """Base of decorator to register a function for specific purpose
26
26
27 This decorator stores decorated functions into own dict 'table'.
27 This decorator stores decorated functions into own dict 'table'.
28
28
29 The least derived class can be defined by overriding 'formatdoc',
29 The least derived class can be defined by overriding 'formatdoc',
30 for example::
30 for example::
31
31
32 class keyword(_funcregistrarbase):
32 class keyword(_funcregistrarbase):
33 _docformat = ":%s: %s"
33 _docformat = ":%s: %s"
34
34
35 This should be used as below:
35 This should be used as below:
36
36
37 keyword = registrar.keyword()
37 keyword = registrar.keyword()
38
38
39 @keyword('bar')
39 @keyword('bar')
40 def barfunc(*args, **kwargs):
40 def barfunc(*args, **kwargs):
41 '''Explanation of bar keyword ....
41 '''Explanation of bar keyword ....
42 '''
42 '''
43 pass
43 pass
44
44
45 In this case:
45 In this case:
46
46
47 - 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above
47 - 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above
48 - 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword"
48 - 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword"
49 """
49 """
50 def __init__(self, table=None):
50 def __init__(self, table=None):
51 if table is None:
51 if table is None:
52 self._table = {}
52 self._table = {}
53 else:
53 else:
54 self._table = table
54 self._table = table
55
55
56 def __call__(self, decl, *args, **kwargs):
56 def __call__(self, decl, *args, **kwargs):
57 return lambda func: self._doregister(func, decl, *args, **kwargs)
57 return lambda func: self._doregister(func, decl, *args, **kwargs)
58
58
59 def _doregister(self, func, decl, *args, **kwargs):
59 def _doregister(self, func, decl, *args, **kwargs):
60 name = self._getname(decl)
60 name = self._getname(decl)
61
61
62 if name in self._table:
62 if name in self._table:
63 msg = 'duplicate registration for name: "%s"' % name
63 msg = 'duplicate registration for name: "%s"' % name
64 raise error.ProgrammingError(msg)
64 raise error.ProgrammingError(msg)
65
65
66 if func.__doc__ and not util.safehasattr(func, '_origdoc'):
66 if func.__doc__ and not util.safehasattr(func, '_origdoc'):
67 doc = pycompat.sysbytes(func.__doc__).strip()
67 doc = pycompat.sysbytes(func.__doc__).strip()
68 func._origdoc = doc
68 func._origdoc = doc
69 func.__doc__ = pycompat.sysstr(self._formatdoc(decl, doc))
69 func.__doc__ = pycompat.sysstr(self._formatdoc(decl, doc))
70
70
71 self._table[name] = func
71 self._table[name] = func
72 self._extrasetup(name, func, *args, **kwargs)
72 self._extrasetup(name, func, *args, **kwargs)
73
73
74 return func
74 return func
75
75
76 def _parsefuncdecl(self, decl):
76 def _parsefuncdecl(self, decl):
77 """Parse function declaration and return the name of function in it
77 """Parse function declaration and return the name of function in it
78 """
78 """
79 i = decl.find('(')
79 i = decl.find('(')
80 if i >= 0:
80 if i >= 0:
81 return decl[:i]
81 return decl[:i]
82 else:
82 else:
83 return decl
83 return decl
84
84
85 def _getname(self, decl):
85 def _getname(self, decl):
86 """Return the name of the registered function from decl
86 """Return the name of the registered function from decl
87
87
88 Derived class should override this, if it allows more
88 Derived class should override this, if it allows more
89 descriptive 'decl' string than just a name.
89 descriptive 'decl' string than just a name.
90 """
90 """
91 return decl
91 return decl
92
92
93 _docformat = None
93 _docformat = None
94
94
95 def _formatdoc(self, decl, doc):
95 def _formatdoc(self, decl, doc):
96 """Return formatted document of the registered function for help
96 """Return formatted document of the registered function for help
97
97
98 'doc' is '__doc__.strip()' of the registered function.
98 'doc' is '__doc__.strip()' of the registered function.
99 """
99 """
100 return self._docformat % (decl, doc)
100 return self._docformat % (decl, doc)
101
101
102 def _extrasetup(self, name, func):
102 def _extrasetup(self, name, func):
103 """Execute exra setup for registered function, if needed
103 """Execute exra setup for registered function, if needed
104 """
104 """
105
105
106 class command(_funcregistrarbase):
106 class command(_funcregistrarbase):
107 """Decorator to register a command function to table
107 """Decorator to register a command function to table
108
108
109 This class receives a command table as its argument. The table should
109 This class receives a command table as its argument. The table should
110 be a dict.
110 be a dict.
111
111
112 The created object can be used as a decorator for adding commands to
112 The created object can be used as a decorator for adding commands to
113 that command table. This accepts multiple arguments to define a command.
113 that command table. This accepts multiple arguments to define a command.
114
114
115 The first argument is the command name (as bytes).
115 The first argument is the command name (as bytes).
116
116
117 The `options` keyword argument is an iterable of tuples defining command
117 The `options` keyword argument is an iterable of tuples defining command
118 arguments. See ``mercurial.fancyopts.fancyopts()`` for the format of each
118 arguments. See ``mercurial.fancyopts.fancyopts()`` for the format of each
119 tuple.
119 tuple.
120
120
121 The `synopsis` argument defines a short, one line summary of how to use the
121 The `synopsis` argument defines a short, one line summary of how to use the
122 command. This shows up in the help output.
122 command. This shows up in the help output.
123
123
124 There are three arguments that control what repository (if any) is found
124 There are three arguments that control what repository (if any) is found
125 and passed to the decorated function: `norepo`, `optionalrepo`, and
125 and passed to the decorated function: `norepo`, `optionalrepo`, and
126 `inferrepo`.
126 `inferrepo`.
127
127
128 The `norepo` argument defines whether the command does not require a
128 The `norepo` argument defines whether the command does not require a
129 local repository. Most commands operate against a repository, thus the
129 local repository. Most commands operate against a repository, thus the
130 default is False. When True, no repository will be passed.
130 default is False. When True, no repository will be passed.
131
131
132 The `optionalrepo` argument defines whether the command optionally requires
132 The `optionalrepo` argument defines whether the command optionally requires
133 a local repository. If no repository can be found, None will be passed
133 a local repository. If no repository can be found, None will be passed
134 to the decorated function.
134 to the decorated function.
135
135
136 The `inferrepo` argument defines whether to try to find a repository from
136 The `inferrepo` argument defines whether to try to find a repository from
137 the command line arguments. If True, arguments will be examined for
137 the command line arguments. If True, arguments will be examined for
138 potential repository locations. See ``findrepo()``. If a repository is
138 potential repository locations. See ``findrepo()``. If a repository is
139 found, it will be used and passed to the decorated function.
139 found, it will be used and passed to the decorated function.
140
140
141 The `intents` argument defines a set of intended actions or capabilities
141 The `intents` argument defines a set of intended actions or capabilities
142 the command is taking. These intents can be used to affect the construction
142 the command is taking. These intents can be used to affect the construction
143 of the repository object passed to the command. For example, commands
143 of the repository object passed to the command. For example, commands
144 declaring that they are read-only could receive a repository that doesn't
144 declaring that they are read-only could receive a repository that doesn't
145 have any methods allowing repository mutation. Other intents could be used
145 have any methods allowing repository mutation. Other intents could be used
146 to prevent the command from running if the requested intent could not be
146 to prevent the command from running if the requested intent could not be
147 fulfilled.
147 fulfilled.
148
148
149 The following intents are defined:
149 The following intents are defined:
150
150
151 readonly
151 readonly
152 The command is read-only
152 The command is read-only
153
153
154 The signature of the decorated function looks like this:
154 The signature of the decorated function looks like this:
155 def cmd(ui[, repo] [, <args>] [, <options>])
155 def cmd(ui[, repo] [, <args>] [, <options>])
156
156
157 `repo` is required if `norepo` is False.
157 `repo` is required if `norepo` is False.
158 `<args>` are positional args (or `*args`) arguments, of non-option
158 `<args>` are positional args (or `*args`) arguments, of non-option
159 arguments from the command line.
159 arguments from the command line.
160 `<options>` are keyword arguments (or `**options`) of option arguments
160 `<options>` are keyword arguments (or `**options`) of option arguments
161 from the command line.
161 from the command line.
162
162
163 See the WritingExtensions and MercurialApi documentation for more exhaustive
163 See the WritingExtensions and MercurialApi documentation for more exhaustive
164 descriptions and examples.
164 descriptions and examples.
165 """
165 """
166
166
167 def _doregister(self, func, name, options=(), synopsis=None,
167 def _doregister(self, func, name, options=(), synopsis=None,
168 norepo=False, optionalrepo=False, inferrepo=False,
168 norepo=False, optionalrepo=False, inferrepo=False,
169 intents=None):
169 intents=None):
170
170
171 func.norepo = norepo
171 func.norepo = norepo
172 func.optionalrepo = optionalrepo
172 func.optionalrepo = optionalrepo
173 func.inferrepo = inferrepo
173 func.inferrepo = inferrepo
174 func.intents = intents or set()
174 func.intents = intents or set()
175 if synopsis:
175 if synopsis:
176 self._table[name] = func, list(options), synopsis
176 self._table[name] = func, list(options), synopsis
177 else:
177 else:
178 self._table[name] = func, list(options)
178 self._table[name] = func, list(options)
179 return func
179 return func
180
180
181 INTENT_READONLY = b'readonly'
181 INTENT_READONLY = b'readonly'
182
182
183 class revsetpredicate(_funcregistrarbase):
183 class revsetpredicate(_funcregistrarbase):
184 """Decorator to register revset predicate
184 """Decorator to register revset predicate
185
185
186 Usage::
186 Usage::
187
187
188 revsetpredicate = registrar.revsetpredicate()
188 revsetpredicate = registrar.revsetpredicate()
189
189
190 @revsetpredicate('mypredicate(arg1, arg2[, arg3])')
190 @revsetpredicate('mypredicate(arg1, arg2[, arg3])')
191 def mypredicatefunc(repo, subset, x):
191 def mypredicatefunc(repo, subset, x):
192 '''Explanation of this revset predicate ....
192 '''Explanation of this revset predicate ....
193 '''
193 '''
194 pass
194 pass
195
195
196 The first string argument is used also in online help.
196 The first string argument is used also in online help.
197
197
198 Optional argument 'safe' indicates whether a predicate is safe for
198 Optional argument 'safe' indicates whether a predicate is safe for
199 DoS attack (False by default).
199 DoS attack (False by default).
200
200
201 Optional argument 'takeorder' indicates whether a predicate function
201 Optional argument 'takeorder' indicates whether a predicate function
202 takes ordering policy as the last argument.
202 takes ordering policy as the last argument.
203
203
204 Optional argument 'weight' indicates the estimated run-time cost, useful
204 Optional argument 'weight' indicates the estimated run-time cost, useful
205 for static optimization, default is 1. Higher weight means more expensive.
205 for static optimization, default is 1. Higher weight means more expensive.
206 Usually, revsets that are fast and return only one revision has a weight of
206 Usually, revsets that are fast and return only one revision has a weight of
207 0.5 (ex. a symbol); revsets with O(changelog) complexity and read only the
207 0.5 (ex. a symbol); revsets with O(changelog) complexity and read only the
208 changelog have weight 10 (ex. author); revsets reading manifest deltas have
208 changelog have weight 10 (ex. author); revsets reading manifest deltas have
209 weight 30 (ex. adds); revset reading manifest contents have weight 100
209 weight 30 (ex. adds); revset reading manifest contents have weight 100
210 (ex. contains). Note: those values are flexible. If the revset has a
210 (ex. contains). Note: those values are flexible. If the revset has a
211 same big-O time complexity as 'contains', but with a smaller constant, it
211 same big-O time complexity as 'contains', but with a smaller constant, it
212 might have a weight of 90.
212 might have a weight of 90.
213
213
214 'revsetpredicate' instance in example above can be used to
214 'revsetpredicate' instance in example above can be used to
215 decorate multiple functions.
215 decorate multiple functions.
216
216
217 Decorated functions are registered automatically at loading
217 Decorated functions are registered automatically at loading
218 extension, if an instance named as 'revsetpredicate' is used for
218 extension, if an instance named as 'revsetpredicate' is used for
219 decorating in extension.
219 decorating in extension.
220
220
221 Otherwise, explicit 'revset.loadpredicate()' is needed.
221 Otherwise, explicit 'revset.loadpredicate()' is needed.
222 """
222 """
223 _getname = _funcregistrarbase._parsefuncdecl
223 _getname = _funcregistrarbase._parsefuncdecl
224 _docformat = "``%s``\n %s"
224 _docformat = "``%s``\n %s"
225
225
226 def _extrasetup(self, name, func, safe=False, takeorder=False, weight=1):
226 def _extrasetup(self, name, func, safe=False, takeorder=False, weight=1):
227 func._safe = safe
227 func._safe = safe
228 func._takeorder = takeorder
228 func._takeorder = takeorder
229 func._weight = weight
229 func._weight = weight
230
230
231 class filesetpredicate(_funcregistrarbase):
231 class filesetpredicate(_funcregistrarbase):
232 """Decorator to register fileset predicate
232 """Decorator to register fileset predicate
233
233
234 Usage::
234 Usage::
235
235
236 filesetpredicate = registrar.filesetpredicate()
236 filesetpredicate = registrar.filesetpredicate()
237
237
238 @filesetpredicate('mypredicate()')
238 @filesetpredicate('mypredicate()')
239 def mypredicatefunc(mctx, x):
239 def mypredicatefunc(mctx, x):
240 '''Explanation of this fileset predicate ....
240 '''Explanation of this fileset predicate ....
241 '''
241 '''
242 pass
242 pass
243
243
244 The first string argument is used also in online help.
244 The first string argument is used also in online help.
245
245
246 Optional argument 'callstatus' indicates whether a predicate
246 Optional argument 'callstatus' indicates whether a predicate
247 implies 'matchctx.status()' at runtime or not (False, by
247 implies 'matchctx.status()' at runtime or not (False, by
248 default).
248 default).
249
249
250 Optional argument 'weight' indicates the estimated run-time cost, useful
250 Optional argument 'weight' indicates the estimated run-time cost, useful
251 for static optimization, default is 1. Higher weight means more expensive.
251 for static optimization, default is 1. Higher weight means more expensive.
252
252
253 ====== =============================================================
254 Weight Description and examples
255 ====== =============================================================
256 0.5 basic match patterns (e.g. a symbol)
257 10 computing status (e.g. added()) or accessing a few files
258 30 reading file content for each (e.g. grep())
259 50 scanning working directory (ignored())
260 ====== =============================================================
261
253 'filesetpredicate' instance in example above can be used to
262 'filesetpredicate' instance in example above can be used to
254 decorate multiple functions.
263 decorate multiple functions.
255
264
256 Decorated functions are registered automatically at loading
265 Decorated functions are registered automatically at loading
257 extension, if an instance named as 'filesetpredicate' is used for
266 extension, if an instance named as 'filesetpredicate' is used for
258 decorating in extension.
267 decorating in extension.
259
268
260 Otherwise, explicit 'fileset.loadpredicate()' is needed.
269 Otherwise, explicit 'fileset.loadpredicate()' is needed.
261 """
270 """
262 _getname = _funcregistrarbase._parsefuncdecl
271 _getname = _funcregistrarbase._parsefuncdecl
263 _docformat = "``%s``\n %s"
272 _docformat = "``%s``\n %s"
264
273
265 def _extrasetup(self, name, func, callstatus=False, weight=1):
274 def _extrasetup(self, name, func, callstatus=False, weight=1):
266 func._callstatus = callstatus
275 func._callstatus = callstatus
267 func._weight = weight
276 func._weight = weight
268
277
269 class _templateregistrarbase(_funcregistrarbase):
278 class _templateregistrarbase(_funcregistrarbase):
270 """Base of decorator to register functions as template specific one
279 """Base of decorator to register functions as template specific one
271 """
280 """
272 _docformat = ":%s: %s"
281 _docformat = ":%s: %s"
273
282
274 class templatekeyword(_templateregistrarbase):
283 class templatekeyword(_templateregistrarbase):
275 """Decorator to register template keyword
284 """Decorator to register template keyword
276
285
277 Usage::
286 Usage::
278
287
279 templatekeyword = registrar.templatekeyword()
288 templatekeyword = registrar.templatekeyword()
280
289
281 # new API (since Mercurial 4.6)
290 # new API (since Mercurial 4.6)
282 @templatekeyword('mykeyword', requires={'repo', 'ctx'})
291 @templatekeyword('mykeyword', requires={'repo', 'ctx'})
283 def mykeywordfunc(context, mapping):
292 def mykeywordfunc(context, mapping):
284 '''Explanation of this template keyword ....
293 '''Explanation of this template keyword ....
285 '''
294 '''
286 pass
295 pass
287
296
288 # old API
297 # old API
289 @templatekeyword('mykeyword')
298 @templatekeyword('mykeyword')
290 def mykeywordfunc(repo, ctx, templ, cache, revcache, **args):
299 def mykeywordfunc(repo, ctx, templ, cache, revcache, **args):
291 '''Explanation of this template keyword ....
300 '''Explanation of this template keyword ....
292 '''
301 '''
293 pass
302 pass
294
303
295 The first string argument is used also in online help.
304 The first string argument is used also in online help.
296
305
297 Optional argument 'requires' should be a collection of resource names
306 Optional argument 'requires' should be a collection of resource names
298 which the template keyword depends on. This also serves as a flag to
307 which the template keyword depends on. This also serves as a flag to
299 switch to the new API. If 'requires' is unspecified, all template
308 switch to the new API. If 'requires' is unspecified, all template
300 keywords and resources are expanded to the function arguments.
309 keywords and resources are expanded to the function arguments.
301
310
302 'templatekeyword' instance in example above can be used to
311 'templatekeyword' instance in example above can be used to
303 decorate multiple functions.
312 decorate multiple functions.
304
313
305 Decorated functions are registered automatically at loading
314 Decorated functions are registered automatically at loading
306 extension, if an instance named as 'templatekeyword' is used for
315 extension, if an instance named as 'templatekeyword' is used for
307 decorating in extension.
316 decorating in extension.
308
317
309 Otherwise, explicit 'templatekw.loadkeyword()' is needed.
318 Otherwise, explicit 'templatekw.loadkeyword()' is needed.
310 """
319 """
311
320
312 def _extrasetup(self, name, func, requires=None):
321 def _extrasetup(self, name, func, requires=None):
313 func._requires = requires
322 func._requires = requires
314
323
315 class templatefilter(_templateregistrarbase):
324 class templatefilter(_templateregistrarbase):
316 """Decorator to register template filer
325 """Decorator to register template filer
317
326
318 Usage::
327 Usage::
319
328
320 templatefilter = registrar.templatefilter()
329 templatefilter = registrar.templatefilter()
321
330
322 @templatefilter('myfilter', intype=bytes)
331 @templatefilter('myfilter', intype=bytes)
323 def myfilterfunc(text):
332 def myfilterfunc(text):
324 '''Explanation of this template filter ....
333 '''Explanation of this template filter ....
325 '''
334 '''
326 pass
335 pass
327
336
328 The first string argument is used also in online help.
337 The first string argument is used also in online help.
329
338
330 Optional argument 'intype' defines the type of the input argument,
339 Optional argument 'intype' defines the type of the input argument,
331 which should be (bytes, int, templateutil.date, or None for any.)
340 which should be (bytes, int, templateutil.date, or None for any.)
332
341
333 'templatefilter' instance in example above can be used to
342 'templatefilter' instance in example above can be used to
334 decorate multiple functions.
343 decorate multiple functions.
335
344
336 Decorated functions are registered automatically at loading
345 Decorated functions are registered automatically at loading
337 extension, if an instance named as 'templatefilter' is used for
346 extension, if an instance named as 'templatefilter' is used for
338 decorating in extension.
347 decorating in extension.
339
348
340 Otherwise, explicit 'templatefilters.loadkeyword()' is needed.
349 Otherwise, explicit 'templatefilters.loadkeyword()' is needed.
341 """
350 """
342
351
343 def _extrasetup(self, name, func, intype=None):
352 def _extrasetup(self, name, func, intype=None):
344 func._intype = intype
353 func._intype = intype
345
354
346 class templatefunc(_templateregistrarbase):
355 class templatefunc(_templateregistrarbase):
347 """Decorator to register template function
356 """Decorator to register template function
348
357
349 Usage::
358 Usage::
350
359
351 templatefunc = registrar.templatefunc()
360 templatefunc = registrar.templatefunc()
352
361
353 @templatefunc('myfunc(arg1, arg2[, arg3])', argspec='arg1 arg2 arg3',
362 @templatefunc('myfunc(arg1, arg2[, arg3])', argspec='arg1 arg2 arg3',
354 requires={'ctx'})
363 requires={'ctx'})
355 def myfuncfunc(context, mapping, args):
364 def myfuncfunc(context, mapping, args):
356 '''Explanation of this template function ....
365 '''Explanation of this template function ....
357 '''
366 '''
358 pass
367 pass
359
368
360 The first string argument is used also in online help.
369 The first string argument is used also in online help.
361
370
362 If optional 'argspec' is defined, the function will receive 'args' as
371 If optional 'argspec' is defined, the function will receive 'args' as
363 a dict of named arguments. Otherwise 'args' is a list of positional
372 a dict of named arguments. Otherwise 'args' is a list of positional
364 arguments.
373 arguments.
365
374
366 Optional argument 'requires' should be a collection of resource names
375 Optional argument 'requires' should be a collection of resource names
367 which the template function depends on.
376 which the template function depends on.
368
377
369 'templatefunc' instance in example above can be used to
378 'templatefunc' instance in example above can be used to
370 decorate multiple functions.
379 decorate multiple functions.
371
380
372 Decorated functions are registered automatically at loading
381 Decorated functions are registered automatically at loading
373 extension, if an instance named as 'templatefunc' is used for
382 extension, if an instance named as 'templatefunc' is used for
374 decorating in extension.
383 decorating in extension.
375
384
376 Otherwise, explicit 'templatefuncs.loadfunction()' is needed.
385 Otherwise, explicit 'templatefuncs.loadfunction()' is needed.
377 """
386 """
378 _getname = _funcregistrarbase._parsefuncdecl
387 _getname = _funcregistrarbase._parsefuncdecl
379
388
380 def _extrasetup(self, name, func, argspec=None, requires=()):
389 def _extrasetup(self, name, func, argspec=None, requires=()):
381 func._argspec = argspec
390 func._argspec = argspec
382 func._requires = requires
391 func._requires = requires
383
392
384 class internalmerge(_funcregistrarbase):
393 class internalmerge(_funcregistrarbase):
385 """Decorator to register in-process merge tool
394 """Decorator to register in-process merge tool
386
395
387 Usage::
396 Usage::
388
397
389 internalmerge = registrar.internalmerge()
398 internalmerge = registrar.internalmerge()
390
399
391 @internalmerge('mymerge', internalmerge.mergeonly,
400 @internalmerge('mymerge', internalmerge.mergeonly,
392 onfailure=None, precheck=None):
401 onfailure=None, precheck=None):
393 def mymergefunc(repo, mynode, orig, fcd, fco, fca,
402 def mymergefunc(repo, mynode, orig, fcd, fco, fca,
394 toolconf, files, labels=None):
403 toolconf, files, labels=None):
395 '''Explanation of this internal merge tool ....
404 '''Explanation of this internal merge tool ....
396 '''
405 '''
397 return 1, False # means "conflicted", "no deletion needed"
406 return 1, False # means "conflicted", "no deletion needed"
398
407
399 The first string argument is used to compose actual merge tool name,
408 The first string argument is used to compose actual merge tool name,
400 ":name" and "internal:name" (the latter is historical one).
409 ":name" and "internal:name" (the latter is historical one).
401
410
402 The second argument is one of merge types below:
411 The second argument is one of merge types below:
403
412
404 ========== ======== ======== =========
413 ========== ======== ======== =========
405 merge type precheck premerge fullmerge
414 merge type precheck premerge fullmerge
406 ========== ======== ======== =========
415 ========== ======== ======== =========
407 nomerge x x x
416 nomerge x x x
408 mergeonly o x o
417 mergeonly o x o
409 fullmerge o o o
418 fullmerge o o o
410 ========== ======== ======== =========
419 ========== ======== ======== =========
411
420
412 Optional argument 'onfailure' is the format of warning message
421 Optional argument 'onfailure' is the format of warning message
413 to be used at failure of merging (target filename is specified
422 to be used at failure of merging (target filename is specified
414 at formatting). Or, None or so, if warning message should be
423 at formatting). Or, None or so, if warning message should be
415 suppressed.
424 suppressed.
416
425
417 Optional argument 'precheck' is the function to be used
426 Optional argument 'precheck' is the function to be used
418 before actual invocation of internal merge tool itself.
427 before actual invocation of internal merge tool itself.
419 It takes as same arguments as internal merge tool does, other than
428 It takes as same arguments as internal merge tool does, other than
420 'files' and 'labels'. If it returns false value, merging is aborted
429 'files' and 'labels'. If it returns false value, merging is aborted
421 immediately (and file is marked as "unresolved").
430 immediately (and file is marked as "unresolved").
422
431
423 'internalmerge' instance in example above can be used to
432 'internalmerge' instance in example above can be used to
424 decorate multiple functions.
433 decorate multiple functions.
425
434
426 Decorated functions are registered automatically at loading
435 Decorated functions are registered automatically at loading
427 extension, if an instance named as 'internalmerge' is used for
436 extension, if an instance named as 'internalmerge' is used for
428 decorating in extension.
437 decorating in extension.
429
438
430 Otherwise, explicit 'filemerge.loadinternalmerge()' is needed.
439 Otherwise, explicit 'filemerge.loadinternalmerge()' is needed.
431 """
440 """
432 _docformat = "``:%s``\n %s"
441 _docformat = "``:%s``\n %s"
433
442
434 # merge type definitions:
443 # merge type definitions:
435 nomerge = None
444 nomerge = None
436 mergeonly = 'mergeonly' # just the full merge, no premerge
445 mergeonly = 'mergeonly' # just the full merge, no premerge
437 fullmerge = 'fullmerge' # both premerge and merge
446 fullmerge = 'fullmerge' # both premerge and merge
438
447
439 def _extrasetup(self, name, func, mergetype,
448 def _extrasetup(self, name, func, mergetype,
440 onfailure=None, precheck=None):
449 onfailure=None, precheck=None):
441 func.mergetype = mergetype
450 func.mergetype = mergetype
442 func.onfailure = onfailure
451 func.onfailure = onfailure
443 func.precheck = precheck
452 func.precheck = precheck
General Comments 0
You need to be logged in to leave comments. Login now