diff --git a/editorconfig/full_file_checkers.go b/editorconfig/full_file_checkers.go index e59b6d4..bcf14e6 100644 --- a/editorconfig/full_file_checkers.go +++ b/editorconfig/full_file_checkers.go @@ -6,7 +6,10 @@ import ( ) var fullFileCheckers = map[string]FullFileChecker{ - "end_of_line": CheckEndOfLineRule, + "end_of_line": CheckEndOfLineRule, + "insert_final_newline": CheckInsertFinalNewLineRule, + // @todo - add checker for charset. + // "charset": CheckCharsetRule, } type FullFileChecker func(ruleValue string, fileContent string) *FullFileCheckResult @@ -59,3 +62,36 @@ func CheckEndOfLineRule(ruleValue string, fileContent string) *FullFileCheckResu return &FullFileCheckResult{isOk: true} } + +var endsWithFinalNewLineRegexp = regexp.MustCompile(`(\n|\r|\r\n)$`) + +func CheckInsertFinalNewLineRule(ruleValue string, fileContent string) *FullFileCheckResult { + // Valid rules values are "true" or "false". The values are case insensitive. + ruleValueLowercase := strings.ToLower(ruleValue) + + if ruleValueLowercase != "true" && ruleValueLowercase != "false" { + return &FullFileCheckResult{isOk: false, messageIfNotOk: "insert_final_new_line value should be true or false, is: " + ruleValue} + } + + if len(fileContent) == 0 { + return &FullFileCheckResult{isOk: true} + } + + if ruleValueLowercase == "true" { + if endsWithFinalNewLineRegexp.MatchString(fileContent) { + return &FullFileCheckResult{isOk: true} + } else { + return &FullFileCheckResult{isOk: false, messageIfNotOk: "should end with an empty line but it does not"} + } + } + + if ruleValueLowercase == "false" { + if !endsWithFinalNewLineRegexp.MatchString(fileContent) { + return &FullFileCheckResult{isOk: true} + } else { + return &FullFileCheckResult{isOk: false, messageIfNotOk: "should not end with an empty line but it does"} + } + } + + return &FullFileCheckResult{isOk: false, messageIfNotOk: "unexpected condition"} +} diff --git a/editorconfig/full_file_checkers_test.go b/editorconfig/full_file_checkers_test.go index 5cf4528..0ea58ff 100644 --- a/editorconfig/full_file_checkers_test.go +++ b/editorconfig/full_file_checkers_test.go @@ -39,3 +39,31 @@ func TestCheckEndOfLineRule(t *testing.T) { 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") } + +func TestCheckInsertFinalNewLineRule(t *testing.T) { + f := CheckInsertFinalNewLineRule + + // Empty files should never error in this rule. + ExpectFilePass("", "true", f, t) + ExpectFilePass("", "false", f, t) + + ExpectFilePass("\n", "true", f, t) + ExpectFilePass("\r", "true", f, t) + ExpectFilePass("a\n", "true", f, t) + ExpectFilePass("a\r", "true", f, t) + ExpectFilePass("a\r\n", "true", f, t) + ExpectFilePass("a\r\n", "true", f, t) + ExpectFilePass("a\nb\n", "true", f, t) + ExpectFileFail("a", "true", f, t, "should end with an empty line but it does not") + ExpectFileFail("a\nb", "true", f, t, "should end with an empty line but it does not") + ExpectFileFail("a\rb", "true", f, t, "should end with an empty line but it does not") + + ExpectFilePass("a", "false", f, t) + ExpectFilePass("a\nb", "false", f, t) + ExpectFileFail("\n", "false", f, t, "should not end with an empty line but it does") + ExpectFileFail("\r", "false", f, t, "should not end with an empty line but it does") + ExpectFileFail("\r\n", "false", f, t, "should not end with an empty line but it does") + ExpectFileFail("a\r\n", "false", f, t, "should not end with an empty line but it does") + + ExpectFileFail("anything", "rabbits", f, t, "insert_final_new_line value should be true or false, is: rabbits") +}