code: func NewPatternMatcher(patterns []string) (*PatternMatcher, error) {
  	pm := &PatternMatcher{
  		patterns: make([]*Pattern, 0, len(patterns)),
  	}
  	for _, p := range patterns {
  		// Eliminate leading and trailing whitespace.
  		p = strings.TrimSpace(p)
  		if p == "" {
  			continue
  		}
  		p = filepath.Clean(p)
  		newp := &Pattern{}
  		if p[0] == '!' {
  			if len(p) == 1 {
  				return nil, errors.New("illegal exclusion pattern: \"!\"")
  			}
  			newp.exclusion = true
  			p = p[1:]
  			pm.exclusions = true
  		}
  		// Do some syntax checking on the pattern.
  		// filepath's Match() has some really weird rules that are inconsistent
  		// so instead of trying to dup their logic, just call Match() for its
  		// error state and if there is an error in the pattern return it.
  		// If this becomes an issue we can remove this since its really only
  		// needed in the error (syntax) case - which isn't really critical.
  		if _, err := filepath.Match(p, "."); err != nil {
  			return nil, err
  		}
  		newp.cleanedPattern = p
  		newp.dirs = strings.Split(p, string(os.PathSeparator))
  		pm.patterns = append(pm.patterns, newp)
  	}
  	return pm, nil
  }
docs: // NewPatternMatcher creates a new matcher object for specific patterns that can
  // be used later to match against patterns against paths