##// END OF EJS Templates
revset: fix undefined name ParseError
Brodie Rao -
r14057:ef1217a7 default
parent child Browse files
Show More
@@ -1,845 +1,845 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision 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, util, error, discovery, help, hbisect
9 import parser, util, error, discovery, help, hbisect
10 import bookmarks as bookmarksmod
10 import bookmarks as bookmarksmod
11 import match as matchmod
11 import match as matchmod
12 from i18n import _
12 from i18n import _
13
13
14 elements = {
14 elements = {
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
16 "-": (5, ("negate", 19), ("minus", 5)),
16 "-": (5, ("negate", 19), ("minus", 5)),
17 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
17 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
18 ("dagrangepost", 17)),
18 ("dagrangepost", 17)),
19 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
19 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
20 ("dagrangepost", 17)),
20 ("dagrangepost", 17)),
21 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
21 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
22 "not": (10, ("not", 10)),
22 "not": (10, ("not", 10)),
23 "!": (10, ("not", 10)),
23 "!": (10, ("not", 10)),
24 "and": (5, None, ("and", 5)),
24 "and": (5, None, ("and", 5)),
25 "&": (5, None, ("and", 5)),
25 "&": (5, None, ("and", 5)),
26 "or": (4, None, ("or", 4)),
26 "or": (4, None, ("or", 4)),
27 "|": (4, None, ("or", 4)),
27 "|": (4, None, ("or", 4)),
28 "+": (4, None, ("or", 4)),
28 "+": (4, None, ("or", 4)),
29 ",": (2, None, ("list", 2)),
29 ",": (2, None, ("list", 2)),
30 ")": (0, None, None),
30 ")": (0, None, None),
31 "symbol": (0, ("symbol",), None),
31 "symbol": (0, ("symbol",), None),
32 "string": (0, ("string",), None),
32 "string": (0, ("string",), None),
33 "end": (0, None, None),
33 "end": (0, None, None),
34 }
34 }
35
35
36 keywords = set(['and', 'or', 'not'])
36 keywords = set(['and', 'or', 'not'])
37
37
38 def tokenize(program):
38 def tokenize(program):
39 pos, l = 0, len(program)
39 pos, l = 0, len(program)
40 while pos < l:
40 while pos < l:
41 c = program[pos]
41 c = program[pos]
42 if c.isspace(): # skip inter-token whitespace
42 if c.isspace(): # skip inter-token whitespace
43 pass
43 pass
44 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
44 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
45 yield ('::', None, pos)
45 yield ('::', None, pos)
46 pos += 1 # skip ahead
46 pos += 1 # skip ahead
47 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
47 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
48 yield ('..', None, pos)
48 yield ('..', None, pos)
49 pos += 1 # skip ahead
49 pos += 1 # skip ahead
50 elif c in "():,-|&+!": # handle simple operators
50 elif c in "():,-|&+!": # handle simple operators
51 yield (c, None, pos)
51 yield (c, None, pos)
52 elif (c in '"\'' or c == 'r' and
52 elif (c in '"\'' or c == 'r' and
53 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
53 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
54 if c == 'r':
54 if c == 'r':
55 pos += 1
55 pos += 1
56 c = program[pos]
56 c = program[pos]
57 decode = lambda x: x
57 decode = lambda x: x
58 else:
58 else:
59 decode = lambda x: x.decode('string-escape')
59 decode = lambda x: x.decode('string-escape')
60 pos += 1
60 pos += 1
61 s = pos
61 s = pos
62 while pos < l: # find closing quote
62 while pos < l: # find closing quote
63 d = program[pos]
63 d = program[pos]
64 if d == '\\': # skip over escaped characters
64 if d == '\\': # skip over escaped characters
65 pos += 2
65 pos += 2
66 continue
66 continue
67 if d == c:
67 if d == c:
68 yield ('string', decode(program[s:pos]), s)
68 yield ('string', decode(program[s:pos]), s)
69 break
69 break
70 pos += 1
70 pos += 1
71 else:
71 else:
72 raise error.ParseError(_("unterminated string"), s)
72 raise error.ParseError(_("unterminated string"), s)
73 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
73 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
74 s = pos
74 s = pos
75 pos += 1
75 pos += 1
76 while pos < l: # find end of symbol
76 while pos < l: # find end of symbol
77 d = program[pos]
77 d = program[pos]
78 if not (d.isalnum() or d in "._" or ord(d) > 127):
78 if not (d.isalnum() or d in "._" or ord(d) > 127):
79 break
79 break
80 if d == '.' and program[pos - 1] == '.': # special case for ..
80 if d == '.' and program[pos - 1] == '.': # special case for ..
81 pos -= 1
81 pos -= 1
82 break
82 break
83 pos += 1
83 pos += 1
84 sym = program[s:pos]
84 sym = program[s:pos]
85 if sym in keywords: # operator keywords
85 if sym in keywords: # operator keywords
86 yield (sym, None, s)
86 yield (sym, None, s)
87 else:
87 else:
88 yield ('symbol', sym, s)
88 yield ('symbol', sym, s)
89 pos -= 1
89 pos -= 1
90 else:
90 else:
91 raise error.ParseError(_("syntax error"), pos)
91 raise error.ParseError(_("syntax error"), pos)
92 pos += 1
92 pos += 1
93 yield ('end', None, pos)
93 yield ('end', None, pos)
94
94
95 # helpers
95 # helpers
96
96
97 def getstring(x, err):
97 def getstring(x, err):
98 if x and (x[0] == 'string' or x[0] == 'symbol'):
98 if x and (x[0] == 'string' or x[0] == 'symbol'):
99 return x[1]
99 return x[1]
100 raise error.ParseError(err)
100 raise error.ParseError(err)
101
101
102 def getlist(x):
102 def getlist(x):
103 if not x:
103 if not x:
104 return []
104 return []
105 if x[0] == 'list':
105 if x[0] == 'list':
106 return getlist(x[1]) + [x[2]]
106 return getlist(x[1]) + [x[2]]
107 return [x]
107 return [x]
108
108
109 def getargs(x, min, max, err):
109 def getargs(x, min, max, err):
110 l = getlist(x)
110 l = getlist(x)
111 if len(l) < min or len(l) > max:
111 if len(l) < min or len(l) > max:
112 raise error.ParseError(err)
112 raise error.ParseError(err)
113 return l
113 return l
114
114
115 def getset(repo, subset, x):
115 def getset(repo, subset, x):
116 if not x:
116 if not x:
117 raise error.ParseError(_("missing argument"))
117 raise error.ParseError(_("missing argument"))
118 return methods[x[0]](repo, subset, *x[1:])
118 return methods[x[0]](repo, subset, *x[1:])
119
119
120 # operator methods
120 # operator methods
121
121
122 def stringset(repo, subset, x):
122 def stringset(repo, subset, x):
123 x = repo[x].rev()
123 x = repo[x].rev()
124 if x == -1 and len(subset) == len(repo):
124 if x == -1 and len(subset) == len(repo):
125 return [-1]
125 return [-1]
126 if len(subset) == len(repo) or x in subset:
126 if len(subset) == len(repo) or x in subset:
127 return [x]
127 return [x]
128 return []
128 return []
129
129
130 def symbolset(repo, subset, x):
130 def symbolset(repo, subset, x):
131 if x in symbols:
131 if x in symbols:
132 raise error.ParseError(_("can't use %s here") % x)
132 raise error.ParseError(_("can't use %s here") % x)
133 return stringset(repo, subset, x)
133 return stringset(repo, subset, x)
134
134
135 def rangeset(repo, subset, x, y):
135 def rangeset(repo, subset, x, y):
136 m = getset(repo, subset, x)
136 m = getset(repo, subset, x)
137 if not m:
137 if not m:
138 m = getset(repo, range(len(repo)), x)
138 m = getset(repo, range(len(repo)), x)
139
139
140 n = getset(repo, subset, y)
140 n = getset(repo, subset, y)
141 if not n:
141 if not n:
142 n = getset(repo, range(len(repo)), y)
142 n = getset(repo, range(len(repo)), y)
143
143
144 if not m or not n:
144 if not m or not n:
145 return []
145 return []
146 m, n = m[0], n[-1]
146 m, n = m[0], n[-1]
147
147
148 if m < n:
148 if m < n:
149 r = range(m, n + 1)
149 r = range(m, n + 1)
150 else:
150 else:
151 r = range(m, n - 1, -1)
151 r = range(m, n - 1, -1)
152 s = set(subset)
152 s = set(subset)
153 return [x for x in r if x in s]
153 return [x for x in r if x in s]
154
154
155 def andset(repo, subset, x, y):
155 def andset(repo, subset, x, y):
156 return getset(repo, getset(repo, subset, x), y)
156 return getset(repo, getset(repo, subset, x), y)
157
157
158 def orset(repo, subset, x, y):
158 def orset(repo, subset, x, y):
159 xl = getset(repo, subset, x)
159 xl = getset(repo, subset, x)
160 s = set(xl)
160 s = set(xl)
161 yl = getset(repo, [r for r in subset if r not in s], y)
161 yl = getset(repo, [r for r in subset if r not in s], y)
162 return xl + yl
162 return xl + yl
163
163
164 def notset(repo, subset, x):
164 def notset(repo, subset, x):
165 s = set(getset(repo, subset, x))
165 s = set(getset(repo, subset, x))
166 return [r for r in subset if r not in s]
166 return [r for r in subset if r not in s]
167
167
168 def listset(repo, subset, a, b):
168 def listset(repo, subset, a, b):
169 raise error.ParseError(_("can't use a list in this context"))
169 raise error.ParseError(_("can't use a list in this context"))
170
170
171 def func(repo, subset, a, b):
171 def func(repo, subset, a, b):
172 if a[0] == 'symbol' and a[1] in symbols:
172 if a[0] == 'symbol' and a[1] in symbols:
173 return symbols[a[1]](repo, subset, b)
173 return symbols[a[1]](repo, subset, b)
174 raise error.ParseError(_("not a function: %s") % a[1])
174 raise error.ParseError(_("not a function: %s") % a[1])
175
175
176 # functions
176 # functions
177
177
178 def adds(repo, subset, x):
178 def adds(repo, subset, x):
179 """``adds(pattern)``
179 """``adds(pattern)``
180 Changesets that add a file matching pattern.
180 Changesets that add a file matching pattern.
181 """
181 """
182 # i18n: "adds" is a keyword
182 # i18n: "adds" is a keyword
183 pat = getstring(x, _("adds requires a pattern"))
183 pat = getstring(x, _("adds requires a pattern"))
184 return checkstatus(repo, subset, pat, 1)
184 return checkstatus(repo, subset, pat, 1)
185
185
186 def ancestor(repo, subset, x):
186 def ancestor(repo, subset, x):
187 """``ancestor(single, single)``
187 """``ancestor(single, single)``
188 Greatest common ancestor of the two changesets.
188 Greatest common ancestor of the two changesets.
189 """
189 """
190 # i18n: "ancestor" is a keyword
190 # i18n: "ancestor" is a keyword
191 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
191 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
192 r = range(len(repo))
192 r = range(len(repo))
193 a = getset(repo, r, l[0])
193 a = getset(repo, r, l[0])
194 b = getset(repo, r, l[1])
194 b = getset(repo, r, l[1])
195 if len(a) != 1 or len(b) != 1:
195 if len(a) != 1 or len(b) != 1:
196 # i18n: "ancestor" is a keyword
196 # i18n: "ancestor" is a keyword
197 raise error.ParseError(_("ancestor arguments must be single revisions"))
197 raise error.ParseError(_("ancestor arguments must be single revisions"))
198 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
198 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
199
199
200 return [r for r in an if r in subset]
200 return [r for r in an if r in subset]
201
201
202 def ancestors(repo, subset, x):
202 def ancestors(repo, subset, x):
203 """``ancestors(set)``
203 """``ancestors(set)``
204 Changesets that are ancestors of a changeset in set.
204 Changesets that are ancestors of a changeset in set.
205 """
205 """
206 args = getset(repo, range(len(repo)), x)
206 args = getset(repo, range(len(repo)), x)
207 if not args:
207 if not args:
208 return []
208 return []
209 s = set(repo.changelog.ancestors(*args)) | set(args)
209 s = set(repo.changelog.ancestors(*args)) | set(args)
210 return [r for r in subset if r in s]
210 return [r for r in subset if r in s]
211
211
212 def author(repo, subset, x):
212 def author(repo, subset, x):
213 """``author(string)``
213 """``author(string)``
214 Alias for ``user(string)``.
214 Alias for ``user(string)``.
215 """
215 """
216 # i18n: "author" is a keyword
216 # i18n: "author" is a keyword
217 n = getstring(x, _("author requires a string")).lower()
217 n = getstring(x, _("author requires a string")).lower()
218 return [r for r in subset if n in repo[r].user().lower()]
218 return [r for r in subset if n in repo[r].user().lower()]
219
219
220 def bisected(repo, subset, x):
220 def bisected(repo, subset, x):
221 """``bisected(string)``
221 """``bisected(string)``
222 Changesets marked in the specified bisect state (good, bad, skip).
222 Changesets marked in the specified bisect state (good, bad, skip).
223 """
223 """
224 state = getstring(x, _("bisect requires a string")).lower()
224 state = getstring(x, _("bisect requires a string")).lower()
225 if state not in ('good', 'bad', 'skip', 'unknown'):
225 if state not in ('good', 'bad', 'skip', 'unknown'):
226 raise ParseError(_('invalid bisect state'))
226 raise error.ParseError(_('invalid bisect state'))
227 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
227 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
228 return [r for r in subset if r in marked]
228 return [r for r in subset if r in marked]
229
229
230 def bookmark(repo, subset, x):
230 def bookmark(repo, subset, x):
231 """``bookmark([name])``
231 """``bookmark([name])``
232 The named bookmark or all bookmarks.
232 The named bookmark or all bookmarks.
233 """
233 """
234 # i18n: "bookmark" is a keyword
234 # i18n: "bookmark" is a keyword
235 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
235 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
236 if args:
236 if args:
237 bm = getstring(args[0],
237 bm = getstring(args[0],
238 # i18n: "bookmark" is a keyword
238 # i18n: "bookmark" is a keyword
239 _('the argument to bookmark must be a string'))
239 _('the argument to bookmark must be a string'))
240 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
240 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
241 if not bmrev:
241 if not bmrev:
242 raise util.Abort(_("bookmark '%s' does not exist") % bm)
242 raise util.Abort(_("bookmark '%s' does not exist") % bm)
243 bmrev = repo[bmrev].rev()
243 bmrev = repo[bmrev].rev()
244 return [r for r in subset if r == bmrev]
244 return [r for r in subset if r == bmrev]
245 bms = set([repo[r].rev()
245 bms = set([repo[r].rev()
246 for r in bookmarksmod.listbookmarks(repo).values()])
246 for r in bookmarksmod.listbookmarks(repo).values()])
247 return [r for r in subset if r in bms]
247 return [r for r in subset if r in bms]
248
248
249 def branch(repo, subset, x):
249 def branch(repo, subset, x):
250 """``branch(string or set)``
250 """``branch(string or set)``
251 All changesets belonging to the given branch or the branches of the given
251 All changesets belonging to the given branch or the branches of the given
252 changesets.
252 changesets.
253 """
253 """
254 try:
254 try:
255 b = getstring(x, '')
255 b = getstring(x, '')
256 if b in repo.branchmap():
256 if b in repo.branchmap():
257 return [r for r in subset if repo[r].branch() == b]
257 return [r for r in subset if repo[r].branch() == b]
258 except error.ParseError:
258 except error.ParseError:
259 # not a string, but another revspec, e.g. tip()
259 # not a string, but another revspec, e.g. tip()
260 pass
260 pass
261
261
262 s = getset(repo, range(len(repo)), x)
262 s = getset(repo, range(len(repo)), x)
263 b = set()
263 b = set()
264 for r in s:
264 for r in s:
265 b.add(repo[r].branch())
265 b.add(repo[r].branch())
266 s = set(s)
266 s = set(s)
267 return [r for r in subset if r in s or repo[r].branch() in b]
267 return [r for r in subset if r in s or repo[r].branch() in b]
268
268
269 def checkstatus(repo, subset, pat, field):
269 def checkstatus(repo, subset, pat, field):
270 m = matchmod.match(repo.root, repo.getcwd(), [pat])
270 m = matchmod.match(repo.root, repo.getcwd(), [pat])
271 s = []
271 s = []
272 fast = (m.files() == [pat])
272 fast = (m.files() == [pat])
273 for r in subset:
273 for r in subset:
274 c = repo[r]
274 c = repo[r]
275 if fast:
275 if fast:
276 if pat not in c.files():
276 if pat not in c.files():
277 continue
277 continue
278 else:
278 else:
279 for f in c.files():
279 for f in c.files():
280 if m(f):
280 if m(f):
281 break
281 break
282 else:
282 else:
283 continue
283 continue
284 files = repo.status(c.p1().node(), c.node())[field]
284 files = repo.status(c.p1().node(), c.node())[field]
285 if fast:
285 if fast:
286 if pat in files:
286 if pat in files:
287 s.append(r)
287 s.append(r)
288 else:
288 else:
289 for f in files:
289 for f in files:
290 if m(f):
290 if m(f):
291 s.append(r)
291 s.append(r)
292 break
292 break
293 return s
293 return s
294
294
295 def children(repo, subset, x):
295 def children(repo, subset, x):
296 """``children(set)``
296 """``children(set)``
297 Child changesets of changesets in set.
297 Child changesets of changesets in set.
298 """
298 """
299 cs = set()
299 cs = set()
300 cl = repo.changelog
300 cl = repo.changelog
301 s = set(getset(repo, range(len(repo)), x))
301 s = set(getset(repo, range(len(repo)), x))
302 for r in xrange(0, len(repo)):
302 for r in xrange(0, len(repo)):
303 for p in cl.parentrevs(r):
303 for p in cl.parentrevs(r):
304 if p in s:
304 if p in s:
305 cs.add(r)
305 cs.add(r)
306 return [r for r in subset if r in cs]
306 return [r for r in subset if r in cs]
307
307
308 def closed(repo, subset, x):
308 def closed(repo, subset, x):
309 """``closed()``
309 """``closed()``
310 Changeset is closed.
310 Changeset is closed.
311 """
311 """
312 # i18n: "closed" is a keyword
312 # i18n: "closed" is a keyword
313 getargs(x, 0, 0, _("closed takes no arguments"))
313 getargs(x, 0, 0, _("closed takes no arguments"))
314 return [r for r in subset if repo[r].extra().get('close')]
314 return [r for r in subset if repo[r].extra().get('close')]
315
315
316 def contains(repo, subset, x):
316 def contains(repo, subset, x):
317 """``contains(pattern)``
317 """``contains(pattern)``
318 Revision contains pattern.
318 Revision contains pattern.
319 """
319 """
320 # i18n: "contains" is a keyword
320 # i18n: "contains" is a keyword
321 pat = getstring(x, _("contains requires a pattern"))
321 pat = getstring(x, _("contains requires a pattern"))
322 m = matchmod.match(repo.root, repo.getcwd(), [pat])
322 m = matchmod.match(repo.root, repo.getcwd(), [pat])
323 s = []
323 s = []
324 if m.files() == [pat]:
324 if m.files() == [pat]:
325 for r in subset:
325 for r in subset:
326 if pat in repo[r]:
326 if pat in repo[r]:
327 s.append(r)
327 s.append(r)
328 else:
328 else:
329 for r in subset:
329 for r in subset:
330 for f in repo[r].manifest():
330 for f in repo[r].manifest():
331 if m(f):
331 if m(f):
332 s.append(r)
332 s.append(r)
333 break
333 break
334 return s
334 return s
335
335
336 def date(repo, subset, x):
336 def date(repo, subset, x):
337 """``date(interval)``
337 """``date(interval)``
338 Changesets within the interval, see :hg:`help dates`.
338 Changesets within the interval, see :hg:`help dates`.
339 """
339 """
340 # i18n: "date" is a keyword
340 # i18n: "date" is a keyword
341 ds = getstring(x, _("date requires a string"))
341 ds = getstring(x, _("date requires a string"))
342 dm = util.matchdate(ds)
342 dm = util.matchdate(ds)
343 return [r for r in subset if dm(repo[r].date()[0])]
343 return [r for r in subset if dm(repo[r].date()[0])]
344
344
345 def descendants(repo, subset, x):
345 def descendants(repo, subset, x):
346 """``descendants(set)``
346 """``descendants(set)``
347 Changesets which are descendants of changesets in set.
347 Changesets which are descendants of changesets in set.
348 """
348 """
349 args = getset(repo, range(len(repo)), x)
349 args = getset(repo, range(len(repo)), x)
350 if not args:
350 if not args:
351 return []
351 return []
352 s = set(repo.changelog.descendants(*args)) | set(args)
352 s = set(repo.changelog.descendants(*args)) | set(args)
353 return [r for r in subset if r in s]
353 return [r for r in subset if r in s]
354
354
355 def follow(repo, subset, x):
355 def follow(repo, subset, x):
356 """``follow()``
356 """``follow()``
357 An alias for ``::.`` (ancestors of the working copy's first parent).
357 An alias for ``::.`` (ancestors of the working copy's first parent).
358 """
358 """
359 # i18n: "follow" is a keyword
359 # i18n: "follow" is a keyword
360 getargs(x, 0, 0, _("follow takes no arguments"))
360 getargs(x, 0, 0, _("follow takes no arguments"))
361 p = repo['.'].rev()
361 p = repo['.'].rev()
362 s = set(repo.changelog.ancestors(p)) | set([p])
362 s = set(repo.changelog.ancestors(p)) | set([p])
363 return [r for r in subset if r in s]
363 return [r for r in subset if r in s]
364
364
365 def getall(repo, subset, x):
365 def getall(repo, subset, x):
366 """``all()``
366 """``all()``
367 All changesets, the same as ``0:tip``.
367 All changesets, the same as ``0:tip``.
368 """
368 """
369 # i18n: "all" is a keyword
369 # i18n: "all" is a keyword
370 getargs(x, 0, 0, _("all takes no arguments"))
370 getargs(x, 0, 0, _("all takes no arguments"))
371 return subset
371 return subset
372
372
373 def grep(repo, subset, x):
373 def grep(repo, subset, x):
374 """``grep(regex)``
374 """``grep(regex)``
375 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
375 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
376 to ensure special escape characters are handled correctly.
376 to ensure special escape characters are handled correctly.
377 """
377 """
378 try:
378 try:
379 # i18n: "grep" is a keyword
379 # i18n: "grep" is a keyword
380 gr = re.compile(getstring(x, _("grep requires a string")))
380 gr = re.compile(getstring(x, _("grep requires a string")))
381 except re.error, e:
381 except re.error, e:
382 raise error.ParseError(_('invalid match pattern: %s') % e)
382 raise error.ParseError(_('invalid match pattern: %s') % e)
383 l = []
383 l = []
384 for r in subset:
384 for r in subset:
385 c = repo[r]
385 c = repo[r]
386 for e in c.files() + [c.user(), c.description()]:
386 for e in c.files() + [c.user(), c.description()]:
387 if gr.search(e):
387 if gr.search(e):
388 l.append(r)
388 l.append(r)
389 break
389 break
390 return l
390 return l
391
391
392 def hasfile(repo, subset, x):
392 def hasfile(repo, subset, x):
393 """``file(pattern)``
393 """``file(pattern)``
394 Changesets affecting files matched by pattern.
394 Changesets affecting files matched by pattern.
395 """
395 """
396 # i18n: "file" is a keyword
396 # i18n: "file" is a keyword
397 pat = getstring(x, _("file requires a pattern"))
397 pat = getstring(x, _("file requires a pattern"))
398 m = matchmod.match(repo.root, repo.getcwd(), [pat])
398 m = matchmod.match(repo.root, repo.getcwd(), [pat])
399 s = []
399 s = []
400 for r in subset:
400 for r in subset:
401 for f in repo[r].files():
401 for f in repo[r].files():
402 if m(f):
402 if m(f):
403 s.append(r)
403 s.append(r)
404 break
404 break
405 return s
405 return s
406
406
407 def head(repo, subset, x):
407 def head(repo, subset, x):
408 """``head()``
408 """``head()``
409 Changeset is a named branch head.
409 Changeset is a named branch head.
410 """
410 """
411 # i18n: "head" is a keyword
411 # i18n: "head" is a keyword
412 getargs(x, 0, 0, _("head takes no arguments"))
412 getargs(x, 0, 0, _("head takes no arguments"))
413 hs = set()
413 hs = set()
414 for b, ls in repo.branchmap().iteritems():
414 for b, ls in repo.branchmap().iteritems():
415 hs.update(repo[h].rev() for h in ls)
415 hs.update(repo[h].rev() for h in ls)
416 return [r for r in subset if r in hs]
416 return [r for r in subset if r in hs]
417
417
418 def heads(repo, subset, x):
418 def heads(repo, subset, x):
419 """``heads(set)``
419 """``heads(set)``
420 Members of set with no children in set.
420 Members of set with no children in set.
421 """
421 """
422 s = getset(repo, subset, x)
422 s = getset(repo, subset, x)
423 ps = set(parents(repo, subset, x))
423 ps = set(parents(repo, subset, x))
424 return [r for r in s if r not in ps]
424 return [r for r in s if r not in ps]
425
425
426 def keyword(repo, subset, x):
426 def keyword(repo, subset, x):
427 """``keyword(string)``
427 """``keyword(string)``
428 Search commit message, user name, and names of changed files for
428 Search commit message, user name, and names of changed files for
429 string.
429 string.
430 """
430 """
431 # i18n: "keyword" is a keyword
431 # i18n: "keyword" is a keyword
432 kw = getstring(x, _("keyword requires a string")).lower()
432 kw = getstring(x, _("keyword requires a string")).lower()
433 l = []
433 l = []
434 for r in subset:
434 for r in subset:
435 c = repo[r]
435 c = repo[r]
436 t = " ".join(c.files() + [c.user(), c.description()])
436 t = " ".join(c.files() + [c.user(), c.description()])
437 if kw in t.lower():
437 if kw in t.lower():
438 l.append(r)
438 l.append(r)
439 return l
439 return l
440
440
441 def limit(repo, subset, x):
441 def limit(repo, subset, x):
442 """``limit(set, n)``
442 """``limit(set, n)``
443 First n members of set.
443 First n members of set.
444 """
444 """
445 # i18n: "limit" is a keyword
445 # i18n: "limit" is a keyword
446 l = getargs(x, 2, 2, _("limit requires two arguments"))
446 l = getargs(x, 2, 2, _("limit requires two arguments"))
447 try:
447 try:
448 # i18n: "limit" is a keyword
448 # i18n: "limit" is a keyword
449 lim = int(getstring(l[1], _("limit requires a number")))
449 lim = int(getstring(l[1], _("limit requires a number")))
450 except ValueError:
450 except ValueError:
451 # i18n: "limit" is a keyword
451 # i18n: "limit" is a keyword
452 raise error.ParseError(_("limit expects a number"))
452 raise error.ParseError(_("limit expects a number"))
453 return getset(repo, subset, l[0])[:lim]
453 return getset(repo, subset, l[0])[:lim]
454
454
455 def maxrev(repo, subset, x):
455 def maxrev(repo, subset, x):
456 """``max(set)``
456 """``max(set)``
457 Changeset with highest revision number in set.
457 Changeset with highest revision number in set.
458 """
458 """
459 s = getset(repo, subset, x)
459 s = getset(repo, subset, x)
460 if s:
460 if s:
461 m = max(s)
461 m = max(s)
462 if m in subset:
462 if m in subset:
463 return [m]
463 return [m]
464 return []
464 return []
465
465
466 def merge(repo, subset, x):
466 def merge(repo, subset, x):
467 """``merge()``
467 """``merge()``
468 Changeset is a merge changeset.
468 Changeset is a merge changeset.
469 """
469 """
470 # i18n: "merge" is a keyword
470 # i18n: "merge" is a keyword
471 getargs(x, 0, 0, _("merge takes no arguments"))
471 getargs(x, 0, 0, _("merge takes no arguments"))
472 cl = repo.changelog
472 cl = repo.changelog
473 return [r for r in subset if cl.parentrevs(r)[1] != -1]
473 return [r for r in subset if cl.parentrevs(r)[1] != -1]
474
474
475 def minrev(repo, subset, x):
475 def minrev(repo, subset, x):
476 """``min(set)``
476 """``min(set)``
477 Changeset with lowest revision number in set.
477 Changeset with lowest revision number in set.
478 """
478 """
479 s = getset(repo, subset, x)
479 s = getset(repo, subset, x)
480 if s:
480 if s:
481 m = min(s)
481 m = min(s)
482 if m in subset:
482 if m in subset:
483 return [m]
483 return [m]
484 return []
484 return []
485
485
486 def modifies(repo, subset, x):
486 def modifies(repo, subset, x):
487 """``modifies(pattern)``
487 """``modifies(pattern)``
488 Changesets modifying files matched by pattern.
488 Changesets modifying files matched by pattern.
489 """
489 """
490 # i18n: "modifies" is a keyword
490 # i18n: "modifies" is a keyword
491 pat = getstring(x, _("modifies requires a pattern"))
491 pat = getstring(x, _("modifies requires a pattern"))
492 return checkstatus(repo, subset, pat, 0)
492 return checkstatus(repo, subset, pat, 0)
493
493
494 def node(repo, subset, x):
494 def node(repo, subset, x):
495 """``id(string)``
495 """``id(string)``
496 Revision non-ambiguously specified by the given hex string prefix.
496 Revision non-ambiguously specified by the given hex string prefix.
497 """
497 """
498 # i18n: "id" is a keyword
498 # i18n: "id" is a keyword
499 l = getargs(x, 1, 1, _("id requires one argument"))
499 l = getargs(x, 1, 1, _("id requires one argument"))
500 # i18n: "id" is a keyword
500 # i18n: "id" is a keyword
501 n = getstring(l[0], _("id requires a string"))
501 n = getstring(l[0], _("id requires a string"))
502 if len(n) == 40:
502 if len(n) == 40:
503 rn = repo[n].rev()
503 rn = repo[n].rev()
504 else:
504 else:
505 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
505 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
506 return [r for r in subset if r == rn]
506 return [r for r in subset if r == rn]
507
507
508 def outgoing(repo, subset, x):
508 def outgoing(repo, subset, x):
509 """``outgoing([path])``
509 """``outgoing([path])``
510 Changesets not found in the specified destination repository, or the
510 Changesets not found in the specified destination repository, or the
511 default push location.
511 default push location.
512 """
512 """
513 import hg # avoid start-up nasties
513 import hg # avoid start-up nasties
514 # i18n: "outgoing" is a keyword
514 # i18n: "outgoing" is a keyword
515 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
515 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
516 # i18n: "outgoing" is a keyword
516 # i18n: "outgoing" is a keyword
517 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
517 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
518 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
518 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
519 dest, branches = hg.parseurl(dest)
519 dest, branches = hg.parseurl(dest)
520 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
520 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
521 if revs:
521 if revs:
522 revs = [repo.lookup(rev) for rev in revs]
522 revs = [repo.lookup(rev) for rev in revs]
523 other = hg.repository(hg.remoteui(repo, {}), dest)
523 other = hg.repository(hg.remoteui(repo, {}), dest)
524 repo.ui.pushbuffer()
524 repo.ui.pushbuffer()
525 o = discovery.findoutgoing(repo, other)
525 o = discovery.findoutgoing(repo, other)
526 repo.ui.popbuffer()
526 repo.ui.popbuffer()
527 cl = repo.changelog
527 cl = repo.changelog
528 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
528 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
529 return [r for r in subset if r in o]
529 return [r for r in subset if r in o]
530
530
531 def p1(repo, subset, x):
531 def p1(repo, subset, x):
532 """``p1([set])``
532 """``p1([set])``
533 First parent of changesets in set, or the working directory.
533 First parent of changesets in set, or the working directory.
534 """
534 """
535 if x is None:
535 if x is None:
536 p = repo[x].p1().rev()
536 p = repo[x].p1().rev()
537 return [r for r in subset if r == p]
537 return [r for r in subset if r == p]
538
538
539 ps = set()
539 ps = set()
540 cl = repo.changelog
540 cl = repo.changelog
541 for r in getset(repo, range(len(repo)), x):
541 for r in getset(repo, range(len(repo)), x):
542 ps.add(cl.parentrevs(r)[0])
542 ps.add(cl.parentrevs(r)[0])
543 return [r for r in subset if r in ps]
543 return [r for r in subset if r in ps]
544
544
545 def p2(repo, subset, x):
545 def p2(repo, subset, x):
546 """``p2([set])``
546 """``p2([set])``
547 Second parent of changesets in set, or the working directory.
547 Second parent of changesets in set, or the working directory.
548 """
548 """
549 if x is None:
549 if x is None:
550 ps = repo[x].parents()
550 ps = repo[x].parents()
551 try:
551 try:
552 p = ps[1].rev()
552 p = ps[1].rev()
553 return [r for r in subset if r == p]
553 return [r for r in subset if r == p]
554 except IndexError:
554 except IndexError:
555 return []
555 return []
556
556
557 ps = set()
557 ps = set()
558 cl = repo.changelog
558 cl = repo.changelog
559 for r in getset(repo, range(len(repo)), x):
559 for r in getset(repo, range(len(repo)), x):
560 ps.add(cl.parentrevs(r)[1])
560 ps.add(cl.parentrevs(r)[1])
561 return [r for r in subset if r in ps]
561 return [r for r in subset if r in ps]
562
562
563 def parents(repo, subset, x):
563 def parents(repo, subset, x):
564 """``parents([set])``
564 """``parents([set])``
565 The set of all parents for all changesets in set, or the working directory.
565 The set of all parents for all changesets in set, or the working directory.
566 """
566 """
567 if x is None:
567 if x is None:
568 ps = tuple(p.rev() for p in repo[x].parents())
568 ps = tuple(p.rev() for p in repo[x].parents())
569 return [r for r in subset if r in ps]
569 return [r for r in subset if r in ps]
570
570
571 ps = set()
571 ps = set()
572 cl = repo.changelog
572 cl = repo.changelog
573 for r in getset(repo, range(len(repo)), x):
573 for r in getset(repo, range(len(repo)), x):
574 ps.update(cl.parentrevs(r))
574 ps.update(cl.parentrevs(r))
575 return [r for r in subset if r in ps]
575 return [r for r in subset if r in ps]
576
576
577 def present(repo, subset, x):
577 def present(repo, subset, x):
578 """``present(set)``
578 """``present(set)``
579 An empty set, if any revision in set isn't found; otherwise,
579 An empty set, if any revision in set isn't found; otherwise,
580 all revisions in set.
580 all revisions in set.
581 """
581 """
582 try:
582 try:
583 return getset(repo, subset, x)
583 return getset(repo, subset, x)
584 except error.RepoLookupError:
584 except error.RepoLookupError:
585 return []
585 return []
586
586
587 def removes(repo, subset, x):
587 def removes(repo, subset, x):
588 """``removes(pattern)``
588 """``removes(pattern)``
589 Changesets which remove files matching pattern.
589 Changesets which remove files matching pattern.
590 """
590 """
591 # i18n: "removes" is a keyword
591 # i18n: "removes" is a keyword
592 pat = getstring(x, _("removes requires a pattern"))
592 pat = getstring(x, _("removes requires a pattern"))
593 return checkstatus(repo, subset, pat, 2)
593 return checkstatus(repo, subset, pat, 2)
594
594
595 def rev(repo, subset, x):
595 def rev(repo, subset, x):
596 """``rev(number)``
596 """``rev(number)``
597 Revision with the given numeric identifier.
597 Revision with the given numeric identifier.
598 """
598 """
599 # i18n: "rev" is a keyword
599 # i18n: "rev" is a keyword
600 l = getargs(x, 1, 1, _("rev requires one argument"))
600 l = getargs(x, 1, 1, _("rev requires one argument"))
601 try:
601 try:
602 # i18n: "rev" is a keyword
602 # i18n: "rev" is a keyword
603 l = int(getstring(l[0], _("rev requires a number")))
603 l = int(getstring(l[0], _("rev requires a number")))
604 except ValueError:
604 except ValueError:
605 # i18n: "rev" is a keyword
605 # i18n: "rev" is a keyword
606 raise error.ParseError(_("rev expects a number"))
606 raise error.ParseError(_("rev expects a number"))
607 return [r for r in subset if r == l]
607 return [r for r in subset if r == l]
608
608
609 def reverse(repo, subset, x):
609 def reverse(repo, subset, x):
610 """``reverse(set)``
610 """``reverse(set)``
611 Reverse order of set.
611 Reverse order of set.
612 """
612 """
613 l = getset(repo, subset, x)
613 l = getset(repo, subset, x)
614 l.reverse()
614 l.reverse()
615 return l
615 return l
616
616
617 def roots(repo, subset, x):
617 def roots(repo, subset, x):
618 """``roots(set)``
618 """``roots(set)``
619 Changesets with no parent changeset in set.
619 Changesets with no parent changeset in set.
620 """
620 """
621 s = getset(repo, subset, x)
621 s = getset(repo, subset, x)
622 cs = set(children(repo, subset, x))
622 cs = set(children(repo, subset, x))
623 return [r for r in s if r not in cs]
623 return [r for r in s if r not in cs]
624
624
625 def sort(repo, subset, x):
625 def sort(repo, subset, x):
626 """``sort(set[, [-]key...])``
626 """``sort(set[, [-]key...])``
627 Sort set by keys. The default sort order is ascending, specify a key
627 Sort set by keys. The default sort order is ascending, specify a key
628 as ``-key`` to sort in descending order.
628 as ``-key`` to sort in descending order.
629
629
630 The keys can be:
630 The keys can be:
631
631
632 - ``rev`` for the revision number,
632 - ``rev`` for the revision number,
633 - ``branch`` for the branch name,
633 - ``branch`` for the branch name,
634 - ``desc`` for the commit message (description),
634 - ``desc`` for the commit message (description),
635 - ``user`` for user name (``author`` can be used as an alias),
635 - ``user`` for user name (``author`` can be used as an alias),
636 - ``date`` for the commit date
636 - ``date`` for the commit date
637 """
637 """
638 # i18n: "sort" is a keyword
638 # i18n: "sort" is a keyword
639 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
639 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
640 keys = "rev"
640 keys = "rev"
641 if len(l) == 2:
641 if len(l) == 2:
642 keys = getstring(l[1], _("sort spec must be a string"))
642 keys = getstring(l[1], _("sort spec must be a string"))
643
643
644 s = l[0]
644 s = l[0]
645 keys = keys.split()
645 keys = keys.split()
646 l = []
646 l = []
647 def invert(s):
647 def invert(s):
648 return "".join(chr(255 - ord(c)) for c in s)
648 return "".join(chr(255 - ord(c)) for c in s)
649 for r in getset(repo, subset, s):
649 for r in getset(repo, subset, s):
650 c = repo[r]
650 c = repo[r]
651 e = []
651 e = []
652 for k in keys:
652 for k in keys:
653 if k == 'rev':
653 if k == 'rev':
654 e.append(r)
654 e.append(r)
655 elif k == '-rev':
655 elif k == '-rev':
656 e.append(-r)
656 e.append(-r)
657 elif k == 'branch':
657 elif k == 'branch':
658 e.append(c.branch())
658 e.append(c.branch())
659 elif k == '-branch':
659 elif k == '-branch':
660 e.append(invert(c.branch()))
660 e.append(invert(c.branch()))
661 elif k == 'desc':
661 elif k == 'desc':
662 e.append(c.description())
662 e.append(c.description())
663 elif k == '-desc':
663 elif k == '-desc':
664 e.append(invert(c.description()))
664 e.append(invert(c.description()))
665 elif k in 'user author':
665 elif k in 'user author':
666 e.append(c.user())
666 e.append(c.user())
667 elif k in '-user -author':
667 elif k in '-user -author':
668 e.append(invert(c.user()))
668 e.append(invert(c.user()))
669 elif k == 'date':
669 elif k == 'date':
670 e.append(c.date()[0])
670 e.append(c.date()[0])
671 elif k == '-date':
671 elif k == '-date':
672 e.append(-c.date()[0])
672 e.append(-c.date()[0])
673 else:
673 else:
674 raise error.ParseError(_("unknown sort key %r") % k)
674 raise error.ParseError(_("unknown sort key %r") % k)
675 e.append(r)
675 e.append(r)
676 l.append(e)
676 l.append(e)
677 l.sort()
677 l.sort()
678 return [e[-1] for e in l]
678 return [e[-1] for e in l]
679
679
680 def tag(repo, subset, x):
680 def tag(repo, subset, x):
681 """``tag(name)``
681 """``tag(name)``
682 The specified tag by name, or all tagged revisions if no name is given.
682 The specified tag by name, or all tagged revisions if no name is given.
683 """
683 """
684 # i18n: "tag" is a keyword
684 # i18n: "tag" is a keyword
685 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
685 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
686 cl = repo.changelog
686 cl = repo.changelog
687 if args:
687 if args:
688 tn = getstring(args[0],
688 tn = getstring(args[0],
689 # i18n: "tag" is a keyword
689 # i18n: "tag" is a keyword
690 _('the argument to tag must be a string'))
690 _('the argument to tag must be a string'))
691 if not repo.tags().get(tn, None):
691 if not repo.tags().get(tn, None):
692 raise util.Abort(_("tag '%s' does not exist") % tn)
692 raise util.Abort(_("tag '%s' does not exist") % tn)
693 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
693 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
694 else:
694 else:
695 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
695 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
696 return [r for r in subset if r in s]
696 return [r for r in subset if r in s]
697
697
698 def tagged(repo, subset, x):
698 def tagged(repo, subset, x):
699 return tag(repo, subset, x)
699 return tag(repo, subset, x)
700
700
701 def user(repo, subset, x):
701 def user(repo, subset, x):
702 """``user(string)``
702 """``user(string)``
703 User name is string.
703 User name is string.
704 """
704 """
705 return author(repo, subset, x)
705 return author(repo, subset, x)
706
706
707 symbols = {
707 symbols = {
708 "adds": adds,
708 "adds": adds,
709 "all": getall,
709 "all": getall,
710 "ancestor": ancestor,
710 "ancestor": ancestor,
711 "ancestors": ancestors,
711 "ancestors": ancestors,
712 "author": author,
712 "author": author,
713 "bisected": bisected,
713 "bisected": bisected,
714 "bookmark": bookmark,
714 "bookmark": bookmark,
715 "branch": branch,
715 "branch": branch,
716 "children": children,
716 "children": children,
717 "closed": closed,
717 "closed": closed,
718 "contains": contains,
718 "contains": contains,
719 "date": date,
719 "date": date,
720 "descendants": descendants,
720 "descendants": descendants,
721 "file": hasfile,
721 "file": hasfile,
722 "follow": follow,
722 "follow": follow,
723 "grep": grep,
723 "grep": grep,
724 "head": head,
724 "head": head,
725 "heads": heads,
725 "heads": heads,
726 "keyword": keyword,
726 "keyword": keyword,
727 "limit": limit,
727 "limit": limit,
728 "max": maxrev,
728 "max": maxrev,
729 "min": minrev,
729 "min": minrev,
730 "merge": merge,
730 "merge": merge,
731 "modifies": modifies,
731 "modifies": modifies,
732 "id": node,
732 "id": node,
733 "outgoing": outgoing,
733 "outgoing": outgoing,
734 "p1": p1,
734 "p1": p1,
735 "p2": p2,
735 "p2": p2,
736 "parents": parents,
736 "parents": parents,
737 "present": present,
737 "present": present,
738 "removes": removes,
738 "removes": removes,
739 "reverse": reverse,
739 "reverse": reverse,
740 "rev": rev,
740 "rev": rev,
741 "roots": roots,
741 "roots": roots,
742 "sort": sort,
742 "sort": sort,
743 "tag": tag,
743 "tag": tag,
744 "tagged": tagged,
744 "tagged": tagged,
745 "user": user,
745 "user": user,
746 }
746 }
747
747
748 methods = {
748 methods = {
749 "range": rangeset,
749 "range": rangeset,
750 "string": stringset,
750 "string": stringset,
751 "symbol": symbolset,
751 "symbol": symbolset,
752 "and": andset,
752 "and": andset,
753 "or": orset,
753 "or": orset,
754 "not": notset,
754 "not": notset,
755 "list": listset,
755 "list": listset,
756 "func": func,
756 "func": func,
757 }
757 }
758
758
759 def optimize(x, small):
759 def optimize(x, small):
760 if x is None:
760 if x is None:
761 return 0, x
761 return 0, x
762
762
763 smallbonus = 1
763 smallbonus = 1
764 if small:
764 if small:
765 smallbonus = .5
765 smallbonus = .5
766
766
767 op = x[0]
767 op = x[0]
768 if op == 'minus':
768 if op == 'minus':
769 return optimize(('and', x[1], ('not', x[2])), small)
769 return optimize(('and', x[1], ('not', x[2])), small)
770 elif op == 'dagrange':
770 elif op == 'dagrange':
771 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
771 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
772 ('func', ('symbol', 'ancestors'), x[2])), small)
772 ('func', ('symbol', 'ancestors'), x[2])), small)
773 elif op == 'dagrangepre':
773 elif op == 'dagrangepre':
774 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
774 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
775 elif op == 'dagrangepost':
775 elif op == 'dagrangepost':
776 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
776 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
777 elif op == 'rangepre':
777 elif op == 'rangepre':
778 return optimize(('range', ('string', '0'), x[1]), small)
778 return optimize(('range', ('string', '0'), x[1]), small)
779 elif op == 'rangepost':
779 elif op == 'rangepost':
780 return optimize(('range', x[1], ('string', 'tip')), small)
780 return optimize(('range', x[1], ('string', 'tip')), small)
781 elif op == 'negate':
781 elif op == 'negate':
782 return optimize(('string',
782 return optimize(('string',
783 '-' + getstring(x[1], _("can't negate that"))), small)
783 '-' + getstring(x[1], _("can't negate that"))), small)
784 elif op in 'string symbol negate':
784 elif op in 'string symbol negate':
785 return smallbonus, x # single revisions are small
785 return smallbonus, x # single revisions are small
786 elif op == 'and' or op == 'dagrange':
786 elif op == 'and' or op == 'dagrange':
787 wa, ta = optimize(x[1], True)
787 wa, ta = optimize(x[1], True)
788 wb, tb = optimize(x[2], True)
788 wb, tb = optimize(x[2], True)
789 w = min(wa, wb)
789 w = min(wa, wb)
790 if wa > wb:
790 if wa > wb:
791 return w, (op, tb, ta)
791 return w, (op, tb, ta)
792 return w, (op, ta, tb)
792 return w, (op, ta, tb)
793 elif op == 'or':
793 elif op == 'or':
794 wa, ta = optimize(x[1], False)
794 wa, ta = optimize(x[1], False)
795 wb, tb = optimize(x[2], False)
795 wb, tb = optimize(x[2], False)
796 if wb < wa:
796 if wb < wa:
797 wb, wa = wa, wb
797 wb, wa = wa, wb
798 return max(wa, wb), (op, ta, tb)
798 return max(wa, wb), (op, ta, tb)
799 elif op == 'not':
799 elif op == 'not':
800 o = optimize(x[1], not small)
800 o = optimize(x[1], not small)
801 return o[0], (op, o[1])
801 return o[0], (op, o[1])
802 elif op == 'group':
802 elif op == 'group':
803 return optimize(x[1], small)
803 return optimize(x[1], small)
804 elif op in 'range list':
804 elif op in 'range list':
805 wa, ta = optimize(x[1], small)
805 wa, ta = optimize(x[1], small)
806 wb, tb = optimize(x[2], small)
806 wb, tb = optimize(x[2], small)
807 return wa + wb, (op, ta, tb)
807 return wa + wb, (op, ta, tb)
808 elif op == 'func':
808 elif op == 'func':
809 f = getstring(x[1], _("not a symbol"))
809 f = getstring(x[1], _("not a symbol"))
810 wa, ta = optimize(x[2], small)
810 wa, ta = optimize(x[2], small)
811 if f in "grep date user author keyword branch file outgoing closed":
811 if f in "grep date user author keyword branch file outgoing closed":
812 w = 10 # slow
812 w = 10 # slow
813 elif f in "modifies adds removes":
813 elif f in "modifies adds removes":
814 w = 30 # slower
814 w = 30 # slower
815 elif f == "contains":
815 elif f == "contains":
816 w = 100 # very slow
816 w = 100 # very slow
817 elif f == "ancestor":
817 elif f == "ancestor":
818 w = 1 * smallbonus
818 w = 1 * smallbonus
819 elif f in "reverse limit":
819 elif f in "reverse limit":
820 w = 0
820 w = 0
821 elif f in "sort":
821 elif f in "sort":
822 w = 10 # assume most sorts look at changelog
822 w = 10 # assume most sorts look at changelog
823 else:
823 else:
824 w = 1
824 w = 1
825 return w + wa, (op, x[1], ta)
825 return w + wa, (op, x[1], ta)
826 return 1, x
826 return 1, x
827
827
828 parse = parser.parser(tokenize, elements).parse
828 parse = parser.parser(tokenize, elements).parse
829
829
830 def match(spec):
830 def match(spec):
831 if not spec:
831 if not spec:
832 raise error.ParseError(_("empty query"))
832 raise error.ParseError(_("empty query"))
833 tree, pos = parse(spec)
833 tree, pos = parse(spec)
834 if (pos != len(spec)):
834 if (pos != len(spec)):
835 raise error.ParseError("invalid token", pos)
835 raise error.ParseError("invalid token", pos)
836 weight, tree = optimize(tree, True)
836 weight, tree = optimize(tree, True)
837 def mfunc(repo, subset):
837 def mfunc(repo, subset):
838 return getset(repo, subset, tree)
838 return getset(repo, subset, tree)
839 return mfunc
839 return mfunc
840
840
841 def makedoc(topic, doc):
841 def makedoc(topic, doc):
842 return help.makeitemsdoc(topic, doc, '.. predicatesmarker', symbols)
842 return help.makeitemsdoc(topic, doc, '.. predicatesmarker', symbols)
843
843
844 # tell hggettext to extract docstrings from these functions:
844 # tell hggettext to extract docstrings from these functions:
845 i18nfunctions = symbols.values()
845 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now