##// END OF EJS Templates
match: join two nested if-blocks...
Martin von Zweigbergk -
r25578:4b1ec70b default
parent child Browse files
Show More
@@ -1,669 +1,669
1 1 # match.py - filename matching
2 2 #
3 3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
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 8 import copy, re
9 9 import util, pathutil
10 10 from i18n import _
11 11
12 12 propertycache = util.propertycache
13 13
14 14 def _rematcher(regex):
15 15 '''compile the regexp with the best available regexp engine and return a
16 16 matcher function'''
17 17 m = util.re.compile(regex)
18 18 try:
19 19 # slightly faster, provided by facebook's re2 bindings
20 20 return m.test_match
21 21 except AttributeError:
22 22 return m.match
23 23
24 24 def _expandsets(kindpats, ctx, listsubrepos):
25 25 '''Returns the kindpats list with the 'set' patterns expanded.'''
26 26 fset = set()
27 27 other = []
28 28
29 29 for kind, pat, source in kindpats:
30 30 if kind == 'set':
31 31 if not ctx:
32 32 raise util.Abort("fileset expression with no context")
33 33 s = ctx.getfileset(pat)
34 34 fset.update(s)
35 35
36 36 if listsubrepos:
37 37 for subpath in ctx.substate:
38 38 s = ctx.sub(subpath).getfileset(pat)
39 39 fset.update(subpath + '/' + f for f in s)
40 40
41 41 continue
42 42 other.append((kind, pat, source))
43 43 return fset, other
44 44
45 45 def _expandsubinclude(kindpats, root):
46 46 '''Returns the list of subinclude matchers and the kindpats without the
47 47 subincludes in it.'''
48 48 relmatchers = []
49 49 other = []
50 50
51 51 for kind, pat, source in kindpats:
52 52 if kind == 'subinclude':
53 53 sourceroot = pathutil.dirname(util.normpath(source))
54 54 pat = util.pconvert(pat)
55 55 path = pathutil.join(sourceroot, pat)
56 56
57 57 newroot = pathutil.dirname(path)
58 58 relmatcher = match(newroot, '', [], ['include:%s' % path])
59 59
60 60 prefix = pathutil.canonpath(root, root, newroot)
61 61 if prefix:
62 62 prefix += '/'
63 63 relmatchers.append((prefix, relmatcher))
64 64 else:
65 65 other.append((kind, pat, source))
66 66
67 67 return relmatchers, other
68 68
69 69 def _kindpatsalwaysmatch(kindpats):
70 70 """"Checks whether the kindspats match everything, as e.g.
71 71 'relpath:.' does.
72 72 """
73 73 for kind, pat, source in kindpats:
74 74 if pat != '' or kind not in ['relpath', 'glob']:
75 75 return False
76 76 return True
77 77
78 78 class match(object):
79 79 def __init__(self, root, cwd, patterns, include=[], exclude=[],
80 80 default='glob', exact=False, auditor=None, ctx=None,
81 81 listsubrepos=False, warn=None, badfn=None):
82 82 """build an object to match a set of file patterns
83 83
84 84 arguments:
85 85 root - the canonical root of the tree you're matching against
86 86 cwd - the current working directory, if relevant
87 87 patterns - patterns to find
88 88 include - patterns to include (unless they are excluded)
89 89 exclude - patterns to exclude (even if they are included)
90 90 default - if a pattern in patterns has no explicit type, assume this one
91 91 exact - patterns are actually filenames (include/exclude still apply)
92 92 warn - optional function used for printing warnings
93 93 badfn - optional bad() callback for this matcher instead of the default
94 94
95 95 a pattern is one of:
96 96 'glob:<glob>' - a glob relative to cwd
97 97 're:<regexp>' - a regular expression
98 98 'path:<path>' - a path relative to repository root
99 99 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
100 100 'relpath:<path>' - a path relative to cwd
101 101 'relre:<regexp>' - a regexp that needn't match the start of a name
102 102 'set:<fileset>' - a fileset expression
103 103 'include:<path>' - a file of patterns to read and include
104 104 'subinclude:<path>' - a file of patterns to match against files under
105 105 the same directory
106 106 '<something>' - a pattern of the specified default type
107 107 """
108 108
109 109 self._root = root
110 110 self._cwd = cwd
111 111 self._files = [] # exact files and roots of patterns
112 112 self._anypats = bool(include or exclude)
113 113 self._always = False
114 114 self._pathrestricted = bool(include or exclude or patterns)
115 115 self._warn = warn
116 116 self._includeroots = set()
117 117 self._includedirs = set(['.'])
118 118 self._excluderoots = set()
119 119
120 120 if badfn is not None:
121 121 self.bad = badfn
122 122
123 123 matchfns = []
124 124 if include:
125 125 kindpats = self._normalize(include, 'glob', root, cwd, auditor)
126 126 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
127 127 listsubrepos, root)
128 128 self._includeroots.update(_roots(kindpats))
129 129 self._includeroots.discard('.')
130 130 self._includedirs.update(util.dirs(self._includeroots))
131 131 matchfns.append(im)
132 132 if exclude:
133 133 kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
134 134 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
135 135 listsubrepos, root)
136 136 if not _anypats(kindpats):
137 137 self._excluderoots.update(_roots(kindpats))
138 138 matchfns.append(lambda f: not em(f))
139 139 if exact:
140 140 if isinstance(patterns, list):
141 141 self._files = patterns
142 142 else:
143 143 self._files = list(patterns)
144 144 matchfns.append(self.exact)
145 145 elif patterns:
146 146 kindpats = self._normalize(patterns, default, root, cwd, auditor)
147 147 if not _kindpatsalwaysmatch(kindpats):
148 148 self._files = _roots(kindpats)
149 149 self._anypats = self._anypats or _anypats(kindpats)
150 150 self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
151 151 listsubrepos, root)
152 152 matchfns.append(pm)
153 153
154 154 if not matchfns:
155 155 m = util.always
156 156 self._always = True
157 157 elif len(matchfns) == 1:
158 158 m = matchfns[0]
159 159 else:
160 160 def m(f):
161 161 for matchfn in matchfns:
162 162 if not matchfn(f):
163 163 return False
164 164 return True
165 165
166 166 self.matchfn = m
167 167 self._fileroots = set(self._files)
168 168
169 169 def __call__(self, fn):
170 170 return self.matchfn(fn)
171 171 def __iter__(self):
172 172 for f in self._files:
173 173 yield f
174 174
175 175 # Callbacks related to how the matcher is used by dirstate.walk.
176 176 # Subscribers to these events must monkeypatch the matcher object.
177 177 def bad(self, f, msg):
178 178 '''Callback from dirstate.walk for each explicit file that can't be
179 179 found/accessed, with an error message.'''
180 180 pass
181 181
182 182 # If an explicitdir is set, it will be called when an explicitly listed
183 183 # directory is visited.
184 184 explicitdir = None
185 185
186 186 # If an traversedir is set, it will be called when a directory discovered
187 187 # by recursive traversal is visited.
188 188 traversedir = None
189 189
190 190 def abs(self, f):
191 191 '''Convert a repo path back to path that is relative to the root of the
192 192 matcher.'''
193 193 return f
194 194
195 195 def rel(self, f):
196 196 '''Convert repo path back to path that is relative to cwd of matcher.'''
197 197 return util.pathto(self._root, self._cwd, f)
198 198
199 199 def uipath(self, f):
200 200 '''Convert repo path to a display path. If patterns or -I/-X were used
201 201 to create this matcher, the display path will be relative to cwd.
202 202 Otherwise it is relative to the root of the repo.'''
203 203 return (self._pathrestricted and self.rel(f)) or self.abs(f)
204 204
205 205 def files(self):
206 206 '''Explicitly listed files or patterns or roots:
207 207 if no patterns or .always(): empty list,
208 208 if exact: list exact files,
209 209 if not .anypats(): list all files and dirs,
210 210 else: optimal roots'''
211 211 return self._files
212 212
213 213 @propertycache
214 214 def _dirs(self):
215 215 return set(util.dirs(self._fileroots)) | set(['.'])
216 216
217 217 def visitdir(self, dir):
218 218 '''Decides whether a directory should be visited based on whether it
219 219 has potential matches in it or one of its subdirectories. This is
220 220 based on the match's primary, included, and excluded patterns.
221 221
222 222 This function's behavior is undefined if it has returned False for
223 223 one of the dir's parent directories.
224 224 '''
225 225 if dir in self._excluderoots:
226 226 return False
227 227 if (self._includeroots and
228 228 dir not in self._includeroots and
229 dir not in self._includedirs):
230 if not any(parent in self._includeroots
231 for parent in util.finddirs(dir)):
232 return False
229 dir not in self._includedirs and
230 not any(parent in self._includeroots
231 for parent in util.finddirs(dir))):
232 return False
233 233 return (not self._fileroots or
234 234 '.' in self._fileroots or
235 235 dir in self._fileroots or
236 236 dir in self._dirs or
237 237 any(parentdir in self._fileroots
238 238 for parentdir in util.finddirs(dir)))
239 239
240 240 def exact(self, f):
241 241 '''Returns True if f is in .files().'''
242 242 return f in self._fileroots
243 243
244 244 def anypats(self):
245 245 '''Matcher uses patterns or include/exclude.'''
246 246 return self._anypats
247 247
248 248 def always(self):
249 249 '''Matcher will match everything and .files() will be empty
250 250 - optimization might be possible and necessary.'''
251 251 return self._always
252 252
253 253 def ispartial(self):
254 254 '''True if the matcher won't always match.
255 255
256 256 Although it's just the inverse of _always in this implementation,
257 257 an extenion such as narrowhg might make it return something
258 258 slightly different.'''
259 259 return not self._always
260 260
261 261 def isexact(self):
262 262 return self.matchfn == self.exact
263 263
264 264 def prefix(self):
265 265 return not self.always() and not self.isexact() and not self.anypats()
266 266
267 267 def _normalize(self, patterns, default, root, cwd, auditor):
268 268 '''Convert 'kind:pat' from the patterns list to tuples with kind and
269 269 normalized and rooted patterns and with listfiles expanded.'''
270 270 kindpats = []
271 271 for kind, pat in [_patsplit(p, default) for p in patterns]:
272 272 if kind in ('glob', 'relpath'):
273 273 pat = pathutil.canonpath(root, cwd, pat, auditor)
274 274 elif kind in ('relglob', 'path'):
275 275 pat = util.normpath(pat)
276 276 elif kind in ('listfile', 'listfile0'):
277 277 try:
278 278 files = util.readfile(pat)
279 279 if kind == 'listfile0':
280 280 files = files.split('\0')
281 281 else:
282 282 files = files.splitlines()
283 283 files = [f for f in files if f]
284 284 except EnvironmentError:
285 285 raise util.Abort(_("unable to read file list (%s)") % pat)
286 286 for k, p, source in self._normalize(files, default, root, cwd,
287 287 auditor):
288 288 kindpats.append((k, p, pat))
289 289 continue
290 290 elif kind == 'include':
291 291 try:
292 292 includepats = readpatternfile(pat, self._warn)
293 293 for k, p, source in self._normalize(includepats, default,
294 294 root, cwd, auditor):
295 295 kindpats.append((k, p, source or pat))
296 296 except util.Abort, inst:
297 297 raise util.Abort('%s: %s' % (pat, inst[0]))
298 298 except IOError, inst:
299 299 if self._warn:
300 300 self._warn(_("skipping unreadable pattern file "
301 301 "'%s': %s\n") % (pat, inst.strerror))
302 302 continue
303 303 # else: re or relre - which cannot be normalized
304 304 kindpats.append((kind, pat, ''))
305 305 return kindpats
306 306
307 307 def exact(root, cwd, files, badfn=None):
308 308 return match(root, cwd, files, exact=True, badfn=badfn)
309 309
310 310 def always(root, cwd):
311 311 return match(root, cwd, [])
312 312
313 313 def badmatch(match, badfn):
314 314 """Make a copy of the given matcher, replacing its bad method with the given
315 315 one.
316 316 """
317 317 m = copy.copy(match)
318 318 m.bad = badfn
319 319 return m
320 320
321 321 class narrowmatcher(match):
322 322 """Adapt a matcher to work on a subdirectory only.
323 323
324 324 The paths are remapped to remove/insert the path as needed:
325 325
326 326 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
327 327 >>> m2 = narrowmatcher('sub', m1)
328 328 >>> bool(m2('a.txt'))
329 329 False
330 330 >>> bool(m2('b.txt'))
331 331 True
332 332 >>> bool(m2.matchfn('a.txt'))
333 333 False
334 334 >>> bool(m2.matchfn('b.txt'))
335 335 True
336 336 >>> m2.files()
337 337 ['b.txt']
338 338 >>> m2.exact('b.txt')
339 339 True
340 340 >>> util.pconvert(m2.rel('b.txt'))
341 341 'sub/b.txt'
342 342 >>> def bad(f, msg):
343 343 ... print "%s: %s" % (f, msg)
344 344 >>> m1.bad = bad
345 345 >>> m2.bad('x.txt', 'No such file')
346 346 sub/x.txt: No such file
347 347 >>> m2.abs('c.txt')
348 348 'sub/c.txt'
349 349 """
350 350
351 351 def __init__(self, path, matcher):
352 352 self._root = matcher._root
353 353 self._cwd = matcher._cwd
354 354 self._path = path
355 355 self._matcher = matcher
356 356 self._always = matcher._always
357 357 self._pathrestricted = matcher._pathrestricted
358 358
359 359 self._files = [f[len(path) + 1:] for f in matcher._files
360 360 if f.startswith(path + "/")]
361 361
362 362 # If the parent repo had a path to this subrepo and no patterns are
363 363 # specified, this submatcher always matches.
364 364 if not self._always and not matcher._anypats:
365 365 self._always = any(f == path for f in matcher._files)
366 366
367 367 self._anypats = matcher._anypats
368 368 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
369 369 self._fileroots = set(self._files)
370 370
371 371 def abs(self, f):
372 372 return self._matcher.abs(self._path + "/" + f)
373 373
374 374 def bad(self, f, msg):
375 375 self._matcher.bad(self._path + "/" + f, msg)
376 376
377 377 def rel(self, f):
378 378 return self._matcher.rel(self._path + "/" + f)
379 379
380 380 class icasefsmatcher(match):
381 381 """A matcher for wdir on case insensitive filesystems, which normalizes the
382 382 given patterns to the case in the filesystem.
383 383 """
384 384
385 385 def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
386 386 ctx, listsubrepos=False, badfn=None):
387 387 init = super(icasefsmatcher, self).__init__
388 388 self._dsnormalize = ctx.repo().dirstate.normalize
389 389
390 390 init(root, cwd, patterns, include, exclude, default, auditor=auditor,
391 391 ctx=ctx, listsubrepos=listsubrepos, badfn=badfn)
392 392
393 393 # m.exact(file) must be based off of the actual user input, otherwise
394 394 # inexact case matches are treated as exact, and not noted without -v.
395 395 if self._files:
396 396 self._fileroots = set(_roots(self._kp))
397 397
398 398 def _normalize(self, patterns, default, root, cwd, auditor):
399 399 self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
400 400 root, cwd, auditor)
401 401 kindpats = []
402 402 for kind, pats, source in self._kp:
403 403 if kind not in ('re', 'relre'): # regex can't be normalized
404 404 pats = self._dsnormalize(pats)
405 405 kindpats.append((kind, pats, source))
406 406 return kindpats
407 407
408 408 def patkind(pattern, default=None):
409 409 '''If pattern is 'kind:pat' with a known kind, return kind.'''
410 410 return _patsplit(pattern, default)[0]
411 411
412 412 def _patsplit(pattern, default):
413 413 """Split a string into the optional pattern kind prefix and the actual
414 414 pattern."""
415 415 if ':' in pattern:
416 416 kind, pat = pattern.split(':', 1)
417 417 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
418 418 'listfile', 'listfile0', 'set', 'include', 'subinclude'):
419 419 return kind, pat
420 420 return default, pattern
421 421
422 422 def _globre(pat):
423 423 r'''Convert an extended glob string to a regexp string.
424 424
425 425 >>> print _globre(r'?')
426 426 .
427 427 >>> print _globre(r'*')
428 428 [^/]*
429 429 >>> print _globre(r'**')
430 430 .*
431 431 >>> print _globre(r'**/a')
432 432 (?:.*/)?a
433 433 >>> print _globre(r'a/**/b')
434 434 a\/(?:.*/)?b
435 435 >>> print _globre(r'[a*?!^][^b][!c]')
436 436 [a*?!^][\^b][^c]
437 437 >>> print _globre(r'{a,b}')
438 438 (?:a|b)
439 439 >>> print _globre(r'.\*\?')
440 440 \.\*\?
441 441 '''
442 442 i, n = 0, len(pat)
443 443 res = ''
444 444 group = 0
445 445 escape = util.re.escape
446 446 def peek():
447 447 return i < n and pat[i]
448 448 while i < n:
449 449 c = pat[i]
450 450 i += 1
451 451 if c not in '*?[{},\\':
452 452 res += escape(c)
453 453 elif c == '*':
454 454 if peek() == '*':
455 455 i += 1
456 456 if peek() == '/':
457 457 i += 1
458 458 res += '(?:.*/)?'
459 459 else:
460 460 res += '.*'
461 461 else:
462 462 res += '[^/]*'
463 463 elif c == '?':
464 464 res += '.'
465 465 elif c == '[':
466 466 j = i
467 467 if j < n and pat[j] in '!]':
468 468 j += 1
469 469 while j < n and pat[j] != ']':
470 470 j += 1
471 471 if j >= n:
472 472 res += '\\['
473 473 else:
474 474 stuff = pat[i:j].replace('\\','\\\\')
475 475 i = j + 1
476 476 if stuff[0] == '!':
477 477 stuff = '^' + stuff[1:]
478 478 elif stuff[0] == '^':
479 479 stuff = '\\' + stuff
480 480 res = '%s[%s]' % (res, stuff)
481 481 elif c == '{':
482 482 group += 1
483 483 res += '(?:'
484 484 elif c == '}' and group:
485 485 res += ')'
486 486 group -= 1
487 487 elif c == ',' and group:
488 488 res += '|'
489 489 elif c == '\\':
490 490 p = peek()
491 491 if p:
492 492 i += 1
493 493 res += escape(p)
494 494 else:
495 495 res += escape(c)
496 496 else:
497 497 res += escape(c)
498 498 return res
499 499
500 500 def _regex(kind, pat, globsuffix):
501 501 '''Convert a (normalized) pattern of any kind into a regular expression.
502 502 globsuffix is appended to the regexp of globs.'''
503 503 if not pat:
504 504 return ''
505 505 if kind == 're':
506 506 return pat
507 507 if kind == 'path':
508 508 return '^' + util.re.escape(pat) + '(?:/|$)'
509 509 if kind == 'relglob':
510 510 return '(?:|.*/)' + _globre(pat) + globsuffix
511 511 if kind == 'relpath':
512 512 return util.re.escape(pat) + '(?:/|$)'
513 513 if kind == 'relre':
514 514 if pat.startswith('^'):
515 515 return pat
516 516 return '.*' + pat
517 517 return _globre(pat) + globsuffix
518 518
519 519 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
520 520 '''Return regexp string and a matcher function for kindpats.
521 521 globsuffix is appended to the regexp of globs.'''
522 522 matchfuncs = []
523 523
524 524 subincludes, kindpats = _expandsubinclude(kindpats, root)
525 525 if subincludes:
526 526 def matchsubinclude(f):
527 527 for prefix, mf in subincludes:
528 528 if f.startswith(prefix) and mf(f[len(prefix):]):
529 529 return True
530 530 return False
531 531 matchfuncs.append(matchsubinclude)
532 532
533 533 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
534 534 if fset:
535 535 matchfuncs.append(fset.__contains__)
536 536
537 537 regex = ''
538 538 if kindpats:
539 539 regex, mf = _buildregexmatch(kindpats, globsuffix)
540 540 matchfuncs.append(mf)
541 541
542 542 if len(matchfuncs) == 1:
543 543 return regex, matchfuncs[0]
544 544 else:
545 545 return regex, lambda f: any(mf(f) for mf in matchfuncs)
546 546
547 547 def _buildregexmatch(kindpats, globsuffix):
548 548 """Build a match function from a list of kinds and kindpats,
549 549 return regexp string and a matcher function."""
550 550 try:
551 551 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
552 552 for (k, p, s) in kindpats])
553 553 if len(regex) > 20000:
554 554 raise OverflowError
555 555 return regex, _rematcher(regex)
556 556 except OverflowError:
557 557 # We're using a Python with a tiny regex engine and we
558 558 # made it explode, so we'll divide the pattern list in two
559 559 # until it works
560 560 l = len(kindpats)
561 561 if l < 2:
562 562 raise
563 563 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
564 564 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
565 565 return regex, lambda s: a(s) or b(s)
566 566 except re.error:
567 567 for k, p, s in kindpats:
568 568 try:
569 569 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
570 570 except re.error:
571 571 if s:
572 572 raise util.Abort(_("%s: invalid pattern (%s): %s") %
573 573 (s, k, p))
574 574 else:
575 575 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
576 576 raise util.Abort(_("invalid pattern"))
577 577
578 578 def _roots(kindpats):
579 579 '''return roots and exact explicitly listed files from patterns
580 580
581 581 >>> _roots([('glob', 'g/*', ''), ('glob', 'g', ''), ('glob', 'g*', '')])
582 582 ['g', 'g', '.']
583 583 >>> _roots([('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
584 584 ['r', 'p/p', '.']
585 585 >>> _roots([('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
586 586 ['.', '.', '.']
587 587 '''
588 588 r = []
589 589 for kind, pat, source in kindpats:
590 590 if kind == 'glob': # find the non-glob prefix
591 591 root = []
592 592 for p in pat.split('/'):
593 593 if '[' in p or '{' in p or '*' in p or '?' in p:
594 594 break
595 595 root.append(p)
596 596 r.append('/'.join(root) or '.')
597 597 elif kind in ('relpath', 'path'):
598 598 r.append(pat or '.')
599 599 else: # relglob, re, relre
600 600 r.append('.')
601 601 return r
602 602
603 603 def _anypats(kindpats):
604 604 for kind, pat, source in kindpats:
605 605 if kind in ('glob', 're', 'relglob', 'relre', 'set'):
606 606 return True
607 607
608 608 _commentre = None
609 609
610 610 def readpatternfile(filepath, warn):
611 611 '''parse a pattern file, returning a list of
612 612 patterns. These patterns should be given to compile()
613 613 to be validated and converted into a match function.
614 614
615 615 trailing white space is dropped.
616 616 the escape character is backslash.
617 617 comments start with #.
618 618 empty lines are skipped.
619 619
620 620 lines can be of the following formats:
621 621
622 622 syntax: regexp # defaults following lines to non-rooted regexps
623 623 syntax: glob # defaults following lines to non-rooted globs
624 624 re:pattern # non-rooted regular expression
625 625 glob:pattern # non-rooted glob
626 626 pattern # pattern of the current default type'''
627 627
628 628 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
629 629 'include': 'include', 'subinclude': 'subinclude'}
630 630 syntax = 'relre:'
631 631 patterns = []
632 632
633 633 fp = open(filepath)
634 634 for line in fp:
635 635 if "#" in line:
636 636 global _commentre
637 637 if not _commentre:
638 638 _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
639 639 # remove comments prefixed by an even number of escapes
640 640 line = _commentre.sub(r'\1', line)
641 641 # fixup properly escaped comments that survived the above
642 642 line = line.replace("\\#", "#")
643 643 line = line.rstrip()
644 644 if not line:
645 645 continue
646 646
647 647 if line.startswith('syntax:'):
648 648 s = line[7:].strip()
649 649 try:
650 650 syntax = syntaxes[s]
651 651 except KeyError:
652 652 if warn:
653 653 warn(_("%s: ignoring invalid syntax '%s'\n") %
654 654 (filepath, s))
655 655 continue
656 656
657 657 linesyntax = syntax
658 658 for s, rels in syntaxes.iteritems():
659 659 if line.startswith(rels):
660 660 linesyntax = rels
661 661 line = line[len(rels):]
662 662 break
663 663 elif line.startswith(s+':'):
664 664 linesyntax = rels
665 665 line = line[len(s) + 1:]
666 666 break
667 667 patterns.append(linesyntax + line)
668 668 fp.close()
669 669 return patterns
General Comments 0
You need to be logged in to leave comments. Login now