Mercurial > hg
comparison mercurial/match.py @ 8574:63a7ed2128d5
match: unnest functions in _matcher
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Sun, 24 May 2009 02:56:14 -0500 |
parents | 62c996d543e6 |
children | aa4fcb5c46f1 |
comparison
equal
deleted
inserted
replaced
8573:62c996d543e6 | 8574:63a7ed2128d5 |
---|---|
115 res += re.escape(c) | 115 res += re.escape(c) |
116 else: | 116 else: |
117 res += re.escape(c) | 117 res += re.escape(c) |
118 return head + res + tail | 118 return head + res + tail |
119 | 119 |
120 def _matcher(canonroot, cwd='', names=[], inc=[], exc=[], dflt_pat='glob'): | 120 def _regex(kind, name, tail): |
121 '''convert a pattern into a regular expression''' | |
122 if not name: | |
123 return '' | |
124 if kind == 're': | |
125 return name | |
126 elif kind == 'path': | |
127 return '^' + re.escape(name) + '(?:/|$)' | |
128 elif kind == 'relglob': | |
129 return _globre(name, '(?:|.*/)', tail) | |
130 elif kind == 'relpath': | |
131 return re.escape(name) + '(?:/|$)' | |
132 elif kind == 'relre': | |
133 if name.startswith('^'): | |
134 return name | |
135 return '.*' + name | |
136 return _globre(name, '', tail) | |
137 | |
138 def _matchfn(pats, tail): | |
139 """build a matching function from a set of patterns""" | |
140 try: | |
141 pat = '(?:%s)' % '|'.join([_regex(k, p, tail) for (k, p) in pats]) | |
142 if len(pat) > 20000: | |
143 raise OverflowError() | |
144 return re.compile(pat).match | |
145 except OverflowError: | |
146 # We're using a Python with a tiny regex engine and we | |
147 # made it explode, so we'll divide the pattern list in two | |
148 # until it works | |
149 l = len(pats) | |
150 if l < 2: | |
151 raise | |
152 a, b = _matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail) | |
153 return lambda s: a(s) or b(s) | |
154 except re.error: | |
155 for k, p in pats: | |
156 try: | |
157 re.compile('(?:%s)' % _regex(k, p, tail)) | |
158 except re.error: | |
159 raise util.Abort("invalid pattern (%s): %s" % (k, p)) | |
160 raise util.Abort("invalid pattern") | |
161 | |
162 def _containsglob(name): | |
163 for c in name: | |
164 if c in _globchars: return True | |
165 return False | |
166 | |
167 def _globprefix(pat): | |
168 '''return the non-glob prefix of a path, e.g. foo/* -> foo''' | |
169 root = [] | |
170 for p in pat.split('/'): | |
171 if _containsglob(p): break | |
172 root.append(p) | |
173 return '/'.join(root) or '.' | |
174 | |
175 def _normalizepats(names, default, canonroot, cwd): | |
176 pats = [] | |
177 roots = [] | |
178 anypats = False | |
179 for kind, name in [_patsplit(p, default) for p in names]: | |
180 if kind in ('glob', 'relpath'): | |
181 name = util.canonpath(canonroot, cwd, name) | |
182 elif kind in ('relglob', 'path'): | |
183 name = util.normpath(name) | |
184 | |
185 pats.append((kind, name)) | |
186 | |
187 if kind in ('glob', 're', 'relglob', 'relre'): | |
188 anypats = True | |
189 | |
190 if kind == 'glob': | |
191 root = _globprefix(name) | |
192 roots.append(root) | |
193 elif kind in ('relpath', 'path'): | |
194 roots.append(name or '.') | |
195 elif kind == 'relglob': | |
196 roots.append('.') | |
197 return roots, pats, anypats | |
198 | |
199 def _matcher(root, cwd='', names=[], inc=[], exc=[], dflt_pat='glob'): | |
121 """build a function to match a set of file patterns | 200 """build a function to match a set of file patterns |
122 | 201 |
123 arguments: | 202 arguments: |
124 canonroot - the canonical root of the tree you're matching against | 203 root - the canonical root of the tree you're matching against |
125 cwd - the current working directory, if relevant | 204 cwd - the current working directory, if relevant |
126 names - patterns to find | 205 names - patterns to find |
127 inc - patterns to include | 206 inc - patterns to include |
128 exc - patterns to exclude | 207 exc - patterns to exclude |
129 dflt_pat - if a pattern in names has no explicit type, assume this one | 208 dflt_pat - if a pattern in names has no explicit type, assume this one |
148 | 227 |
149 # a common case: no patterns at all | 228 # a common case: no patterns at all |
150 if not names and not inc and not exc: | 229 if not names and not inc and not exc: |
151 return [], lambda f: True, False | 230 return [], lambda f: True, False |
152 | 231 |
153 def contains_glob(name): | 232 roots, pats, anypats = _normalizepats(names, dflt_pat, root, cwd) |
154 for c in name: | |
155 if c in _globchars: return True | |
156 return False | |
157 | |
158 def regex(kind, name, tail): | |
159 '''convert a pattern into a regular expression''' | |
160 if not name: | |
161 return '' | |
162 if kind == 're': | |
163 return name | |
164 elif kind == 'path': | |
165 return '^' + re.escape(name) + '(?:/|$)' | |
166 elif kind == 'relglob': | |
167 return _globre(name, '(?:|.*/)', tail) | |
168 elif kind == 'relpath': | |
169 return re.escape(name) + '(?:/|$)' | |
170 elif kind == 'relre': | |
171 if name.startswith('^'): | |
172 return name | |
173 return '.*' + name | |
174 return _globre(name, '', tail) | |
175 | |
176 def matchfn(pats, tail): | |
177 """build a matching function from a set of patterns""" | |
178 try: | |
179 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats]) | |
180 if len(pat) > 20000: | |
181 raise OverflowError() | |
182 return re.compile(pat).match | |
183 except OverflowError: | |
184 # We're using a Python with a tiny regex engine and we | |
185 # made it explode, so we'll divide the pattern list in two | |
186 # until it works | |
187 l = len(pats) | |
188 if l < 2: | |
189 raise | |
190 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail) | |
191 return lambda s: a(s) or b(s) | |
192 except re.error: | |
193 for k, p in pats: | |
194 try: | |
195 re.compile('(?:%s)' % regex(k, p, tail)) | |
196 except re.error: | |
197 raise util.Abort("invalid pattern (%s): %s" % (k, p)) | |
198 raise util.Abort("invalid pattern") | |
199 | |
200 def globprefix(pat): | |
201 '''return the non-glob prefix of a path, e.g. foo/* -> foo''' | |
202 root = [] | |
203 for p in pat.split('/'): | |
204 if contains_glob(p): break | |
205 root.append(p) | |
206 return '/'.join(root) or '.' | |
207 | |
208 def normalizepats(names, default): | |
209 pats = [] | |
210 roots = [] | |
211 anypats = False | |
212 for kind, name in [_patsplit(p, default) for p in names]: | |
213 if kind in ('glob', 'relpath'): | |
214 name = util.canonpath(canonroot, cwd, name) | |
215 elif kind in ('relglob', 'path'): | |
216 name = util.normpath(name) | |
217 | |
218 pats.append((kind, name)) | |
219 | |
220 if kind in ('glob', 're', 'relglob', 'relre'): | |
221 anypats = True | |
222 | |
223 if kind == 'glob': | |
224 root = globprefix(name) | |
225 roots.append(root) | |
226 elif kind in ('relpath', 'path'): | |
227 roots.append(name or '.') | |
228 elif kind == 'relglob': | |
229 roots.append('.') | |
230 return roots, pats, anypats | |
231 | |
232 roots, pats, anypats = normalizepats(names, dflt_pat) | |
233 | 233 |
234 if names: | 234 if names: |
235 patmatch = matchfn(pats, '$') | 235 patmatch = _matchfn(pats, '$') |
236 if inc: | 236 if inc: |
237 dummy, inckinds, dummy = normalizepats(inc, 'glob') | 237 dummy, inckinds, dummy = _normalizepats(inc, 'glob', root, cwd) |
238 incmatch = matchfn(inckinds, '(?:/|$)') | 238 incmatch = _matchfn(inckinds, '(?:/|$)') |
239 if exc: | 239 if exc: |
240 dummy, exckinds, dummy = normalizepats(exc, 'glob') | 240 dummy, exckinds, dummy = _normalizepats(exc, 'glob', root, cwd) |
241 excmatch = matchfn(exckinds, '(?:/|$)') | 241 excmatch = _matchfn(exckinds, '(?:/|$)') |
242 | 242 |
243 if names: | 243 if names: |
244 if inc: | 244 if inc: |
245 if exc: | 245 if exc: |
246 m = lambda f: incmatch(f) and not excmatch(f) and patmatch(f) | 246 m = lambda f: incmatch(f) and not excmatch(f) and patmatch(f) |