diff --git a/docs/config-file.md b/docs/config-file.md index 26b12aa..54b6d19 100644 --- a/docs/config-file.md +++ b/docs/config-file.md @@ -82,6 +82,7 @@ The basic formatter is a barebones formatter that simply takes the data provided | `indentless_arrays` | bool | false | Render `-` array items (block sequence items) without an increased indent. | | `drop_merge_tag` | bool | false | Assume that any well formed merge using just a `<<` token will be a merge, and drop the `!!merge` tag from the formatted result. | | `pad_line_comments` | int | 1 | The number of padding spaces to insert before line comments. | +| `trim_trailing_whitespace` | bool | false | Whether to trim trailing whitespace from lines. | ### Note on `max_line_length` diff --git a/formatters/basic/config.go b/formatters/basic/config.go index 6aea472..a9e4959 100644 --- a/formatters/basic/config.go +++ b/formatters/basic/config.go @@ -32,6 +32,7 @@ type Config struct { IndentlessArrays bool `mapstructure:"indentless_arrays"` DropMergeTag bool `mapstructure:"drop_merge_tag"` PadLineComments int `mapstructure:"pad_line_comments"` + TrimTrailingWhitespace bool `mapstructure:"trim_trailing_whitespace"` } func DefaultConfig() *Config { diff --git a/formatters/basic/features.go b/formatters/basic/features.go index 976c2d6..0f0f382 100644 --- a/formatters/basic/features.go +++ b/formatters/basic/features.go @@ -19,17 +19,26 @@ import ( "github.com/google/yamlfmt" "github.com/google/yamlfmt/formatters/basic/anchors" "github.com/google/yamlfmt/internal/hotfix" + "github.com/google/yamlfmt/internal/trim" ) func ConfigureFeaturesFromConfig(config *Config) yamlfmt.FeatureList { + lineSep, err := config.LineEnding.Separator() + if err != nil { + lineSep = "\n" + } features := []yamlfmt.Feature{} if config.RetainLineBreaks || config.RetainLineBreaksSingle { - lineSep, err := config.LineEnding.Separator() - if err != nil { - lineSep = "\n" - } - featLineBreak := hotfix.MakeFeatureRetainLineBreak(lineSep, config.RetainLineBreaksSingle) - features = append(features, featLineBreak) + features = append( + features, + hotfix.MakeFeatureRetainLineBreak(lineSep, config.RetainLineBreaksSingle), + ) + } + if config.TrimTrailingWhitespace { + features = append( + features, + trim.MakeFeatureTrimTrailingWhitespace(lineSep), + ) } return features } diff --git a/formatters/basic/formatter_test.go b/formatters/basic/formatter_test.go index 39d666e..7ae9ba1 100644 --- a/formatters/basic/formatter_test.go +++ b/formatters/basic/formatter_test.go @@ -309,3 +309,23 @@ func TestPadLineComments(t *testing.T) { t.Fatalf("expected: '%s', got: '%s'", expectedStr, resultStr) } } + +func TestTrimTrailingWhitespace(t *testing.T) { + config := basic.DefaultConfig() + config.TrimTrailingWhitespace = true + f := newFormatter(config) + + yml := `a: 1 +b: 2 ` + expectedYml := `a: 1 +b: 2` + + result, err := f.Format([]byte(yml)) + if err != nil { + t.Fatalf("expected formatting to pass, returned error: %v", err) + } + resultStr := strings.TrimSuffix(string(result), "\n") + if resultStr != expectedYml { + t.Fatalf("expected: '%s', got: '%s'", expectedYml, resultStr) + } +} diff --git a/integrationtest/command/command_test.go b/integrationtest/command/command_test.go index 836c4d1..2b899e4 100644 --- a/integrationtest/command/command_test.go +++ b/integrationtest/command/command_test.go @@ -123,3 +123,11 @@ func TestPrintConfFlagsAndFile(t *testing.T) { Update: *updateFlag, }.Run(t) } + +func TestMultilineStringBug(t *testing.T) { + TestCase{ + Dir: "multiline_string_bug", + Command: yamlfmtWithArgs("-formatter trim_trailing_whitespace=true ."), + Update: *updateFlag, + }.Run(t) +} diff --git a/integrationtest/command/testdata/multiline_string_bug/after/a.yaml b/integrationtest/command/testdata/multiline_string_bug/after/a.yaml new file mode 100755 index 0000000..e719648 --- /dev/null +++ b/integrationtest/command/testdata/multiline_string_bug/after/a.yaml @@ -0,0 +1,4 @@ +# There is a trailing whitespace within the string +a: |- + hello + hi diff --git a/integrationtest/command/testdata/multiline_string_bug/before/a.yaml b/integrationtest/command/testdata/multiline_string_bug/before/a.yaml new file mode 100644 index 0000000..e719648 --- /dev/null +++ b/integrationtest/command/testdata/multiline_string_bug/before/a.yaml @@ -0,0 +1,4 @@ +# There is a trailing whitespace within the string +a: |- + hello + hi diff --git a/integrationtest/command/testdata/multiline_string_bug/stdout/stderr.txt b/integrationtest/command/testdata/multiline_string_bug/stdout/stderr.txt new file mode 100755 index 0000000..e69de29 diff --git a/integrationtest/command/testdata/multiline_string_bug/stdout/stdout.txt b/integrationtest/command/testdata/multiline_string_bug/stdout/stdout.txt new file mode 100755 index 0000000..e69de29 diff --git a/integrationtest/command/testdata/print_conf_file/after/.yamlfmt b/integrationtest/command/testdata/print_conf_file/after/.yamlfmt old mode 100644 new mode 100755 diff --git a/integrationtest/command/testdata/print_conf_file/stdout/stdout.txt b/integrationtest/command/testdata/print_conf_file/stdout/stdout.txt index 10e2412..d8598e4 100644 --- a/integrationtest/command/testdata/print_conf_file/stdout/stdout.txt +++ b/integrationtest/command/testdata/print_conf_file/stdout/stdout.txt @@ -23,4 +23,5 @@ formatter: retain_line_breaks: false retain_line_breaks_single: true scan_folded_as_literal: false + trim_trailing_whitespace: false type: basic diff --git a/integrationtest/command/testdata/print_conf_flags/after/.gitkeep b/integrationtest/command/testdata/print_conf_flags/after/.gitkeep old mode 100644 new mode 100755 diff --git a/integrationtest/command/testdata/print_conf_flags/stdout/stdout.txt b/integrationtest/command/testdata/print_conf_flags/stdout/stdout.txt index 19fdf6c..fff8566 100644 --- a/integrationtest/command/testdata/print_conf_flags/stdout/stdout.txt +++ b/integrationtest/command/testdata/print_conf_flags/stdout/stdout.txt @@ -22,4 +22,5 @@ formatter: retain_line_breaks: true retain_line_breaks_single: false scan_folded_as_literal: false + trim_trailing_whitespace: false type: basic diff --git a/integrationtest/command/testdata/print_conf_flags_and_file/after/.yamlfmt b/integrationtest/command/testdata/print_conf_flags_and_file/after/.yamlfmt old mode 100644 new mode 100755 diff --git a/integrationtest/command/testdata/print_conf_flags_and_file/stdout/stdout.txt b/integrationtest/command/testdata/print_conf_flags_and_file/stdout/stdout.txt index 777d2ac..ce14d09 100644 --- a/integrationtest/command/testdata/print_conf_flags_and_file/stdout/stdout.txt +++ b/integrationtest/command/testdata/print_conf_flags_and_file/stdout/stdout.txt @@ -23,4 +23,5 @@ formatter: retain_line_breaks: true retain_line_breaks_single: true scan_folded_as_literal: false + trim_trailing_whitespace: false type: basic diff --git a/internal/trim/trim_whitespace.go b/internal/trim/trim_whitespace.go new file mode 100644 index 0000000..1bcbcf9 --- /dev/null +++ b/internal/trim/trim_whitespace.go @@ -0,0 +1,42 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package trim + +import ( + "bufio" + "bytes" + "strings" + + "github.com/google/yamlfmt" +) + +func MakeFeatureTrimTrailingWhitespace(linebreakStr string) yamlfmt.Feature { + return yamlfmt.Feature{ + Name: "Trim Trailing Whitespace", + BeforeAction: trimTrailingWhitespaceFeature(linebreakStr), + } +} + +func trimTrailingWhitespaceFeature(linebreakStr string) yamlfmt.FeatureFunc { + return func(content []byte) ([]byte, error) { + buf := bytes.NewBuffer(content) + s := bufio.NewScanner(buf) + newLines := []string{} + for s.Scan() { + newLines = append(newLines, strings.TrimRight(s.Text(), " ")) + } + return []byte(strings.Join(newLines, linebreakStr)), nil + } +}