##// END OF EJS Templates
fileset: use UnknownIdentifier where appropriate
Augie Fackler -
r24218:cecaec0e default
parent child Browse files
Show More
@@ -1,509 +1,509 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 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.UnknownIdentifier(a[1], symbols.keys())
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 # i18n: "hgignore" is a keyword
255 getargs(x, 0, 0, _("hgignore takes no arguments"))
255 getargs(x, 0, 0, _("hgignore takes no arguments"))
256 ignore = mctx.ctx._repo.dirstate._ignore
256 ignore = mctx.ctx._repo.dirstate._ignore
257 return [f for f in mctx.subset if ignore(f)]
257 return [f for f in mctx.subset if ignore(f)]
258
258
259 def grep(mctx, x):
259 def grep(mctx, x):
260 """``grep(regex)``
260 """``grep(regex)``
261 File contains the given regular expression.
261 File contains the given regular expression.
262 """
262 """
263 try:
263 try:
264 # i18n: "grep" is a keyword
264 # i18n: "grep" is a keyword
265 r = re.compile(getstring(x, _("grep requires a pattern")))
265 r = re.compile(getstring(x, _("grep requires a pattern")))
266 except re.error, e:
266 except re.error, e:
267 raise error.ParseError(_('invalid match pattern: %s') % e)
267 raise error.ParseError(_('invalid match pattern: %s') % e)
268 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())]
269
269
270 def _sizetomax(s):
270 def _sizetomax(s):
271 try:
271 try:
272 s = s.strip()
272 s = s.strip()
273 for k, v in util._sizeunits:
273 for k, v in util._sizeunits:
274 if s.endswith(k):
274 if s.endswith(k):
275 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
275 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
276 n = s[:-len(k)]
276 n = s[:-len(k)]
277 inc = 1.0
277 inc = 1.0
278 if "." in n:
278 if "." in n:
279 inc /= 10 ** len(n.split(".")[1])
279 inc /= 10 ** len(n.split(".")[1])
280 return int((float(n) + inc) * v) - 1
280 return int((float(n) + inc) * v) - 1
281 # no extension, this is a precise value
281 # no extension, this is a precise value
282 return int(s)
282 return int(s)
283 except ValueError:
283 except ValueError:
284 raise error.ParseError(_("couldn't parse size: %s") % s)
284 raise error.ParseError(_("couldn't parse size: %s") % s)
285
285
286 def size(mctx, x):
286 def size(mctx, x):
287 """``size(expression)``
287 """``size(expression)``
288 File size matches the given expression. Examples:
288 File size matches the given expression. Examples:
289
289
290 - 1k (files from 1024 to 2047 bytes)
290 - 1k (files from 1024 to 2047 bytes)
291 - < 20k (files less than 20480 bytes)
291 - < 20k (files less than 20480 bytes)
292 - >= .5MB (files at least 524288 bytes)
292 - >= .5MB (files at least 524288 bytes)
293 - 4k - 1MB (files from 4096 bytes to 1048576 bytes)
293 - 4k - 1MB (files from 4096 bytes to 1048576 bytes)
294 """
294 """
295
295
296 # i18n: "size" is a keyword
296 # i18n: "size" is a keyword
297 expr = getstring(x, _("size requires an expression")).strip()
297 expr = getstring(x, _("size requires an expression")).strip()
298 if '-' in expr: # do we have a range?
298 if '-' in expr: # do we have a range?
299 a, b = expr.split('-', 1)
299 a, b = expr.split('-', 1)
300 a = util.sizetoint(a)
300 a = util.sizetoint(a)
301 b = util.sizetoint(b)
301 b = util.sizetoint(b)
302 m = lambda x: x >= a and x <= b
302 m = lambda x: x >= a and x <= b
303 elif expr.startswith("<="):
303 elif expr.startswith("<="):
304 a = util.sizetoint(expr[2:])
304 a = util.sizetoint(expr[2:])
305 m = lambda x: x <= a
305 m = lambda x: x <= a
306 elif expr.startswith("<"):
306 elif expr.startswith("<"):
307 a = util.sizetoint(expr[1:])
307 a = util.sizetoint(expr[1:])
308 m = lambda x: x < a
308 m = lambda x: x < a
309 elif expr.startswith(">="):
309 elif expr.startswith(">="):
310 a = util.sizetoint(expr[2:])
310 a = util.sizetoint(expr[2:])
311 m = lambda x: x >= a
311 m = lambda x: x >= a
312 elif expr.startswith(">"):
312 elif expr.startswith(">"):
313 a = util.sizetoint(expr[1:])
313 a = util.sizetoint(expr[1:])
314 m = lambda x: x > a
314 m = lambda x: x > a
315 elif expr[0].isdigit or expr[0] == '.':
315 elif expr[0].isdigit or expr[0] == '.':
316 a = util.sizetoint(expr)
316 a = util.sizetoint(expr)
317 b = _sizetomax(expr)
317 b = _sizetomax(expr)
318 m = lambda x: x >= a and x <= b
318 m = lambda x: x >= a and x <= b
319 else:
319 else:
320 raise error.ParseError(_("couldn't parse size: %s") % expr)
320 raise error.ParseError(_("couldn't parse size: %s") % expr)
321
321
322 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())]
323
323
324 def encoding(mctx, x):
324 def encoding(mctx, x):
325 """``encoding(name)``
325 """``encoding(name)``
326 File can be successfully decoded with the given character
326 File can be successfully decoded with the given character
327 encoding. May not be useful for encodings other than ASCII and
327 encoding. May not be useful for encodings other than ASCII and
328 UTF-8.
328 UTF-8.
329 """
329 """
330
330
331 # i18n: "encoding" is a keyword
331 # i18n: "encoding" is a keyword
332 enc = getstring(x, _("encoding requires an encoding name"))
332 enc = getstring(x, _("encoding requires an encoding name"))
333
333
334 s = []
334 s = []
335 for f in mctx.existing():
335 for f in mctx.existing():
336 d = mctx.ctx[f].data()
336 d = mctx.ctx[f].data()
337 try:
337 try:
338 d.decode(enc)
338 d.decode(enc)
339 except LookupError:
339 except LookupError:
340 raise util.Abort(_("unknown encoding '%s'") % enc)
340 raise util.Abort(_("unknown encoding '%s'") % enc)
341 except UnicodeDecodeError:
341 except UnicodeDecodeError:
342 continue
342 continue
343 s.append(f)
343 s.append(f)
344
344
345 return s
345 return s
346
346
347 def eol(mctx, x):
347 def eol(mctx, x):
348 """``eol(style)``
348 """``eol(style)``
349 File contains newlines of the given style (dos, unix, mac). Binary
349 File contains newlines of the given style (dos, unix, mac). Binary
350 files are excluded, files with mixed line endings match multiple
350 files are excluded, files with mixed line endings match multiple
351 styles.
351 styles.
352 """
352 """
353
353
354 # i18n: "encoding" is a keyword
354 # i18n: "encoding" is a keyword
355 enc = getstring(x, _("encoding requires an encoding name"))
355 enc = getstring(x, _("encoding requires an encoding name"))
356
356
357 s = []
357 s = []
358 for f in mctx.existing():
358 for f in mctx.existing():
359 d = mctx.ctx[f].data()
359 d = mctx.ctx[f].data()
360 if util.binary(d):
360 if util.binary(d):
361 continue
361 continue
362 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
362 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
363 s.append(f)
363 s.append(f)
364 elif enc == 'unix' and re.search('(?<!\r)\n', d):
364 elif enc == 'unix' and re.search('(?<!\r)\n', d):
365 s.append(f)
365 s.append(f)
366 elif enc == 'mac' and re.search('\r(?!\n)', d):
366 elif enc == 'mac' and re.search('\r(?!\n)', d):
367 s.append(f)
367 s.append(f)
368 return s
368 return s
369
369
370 def copied(mctx, x):
370 def copied(mctx, x):
371 """``copied()``
371 """``copied()``
372 File that is recorded as being copied.
372 File that is recorded as being copied.
373 """
373 """
374 # i18n: "copied" is a keyword
374 # i18n: "copied" is a keyword
375 getargs(x, 0, 0, _("copied takes no arguments"))
375 getargs(x, 0, 0, _("copied takes no arguments"))
376 s = []
376 s = []
377 for f in mctx.subset:
377 for f in mctx.subset:
378 p = mctx.ctx[f].parents()
378 p = mctx.ctx[f].parents()
379 if p and p[0].path() != f:
379 if p and p[0].path() != f:
380 s.append(f)
380 s.append(f)
381 return s
381 return s
382
382
383 def subrepo(mctx, x):
383 def subrepo(mctx, x):
384 """``subrepo([pattern])``
384 """``subrepo([pattern])``
385 Subrepositories whose paths match the given pattern.
385 Subrepositories whose paths match the given pattern.
386 """
386 """
387 # i18n: "subrepo" is a keyword
387 # i18n: "subrepo" is a keyword
388 getargs(x, 0, 1, _("subrepo takes at most one argument"))
388 getargs(x, 0, 1, _("subrepo takes at most one argument"))
389 ctx = mctx.ctx
389 ctx = mctx.ctx
390 sstate = sorted(ctx.substate)
390 sstate = sorted(ctx.substate)
391 if x:
391 if x:
392 # i18n: "subrepo" is a keyword
392 # i18n: "subrepo" is a keyword
393 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
393 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
394
394
395 import match as matchmod # avoid circular import issues
395 import match as matchmod # avoid circular import issues
396 fast = not matchmod.patkind(pat)
396 fast = not matchmod.patkind(pat)
397 if fast:
397 if fast:
398 def m(s):
398 def m(s):
399 return (s == pat)
399 return (s == pat)
400 else:
400 else:
401 m = matchmod.match(ctx._repo.root, '', [pat], ctx=ctx)
401 m = matchmod.match(ctx._repo.root, '', [pat], ctx=ctx)
402 return [sub for sub in sstate if m(sub)]
402 return [sub for sub in sstate if m(sub)]
403 else:
403 else:
404 return [sub for sub in sstate]
404 return [sub for sub in sstate]
405
405
406 symbols = {
406 symbols = {
407 'added': added,
407 'added': added,
408 'binary': binary,
408 'binary': binary,
409 'clean': clean,
409 'clean': clean,
410 'copied': copied,
410 'copied': copied,
411 'deleted': deleted,
411 'deleted': deleted,
412 'encoding': encoding,
412 'encoding': encoding,
413 'eol': eol,
413 'eol': eol,
414 'exec': exec_,
414 'exec': exec_,
415 'grep': grep,
415 'grep': grep,
416 'ignored': ignored,
416 'ignored': ignored,
417 'hgignore': hgignore,
417 'hgignore': hgignore,
418 'modified': modified,
418 'modified': modified,
419 'removed': removed,
419 'removed': removed,
420 'resolved': resolved,
420 'resolved': resolved,
421 'size': size,
421 'size': size,
422 'symlink': symlink,
422 'symlink': symlink,
423 'unknown': unknown,
423 'unknown': unknown,
424 'unresolved': unresolved,
424 'unresolved': unresolved,
425 'subrepo': subrepo,
425 'subrepo': subrepo,
426 }
426 }
427
427
428 methods = {
428 methods = {
429 'string': stringset,
429 'string': stringset,
430 'symbol': stringset,
430 'symbol': stringset,
431 'and': andset,
431 'and': andset,
432 'or': orset,
432 'or': orset,
433 'minus': minusset,
433 'minus': minusset,
434 'list': listset,
434 'list': listset,
435 'group': getset,
435 'group': getset,
436 'not': notset,
436 'not': notset,
437 'func': func,
437 'func': func,
438 }
438 }
439
439
440 class matchctx(object):
440 class matchctx(object):
441 def __init__(self, ctx, subset=None, status=None):
441 def __init__(self, ctx, subset=None, status=None):
442 self.ctx = ctx
442 self.ctx = ctx
443 self.subset = subset
443 self.subset = subset
444 self._status = status
444 self._status = status
445 def status(self):
445 def status(self):
446 return self._status
446 return self._status
447 def matcher(self, patterns):
447 def matcher(self, patterns):
448 return self.ctx.match(patterns)
448 return self.ctx.match(patterns)
449 def filter(self, files):
449 def filter(self, files):
450 return [f for f in files if f in self.subset]
450 return [f for f in files if f in self.subset]
451 def existing(self):
451 def existing(self):
452 if self._status is not None:
452 if self._status is not None:
453 removed = set(self._status[3])
453 removed = set(self._status[3])
454 unknown = set(self._status[4] + self._status[5])
454 unknown = set(self._status[4] + self._status[5])
455 else:
455 else:
456 removed = set()
456 removed = set()
457 unknown = set()
457 unknown = set()
458 return (f for f in self.subset
458 return (f for f in self.subset
459 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)
460 def narrow(self, files):
460 def narrow(self, files):
461 return matchctx(self.ctx, self.filter(files), self._status)
461 return matchctx(self.ctx, self.filter(files), self._status)
462
462
463 def _intree(funcs, tree):
463 def _intree(funcs, tree):
464 if isinstance(tree, tuple):
464 if isinstance(tree, tuple):
465 if tree[0] == 'func' and tree[1][0] == 'symbol':
465 if tree[0] == 'func' and tree[1][0] == 'symbol':
466 if tree[1][1] in funcs:
466 if tree[1][1] in funcs:
467 return True
467 return True
468 for s in tree[1:]:
468 for s in tree[1:]:
469 if _intree(funcs, s):
469 if _intree(funcs, s):
470 return True
470 return True
471 return False
471 return False
472
472
473 # filesets using matchctx.existing()
473 # filesets using matchctx.existing()
474 _existingcallers = [
474 _existingcallers = [
475 'binary',
475 'binary',
476 'exec',
476 'exec',
477 'grep',
477 'grep',
478 'size',
478 'size',
479 'symlink',
479 'symlink',
480 ]
480 ]
481
481
482 def getfileset(ctx, expr):
482 def getfileset(ctx, expr):
483 tree, pos = parse(expr)
483 tree, pos = parse(expr)
484 if (pos != len(expr)):
484 if (pos != len(expr)):
485 raise error.ParseError(_("invalid token"), pos)
485 raise error.ParseError(_("invalid token"), pos)
486
486
487 # do we need status info?
487 # do we need status info?
488 if (_intree(['modified', 'added', 'removed', 'deleted',
488 if (_intree(['modified', 'added', 'removed', 'deleted',
489 'unknown', 'ignored', 'clean'], tree) or
489 'unknown', 'ignored', 'clean'], tree) or
490 # Using matchctx.existing() on a workingctx requires us to check
490 # Using matchctx.existing() on a workingctx requires us to check
491 # for deleted files.
491 # for deleted files.
492 (ctx.rev() is None and _intree(_existingcallers, tree))):
492 (ctx.rev() is None and _intree(_existingcallers, tree))):
493 unknown = _intree(['unknown'], tree)
493 unknown = _intree(['unknown'], tree)
494 ignored = _intree(['ignored'], tree)
494 ignored = _intree(['ignored'], tree)
495
495
496 r = ctx._repo
496 r = ctx._repo
497 status = r.status(ctx.p1(), ctx,
497 status = r.status(ctx.p1(), ctx,
498 unknown=unknown, ignored=ignored, clean=True)
498 unknown=unknown, ignored=ignored, clean=True)
499 subset = []
499 subset = []
500 for c in status:
500 for c in status:
501 subset.extend(c)
501 subset.extend(c)
502 else:
502 else:
503 status = None
503 status = None
504 subset = list(ctx.walk(ctx.match([])))
504 subset = list(ctx.walk(ctx.match([])))
505
505
506 return getset(matchctx(ctx, subset, status), tree)
506 return getset(matchctx(ctx, subset, status), tree)
507
507
508 # tell hggettext to extract docstrings from these functions:
508 # tell hggettext to extract docstrings from these functions:
509 i18nfunctions = symbols.values()
509 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now