Skip to content

Commit c8cc5c5

Browse files
authored
Fix indentation for raw string newlines (#625)
#292
1 parent 6f80c57 commit c8cc5c5

File tree

4 files changed

+83
-56
lines changed

4 files changed

+83
-56
lines changed

ast/ast.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -818,11 +818,12 @@ func (n *StringNode) String() string {
818818
// It works mostly, but inconsistencies occur if line break characters are mixed.
819819
header := token.LiteralBlockHeader(n.Value)
820820
space := strings.Repeat(" ", n.Token.Position.Column-1)
821+
indent := strings.Repeat(" ", n.Token.Position.IndentNum)
821822
values := []string{}
822823
for _, v := range strings.Split(n.Value, lbc) {
823-
values = append(values, fmt.Sprintf("%s %s", space, v))
824+
values = append(values, fmt.Sprintf("%s%s%s", space, indent, v))
824825
}
825-
block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space))
826+
block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s%s%s", lbc, indent, space)), fmt.Sprintf("%s%s", indent, space))
826827
return fmt.Sprintf("%s%s%s", header, lbc, block)
827828
} else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') {
828829
return fmt.Sprintf(`'%s'`, n.Value)
@@ -849,11 +850,12 @@ func (n *StringNode) stringWithoutComment() string {
849850
// It works mostly, but inconsistencies occur if line break characters are mixed.
850851
header := token.LiteralBlockHeader(n.Value)
851852
space := strings.Repeat(" ", n.Token.Position.Column-1)
853+
indent := strings.Repeat(" ", n.Token.Position.IndentNum)
852854
values := []string{}
853855
for _, v := range strings.Split(n.Value, lbc) {
854-
values = append(values, fmt.Sprintf("%s %s", space, v))
856+
values = append(values, fmt.Sprintf("%s%s%s", space, indent, v))
855857
}
856-
block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space))
858+
block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s%s%s", lbc, indent, space)), fmt.Sprintf(" %s", space))
857859
return fmt.Sprintf("%s%s%s", header, lbc, block)
858860
} else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') {
859861
return fmt.Sprintf(`'%s'`, n.Value)

encode.go

+25-22
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ const (
2828
type Encoder struct {
2929
writer io.Writer
3030
opts []EncodeOption
31-
indent int
32-
indentSequence bool
3331
singleQuote bool
3432
isFlowStyle bool
3533
isJSONStyle bool
@@ -41,11 +39,12 @@ type Encoder struct {
4139
commentMap map[*Path][]*Comment
4240
written bool
4341

44-
line int
45-
column int
46-
offset int
47-
indentNum int
48-
indentLevel int
42+
line int
43+
column int
44+
offset int
45+
indentNum int
46+
indentLevel int
47+
indentSequence bool
4948
}
5049

5150
// NewEncoder returns a new encoder that writes to w.
@@ -54,12 +53,12 @@ func NewEncoder(w io.Writer, opts ...EncodeOption) *Encoder {
5453
return &Encoder{
5554
writer: w,
5655
opts: opts,
57-
indent: DefaultIndentSpaces,
5856
anchorPtrToNameMap: map[uintptr]string{},
5957
customMarshalerMap: map[reflect.Type]func(interface{}) ([]byte, error){},
6058
line: 1,
6159
column: 1,
6260
offset: 0,
61+
indentNum: DefaultIndentSpaces,
6362
}
6463
}
6564

@@ -573,8 +572,13 @@ func (e *Encoder) encodeBool(v bool) *ast.BoolNode {
573572

574573
func (e *Encoder) encodeSlice(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) {
575574
if e.indentSequence {
576-
e.column += e.indent
575+
e.column += e.indentNum
577576
}
577+
defer func() {
578+
if e.indentSequence {
579+
e.column -= e.indentNum
580+
}
581+
}()
578582
column := e.column
579583
sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle)
580584
for i := 0; i < value.Len(); i++ {
@@ -584,16 +588,18 @@ func (e *Encoder) encodeSlice(ctx context.Context, value reflect.Value) (*ast.Se
584588
}
585589
sequence.Values = append(sequence.Values, node)
586590
}
587-
if e.indentSequence {
588-
e.column -= e.indent
589-
}
590591
return sequence, nil
591592
}
592593

593594
func (e *Encoder) encodeArray(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) {
594595
if e.indentSequence {
595-
e.column += e.indent
596+
e.column += e.indentNum
596597
}
598+
defer func() {
599+
if e.indentSequence {
600+
e.column -= e.indentNum
601+
}
602+
}()
597603
column := e.column
598604
sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle)
599605
for i := 0; i < value.Len(); i++ {
@@ -603,9 +609,6 @@ func (e *Encoder) encodeArray(ctx context.Context, value reflect.Value) (*ast.Se
603609
}
604610
sequence.Values = append(sequence.Values, node)
605611
}
606-
if e.indentSequence {
607-
e.column -= e.indent
608-
}
609612
return sequence, nil
610613
}
611614

@@ -617,7 +620,7 @@ func (e *Encoder) encodeMapItem(ctx context.Context, item MapItem, column int) (
617620
return nil, err
618621
}
619622
if e.isMapNode(value) {
620-
value.AddColumn(e.indent)
623+
value.AddColumn(e.indentNum)
621624
}
622625
return ast.MappingValue(
623626
token.New("", "", e.pos(column)),
@@ -660,7 +663,7 @@ func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int
660663
return nil
661664
}
662665
if e.isMapNode(value) {
663-
value.AddColumn(e.indent)
666+
value.AddColumn(e.indentNum)
664667
}
665668
node.Values = append(node.Values, ast.MappingValue(
666669
nil,
@@ -783,7 +786,7 @@ func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column
783786
return nil, err
784787
}
785788
if e.isMapNode(value) {
786-
value.AddColumn(e.indent)
789+
value.AddColumn(e.indentNum)
787790
}
788791
var key ast.MapKeyNode = e.encodeString(structField.RenderName, column)
789792
switch {
@@ -833,8 +836,8 @@ func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column
833836
// if declared same key name, skip encoding this field
834837
continue
835838
}
836-
key.AddColumn(-e.indent)
837-
value.AddColumn(-e.indent)
839+
key.AddColumn(-e.indentNum)
840+
value.AddColumn(-e.indentNum)
838841
node.Values = append(node.Values, ast.MappingValue(nil, key, value))
839842
}
840843
continue
@@ -848,7 +851,7 @@ func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column
848851
node.Values = append(node.Values, ast.MappingValue(nil, key, value))
849852
}
850853
if hasInlineAnchorField {
851-
node.AddColumn(e.indent)
854+
node.AddColumn(e.indentNum)
852855
anchorName := "anchor"
853856
anchorNode := ast.Anchor(token.New("&", "&", e.pos(column)))
854857
anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column)))

encode_test.go

+51-29
Original file line numberDiff line numberDiff line change
@@ -1540,49 +1540,71 @@ func TestIssue356(t *testing.T) {
15401540
}
15411541

15421542
func TestMarshalIndentWithMultipleText(t *testing.T) {
1543-
t.Run("depth1", func(t *testing.T) {
1544-
b, err := yaml.MarshalWithOptions(map[string]interface{}{
1545-
"key": []string{`line1
1543+
tests := []struct {
1544+
name string
1545+
input map[string]interface{}
1546+
indent yaml.EncodeOption
1547+
want string
1548+
}{
1549+
{
1550+
name: "depth1",
1551+
input: map[string]interface{}{
1552+
"key": []string{`line1
15461553
line2
15471554
line3`},
1548-
}, yaml.Indent(2))
1549-
if err != nil {
1550-
t.Fatal(err)
1551-
}
1552-
got := string(b)
1553-
expected := `key:
1555+
},
1556+
indent: yaml.Indent(2),
1557+
want: `key:
15541558
- |-
15551559
line1
15561560
line2
15571561
line3
1558-
`
1559-
if expected != got {
1560-
t.Fatalf("failed to encode.\nexpected:\n%s\nbut got:\n%s\n", expected, got)
1561-
}
1562-
})
1563-
t.Run("depth2", func(t *testing.T) {
1564-
b, err := yaml.MarshalWithOptions(map[string]interface{}{
1565-
"key": map[string]interface{}{
1566-
"key2": []string{`line1
1562+
`,
1563+
},
1564+
{
1565+
name: "depth2",
1566+
input: map[string]interface{}{
1567+
"key": map[string]interface{}{
1568+
"key2": []string{`line1
15671569
line2
15681570
line3`},
1571+
},
15691572
},
1570-
}, yaml.Indent(2))
1571-
if err != nil {
1572-
t.Fatal(err)
1573-
}
1574-
got := string(b)
1575-
expected := `key:
1573+
indent: yaml.Indent(2),
1574+
want: `key:
15761575
key2:
15771576
- |-
15781577
line1
15791578
line2
15801579
line3
1581-
`
1582-
if expected != got {
1583-
t.Fatalf("failed to encode.\nexpected:\n%s\nbut got:\n%s\n", expected, got)
1584-
}
1585-
})
1580+
`,
1581+
},
1582+
{
1583+
name: "raw string new lines",
1584+
input: map[string]interface{}{
1585+
"key": "line1\nline2\nline3",
1586+
},
1587+
indent: yaml.Indent(4),
1588+
want: `key: |-
1589+
line1
1590+
line2
1591+
line3
1592+
`,
1593+
},
1594+
}
1595+
1596+
for _, tt := range tests {
1597+
t.Run(tt.name, func(t *testing.T) {
1598+
b, err := yaml.MarshalWithOptions(tt.input, tt.indent)
1599+
if err != nil {
1600+
t.Fatalf("failed to marshal yaml: %v", err)
1601+
}
1602+
got := string(b)
1603+
if tt.want != got {
1604+
t.Fatalf("failed to encode.\nexpected:\n%s\nbut got:\n%s\n", tt.want, got)
1605+
}
1606+
})
1607+
}
15861608
}
15871609

15881610
type bytesMarshaler struct{}

option.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ type EncodeOption func(e *Encoder) error
114114
// Indent change indent number
115115
func Indent(spaces int) EncodeOption {
116116
return func(e *Encoder) error {
117-
e.indent = spaces
117+
e.indentNum = spaces
118118
return nil
119119
}
120120
}

0 commit comments

Comments
 (0)