Repository: editorconfig-checker/editorconfig-checker
Pull request #303 (2023-10-11 06:44:45)

feat: enable cached parser
Reading and parsing the ini file is done *once* per ini file rather than *the number of .editorconfig files* per file. So O(n) -> O(1) when it comes to reading the Editorconfig definition.

It doesn't seem to affect much the performance on the quick benchmark I did on saltstack/salt.

mstruebing: @greut the tests are failing - can you figure out if it has to do with your changes or something else is up?
You can execute them by running `make test`.

Patch:
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 140b60f..df7c9d9 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -7,6 +7,8 @@ import (
 	"io/ioutil"
 	"strings"
 
+	"github.com/editorconfig/editorconfig-core-go/v2"
+
 	"github.com/editorconfig-checker/editorconfig-checker/pkg/logger"
 	"github.com/editorconfig-checker/editorconfig-checker/pkg/utils"
 )
@@ -90,7 +92,8 @@ type Config struct {
 	Disable             DisabledChecks
 
 	// MISC
-	Logger logger.Logger
+	Logger             logger.Logger
+	EditorconfigConfig *editorconfig.Config
 }
 
 // DisabledChecks is a Struct which represents disabled checks
@@ -112,6 +115,10 @@ func NewConfig(configPath string) (*Config, error) {
 	config.Exclude = []string{}
 	config.PassedFiles = []string{}
 
+	config.EditorconfigConfig = &editorconfig.Config{
+		Parser: editorconfig.NewCachedParser(),
+	}
+
 	if !utils.IsRegularFile(configPath) {
 		return &config, fmt.Errorf("No file found at %s", configPath)
 	}
@@ -197,7 +204,8 @@ func (c *Config) Merge(config Config) {
 	c.Logger = logger.Logger{
 		Verbosee: c.Verbose || config.Verbose,
 		Debugg:   c.Debug || config.Debug,
-		NoColor:  c.NoColor || config.NoColor}
+		NoColor:  c.NoColor || config.NoColor,
+	}
 }
 
 // mergeDisabled merges the disabled checks into the config
diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go
index 420ab8a..0d65c9f 100644
--- a/pkg/validation/validation.go
+++ b/pkg/validation/validation.go
@@ -8,13 +8,12 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/editorconfig/editorconfig-core-go/v2"
-
 	"github.com/editorconfig-checker/editorconfig-checker/pkg/config"
 	"github.com/editorconfig-checker/editorconfig-checker/pkg/encoding"
 	"github.com/editorconfig-checker/editorconfig-checker/pkg/error"
 	"github.com/editorconfig-checker/editorconfig-checker/pkg/files"
 	"github.com/editorconfig-checker/editorconfig-checker/pkg/validation/validators"
+	"github.com/editorconfig/editorconfig-core-go/v2"
 )
 
 // keep synced with /pkg/config/config.go#L59
@@ -70,7 +69,12 @@ func ValidateFile(filePath string, config config.Config) []error.ValidationError
 		return validationErrors
 	}
 
-	editorconfig, warnings, err := editorconfig.GetDefinitionForFilenameGraceful(filePath)
+	// idiomatic Go allows empty struct
+	if config.EditorconfigConfig == nil {
+		config.EditorconfigConfig = &editorconfig.Config{}
+	}
+
+	definition, warnings, err := config.EditorconfigConfig.LoadGraceful(filePath)
 	if err != nil {
 		config.Logger.Error("cannot load %s as .editorconfig: %s", filePath, err)
 		return validationErrors
@@ -79,13 +83,13 @@ func ValidateFile(filePath string, config config.Config) []error.ValidationError
 		config.Logger.Warning(warnings.Error())
 	}
 
-	fileInformation := files.FileInformation{Content: fileContent, FilePath: filePath, Editorconfig: editorconfig}
+	fileInformation := files.FileInformation{Content: fileContent, FilePath: filePath, Editorconfig: definition}
 	validationError := ValidateFinalNewline(fileInformation, config)
 	if validationError.Message != nil {
 		validationErrors = append(validationErrors, validationError)
 	}
 
-	fileInformation = files.FileInformation{Content: fileContent, FilePath: filePath, Editorconfig: editorconfig}
+	fileInformation = files.FileInformation{Content: fileContent, FilePath: filePath, Editorconfig: definition}
 	validationError = ValidateLineEnding(fileInformation, config)
 	if validationError.Message != nil {
 		validationErrors = append(validationErrors, validationError)
@@ -107,7 +111,7 @@ func ValidateFile(filePath string, config config.Config) []error.ValidationError
 			continue
 		}
 
-		fileInformation = files.FileInformation{Line: line, FilePath: filePath, LineNumber: lineNumber, Editorconfig: editorconfig}
+		fileInformation = files.FileInformation{Line: line, FilePath: filePath, LineNumber: lineNumber, Editorconfig: definition}
 		validationError = ValidateTrailingWhitespace(fileInformation, config)
 		if validationError.Message != nil {
 			validationErrors = append(validationErrors, validationError)


Test patch:
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index a9cb5d5..d8564e1 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -5,11 +5,14 @@ import (
 	"os"
 	"path/filepath"
 	"reflect"
+	"strings"
 	"testing"
 )
 
-const rootConfigFilePath = "../../.ecrc"
-const configWithIgnoredDefaults = "../../testfiles/.ecrc"
+const (
+	rootConfigFilePath        = "../../.ecrc"
+	configWithIgnoredDefaults = "../../testfiles/.ecrc"
+)
 
 func TestNewConfig(t *testing.T) {
 	actual, _ := NewConfig("abc")
@@ -111,6 +114,7 @@ func TestMerge(t *testing.T) {
 	expected.Logger.Verbosee = true
 	expected.Logger.Debugg = true
 	expected.Logger.NoColor = true
+	expected.EditorconfigConfig = c1.EditorconfigConfig
 
 	if !reflect.DeepEqual(c1, &expected) {
 		t.Errorf("%#v", &expected)
@@ -171,9 +175,9 @@ func TestGetAsString(t *testing.T) {
 	_ = c.Parse()
 
 	actual := c.GetAsString()
-	expected := "Config: {ShowVersion:false Help:false DryRun:false Path:../../.ecrc Version:2.7.2 Verbose:false Debug:false IgnoreDefaults:false SpacesAftertabs:false NoColor:false Exclude:[testfiles testdata] AllowedContentTypes:[text/ application/octet-stream application/ecmascript application/json application/x-ndjson application/xml +json +xml] PassedFiles:[] Disable:{EndOfLine:false Indentation:false InsertFinalNewline:false TrimTrailingWhitespace:false IndentSize:false MaxLineLength:false} Logger:{Verbosee:false Debugg:false NoColor:false}}"
+	expected := "Config: {ShowVersion:false Help:false DryRun:false Path:../../.ecrc Version:2.7.2 Verbose:false Debug:false IgnoreDefaults:false SpacesAftertabs:false NoColor:false Exclude:[testfiles testdata] AllowedContentTypes:[text/ application/octet-stream application/ecmascript application/json application/x-ndjson application/xml +json +xml] PassedFiles:[] Disable:{EndOfLine:false Indentation:false InsertFinalNewline:false TrimTrailingWhitespace:false IndentSize:false MaxLineLength:false} Logger:{Verbosee:false Debugg:false NoColor:false} EditorconfigConfig:0x"
 
-	if actual != expected {
+	if !strings.HasPrefix(actual, expected) {
 		t.Errorf("Expected: %v, got: %v ", expected, actual)
 	}
 }
diff --git a/pkg/validation/validation_test.go b/pkg/validation/validation_test.go
index 4f7cbf4..4efc6fd 100644
--- a/pkg/validation/validation_test.go
+++ b/pkg/validation/validation_test.go
@@ -7,7 +7,9 @@ import (
 )
 
 func TestProcessValidation(t *testing.T) {
-	configuration := config.Config{Verbose: true}
+	configuration := config.Config{
+		Verbose: true,
+	}
 
 	processValidationResult := ProcessValidation([]string{"./../../cmd/editorconfig-checker/main.go"}, configuration)
 	if len(processValidationResult) > 1 || len(processValidationResult[0].Errors) != 0 {