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