##// END OF EJS Templates
fileset: add resolved and unresolved predicates
Matt Mackall -
r14679:e141e1ce default
parent child Browse files
Show More
@@ -1,247 +1,263
1 1 # fileset.py - file set queries for mercurial
2 2 #
3 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 import parser, error, util
8 import parser, error, util, merge
9 9 from i18n import _
10 10
11 11 elements = {
12 12 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
13 13 "-": (5, ("negate", 19), ("minus", 5)),
14 14 "not": (10, ("not", 10)),
15 15 "!": (10, ("not", 10)),
16 16 "and": (5, None, ("and", 5)),
17 17 "&": (5, None, ("and", 5)),
18 18 "or": (4, None, ("or", 4)),
19 19 "|": (4, None, ("or", 4)),
20 20 "+": (4, None, ("or", 4)),
21 21 ",": (2, None, ("list", 2)),
22 22 ")": (0, None, None),
23 23 "symbol": (0, ("symbol",), None),
24 24 "string": (0, ("string",), None),
25 25 "end": (0, None, None),
26 26 }
27 27
28 28 keywords = set(['and', 'or', 'not'])
29 29
30 30 globchars = ".*{}[]?/\\"
31 31
32 32 def tokenize(program):
33 33 pos, l = 0, len(program)
34 34 while pos < l:
35 35 c = program[pos]
36 36 if c.isspace(): # skip inter-token whitespace
37 37 pass
38 38 elif c in "(),-|&+!": # handle simple operators
39 39 yield (c, None, pos)
40 40 elif (c in '"\'' or c == 'r' and
41 41 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
42 42 if c == 'r':
43 43 pos += 1
44 44 c = program[pos]
45 45 decode = lambda x: x
46 46 else:
47 47 decode = lambda x: x.decode('string-escape')
48 48 pos += 1
49 49 s = pos
50 50 while pos < l: # find closing quote
51 51 d = program[pos]
52 52 if d == '\\': # skip over escaped characters
53 53 pos += 2
54 54 continue
55 55 if d == c:
56 56 yield ('string', decode(program[s:pos]), s)
57 57 break
58 58 pos += 1
59 59 else:
60 60 raise error.ParseError(_("unterminated string"), s)
61 61 elif c.isalnum() or c in globchars or ord(c) > 127:
62 62 # gather up a symbol/keyword
63 63 s = pos
64 64 pos += 1
65 65 while pos < l: # find end of symbol
66 66 d = program[pos]
67 67 if not (d.isalnum() or d in globchars or ord(d) > 127):
68 68 break
69 69 pos += 1
70 70 sym = program[s:pos]
71 71 if sym in keywords: # operator keywords
72 72 yield (sym, None, s)
73 73 else:
74 74 yield ('symbol', sym, s)
75 75 pos -= 1
76 76 else:
77 77 raise error.ParseError(_("syntax error"), pos)
78 78 pos += 1
79 79 yield ('end', None, pos)
80 80
81 81 parse = parser.parser(tokenize, elements).parse
82 82
83 83 def getstring(x, err):
84 84 if x and (x[0] == 'string' or x[0] == 'symbol'):
85 85 return x[1]
86 86 raise error.ParseError(err)
87 87
88 88 def getset(mctx, x):
89 89 if not x:
90 90 raise error.ParseError(_("missing argument"))
91 91 return methods[x[0]](mctx, *x[1:])
92 92
93 93 def stringset(mctx, x):
94 94 m = mctx.matcher([x])
95 95 return [f for f in mctx.subset if m(f)]
96 96
97 97 def andset(mctx, x, y):
98 98 return getset(mctx.narrow(getset(mctx, x)), y)
99 99
100 100 def orset(mctx, x, y):
101 101 # needs optimizing
102 102 xl = getset(mctx, x)
103 103 yl = getset(mctx, y)
104 104 return xl + [f for f in yl if f not in xl]
105 105
106 106 def notset(mctx, x):
107 107 s = set(getset(mctx, x))
108 108 return [r for r in mctx.subset if r not in s]
109 109
110 110 def listset(mctx, a, b):
111 111 raise error.ParseError(_("can't use a list in this context"))
112 112
113 113 def modified(mctx, x):
114 114 getargs(x, 0, 0, _("modified takes no arguments"))
115 115 s = mctx.status()[0]
116 116 return [f for f in mctx.subset if f in s]
117 117
118 118 def added(mctx, x):
119 119 getargs(x, 0, 0, _("added takes no arguments"))
120 120 s = mctx.status()[1]
121 121 return [f for f in mctx.subset if f in s]
122 122
123 123 def removed(mctx, x):
124 124 getargs(x, 0, 0, _("removed takes no arguments"))
125 125 s = mctx.status()[2]
126 126 return [f for f in mctx.subset if f in s]
127 127
128 128 def deleted(mctx, x):
129 129 getargs(x, 0, 0, _("deleted takes no arguments"))
130 130 s = mctx.status()[3]
131 131 return [f for f in mctx.subset if f in s]
132 132
133 133 def unknown(mctx, x):
134 134 getargs(x, 0, 0, _("unknown takes no arguments"))
135 135 s = mctx.status()[4]
136 136 return [f for f in mctx.subset if f in s]
137 137
138 138 def ignored(mctx, x):
139 139 getargs(x, 0, 0, _("ignored takes no arguments"))
140 140 s = mctx.status()[5]
141 141 return [f for f in mctx.subset if f in s]
142 142
143 143 def clean(mctx, x):
144 144 getargs(x, 0, 0, _("clean takes no arguments"))
145 145 s = mctx.status()[6]
146 146 return [f for f in mctx.subset if f in s]
147 147
148 148 def func(mctx, a, b):
149 149 if a[0] == 'symbol' and a[1] in symbols:
150 150 return symbols[a[1]](mctx, b)
151 151 raise error.ParseError(_("not a function: %s") % a[1])
152 152
153 153 def getlist(x):
154 154 if not x:
155 155 return []
156 156 if x[0] == 'list':
157 157 return getlist(x[1]) + [x[2]]
158 158 return [x]
159 159
160 160 def getargs(x, min, max, err):
161 161 l = getlist(x)
162 162 if len(l) < min or len(l) > max:
163 163 raise error.ParseError(err)
164 164 return l
165 165
166 166 def binary(mctx, x):
167 167 getargs(x, 0, 0, _("binary takes no arguments"))
168 168 return [f for f in mctx.subset if util.binary(mctx.ctx[f].data())]
169 169
170 170 def exec_(mctx, x):
171 171 getargs(x, 0, 0, _("exec takes no arguments"))
172 172 return [f for f in mctx.subset if mctx.ctx.flags(f) == 'x']
173 173
174 174 def symlink(mctx, x):
175 175 getargs(x, 0, 0, _("symlink takes no arguments"))
176 176 return [f for f in mctx.subset if mctx.ctx.flags(f) == 'l']
177 177
178 def resolved(mctx, x):
179 getargs(x, 0, 0, _("resolved takes no arguments"))
180 if mctx.ctx.rev() is not None:
181 return []
182 ms = merge.mergestate(mctx.ctx._repo)
183 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
184
185 def unresolved(mctx, x):
186 getargs(x, 0, 0, _("unresolved takes no arguments"))
187 if mctx.ctx.rev() is not None:
188 return []
189 ms = merge.mergestate(mctx.ctx._repo)
190 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
191
178 192 symbols = {
179 193 'added': added,
180 194 'binary': binary,
181 195 'clean': clean,
182 196 'deleted': deleted,
183 197 'exec': exec_,
184 198 'ignored': ignored,
185 199 'modified': modified,
186 200 'removed': removed,
201 'resolved': resolved,
187 202 'symlink': symlink,
188 203 'unknown': unknown,
204 'unresolved': unresolved,
189 205 }
190 206
191 207 methods = {
192 208 'string': stringset,
193 209 'symbol': stringset,
194 210 'and': andset,
195 211 'or': orset,
196 212 'list': listset,
197 213 'group': getset,
198 214 'not': notset,
199 215 'func': func,
200 216 }
201 217
202 218 class matchctx(object):
203 219 def __init__(self, ctx, subset=None, status=None):
204 220 self.ctx = ctx
205 221 self.subset = subset
206 222 self._status = status
207 223 def status(self):
208 224 return self._status
209 225 def matcher(self, patterns):
210 226 return self.ctx.match(patterns)
211 227 def filter(self, files):
212 228 return [f for f in files if f in self.subset]
213 229 def narrow(self, files):
214 230 return matchctx(self.ctx, self.filter(files), self._status)
215 231
216 232 def _intree(funcs, tree):
217 233 if isinstance(tree, tuple):
218 234 if tree[0] == 'func' and tree[1][0] == 'symbol':
219 235 if tree[1][1] in funcs:
220 236 return True
221 237 for s in tree[1:]:
222 238 if _intree(funcs, s):
223 239 return True
224 240 return False
225 241
226 242 def getfileset(ctx, expr):
227 243 tree, pos = parse(expr)
228 244 if (pos != len(expr)):
229 245 raise error.ParseError("invalid token", pos)
230 246
231 247 # do we need status info?
232 248 if _intree(['modified', 'added', 'removed', 'deleted',
233 249 'unknown', 'ignored', 'clean'], tree):
234 250 unknown = _intree(['unknown'], tree)
235 251 ignored = _intree(['ignored'], tree)
236 252
237 253 r = ctx._repo
238 254 status = r.status(ctx.p1(), ctx,
239 255 unknown=unknown, ignored=ignored, clean=True)
240 256 subset = []
241 257 for c in status:
242 258 subset.extend(c)
243 259 else:
244 260 status = None
245 261 subset = ctx.walk(ctx.match([]))
246 262
247 263 return getset(matchctx(ctx, subset, status), tree)
General Comments 0
You need to be logged in to leave comments. Login now