Add config file finder.

This commit is contained in:
Amy Boyd
2016-06-22 23:11:59 +01:00
parent 4e3926d43b
commit 39ea30074e
3 changed files with 225 additions and 0 deletions

103
editorconfig/config_file.go Normal file
View File

@@ -0,0 +1,103 @@
package editorconfig
import (
"github.com/go-ini/ini"
"regexp"
"strings"
)
type ConfigFile struct {
Path string
DefaultRuleSet RuleSet
FileConstrainedRuleSets []FileConstrainedRuleSet
}
func (cf *ConfigFile) Dir() string {
return GetParentDir(cf.Path)
}
func (cf *ConfigFile) IsRoot() bool {
return cf.DefaultRuleSet.Get("root") == "true"
}
func (cf *ConfigFile) Precedence() int {
return strings.Count(cf.Path, "/")
}
type Rule struct {
Name string
Value string
}
type RuleSet []Rule
func (rs *RuleSet) Add(r Rule) {
*rs = append(*rs, r)
}
func (rs RuleSet) Get(name string) string {
for i := 0; i < len(rs); i++ {
if rs[i].Name == name {
return rs[i].Value
}
}
return ""
}
type FileConstrainedRuleSet struct {
Constraint string
ConstraintRegexp *regexp.Regexp
Rules RuleSet
}
func CreateConfigFileStruct(path string) ConfigFile {
ini, err := ini.Load(path)
if err != nil {
ExitBecauseOfInternalError("Could not parse " + path)
}
cf := ConfigFile{}
cf.Path = path
cf.DefaultRuleSet = CreateRuleSetFromIniSectonName(ini, "")
for _, sectionName := range ini.SectionStrings() {
if sectionName == "DEFAULT" {
continue
}
fcrs := FileConstrainedRuleSet{
Constraint: sectionName,
ConstraintRegexp: ConvertWildcardPatternToGoRegexp(sectionName),
Rules: CreateRuleSetFromIniSectonName(ini, sectionName),
}
cf.FileConstrainedRuleSets = append(cf.FileConstrainedRuleSets, fcrs)
}
return cf
}
func CreateRuleSetFromIniSectonName(ini *ini.File, name string) RuleSet {
section, err := ini.GetSection(name)
if err != nil {
ExitBecauseOfInternalError(err.Error())
// return RuleSet{}
}
return CreateRuleSetFromIniSecton(section)
}
func CreateRuleSetFromIniSecton(section *ini.Section) RuleSet {
rs := RuleSet{}
for name, val := range section.KeysHash() {
// The spec says rule names should be case-insensitive.
name = strings.ToLower(name)
rs.Add(Rule{
Name: name,
Value: val,
})
}
return rs
}

View File

@@ -0,0 +1,88 @@
package editorconfig
import (
"os"
"path/filepath"
"strings"
)
var configFiles = []ConfigFile{}
var checkedDirectories = []string{}
func FindConfigFiles(sourceFilePaths []string) []ConfigFile {
configFiles = []ConfigFile{}
for _, path := range sourceFilePaths {
// Convert to absolute path so we can go all the way up the file system path looking for configs.
path, err := filepath.Abs(path)
if err != nil {
ExitBecauseOfInternalError("Could not get absolute path for " + path)
}
path = GetParentDir(path)
var searchParents bool
for strings.Contains(path, "/") {
searchParents = CheckDirectoryForConfigFile(path)
if !searchParents {
break
}
path = GetParentDir(path)
}
if searchParents {
// `path` is now the volume root without a trailing slash, e.g. '' for '/' or 'C:' for 'C:/'.
CheckDirectoryForConfigFile(path + "/")
}
}
return configFiles
}
/**
* @return Whether or not to search parent directories.
*/
func CheckDirectoryForConfigFile(dir string) bool {
for _, cd := range checkedDirectories {
if dir == cd {
return false
}
}
stat, err := os.Stat(dir)
if err != nil {
ExitBecauseOfInternalError("Could not stat directory: " + dir)
}
if !stat.IsDir() {
ExitBecauseOfInternalError("Expected this path to be a directory, but it is not: " + dir)
}
checkedDirectories = append(checkedDirectories, dir)
possibleFile := GetConfigFilePathInDirectory(dir)
stat, err = os.Stat(possibleFile)
if err != nil {
return true
}
if stat.IsDir() {
return true
}
if stat.Size() == 0 {
return true
}
cf := CreateConfigFileStruct(possibleFile)
configFiles = append(configFiles, cf)
return !cf.IsRoot()
}
func GetConfigFilePathInDirectory(dir string) string {
if strings.HasSuffix(dir, "/") {
// This will only be possible in Unix for the '/' directory.
return dir + ".editorconfig"
} else {
return dir + "/.editorconfig"
}
}

View File

@@ -0,0 +1,34 @@
package editorconfig
import (
"strconv"
"testing"
)
func TestGetConfigFilePathInDirectoryForNonRootPath(t *testing.T) {
if GetConfigFilePathInDirectory("/home/x") != "/home/x/.editorconfig" {
t.Error("GetConfigFilePathInDirectory failed for path /home/x")
}
}
func TestGetConfigFilePathInDirectoryForRootPath(t *testing.T) {
if GetConfigFilePathInDirectory("/") != "/.editorconfig" {
t.Error("GetConfigFilePathInDirectory failed for path /")
}
}
func TestFindConfigFilesWhenDirectoryHasRootConfig(t *testing.T) {
files := FindConfigFiles([]string{"tests/a/b/has-root-config/file.java"})
if len(files) != 1 {
t.Error("Should have found 1 .editorconfig file but found " + strconv.Itoa(len(files)))
}
}
func TestFindConfigFilesFindsRootConfigInParentDirectory(t *testing.T) {
files := FindConfigFiles([]string{"tests/a/b/c/d/file.java"})
if len(files) != 2 {
t.Error("Should have found 2 .editorconfig files but found " + strconv.Itoa(len(files)))
}
}