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