diff --git a/editorconfig/full_file_checkers.go b/editorconfig/full_file_checkers.go new file mode 100644 index 0000000..e59b6d4 --- /dev/null +++ b/editorconfig/full_file_checkers.go @@ -0,0 +1,61 @@ +package editorconfig + +import ( + "regexp" + "strings" +) + +var fullFileCheckers = map[string]FullFileChecker{ + "end_of_line": CheckEndOfLineRule, +} + +type FullFileChecker func(ruleValue string, fileContent string) *FullFileCheckResult + +// @todo - add fixers to each instance of FullFileCheckResult. +type FullFileCheckResult struct { + isOk bool + messageIfNotOk string +} + +var lfRegexp = regexp.MustCompile(`\n`) +var crRegexp = regexp.MustCompile(`\r`) +var crlfRegexp = regexp.MustCompile(`\r\n`) + +func CheckEndOfLineRule(ruleValue string, fileContent string) *FullFileCheckResult { + // Valid rules values are "lf", "cr", or "crlf". The values are case insensitive. + ruleValueLowercase := strings.ToLower(ruleValue) + + if ruleValueLowercase != "lf" && ruleValueLowercase != "cr" && ruleValueLowercase != "crlf" { + return &FullFileCheckResult{isOk: false, messageIfNotOk: "end_of_line value is not valid: " + ruleValue} + } + + if ruleValueLowercase == "lf" { + if crlfRegexp.MatchString(fileContent) { + return &FullFileCheckResult{isOk: false, messageIfNotOk: "should use LF for new lines but contains CRLF"} + } + if crRegexp.MatchString(fileContent) { + return &FullFileCheckResult{isOk: false, messageIfNotOk: "should use LF for new lines but contains CR"} + } + } + + if ruleValueLowercase == "cr" { + if crlfRegexp.MatchString(fileContent) { + return &FullFileCheckResult{isOk: false, messageIfNotOk: "should use CR for new lines but contains CRLF"} + } + if lfRegexp.MatchString(fileContent) { + return &FullFileCheckResult{isOk: false, messageIfNotOk: "should use CR for new lines but contains LF"} + } + } + + if ruleValueLowercase == "crlf" { + fileContent := crlfRegexp.ReplaceAllString(fileContent, "") + if lfRegexp.MatchString(fileContent) { + return &FullFileCheckResult{isOk: false, messageIfNotOk: "should use CRLF for new lines but contains LF"} + } + if crRegexp.MatchString(fileContent) { + return &FullFileCheckResult{isOk: false, messageIfNotOk: "should use CRLF for new lines but contains CR"} + } + } + + return &FullFileCheckResult{isOk: true} +} diff --git a/editorconfig/full_file_checkers_test.go b/editorconfig/full_file_checkers_test.go new file mode 100644 index 0000000..5cf4528 --- /dev/null +++ b/editorconfig/full_file_checkers_test.go @@ -0,0 +1,41 @@ +package editorconfig + +import ( + "testing" +) + +func ExpectFilePass(file string, ruleValue string, fileChecker FullFileChecker, t *testing.T) { + result := fileChecker(ruleValue, file) + if !result.isOk { + t.Error("Expected file to pass, but it failed: \"" + file + "\" for rule value \"" + ruleValue + "\", had error message: " + result.messageIfNotOk) + } +} + +func ExpectFileFail(file string, ruleValue string, fileChecker FullFileChecker, t *testing.T, expectedError string) { + result := fileChecker(ruleValue, file) + if result.isOk { + t.Error("Expected file to fail, but it passed: \"" + file + "\" for rule value \"" + ruleValue + "\"") + return + } + + if !result.isOk && result.messageIfNotOk != expectedError { + t.Error("File \"" + file + "\" failed with error message \"" + result.messageIfNotOk + "\" but had expected \"" + expectedError + "\"") + return + } +} + +func TestCheckEndOfLineRule(t *testing.T) { + f := CheckEndOfLineRule + + ExpectFilePass("", "lf", f, t) + ExpectFilePass("", "cr", f, t) + ExpectFilePass("", "crlf", f, t) + + ExpectFilePass("Aardvark\nBunny\n", "lf", f, t) + ExpectFilePass("Aardvark\rBunny\r", "cr", f, t) + ExpectFilePass("Aardvark\r\nBunny\r\n", "crlf", f, t) + + ExpectFileFail("Aardvark\nBunny\n", "cr", f, t, "should use CR for new lines but contains LF") + ExpectFileFail("Aardvark\rBunny\r", "crlf", f, t, "should use CRLF for new lines but contains CR") + ExpectFileFail("Aardvark\r\nBunny\r\n", "lf", f, t, "should use LF for new lines but contains CRLF") +}