func initLinguistAttributes()

in pkg/linguist/linguist.go [51:173]


func initLinguistAttributes(dir string) error {
	ignore := []string{}
	except := []string{}
	detected := make(map[string]string)

	gitignoreExists, err := osutil.Exists(filepath.Join(dir, ".gitignore"))
	if err != nil {
		return err
	}
	if gitignoreExists {
		log.Debugln("found .gitignore")

		f, err := os.Open(filepath.Join(dir, ".gitignore"))
		if err != nil {
			return err
		}
		defer f.Close()

		ignoreScanner := bufio.NewScanner(f)
		for ignoreScanner.Scan() {
			var isExcept bool
			path := strings.TrimSpace(ignoreScanner.Text())
			// if it's whitespace or a comment
			if len(path) == 0 || string(path[0]) == "#" {
				continue
			}
			if string(path[0]) == "!" {
				isExcept = true
				path = path[1:]
			}
			p := strings.Trim(path, string(filepath.Separator))
			if isExcept {
				except = append(except, p)
			} else {
				ignore = append(ignore, p)
			}
		}
		if err := ignoreScanner.Err(); err != nil {
			return fmt.Errorf("error reading .gitignore: %v", err)
		}
	}

	gitAttributesExists, err := osutil.Exists(filepath.Join(dir, ".gitattributes"))
	if err != nil {
		return err
	}
	if gitAttributesExists {
		log.Debugln("found .gitattributes")

		f, err := os.Open(filepath.Join(dir, ".gitattributes"))
		if err != nil {
			return err
		}
		defer f.Close()

		attributeScanner := bufio.NewScanner(f)
		var lineNumber int
		for attributeScanner.Scan() {
			lineNumber++
			line := strings.TrimSpace(attributeScanner.Text())
			words := strings.Fields(line)
			if len(words) != 2 {
				log.Printf("invalid line in .gitattributes at L%d: '%s'\n", lineNumber, line)
				continue
			}
			path := strings.Trim(words[0], string(filepath.Separator))
			if runtime.GOOS == "windows" {
				// on Windows, we also accept / as a path separator, so let's strip those as well
				path = strings.Trim(words[0], "/")
			}
			attribute := words[1]
			if strings.HasPrefix(attribute, "linguist-documentation") || strings.HasPrefix(attribute, "linguist-vendored") || strings.HasPrefix(attribute, "linguist-generated") {
				if !strings.HasSuffix(strings.ToLower(attribute), "false") {
					ignore = append(ignore, path)
				}
			} else if strings.HasPrefix(attribute, "linguist-language") {
				attr := strings.Split(attribute, "=")
				if len(attr) != 2 {
					log.Printf("invalid line in .gitattributes at L%d: '%s'\n", lineNumber, line)
					continue
				}
				language := attr[1]
				detected[path] = language
			}
		}
		if err := attributeScanner.Err(); err != nil {
			return fmt.Errorf("error reading .gitattributes: %v", err)
		}
	}

	isIgnored = func(filename string) bool {
		for _, p := range ignore {
			cleanPath, err := filepath.Rel(dir, filename)
			if err != nil {
				log.Debugf("could not get relative path: %v", err)
				return false
			}
			if m, _ := filepath.Match(p, cleanPath); m {
				for _, e := range except {
					if m, _ := filepath.Match(e, cleanPath); m {
						return false
					}
				}
				return true
			}
		}
		return false
	}
	isDetectedInGitAttributes = func(filename string) string {
		for p, lang := range detected {
			cleanPath, err := filepath.Rel(dir, filename)
			if err != nil {
				log.Debugf("could not get relative path: %v", err)
				return ""
			}
			if m, _ := filepath.Match(p, cleanPath); m {
				return lang
			}
		}
		return ""
	}
	return nil
}