##// END OF EJS Templates
match: mark error messages for translation
Martin Geisler -
r12133:b046b90c stable
parent child Browse files
Show More
@@ -1,250 +1,251 b''
1 # match.py - filename matching
1 # match.py - filename matching
2 #
2 #
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
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 util
9 import util
10 from i18n import _
10
11
11 class match(object):
12 class match(object):
12 def __init__(self, root, cwd, patterns, include=[], exclude=[],
13 def __init__(self, root, cwd, patterns, include=[], exclude=[],
13 default='glob', exact=False):
14 default='glob', exact=False):
14 """build an object to match a set of file patterns
15 """build an object to match a set of file patterns
15
16
16 arguments:
17 arguments:
17 root - the canonical root of the tree you're matching against
18 root - the canonical root of the tree you're matching against
18 cwd - the current working directory, if relevant
19 cwd - the current working directory, if relevant
19 patterns - patterns to find
20 patterns - patterns to find
20 include - patterns to include
21 include - patterns to include
21 exclude - patterns to exclude
22 exclude - patterns to exclude
22 default - if a pattern in names has no explicit type, assume this one
23 default - if a pattern in names has no explicit type, assume this one
23 exact - patterns are actually literals
24 exact - patterns are actually literals
24
25
25 a pattern is one of:
26 a pattern is one of:
26 'glob:<glob>' - a glob relative to cwd
27 'glob:<glob>' - a glob relative to cwd
27 're:<regexp>' - a regular expression
28 're:<regexp>' - a regular expression
28 'path:<path>' - a path relative to canonroot
29 'path:<path>' - a path relative to canonroot
29 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
30 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
30 'relpath:<path>' - a path relative to cwd
31 'relpath:<path>' - a path relative to cwd
31 'relre:<regexp>' - a regexp that needn't match the start of a name
32 'relre:<regexp>' - a regexp that needn't match the start of a name
32 '<something>' - a pattern of the specified default type
33 '<something>' - a pattern of the specified default type
33 """
34 """
34
35
35 self._root = root
36 self._root = root
36 self._cwd = cwd
37 self._cwd = cwd
37 self._files = []
38 self._files = []
38 self._anypats = bool(include or exclude)
39 self._anypats = bool(include or exclude)
39
40
40 if include:
41 if include:
41 im = _buildmatch(_normalize(include, 'glob', root, cwd), '(?:/|$)')
42 im = _buildmatch(_normalize(include, 'glob', root, cwd), '(?:/|$)')
42 if exclude:
43 if exclude:
43 em = _buildmatch(_normalize(exclude, 'glob', root, cwd), '(?:/|$)')
44 em = _buildmatch(_normalize(exclude, 'glob', root, cwd), '(?:/|$)')
44 if exact:
45 if exact:
45 self._files = patterns
46 self._files = patterns
46 pm = self.exact
47 pm = self.exact
47 elif patterns:
48 elif patterns:
48 pats = _normalize(patterns, default, root, cwd)
49 pats = _normalize(patterns, default, root, cwd)
49 self._files = _roots(pats)
50 self._files = _roots(pats)
50 self._anypats = self._anypats or _anypats(pats)
51 self._anypats = self._anypats or _anypats(pats)
51 pm = _buildmatch(pats, '$')
52 pm = _buildmatch(pats, '$')
52
53
53 if patterns or exact:
54 if patterns or exact:
54 if include:
55 if include:
55 if exclude:
56 if exclude:
56 m = lambda f: im(f) and not em(f) and pm(f)
57 m = lambda f: im(f) and not em(f) and pm(f)
57 else:
58 else:
58 m = lambda f: im(f) and pm(f)
59 m = lambda f: im(f) and pm(f)
59 else:
60 else:
60 if exclude:
61 if exclude:
61 m = lambda f: not em(f) and pm(f)
62 m = lambda f: not em(f) and pm(f)
62 else:
63 else:
63 m = pm
64 m = pm
64 else:
65 else:
65 if include:
66 if include:
66 if exclude:
67 if exclude:
67 m = lambda f: im(f) and not em(f)
68 m = lambda f: im(f) and not em(f)
68 else:
69 else:
69 m = im
70 m = im
70 else:
71 else:
71 if exclude:
72 if exclude:
72 m = lambda f: not em(f)
73 m = lambda f: not em(f)
73 else:
74 else:
74 m = lambda f: True
75 m = lambda f: True
75
76
76 self.matchfn = m
77 self.matchfn = m
77 self._fmap = set(self._files)
78 self._fmap = set(self._files)
78
79
79 def __call__(self, fn):
80 def __call__(self, fn):
80 return self.matchfn(fn)
81 return self.matchfn(fn)
81 def __iter__(self):
82 def __iter__(self):
82 for f in self._files:
83 for f in self._files:
83 yield f
84 yield f
84 def bad(self, f, msg):
85 def bad(self, f, msg):
85 '''callback for each explicit file that can't be
86 '''callback for each explicit file that can't be
86 found/accessed, with an error message
87 found/accessed, with an error message
87 '''
88 '''
88 pass
89 pass
89 def dir(self, f):
90 def dir(self, f):
90 pass
91 pass
91 def missing(self, f):
92 def missing(self, f):
92 pass
93 pass
93 def exact(self, f):
94 def exact(self, f):
94 return f in self._fmap
95 return f in self._fmap
95 def rel(self, f):
96 def rel(self, f):
96 return util.pathto(self._root, self._cwd, f)
97 return util.pathto(self._root, self._cwd, f)
97 def files(self):
98 def files(self):
98 return self._files
99 return self._files
99 def anypats(self):
100 def anypats(self):
100 return self._anypats
101 return self._anypats
101
102
102 class exact(match):
103 class exact(match):
103 def __init__(self, root, cwd, files):
104 def __init__(self, root, cwd, files):
104 match.__init__(self, root, cwd, files, exact = True)
105 match.__init__(self, root, cwd, files, exact = True)
105
106
106 class always(match):
107 class always(match):
107 def __init__(self, root, cwd):
108 def __init__(self, root, cwd):
108 match.__init__(self, root, cwd, [])
109 match.__init__(self, root, cwd, [])
109
110
110 def patkind(pat):
111 def patkind(pat):
111 return _patsplit(pat, None)[0]
112 return _patsplit(pat, None)[0]
112
113
113 def _patsplit(pat, default):
114 def _patsplit(pat, default):
114 """Split a string into an optional pattern kind prefix and the
115 """Split a string into an optional pattern kind prefix and the
115 actual pattern."""
116 actual pattern."""
116 if ':' in pat:
117 if ':' in pat:
117 kind, val = pat.split(':', 1)
118 kind, val = pat.split(':', 1)
118 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre'):
119 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre'):
119 return kind, val
120 return kind, val
120 return default, pat
121 return default, pat
121
122
122 def _globre(pat):
123 def _globre(pat):
123 "convert a glob pattern into a regexp"
124 "convert a glob pattern into a regexp"
124 i, n = 0, len(pat)
125 i, n = 0, len(pat)
125 res = ''
126 res = ''
126 group = 0
127 group = 0
127 escape = re.escape
128 escape = re.escape
128 def peek():
129 def peek():
129 return i < n and pat[i]
130 return i < n and pat[i]
130 while i < n:
131 while i < n:
131 c = pat[i]
132 c = pat[i]
132 i += 1
133 i += 1
133 if c not in '*?[{},\\':
134 if c not in '*?[{},\\':
134 res += escape(c)
135 res += escape(c)
135 elif c == '*':
136 elif c == '*':
136 if peek() == '*':
137 if peek() == '*':
137 i += 1
138 i += 1
138 res += '.*'
139 res += '.*'
139 else:
140 else:
140 res += '[^/]*'
141 res += '[^/]*'
141 elif c == '?':
142 elif c == '?':
142 res += '.'
143 res += '.'
143 elif c == '[':
144 elif c == '[':
144 j = i
145 j = i
145 if j < n and pat[j] in '!]':
146 if j < n and pat[j] in '!]':
146 j += 1
147 j += 1
147 while j < n and pat[j] != ']':
148 while j < n and pat[j] != ']':
148 j += 1
149 j += 1
149 if j >= n:
150 if j >= n:
150 res += '\\['
151 res += '\\['
151 else:
152 else:
152 stuff = pat[i:j].replace('\\','\\\\')
153 stuff = pat[i:j].replace('\\','\\\\')
153 i = j + 1
154 i = j + 1
154 if stuff[0] == '!':
155 if stuff[0] == '!':
155 stuff = '^' + stuff[1:]
156 stuff = '^' + stuff[1:]
156 elif stuff[0] == '^':
157 elif stuff[0] == '^':
157 stuff = '\\' + stuff
158 stuff = '\\' + stuff
158 res = '%s[%s]' % (res, stuff)
159 res = '%s[%s]' % (res, stuff)
159 elif c == '{':
160 elif c == '{':
160 group += 1
161 group += 1
161 res += '(?:'
162 res += '(?:'
162 elif c == '}' and group:
163 elif c == '}' and group:
163 res += ')'
164 res += ')'
164 group -= 1
165 group -= 1
165 elif c == ',' and group:
166 elif c == ',' and group:
166 res += '|'
167 res += '|'
167 elif c == '\\':
168 elif c == '\\':
168 p = peek()
169 p = peek()
169 if p:
170 if p:
170 i += 1
171 i += 1
171 res += escape(p)
172 res += escape(p)
172 else:
173 else:
173 res += escape(c)
174 res += escape(c)
174 else:
175 else:
175 res += escape(c)
176 res += escape(c)
176 return res
177 return res
177
178
178 def _regex(kind, name, tail):
179 def _regex(kind, name, tail):
179 '''convert a pattern into a regular expression'''
180 '''convert a pattern into a regular expression'''
180 if not name:
181 if not name:
181 return ''
182 return ''
182 if kind == 're':
183 if kind == 're':
183 return name
184 return name
184 elif kind == 'path':
185 elif kind == 'path':
185 return '^' + re.escape(name) + '(?:/|$)'
186 return '^' + re.escape(name) + '(?:/|$)'
186 elif kind == 'relglob':
187 elif kind == 'relglob':
187 return '(?:|.*/)' + _globre(name) + tail
188 return '(?:|.*/)' + _globre(name) + tail
188 elif kind == 'relpath':
189 elif kind == 'relpath':
189 return re.escape(name) + '(?:/|$)'
190 return re.escape(name) + '(?:/|$)'
190 elif kind == 'relre':
191 elif kind == 'relre':
191 if name.startswith('^'):
192 if name.startswith('^'):
192 return name
193 return name
193 return '.*' + name
194 return '.*' + name
194 return _globre(name) + tail
195 return _globre(name) + tail
195
196
196 def _buildmatch(pats, tail):
197 def _buildmatch(pats, tail):
197 """build a matching function from a set of patterns"""
198 """build a matching function from a set of patterns"""
198 try:
199 try:
199 pat = '(?:%s)' % '|'.join([_regex(k, p, tail) for (k, p) in pats])
200 pat = '(?:%s)' % '|'.join([_regex(k, p, tail) for (k, p) in pats])
200 if len(pat) > 20000:
201 if len(pat) > 20000:
201 raise OverflowError()
202 raise OverflowError()
202 return re.compile(pat).match
203 return re.compile(pat).match
203 except OverflowError:
204 except OverflowError:
204 # We're using a Python with a tiny regex engine and we
205 # We're using a Python with a tiny regex engine and we
205 # made it explode, so we'll divide the pattern list in two
206 # made it explode, so we'll divide the pattern list in two
206 # until it works
207 # until it works
207 l = len(pats)
208 l = len(pats)
208 if l < 2:
209 if l < 2:
209 raise
210 raise
210 a, b = _buildmatch(pats[:l//2], tail), _buildmatch(pats[l//2:], tail)
211 a, b = _buildmatch(pats[:l//2], tail), _buildmatch(pats[l//2:], tail)
211 return lambda s: a(s) or b(s)
212 return lambda s: a(s) or b(s)
212 except re.error:
213 except re.error:
213 for k, p in pats:
214 for k, p in pats:
214 try:
215 try:
215 re.compile('(?:%s)' % _regex(k, p, tail))
216 re.compile('(?:%s)' % _regex(k, p, tail))
216 except re.error:
217 except re.error:
217 raise util.Abort("invalid pattern (%s): %s" % (k, p))
218 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
218 raise util.Abort("invalid pattern")
219 raise util.Abort(_("invalid pattern"))
219
220
220 def _normalize(names, default, root, cwd):
221 def _normalize(names, default, root, cwd):
221 pats = []
222 pats = []
222 for kind, name in [_patsplit(p, default) for p in names]:
223 for kind, name in [_patsplit(p, default) for p in names]:
223 if kind in ('glob', 'relpath'):
224 if kind in ('glob', 'relpath'):
224 name = util.canonpath(root, cwd, name)
225 name = util.canonpath(root, cwd, name)
225 elif kind in ('relglob', 'path'):
226 elif kind in ('relglob', 'path'):
226 name = util.normpath(name)
227 name = util.normpath(name)
227
228
228 pats.append((kind, name))
229 pats.append((kind, name))
229 return pats
230 return pats
230
231
231 def _roots(patterns):
232 def _roots(patterns):
232 r = []
233 r = []
233 for kind, name in patterns:
234 for kind, name in patterns:
234 if kind == 'glob': # find the non-glob prefix
235 if kind == 'glob': # find the non-glob prefix
235 root = []
236 root = []
236 for p in name.split('/'):
237 for p in name.split('/'):
237 if '[' in p or '{' in p or '*' in p or '?' in p:
238 if '[' in p or '{' in p or '*' in p or '?' in p:
238 break
239 break
239 root.append(p)
240 root.append(p)
240 r.append('/'.join(root) or '.')
241 r.append('/'.join(root) or '.')
241 elif kind in ('relpath', 'path'):
242 elif kind in ('relpath', 'path'):
242 r.append(name or '.')
243 r.append(name or '.')
243 elif kind == 'relglob':
244 elif kind == 'relglob':
244 r.append('.')
245 r.append('.')
245 return r
246 return r
246
247
247 def _anypats(patterns):
248 def _anypats(patterns):
248 for kind, name in patterns:
249 for kind, name in patterns:
249 if kind in ('glob', 're', 'relglob', 'relre'):
250 if kind in ('glob', 're', 'relglob', 'relre'):
250 return True
251 return True
General Comments 0
You need to be logged in to leave comments. Login now