Add config file finder.
This commit is contained in:
103
editorconfig/config_file.go
Normal file
103
editorconfig/config_file.go
Normal 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
|
||||
}
|
||||
88
editorconfig/config_file_finder.go
Normal file
88
editorconfig/config_file_finder.go
Normal 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"
|
||||
}
|
||||
}
|
||||
34
editorconfig/config_file_finder_test.go
Normal file
34
editorconfig/config_file_finder_test.go
Normal 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)))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user