Mercurial > hg
comparison mercurial/match.py @ 32458:a04bc55201c3
match: extract base class for matchers
We will soon start splitting up the current matcher class into more
specialized classes, so we'll want a base class for all the things
that don't vary much between different matchers.
author | Martin von Zweigbergk <martinvonz@google.com> |
---|---|
date | Wed, 17 May 2017 23:45:13 -0700 |
parents | 57d6c0c74b1b |
children | 9f781f43f2ce |
comparison
equal
deleted
inserted
replaced
32457:2def402bd16d | 32458:a04bc55201c3 |
---|---|
200 continue | 200 continue |
201 # else: re or relre - which cannot be normalized | 201 # else: re or relre - which cannot be normalized |
202 kindpats.append((kind, pat, '')) | 202 kindpats.append((kind, pat, '')) |
203 return kindpats | 203 return kindpats |
204 | 204 |
205 class matcher(object): | 205 class basematcher(object): |
206 | |
207 def __init__(self, root, cwd, badfn=None): | |
208 self._root = root | |
209 self._cwd = cwd | |
210 if badfn is not None: | |
211 self.bad = badfn | |
212 self._files = [] # exact files and roots of patterns | |
213 self.matchfn = lambda f: False | |
214 | |
215 def __call__(self, fn): | |
216 return self.matchfn(fn) | |
217 def __iter__(self): | |
218 for f in self._files: | |
219 yield f | |
220 # Callbacks related to how the matcher is used by dirstate.walk. | |
221 # Subscribers to these events must monkeypatch the matcher object. | |
222 def bad(self, f, msg): | |
223 '''Callback from dirstate.walk for each explicit file that can't be | |
224 found/accessed, with an error message.''' | |
225 pass | |
226 | |
227 # If an explicitdir is set, it will be called when an explicitly listed | |
228 # directory is visited. | |
229 explicitdir = None | |
230 | |
231 # If an traversedir is set, it will be called when a directory discovered | |
232 # by recursive traversal is visited. | |
233 traversedir = None | |
234 | |
235 def abs(self, f): | |
236 '''Convert a repo path back to path that is relative to the root of the | |
237 matcher.''' | |
238 return f | |
239 | |
240 def rel(self, f): | |
241 '''Convert repo path back to path that is relative to cwd of matcher.''' | |
242 return util.pathto(self._root, self._cwd, f) | |
243 | |
244 def uipath(self, f): | |
245 '''Convert repo path to a display path. If patterns or -I/-X were used | |
246 to create this matcher, the display path will be relative to cwd. | |
247 Otherwise it is relative to the root of the repo.''' | |
248 return self.rel(f) | |
249 | |
250 def files(self): | |
251 '''Explicitly listed files or patterns or roots: | |
252 if no patterns or .always(): empty list, | |
253 if exact: list exact files, | |
254 if not .anypats(): list all files and dirs, | |
255 else: optimal roots''' | |
256 return self._files | |
257 | |
258 @propertycache | |
259 def _fileset(self): | |
260 return set(self._files) | |
261 | |
262 def exact(self, f): | |
263 '''Returns True if f is in .files().''' | |
264 return f in self._fileset | |
265 | |
266 def visitdir(self, dir): | |
267 '''Decides whether a directory should be visited based on whether it | |
268 has potential matches in it or one of its subdirectories. This is | |
269 based on the match's primary, included, and excluded patterns. | |
270 | |
271 Returns the string 'all' if the given directory and all subdirectories | |
272 should be visited. Otherwise returns True or False indicating whether | |
273 the given directory should be visited. | |
274 | |
275 This function's behavior is undefined if it has returned False for | |
276 one of the dir's parent directories. | |
277 ''' | |
278 return False | |
279 | |
280 def anypats(self): | |
281 '''Matcher uses patterns or include/exclude.''' | |
282 return False | |
283 | |
284 def always(self): | |
285 '''Matcher will match everything and .files() will be empty | |
286 - optimization might be possible and necessary.''' | |
287 return False | |
288 | |
289 def isexact(self): | |
290 return False | |
291 | |
292 def prefix(self): | |
293 return not self.always() and not self.isexact() and not self.anypats() | |
294 | |
295 class matcher(basematcher): | |
206 | 296 |
207 def __init__(self, root, cwd, normalize, patterns, include=None, | 297 def __init__(self, root, cwd, normalize, patterns, include=None, |
208 exclude=None, default='glob', exact=False, auditor=None, | 298 exclude=None, default='glob', exact=False, auditor=None, |
209 ctx=None, listsubrepos=False, warn=None, badfn=None): | 299 ctx=None, listsubrepos=False, warn=None, badfn=None): |
300 super(matcher, self).__init__(root, cwd, badfn) | |
210 if include is None: | 301 if include is None: |
211 include = [] | 302 include = [] |
212 if exclude is None: | 303 if exclude is None: |
213 exclude = [] | 304 exclude = [] |
214 | 305 |
215 self._root = root | |
216 self._cwd = cwd | |
217 self._files = [] # exact files and roots of patterns | |
218 self._anypats = bool(include or exclude) | 306 self._anypats = bool(include or exclude) |
219 self._always = False | 307 self._always = False |
220 self._pathrestricted = bool(include or exclude or patterns) | 308 self._pathrestricted = bool(include or exclude or patterns) |
221 self.patternspat = None | 309 self.patternspat = None |
222 self.includepat = None | 310 self.includepat = None |
225 # roots are directories which are recursively included/excluded. | 313 # roots are directories which are recursively included/excluded. |
226 self._includeroots = set() | 314 self._includeroots = set() |
227 self._excluderoots = set() | 315 self._excluderoots = set() |
228 # dirs are directories which are non-recursively included. | 316 # dirs are directories which are non-recursively included. |
229 self._includedirs = set() | 317 self._includedirs = set() |
230 | |
231 if badfn is not None: | |
232 self.bad = badfn | |
233 | 318 |
234 matchfns = [] | 319 matchfns = [] |
235 if include: | 320 if include: |
236 kindpats = normalize(include, 'glob', root, cwd, auditor, warn) | 321 kindpats = normalize(include, 'glob', root, cwd, auditor, warn) |
237 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)', | 322 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)', |
279 return False | 364 return False |
280 return True | 365 return True |
281 | 366 |
282 self.matchfn = m | 367 self.matchfn = m |
283 | 368 |
284 def __call__(self, fn): | |
285 return self.matchfn(fn) | |
286 def __iter__(self): | |
287 for f in self._files: | |
288 yield f | |
289 | |
290 # Callbacks related to how the matcher is used by dirstate.walk. | |
291 # Subscribers to these events must monkeypatch the matcher object. | |
292 def bad(self, f, msg): | |
293 '''Callback from dirstate.walk for each explicit file that can't be | |
294 found/accessed, with an error message.''' | |
295 pass | |
296 | |
297 # If an explicitdir is set, it will be called when an explicitly listed | |
298 # directory is visited. | |
299 explicitdir = None | |
300 | |
301 # If an traversedir is set, it will be called when a directory discovered | |
302 # by recursive traversal is visited. | |
303 traversedir = None | |
304 | |
305 def abs(self, f): | |
306 '''Convert a repo path back to path that is relative to the root of the | |
307 matcher.''' | |
308 return f | |
309 | |
310 def rel(self, f): | |
311 '''Convert repo path back to path that is relative to cwd of matcher.''' | |
312 return util.pathto(self._root, self._cwd, f) | |
313 | |
314 def uipath(self, f): | 369 def uipath(self, f): |
315 '''Convert repo path to a display path. If patterns or -I/-X were used | |
316 to create this matcher, the display path will be relative to cwd. | |
317 Otherwise it is relative to the root of the repo.''' | |
318 return (self._pathrestricted and self.rel(f)) or self.abs(f) | 370 return (self._pathrestricted and self.rel(f)) or self.abs(f) |
319 | |
320 def files(self): | |
321 '''Explicitly listed files or patterns or roots: | |
322 if no patterns or .always(): empty list, | |
323 if exact: list exact files, | |
324 if not .anypats(): list all files and dirs, | |
325 else: optimal roots''' | |
326 return self._files | |
327 | |
328 @propertycache | |
329 def _fileset(self): | |
330 return set(self._files) | |
331 | 371 |
332 @propertycache | 372 @propertycache |
333 def _dirs(self): | 373 def _dirs(self): |
334 return set(util.dirs(self._fileset)) | {'.'} | 374 return set(util.dirs(self._fileset)) | {'.'} |
335 | 375 |
336 def visitdir(self, dir): | 376 def visitdir(self, dir): |
337 '''Decides whether a directory should be visited based on whether it | |
338 has potential matches in it or one of its subdirectories. This is | |
339 based on the match's primary, included, and excluded patterns. | |
340 | |
341 Returns the string 'all' if the given directory and all subdirectories | |
342 should be visited. Otherwise returns True or False indicating whether | |
343 the given directory should be visited. | |
344 | |
345 This function's behavior is undefined if it has returned False for | |
346 one of the dir's parent directories. | |
347 ''' | |
348 if self.prefix() and dir in self._fileset: | 377 if self.prefix() and dir in self._fileset: |
349 return 'all' | 378 return 'all' |
350 if dir in self._excluderoots: | 379 if dir in self._excluderoots: |
351 return False | 380 return False |
352 if ((self._includeroots or self._includedirs) and | 381 if ((self._includeroots or self._includedirs) and |
361 dir in self._fileset or | 390 dir in self._fileset or |
362 dir in self._dirs or | 391 dir in self._dirs or |
363 any(parentdir in self._fileset | 392 any(parentdir in self._fileset |
364 for parentdir in util.finddirs(dir))) | 393 for parentdir in util.finddirs(dir))) |
365 | 394 |
366 def exact(self, f): | |
367 '''Returns True if f is in .files().''' | |
368 return f in self._fileset | |
369 | |
370 def anypats(self): | 395 def anypats(self): |
371 '''Matcher uses patterns or include/exclude.''' | |
372 return self._anypats | 396 return self._anypats |
373 | 397 |
374 def always(self): | 398 def always(self): |
375 '''Matcher will match everything and .files() will be empty | |
376 - optimization might be possible and necessary.''' | |
377 return self._always | 399 return self._always |
378 | 400 |
379 def isexact(self): | 401 def isexact(self): |
380 return self.matchfn == self.exact | 402 return self.matchfn == self.exact |
381 | |
382 def prefix(self): | |
383 return not self.always() and not self.isexact() and not self.anypats() | |
384 | 403 |
385 def __repr__(self): | 404 def __repr__(self): |
386 return ('<matcher files=%r, patterns=%r, includes=%r, excludes=%r>' % | 405 return ('<matcher files=%r, patterns=%r, includes=%r, excludes=%r>' % |
387 (self._files, self.patternspat, self.includepat, | 406 (self._files, self.patternspat, self.includepat, |
388 self.excludepat)) | 407 self.excludepat)) |