##// END OF EJS Templates
i18n: add i18n comment to error messages of filesets predicates
FUJIWARA Katsunori -
r23113:c2dd79ad stable
parent child Browse files
Show More
@@ -1,507 +1,509
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 import re
8 import re
9 import parser, error, util, merge
9 import parser, error, util, merge
10 from i18n import _
10 from i18n import _
11
11
12 elements = {
12 elements = {
13 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
13 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
14 "-": (5, ("negate", 19), ("minus", 5)),
14 "-": (5, ("negate", 19), ("minus", 5)),
15 "not": (10, ("not", 10)),
15 "not": (10, ("not", 10)),
16 "!": (10, ("not", 10)),
16 "!": (10, ("not", 10)),
17 "and": (5, None, ("and", 5)),
17 "and": (5, None, ("and", 5)),
18 "&": (5, None, ("and", 5)),
18 "&": (5, None, ("and", 5)),
19 "or": (4, None, ("or", 4)),
19 "or": (4, None, ("or", 4)),
20 "|": (4, None, ("or", 4)),
20 "|": (4, None, ("or", 4)),
21 "+": (4, None, ("or", 4)),
21 "+": (4, None, ("or", 4)),
22 ",": (2, None, ("list", 2)),
22 ",": (2, None, ("list", 2)),
23 ")": (0, None, None),
23 ")": (0, None, None),
24 "symbol": (0, ("symbol",), None),
24 "symbol": (0, ("symbol",), None),
25 "string": (0, ("string",), None),
25 "string": (0, ("string",), None),
26 "end": (0, None, None),
26 "end": (0, None, None),
27 }
27 }
28
28
29 keywords = set(['and', 'or', 'not'])
29 keywords = set(['and', 'or', 'not'])
30
30
31 globchars = ".*{}[]?/\\_"
31 globchars = ".*{}[]?/\\_"
32
32
33 def tokenize(program):
33 def tokenize(program):
34 pos, l = 0, len(program)
34 pos, l = 0, len(program)
35 while pos < l:
35 while pos < l:
36 c = program[pos]
36 c = program[pos]
37 if c.isspace(): # skip inter-token whitespace
37 if c.isspace(): # skip inter-token whitespace
38 pass
38 pass
39 elif c in "(),-|&+!": # handle simple operators
39 elif c in "(),-|&+!": # handle simple operators
40 yield (c, None, pos)
40 yield (c, None, pos)
41 elif (c in '"\'' or c == 'r' and
41 elif (c in '"\'' or c == 'r' and
42 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
42 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
43 if c == 'r':
43 if c == 'r':
44 pos += 1
44 pos += 1
45 c = program[pos]
45 c = program[pos]
46 decode = lambda x: x
46 decode = lambda x: x
47 else:
47 else:
48 decode = lambda x: x.decode('string-escape')
48 decode = lambda x: x.decode('string-escape')
49 pos += 1
49 pos += 1
50 s = pos
50 s = pos
51 while pos < l: # find closing quote
51 while pos < l: # find closing quote
52 d = program[pos]
52 d = program[pos]
53 if d == '\\': # skip over escaped characters
53 if d == '\\': # skip over escaped characters
54 pos += 2
54 pos += 2
55 continue
55 continue
56 if d == c:
56 if d == c:
57 yield ('string', decode(program[s:pos]), s)
57 yield ('string', decode(program[s:pos]), s)
58 break
58 break
59 pos += 1
59 pos += 1
60 else:
60 else:
61 raise error.ParseError(_("unterminated string"), s)
61 raise error.ParseError(_("unterminated string"), s)
62 elif c.isalnum() or c in globchars or ord(c) > 127:
62 elif c.isalnum() or c in globchars or ord(c) > 127:
63 # gather up a symbol/keyword
63 # gather up a symbol/keyword
64 s = pos
64 s = pos
65 pos += 1
65 pos += 1
66 while pos < l: # find end of symbol
66 while pos < l: # find end of symbol
67 d = program[pos]
67 d = program[pos]
68 if not (d.isalnum() or d in globchars or ord(d) > 127):
68 if not (d.isalnum() or d in globchars or ord(d) > 127):
69 break
69 break
70 pos += 1
70 pos += 1
71 sym = program[s:pos]
71 sym = program[s:pos]
72 if sym in keywords: # operator keywords
72 if sym in keywords: # operator keywords
73 yield (sym, None, s)
73 yield (sym, None, s)
74 else:
74 else:
75 yield ('symbol', sym, s)
75 yield ('symbol', sym, s)
76 pos -= 1
76 pos -= 1
77 else:
77 else:
78 raise error.ParseError(_("syntax error"), pos)
78 raise error.ParseError(_("syntax error"), pos)
79 pos += 1
79 pos += 1
80 yield ('end', None, pos)
80 yield ('end', None, pos)
81
81
82 def parse(expr):
82 def parse(expr):
83 p = parser.parser(tokenize, elements)
83 p = parser.parser(tokenize, elements)
84 return p.parse(expr)
84 return p.parse(expr)
85
85
86 def getstring(x, err):
86 def getstring(x, err):
87 if x and (x[0] == 'string' or x[0] == 'symbol'):
87 if x and (x[0] == 'string' or x[0] == 'symbol'):
88 return x[1]
88 return x[1]
89 raise error.ParseError(err)
89 raise error.ParseError(err)
90
90
91 def getset(mctx, x):
91 def getset(mctx, x):
92 if not x:
92 if not x:
93 raise error.ParseError(_("missing argument"))
93 raise error.ParseError(_("missing argument"))
94 return methods[x[0]](mctx, *x[1:])
94 return methods[x[0]](mctx, *x[1:])
95
95
96 def stringset(mctx, x):
96 def stringset(mctx, x):
97 m = mctx.matcher([x])
97 m = mctx.matcher([x])
98 return [f for f in mctx.subset if m(f)]
98 return [f for f in mctx.subset if m(f)]
99
99
100 def andset(mctx, x, y):
100 def andset(mctx, x, y):
101 return getset(mctx.narrow(getset(mctx, x)), y)
101 return getset(mctx.narrow(getset(mctx, x)), y)
102
102
103 def orset(mctx, x, y):
103 def orset(mctx, x, y):
104 # needs optimizing
104 # needs optimizing
105 xl = getset(mctx, x)
105 xl = getset(mctx, x)
106 yl = getset(mctx, y)
106 yl = getset(mctx, y)
107 return xl + [f for f in yl if f not in xl]
107 return xl + [f for f in yl if f not in xl]
108
108
109 def notset(mctx, x):
109 def notset(mctx, x):
110 s = set(getset(mctx, x))
110 s = set(getset(mctx, x))
111 return [r for r in mctx.subset if r not in s]
111 return [r for r in mctx.subset if r not in s]
112
112
113 def minusset(mctx, x, y):
113 def minusset(mctx, x, y):
114 xl = getset(mctx, x)
114 xl = getset(mctx, x)
115 yl = set(getset(mctx, y))
115 yl = set(getset(mctx, y))
116 return [f for f in xl if f not in yl]
116 return [f for f in xl if f not in yl]
117
117
118 def listset(mctx, a, b):
118 def listset(mctx, a, b):
119 raise error.ParseError(_("can't use a list in this context"))
119 raise error.ParseError(_("can't use a list in this context"))
120
120
121 def modified(mctx, x):
121 def modified(mctx, x):
122 """``modified()``
122 """``modified()``
123 File that is modified according to status.
123 File that is modified according to status.
124 """
124 """
125 # i18n: "modified" is a keyword
125 # i18n: "modified" is a keyword
126 getargs(x, 0, 0, _("modified takes no arguments"))
126 getargs(x, 0, 0, _("modified takes no arguments"))
127 s = mctx.status().modified
127 s = mctx.status().modified
128 return [f for f in mctx.subset if f in s]
128 return [f for f in mctx.subset if f in s]
129
129
130 def added(mctx, x):
130 def added(mctx, x):
131 """``added()``
131 """``added()``
132 File that is added according to status.
132 File that is added according to status.
133 """
133 """
134 # i18n: "added" is a keyword
134 # i18n: "added" is a keyword
135 getargs(x, 0, 0, _("added takes no arguments"))
135 getargs(x, 0, 0, _("added takes no arguments"))
136 s = mctx.status().added
136 s = mctx.status().added
137 return [f for f in mctx.subset if f in s]
137 return [f for f in mctx.subset if f in s]
138
138
139 def removed(mctx, x):
139 def removed(mctx, x):
140 """``removed()``
140 """``removed()``
141 File that is removed according to status.
141 File that is removed according to status.
142 """
142 """
143 # i18n: "removed" is a keyword
143 # i18n: "removed" is a keyword
144 getargs(x, 0, 0, _("removed takes no arguments"))
144 getargs(x, 0, 0, _("removed takes no arguments"))
145 s = mctx.status().removed
145 s = mctx.status().removed
146 return [f for f in mctx.subset if f in s]
146 return [f for f in mctx.subset if f in s]
147
147
148 def deleted(mctx, x):
148 def deleted(mctx, x):
149 """``deleted()``
149 """``deleted()``
150 File that is deleted according to status.
150 File that is deleted according to status.
151 """
151 """
152 # i18n: "deleted" is a keyword
152 # i18n: "deleted" is a keyword
153 getargs(x, 0, 0, _("deleted takes no arguments"))
153 getargs(x, 0, 0, _("deleted takes no arguments"))
154 s = mctx.status().deleted
154 s = mctx.status().deleted
155 return [f for f in mctx.subset if f in s]
155 return [f for f in mctx.subset if f in s]
156
156
157 def unknown(mctx, x):
157 def unknown(mctx, x):
158 """``unknown()``
158 """``unknown()``
159 File that is unknown according to status. These files will only be
159 File that is unknown according to status. These files will only be
160 considered if this predicate is used.
160 considered if this predicate is used.
161 """
161 """
162 # i18n: "unknown" is a keyword
162 # i18n: "unknown" is a keyword
163 getargs(x, 0, 0, _("unknown takes no arguments"))
163 getargs(x, 0, 0, _("unknown takes no arguments"))
164 s = mctx.status().unknown
164 s = mctx.status().unknown
165 return [f for f in mctx.subset if f in s]
165 return [f for f in mctx.subset if f in s]
166
166
167 def ignored(mctx, x):
167 def ignored(mctx, x):
168 """``ignored()``
168 """``ignored()``
169 File that is ignored according to status. These files will only be
169 File that is ignored according to status. These files will only be
170 considered if this predicate is used.
170 considered if this predicate is used.
171 """
171 """
172 # i18n: "ignored" is a keyword
172 # i18n: "ignored" is a keyword
173 getargs(x, 0, 0, _("ignored takes no arguments"))
173 getargs(x, 0, 0, _("ignored takes no arguments"))
174 s = mctx.status().ignored
174 s = mctx.status().ignored
175 return [f for f in mctx.subset if f in s]
175 return [f for f in mctx.subset if f in s]
176
176
177 def clean(mctx, x):
177 def clean(mctx, x):
178 """``clean()``
178 """``clean()``
179 File that is clean according to status.
179 File that is clean according to status.
180 """
180 """
181 # i18n: "clean" is a keyword
181 # i18n: "clean" is a keyword
182 getargs(x, 0, 0, _("clean takes no arguments"))
182 getargs(x, 0, 0, _("clean takes no arguments"))
183 s = mctx.status().clean
183 s = mctx.status().clean
184 return [f for f in mctx.subset if f in s]
184 return [f for f in mctx.subset if f in s]
185
185
186 def func(mctx, a, b):
186 def func(mctx, a, b):
187 if a[0] == 'symbol' and a[1] in symbols:
187 if a[0] == 'symbol' and a[1] in symbols:
188 return symbols[a[1]](mctx, b)
188 return symbols[a[1]](mctx, b)
189 raise error.ParseError(_("not a function: %s") % a[1])
189 raise error.ParseError(_("not a function: %s") % a[1])
190
190
191 def getlist(x):
191 def getlist(x):
192 if not x:
192 if not x:
193 return []
193 return []
194 if x[0] == 'list':
194 if x[0] == 'list':
195 return getlist(x[1]) + [x[2]]
195 return getlist(x[1]) + [x[2]]
196 return [x]
196 return [x]
197
197
198 def getargs(x, min, max, err):
198 def getargs(x, min, max, err):
199 l = getlist(x)
199 l = getlist(x)
200 if len(l) < min or len(l) > max:
200 if len(l) < min or len(l) > max:
201 raise error.ParseError(err)
201 raise error.ParseError(err)
202 return l
202 return l
203
203
204 def binary(mctx, x):
204 def binary(mctx, x):
205 """``binary()``
205 """``binary()``
206 File that appears to be binary (contains NUL bytes).
206 File that appears to be binary (contains NUL bytes).
207 """
207 """
208 # i18n: "binary" is a keyword
208 # i18n: "binary" is a keyword
209 getargs(x, 0, 0, _("binary takes no arguments"))
209 getargs(x, 0, 0, _("binary takes no arguments"))
210 return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())]
210 return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())]
211
211
212 def exec_(mctx, x):
212 def exec_(mctx, x):
213 """``exec()``
213 """``exec()``
214 File that is marked as executable.
214 File that is marked as executable.
215 """
215 """
216 # i18n: "exec" is a keyword
216 # i18n: "exec" is a keyword
217 getargs(x, 0, 0, _("exec takes no arguments"))
217 getargs(x, 0, 0, _("exec takes no arguments"))
218 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
218 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
219
219
220 def symlink(mctx, x):
220 def symlink(mctx, x):
221 """``symlink()``
221 """``symlink()``
222 File that is marked as a symlink.
222 File that is marked as a symlink.
223 """
223 """
224 # i18n: "symlink" is a keyword
224 # i18n: "symlink" is a keyword
225 getargs(x, 0, 0, _("symlink takes no arguments"))
225 getargs(x, 0, 0, _("symlink takes no arguments"))
226 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
226 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
227
227
228 def resolved(mctx, x):
228 def resolved(mctx, x):
229 """``resolved()``
229 """``resolved()``
230 File that is marked resolved according to the resolve state.
230 File that is marked resolved according to the resolve state.
231 """
231 """
232 # i18n: "resolved" is a keyword
232 # i18n: "resolved" is a keyword
233 getargs(x, 0, 0, _("resolved takes no arguments"))
233 getargs(x, 0, 0, _("resolved takes no arguments"))
234 if mctx.ctx.rev() is not None:
234 if mctx.ctx.rev() is not None:
235 return []
235 return []
236 ms = merge.mergestate(mctx.ctx._repo)
236 ms = merge.mergestate(mctx.ctx._repo)
237 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
237 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
238
238
239 def unresolved(mctx, x):
239 def unresolved(mctx, x):
240 """``unresolved()``
240 """``unresolved()``
241 File that is marked unresolved according to the resolve state.
241 File that is marked unresolved according to the resolve state.
242 """
242 """
243 # i18n: "unresolved" is a keyword
243 # i18n: "unresolved" is a keyword
244 getargs(x, 0, 0, _("unresolved takes no arguments"))
244 getargs(x, 0, 0, _("unresolved takes no arguments"))
245 if mctx.ctx.rev() is not None:
245 if mctx.ctx.rev() is not None:
246 return []
246 return []
247 ms = merge.mergestate(mctx.ctx._repo)
247 ms = merge.mergestate(mctx.ctx._repo)
248 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
248 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
249
249
250 def hgignore(mctx, x):
250 def hgignore(mctx, x):
251 """``hgignore()``
251 """``hgignore()``
252 File that matches the active .hgignore pattern.
252 File that matches the active .hgignore pattern.
253 """
253 """
254 # i18n: "hgignore" is a keyword
254 getargs(x, 0, 0, _("hgignore takes no arguments"))
255 getargs(x, 0, 0, _("hgignore takes no arguments"))
255 ignore = mctx.ctx._repo.dirstate._ignore
256 ignore = mctx.ctx._repo.dirstate._ignore
256 return [f for f in mctx.subset if ignore(f)]
257 return [f for f in mctx.subset if ignore(f)]
257
258
258 def grep(mctx, x):
259 def grep(mctx, x):
259 """``grep(regex)``
260 """``grep(regex)``
260 File contains the given regular expression.
261 File contains the given regular expression.
261 """
262 """
262 try:
263 try:
263 # i18n: "grep" is a keyword
264 # i18n: "grep" is a keyword
264 r = re.compile(getstring(x, _("grep requires a pattern")))
265 r = re.compile(getstring(x, _("grep requires a pattern")))
265 except re.error, e:
266 except re.error, e:
266 raise error.ParseError(_('invalid match pattern: %s') % e)
267 raise error.ParseError(_('invalid match pattern: %s') % e)
267 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
268 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
268
269
269 def _sizetomax(s):
270 def _sizetomax(s):
270 try:
271 try:
271 s = s.strip()
272 s = s.strip()
272 for k, v in util._sizeunits:
273 for k, v in util._sizeunits:
273 if s.endswith(k):
274 if s.endswith(k):
274 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
275 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
275 n = s[:-len(k)]
276 n = s[:-len(k)]
276 inc = 1.0
277 inc = 1.0
277 if "." in n:
278 if "." in n:
278 inc /= 10 ** len(n.split(".")[1])
279 inc /= 10 ** len(n.split(".")[1])
279 return int((float(n) + inc) * v) - 1
280 return int((float(n) + inc) * v) - 1
280 # no extension, this is a precise value
281 # no extension, this is a precise value
281 return int(s)
282 return int(s)
282 except ValueError:
283 except ValueError:
283 raise error.ParseError(_("couldn't parse size: %s") % s)
284 raise error.ParseError(_("couldn't parse size: %s") % s)
284
285
285 def size(mctx, x):
286 def size(mctx, x):
286 """``size(expression)``
287 """``size(expression)``
287 File size matches the given expression. Examples:
288 File size matches the given expression. Examples:
288
289
289 - 1k (files from 1024 to 2047 bytes)
290 - 1k (files from 1024 to 2047 bytes)
290 - < 20k (files less than 20480 bytes)
291 - < 20k (files less than 20480 bytes)
291 - >= .5MB (files at least 524288 bytes)
292 - >= .5MB (files at least 524288 bytes)
292 - 4k - 1MB (files from 4096 bytes to 1048576 bytes)
293 - 4k - 1MB (files from 4096 bytes to 1048576 bytes)
293 """
294 """
294
295
295 # i18n: "size" is a keyword
296 # i18n: "size" is a keyword
296 expr = getstring(x, _("size requires an expression")).strip()
297 expr = getstring(x, _("size requires an expression")).strip()
297 if '-' in expr: # do we have a range?
298 if '-' in expr: # do we have a range?
298 a, b = expr.split('-', 1)
299 a, b = expr.split('-', 1)
299 a = util.sizetoint(a)
300 a = util.sizetoint(a)
300 b = util.sizetoint(b)
301 b = util.sizetoint(b)
301 m = lambda x: x >= a and x <= b
302 m = lambda x: x >= a and x <= b
302 elif expr.startswith("<="):
303 elif expr.startswith("<="):
303 a = util.sizetoint(expr[2:])
304 a = util.sizetoint(expr[2:])
304 m = lambda x: x <= a
305 m = lambda x: x <= a
305 elif expr.startswith("<"):
306 elif expr.startswith("<"):
306 a = util.sizetoint(expr[1:])
307 a = util.sizetoint(expr[1:])
307 m = lambda x: x < a
308 m = lambda x: x < a
308 elif expr.startswith(">="):
309 elif expr.startswith(">="):
309 a = util.sizetoint(expr[2:])
310 a = util.sizetoint(expr[2:])
310 m = lambda x: x >= a
311 m = lambda x: x >= a
311 elif expr.startswith(">"):
312 elif expr.startswith(">"):
312 a = util.sizetoint(expr[1:])
313 a = util.sizetoint(expr[1:])
313 m = lambda x: x > a
314 m = lambda x: x > a
314 elif expr[0].isdigit or expr[0] == '.':
315 elif expr[0].isdigit or expr[0] == '.':
315 a = util.sizetoint(expr)
316 a = util.sizetoint(expr)
316 b = _sizetomax(expr)
317 b = _sizetomax(expr)
317 m = lambda x: x >= a and x <= b
318 m = lambda x: x >= a and x <= b
318 else:
319 else:
319 raise error.ParseError(_("couldn't parse size: %s") % expr)
320 raise error.ParseError(_("couldn't parse size: %s") % expr)
320
321
321 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
322 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
322
323
323 def encoding(mctx, x):
324 def encoding(mctx, x):
324 """``encoding(name)``
325 """``encoding(name)``
325 File can be successfully decoded with the given character
326 File can be successfully decoded with the given character
326 encoding. May not be useful for encodings other than ASCII and
327 encoding. May not be useful for encodings other than ASCII and
327 UTF-8.
328 UTF-8.
328 """
329 """
329
330
330 # i18n: "encoding" is a keyword
331 # i18n: "encoding" is a keyword
331 enc = getstring(x, _("encoding requires an encoding name"))
332 enc = getstring(x, _("encoding requires an encoding name"))
332
333
333 s = []
334 s = []
334 for f in mctx.existing():
335 for f in mctx.existing():
335 d = mctx.ctx[f].data()
336 d = mctx.ctx[f].data()
336 try:
337 try:
337 d.decode(enc)
338 d.decode(enc)
338 except LookupError:
339 except LookupError:
339 raise util.Abort(_("unknown encoding '%s'") % enc)
340 raise util.Abort(_("unknown encoding '%s'") % enc)
340 except UnicodeDecodeError:
341 except UnicodeDecodeError:
341 continue
342 continue
342 s.append(f)
343 s.append(f)
343
344
344 return s
345 return s
345
346
346 def eol(mctx, x):
347 def eol(mctx, x):
347 """``eol(style)``
348 """``eol(style)``
348 File contains newlines of the given style (dos, unix, mac). Binary
349 File contains newlines of the given style (dos, unix, mac). Binary
349 files are excluded, files with mixed line endings match multiple
350 files are excluded, files with mixed line endings match multiple
350 styles.
351 styles.
351 """
352 """
352
353
353 # i18n: "encoding" is a keyword
354 # i18n: "encoding" is a keyword
354 enc = getstring(x, _("encoding requires an encoding name"))
355 enc = getstring(x, _("encoding requires an encoding name"))
355
356
356 s = []
357 s = []
357 for f in mctx.existing():
358 for f in mctx.existing():
358 d = mctx.ctx[f].data()
359 d = mctx.ctx[f].data()
359 if util.binary(d):
360 if util.binary(d):
360 continue
361 continue
361 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
362 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
362 s.append(f)
363 s.append(f)
363 elif enc == 'unix' and re.search('(?<!\r)\n', d):
364 elif enc == 'unix' and re.search('(?<!\r)\n', d):
364 s.append(f)
365 s.append(f)
365 elif enc == 'mac' and re.search('\r(?!\n)', d):
366 elif enc == 'mac' and re.search('\r(?!\n)', d):
366 s.append(f)
367 s.append(f)
367 return s
368 return s
368
369
369 def copied(mctx, x):
370 def copied(mctx, x):
370 """``copied()``
371 """``copied()``
371 File that is recorded as being copied.
372 File that is recorded as being copied.
372 """
373 """
373 # i18n: "copied" is a keyword
374 # i18n: "copied" is a keyword
374 getargs(x, 0, 0, _("copied takes no arguments"))
375 getargs(x, 0, 0, _("copied takes no arguments"))
375 s = []
376 s = []
376 for f in mctx.subset:
377 for f in mctx.subset:
377 p = mctx.ctx[f].parents()
378 p = mctx.ctx[f].parents()
378 if p and p[0].path() != f:
379 if p and p[0].path() != f:
379 s.append(f)
380 s.append(f)
380 return s
381 return s
381
382
382 def subrepo(mctx, x):
383 def subrepo(mctx, x):
383 """``subrepo([pattern])``
384 """``subrepo([pattern])``
384 Subrepositories whose paths match the given pattern.
385 Subrepositories whose paths match the given pattern.
385 """
386 """
386 # i18n: "subrepo" is a keyword
387 # i18n: "subrepo" is a keyword
387 getargs(x, 0, 1, _("subrepo takes at most one argument"))
388 getargs(x, 0, 1, _("subrepo takes at most one argument"))
388 ctx = mctx.ctx
389 ctx = mctx.ctx
389 sstate = sorted(ctx.substate)
390 sstate = sorted(ctx.substate)
390 if x:
391 if x:
392 # i18n: "subrepo" is a keyword
391 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
393 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
392
394
393 import match as matchmod # avoid circular import issues
395 import match as matchmod # avoid circular import issues
394 fast = not matchmod.patkind(pat)
396 fast = not matchmod.patkind(pat)
395 if fast:
397 if fast:
396 def m(s):
398 def m(s):
397 return (s == pat)
399 return (s == pat)
398 else:
400 else:
399 m = matchmod.match(ctx._repo.root, '', [pat], ctx=ctx)
401 m = matchmod.match(ctx._repo.root, '', [pat], ctx=ctx)
400 return [sub for sub in sstate if m(sub)]
402 return [sub for sub in sstate if m(sub)]
401 else:
403 else:
402 return [sub for sub in sstate]
404 return [sub for sub in sstate]
403
405
404 symbols = {
406 symbols = {
405 'added': added,
407 'added': added,
406 'binary': binary,
408 'binary': binary,
407 'clean': clean,
409 'clean': clean,
408 'copied': copied,
410 'copied': copied,
409 'deleted': deleted,
411 'deleted': deleted,
410 'encoding': encoding,
412 'encoding': encoding,
411 'eol': eol,
413 'eol': eol,
412 'exec': exec_,
414 'exec': exec_,
413 'grep': grep,
415 'grep': grep,
414 'ignored': ignored,
416 'ignored': ignored,
415 'hgignore': hgignore,
417 'hgignore': hgignore,
416 'modified': modified,
418 'modified': modified,
417 'removed': removed,
419 'removed': removed,
418 'resolved': resolved,
420 'resolved': resolved,
419 'size': size,
421 'size': size,
420 'symlink': symlink,
422 'symlink': symlink,
421 'unknown': unknown,
423 'unknown': unknown,
422 'unresolved': unresolved,
424 'unresolved': unresolved,
423 'subrepo': subrepo,
425 'subrepo': subrepo,
424 }
426 }
425
427
426 methods = {
428 methods = {
427 'string': stringset,
429 'string': stringset,
428 'symbol': stringset,
430 'symbol': stringset,
429 'and': andset,
431 'and': andset,
430 'or': orset,
432 'or': orset,
431 'minus': minusset,
433 'minus': minusset,
432 'list': listset,
434 'list': listset,
433 'group': getset,
435 'group': getset,
434 'not': notset,
436 'not': notset,
435 'func': func,
437 'func': func,
436 }
438 }
437
439
438 class matchctx(object):
440 class matchctx(object):
439 def __init__(self, ctx, subset=None, status=None):
441 def __init__(self, ctx, subset=None, status=None):
440 self.ctx = ctx
442 self.ctx = ctx
441 self.subset = subset
443 self.subset = subset
442 self._status = status
444 self._status = status
443 def status(self):
445 def status(self):
444 return self._status
446 return self._status
445 def matcher(self, patterns):
447 def matcher(self, patterns):
446 return self.ctx.match(patterns)
448 return self.ctx.match(patterns)
447 def filter(self, files):
449 def filter(self, files):
448 return [f for f in files if f in self.subset]
450 return [f for f in files if f in self.subset]
449 def existing(self):
451 def existing(self):
450 if self._status is not None:
452 if self._status is not None:
451 removed = set(self._status[3])
453 removed = set(self._status[3])
452 unknown = set(self._status[4] + self._status[5])
454 unknown = set(self._status[4] + self._status[5])
453 else:
455 else:
454 removed = set()
456 removed = set()
455 unknown = set()
457 unknown = set()
456 return (f for f in self.subset
458 return (f for f in self.subset
457 if (f in self.ctx and f not in removed) or f in unknown)
459 if (f in self.ctx and f not in removed) or f in unknown)
458 def narrow(self, files):
460 def narrow(self, files):
459 return matchctx(self.ctx, self.filter(files), self._status)
461 return matchctx(self.ctx, self.filter(files), self._status)
460
462
461 def _intree(funcs, tree):
463 def _intree(funcs, tree):
462 if isinstance(tree, tuple):
464 if isinstance(tree, tuple):
463 if tree[0] == 'func' and tree[1][0] == 'symbol':
465 if tree[0] == 'func' and tree[1][0] == 'symbol':
464 if tree[1][1] in funcs:
466 if tree[1][1] in funcs:
465 return True
467 return True
466 for s in tree[1:]:
468 for s in tree[1:]:
467 if _intree(funcs, s):
469 if _intree(funcs, s):
468 return True
470 return True
469 return False
471 return False
470
472
471 # filesets using matchctx.existing()
473 # filesets using matchctx.existing()
472 _existingcallers = [
474 _existingcallers = [
473 'binary',
475 'binary',
474 'exec',
476 'exec',
475 'grep',
477 'grep',
476 'size',
478 'size',
477 'symlink',
479 'symlink',
478 ]
480 ]
479
481
480 def getfileset(ctx, expr):
482 def getfileset(ctx, expr):
481 tree, pos = parse(expr)
483 tree, pos = parse(expr)
482 if (pos != len(expr)):
484 if (pos != len(expr)):
483 raise error.ParseError(_("invalid token"), pos)
485 raise error.ParseError(_("invalid token"), pos)
484
486
485 # do we need status info?
487 # do we need status info?
486 if (_intree(['modified', 'added', 'removed', 'deleted',
488 if (_intree(['modified', 'added', 'removed', 'deleted',
487 'unknown', 'ignored', 'clean'], tree) or
489 'unknown', 'ignored', 'clean'], tree) or
488 # Using matchctx.existing() on a workingctx requires us to check
490 # Using matchctx.existing() on a workingctx requires us to check
489 # for deleted files.
491 # for deleted files.
490 (ctx.rev() is None and _intree(_existingcallers, tree))):
492 (ctx.rev() is None and _intree(_existingcallers, tree))):
491 unknown = _intree(['unknown'], tree)
493 unknown = _intree(['unknown'], tree)
492 ignored = _intree(['ignored'], tree)
494 ignored = _intree(['ignored'], tree)
493
495
494 r = ctx._repo
496 r = ctx._repo
495 status = r.status(ctx.p1(), ctx,
497 status = r.status(ctx.p1(), ctx,
496 unknown=unknown, ignored=ignored, clean=True)
498 unknown=unknown, ignored=ignored, clean=True)
497 subset = []
499 subset = []
498 for c in status:
500 for c in status:
499 subset.extend(c)
501 subset.extend(c)
500 else:
502 else:
501 status = None
503 status = None
502 subset = list(ctx.walk(ctx.match([])))
504 subset = list(ctx.walk(ctx.match([])))
503
505
504 return getset(matchctx(ctx, subset, status), tree)
506 return getset(matchctx(ctx, subset, status), tree)
505
507
506 # tell hggettext to extract docstrings from these functions:
508 # tell hggettext to extract docstrings from these functions:
507 i18nfunctions = symbols.values()
509 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now