Skip to content

Commit ea03c83

Browse files
authored
formatters/basic: add force_array_style (#248)
* formatters/basic: add force_array_style This PR adds the `force_array_style` feature to the basic formatter, which allows the user to force either `block` or `flow` sequence style in the formatted document. * add the license I forgot * remove TODO comment I left in * oops overwrote the test expectation * readded removed newline
1 parent bb1d122 commit ea03c83

File tree

21 files changed

+208
-44
lines changed

21 files changed

+208
-44
lines changed

docs/config-file.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ The basic formatter is a barebones formatter that simply takes the data provided
8989
| `array_indent` | int | = indent | Set a different indentation level for block sequences specifically. |
9090
| `indent_root_array` | int | false | Tells the formatter to indent an array that is at the lowest indentation level of the document. |
9191
| `disable_alias_key_correction` | bool | false | Disables functionality to fix alias nodes being used as keys. See #247 for details. |
92+
| `force_array_style` | `flow`, `block`, or empty | empty | If set, forces arrays to be output in a particular style, either `flow` (`[]`) or `block` (`- x`). If unset, the style from the original document is used. |
9293

9394
## Additional Notes
9495

formatters/basic/config.go

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,28 @@ import (
1818
"runtime"
1919

2020
"github.com/google/yamlfmt"
21+
yamlFeatures "github.com/google/yamlfmt/formatters/basic/features"
2122
)
2223

2324
type Config struct {
24-
Indent int `mapstructure:"indent"`
25-
IncludeDocumentStart bool `mapstructure:"include_document_start"`
26-
LineEnding yamlfmt.LineBreakStyle `mapstructure:"line_ending"`
27-
LineLength int `mapstructure:"max_line_length"`
28-
RetainLineBreaks bool `mapstructure:"retain_line_breaks"`
29-
RetainLineBreaksSingle bool `mapstructure:"retain_line_breaks_single"`
30-
DisallowAnchors bool `mapstructure:"disallow_anchors"`
31-
ScanFoldedAsLiteral bool `mapstructure:"scan_folded_as_literal"`
32-
IndentlessArrays bool `mapstructure:"indentless_arrays"`
33-
DropMergeTag bool `mapstructure:"drop_merge_tag"`
34-
PadLineComments int `mapstructure:"pad_line_comments"`
35-
TrimTrailingWhitespace bool `mapstructure:"trim_trailing_whitespace"`
36-
EOFNewline bool `mapstructure:"eof_newline"`
37-
StripDirectives bool `mapstructure:"strip_directives"`
38-
ArrayIndent int `mapstructure:"array_indent"`
39-
IndentRootArray bool `mapstructure:"indent_root_array"`
40-
DisableAliasKeyCorrection bool `mapstructure:"disable_alias_key_correction"`
25+
Indent int `mapstructure:"indent"`
26+
IncludeDocumentStart bool `mapstructure:"include_document_start"`
27+
LineEnding yamlfmt.LineBreakStyle `mapstructure:"line_ending"`
28+
LineLength int `mapstructure:"max_line_length"`
29+
RetainLineBreaks bool `mapstructure:"retain_line_breaks"`
30+
RetainLineBreaksSingle bool `mapstructure:"retain_line_breaks_single"`
31+
DisallowAnchors bool `mapstructure:"disallow_anchors"`
32+
ScanFoldedAsLiteral bool `mapstructure:"scan_folded_as_literal"`
33+
IndentlessArrays bool `mapstructure:"indentless_arrays"`
34+
DropMergeTag bool `mapstructure:"drop_merge_tag"`
35+
PadLineComments int `mapstructure:"pad_line_comments"`
36+
TrimTrailingWhitespace bool `mapstructure:"trim_trailing_whitespace"`
37+
EOFNewline bool `mapstructure:"eof_newline"`
38+
StripDirectives bool `mapstructure:"strip_directives"`
39+
ArrayIndent int `mapstructure:"array_indent"`
40+
IndentRootArray bool `mapstructure:"indent_root_array"`
41+
DisableAliasKeyCorrection bool `mapstructure:"disable_alias_key_correction"`
42+
ForceArrayStyle yamlFeatures.SequenceStyle `mapstructure:"force_array_style"`
4143
}
4244

4345
func DefaultConfig() *Config {

formatters/basic/features.go

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@
1515
package basic
1616

1717
import (
18-
"github.com/google/yamlfmt/pkg/yaml"
1918
"github.com/google/yamlfmt"
20-
"github.com/google/yamlfmt/formatters/basic/anchors"
19+
yamlFeatures "github.com/google/yamlfmt/formatters/basic/features"
2120
"github.com/google/yamlfmt/internal/features"
2221
"github.com/google/yamlfmt/internal/hotfix"
2322
)
@@ -55,24 +54,19 @@ func ConfigureFeaturesFromConfig(config *Config) yamlfmt.FeatureList {
5554
return configuredFeatures
5655
}
5756

58-
// These features will directly use the `yaml.Node` type and
59-
// as such are specific to this formatter.
60-
type YAMLFeatureFunc func(yaml.Node) error
61-
type YAMLFeatureList []YAMLFeatureFunc
57+
func ConfigureYAMLFeaturesFromConfig(config *Config) yamlFeatures.YAMLFeatureList {
58+
var featureList yamlFeatures.YAMLFeatureList
6259

63-
func (y YAMLFeatureList) ApplyFeatures(node yaml.Node) error {
64-
for _, f := range y {
65-
if err := f(node); err != nil {
66-
return err
67-
}
60+
if config.DisallowAnchors {
61+
featureList = append(featureList, yamlFeatures.Check)
6862
}
69-
return nil
70-
}
7163

72-
func ConfigureYAMLFeaturesFromConfig(config *Config) YAMLFeatureList {
73-
var features YAMLFeatureList
74-
if config.DisallowAnchors {
75-
features = append(features, anchors.Check)
64+
if config.ForceArrayStyle != "" {
65+
featureList = append(
66+
featureList,
67+
yamlFeatures.FeatureForceSequenceStyle(config.ForceArrayStyle),
68+
)
7669
}
77-
return features
70+
71+
return featureList
7872
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package anchors
15+
package features
1616

1717
import (
1818
"errors"
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package anchors_test
15+
package features_test
1616

1717
import (
1818
"strings"
1919
"testing"
2020

21+
"github.com/google/yamlfmt/formatters/basic/features"
2122
"github.com/google/yamlfmt/pkg/yaml"
22-
"github.com/google/yamlfmt/formatters/basic/anchors"
2323
)
2424

2525
func TestCheck(t *testing.T) {
@@ -58,7 +58,7 @@ pipelines:
5858
if err := yaml.NewDecoder(strings.NewReader(c.in)).Decode(&docNode); err != nil {
5959
t.Fatalf("parse error: %v", err)
6060
}
61-
if err := anchors.Check(docNode); (err != nil) != c.wantErr {
61+
if err := features.Check(docNode); (err != nil) != c.wantErr {
6262
t.Errorf("Check() error = %v, wantErr %v", err, c.wantErr)
6363
}
6464
})
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package features
16+
17+
import "github.com/google/yamlfmt/pkg/yaml"
18+
19+
type SequenceStyle string
20+
21+
const (
22+
SequenceStyleBlock SequenceStyle = "block"
23+
SequenceStyleFlow SequenceStyle = "flow"
24+
)
25+
26+
func FeatureForceSequenceStyle(style SequenceStyle) YAMLFeatureFunc {
27+
var styleVal yaml.Style
28+
if style == SequenceStyleFlow {
29+
styleVal = yaml.FlowStyle
30+
}
31+
var forceStyle YAMLFeatureFunc
32+
forceStyle = func(n yaml.Node) error {
33+
var err error
34+
for _, c := range n.Content {
35+
if c.Kind == yaml.SequenceNode {
36+
c.Style = styleVal
37+
}
38+
err = forceStyle(*c)
39+
}
40+
return err
41+
}
42+
return forceStyle
43+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package features
16+
17+
import "github.com/google/yamlfmt/pkg/yaml"
18+
19+
// These features will directly use the `yaml.Node` type and
20+
// as such are specific to this formatter.
21+
type YAMLFeatureFunc func(yaml.Node) error
22+
type YAMLFeatureList []YAMLFeatureFunc
23+
24+
func (y YAMLFeatureList) ApplyFeatures(node yaml.Node) error {
25+
for _, f := range y {
26+
if err := f(node); err != nil {
27+
return err
28+
}
29+
}
30+
return nil
31+
}

formatters/basic/formatter.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"io"
2222

2323
"github.com/google/yamlfmt"
24+
yamlFeature "github.com/google/yamlfmt/formatters/basic/features"
2425
"github.com/google/yamlfmt/pkg/yaml"
2526
"github.com/mitchellh/mapstructure"
2627
)
@@ -30,7 +31,7 @@ const BasicFormatterType string = "basic"
3031
type BasicFormatter struct {
3132
Config *Config
3233
Features yamlfmt.FeatureList
33-
YAMLFeatures YAMLFeatureList
34+
YAMLFeatures yamlFeature.YAMLFeatureList
3435
}
3536

3637
// yamlfmt.Formatter interface

formatters/basic/formatter_test.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ import (
2020

2121
"github.com/google/yamlfmt"
2222
"github.com/google/yamlfmt/formatters/basic"
23+
"github.com/google/yamlfmt/formatters/basic/features"
2324
"github.com/google/yamlfmt/internal/assert"
2425
)
2526

2627
func newFormatter(config *basic.Config) *basic.BasicFormatter {
2728
return &basic.BasicFormatter{
28-
Config: config,
29-
Features: basic.ConfigureFeaturesFromConfig(config),
29+
Config: config,
30+
Features: basic.ConfigureFeaturesFromConfig(config),
31+
YAMLFeatures: basic.ConfigureYAMLFeaturesFromConfig(config),
3032
}
3133
}
3234

@@ -401,3 +403,55 @@ func TestIndentRootArray(t *testing.T) {
401403
t.Fatalf("expected: '%s', got: '%s'", expectedYml, result)
402404
}
403405
}
406+
407+
func TestForceFlowSequence(t *testing.T) {
408+
config := basic.DefaultConfig()
409+
config.ForceArrayStyle = features.SequenceStyleFlow
410+
f := newFormatter(config)
411+
412+
yml := `a:
413+
- 1
414+
- 2
415+
- 3
416+
b: [1, 2, 3]
417+
`
418+
expectedYml := `a: [1, 2, 3]
419+
b: [1, 2, 3]
420+
`
421+
422+
result, err := f.Format([]byte(yml))
423+
assert.NilErr(t, err)
424+
resultStr := string(result)
425+
if resultStr != expectedYml {
426+
t.Fatalf("expected: '%s', got: '%s'", expectedYml, result)
427+
}
428+
}
429+
430+
func TestForceBlockSequence(t *testing.T) {
431+
config := basic.DefaultConfig()
432+
config.ForceArrayStyle = features.SequenceStyleBlock
433+
f := newFormatter(config)
434+
435+
yml := `a:
436+
- 1
437+
- 2
438+
- 3
439+
b: [1, 2, 3]
440+
`
441+
expectedYml := `a:
442+
- 1
443+
- 2
444+
- 3
445+
b:
446+
- 1
447+
- 2
448+
- 3
449+
`
450+
451+
result, err := f.Format([]byte(yml))
452+
assert.NilErr(t, err)
453+
resultStr := string(result)
454+
if resultStr != expectedYml {
455+
t.Fatalf("expected: '%s', got: '%s'", expectedYml, result)
456+
}
457+
}

integrationtest/command/command_test.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,24 @@ func TestPatternFile(t *testing.T) {
166166

167167
func TestAliasKeyCorrection(t *testing.T) {
168168
TestCase{
169-
Dir: "alias_key_correction",
170-
// TODO: Change arguments to match your test case.
169+
Dir: "alias_key_correction",
171170
Command: yamlfmtWithArgs("."),
172171
Update: *updateFlag,
173172
}.Run(t)
174173
}
174+
175+
func TestForceBlockStyle(t *testing.T) {
176+
TestCase{
177+
Dir: "force_block_style",
178+
Command: yamlfmtWithArgs("-formatter force_array_style=block ."),
179+
Update: *updateFlag,
180+
}.Run(t)
181+
}
182+
183+
func TestForceFlowStyle(t *testing.T) {
184+
TestCase{
185+
Dir: "force_flow_style",
186+
Command: yamlfmtWithArgs("-formatter force_array_style=flow ."),
187+
Update: *updateFlag,
188+
}.Run(t)
189+
}

0 commit comments

Comments
 (0)