Skip to content

Commit 548e1b8

Browse files
authored
Fix format indent (#653)
* fix indent of formatted text * add space to origin buffer for implicit null token
1 parent 13e918a commit 548e1b8

File tree

3 files changed

+220
-15
lines changed

3 files changed

+220
-15
lines changed

decode_test.go

+111-7
Original file line numberDiff line numberDiff line change
@@ -3155,10 +3155,15 @@ type bytesUnmershalerWithMapAlias struct{}
31553155

31563156
func (*bytesUnmershalerWithMapAlias) UnmarshalYAML(b []byte) error {
31573157
expected := strings.TrimPrefix(`
3158-
stuff:
3159-
bar:
3160-
- one
3161-
- two
3158+
aaaaa:
3159+
bbbbb:
3160+
bar:
3161+
- |
3162+
foo
3163+
bar
3164+
- name: |
3165+
foo
3166+
bar
31623167
31633168
`, "\n")
31643169
if string(b) != expected {
@@ -3171,11 +3176,16 @@ func TestBytesUnmarshalerWithMapAlias(t *testing.T) {
31713176
yml := `
31723177
x-foo: &data
31733178
bar:
3174-
- one
3175-
- two
3179+
- |
3180+
foo
3181+
bar
3182+
- name: |
3183+
foo
3184+
bar
31763185
31773186
foo:
3178-
stuff: *data
3187+
aaaaa:
3188+
bbbbb: *data
31793189
`
31803190
type T struct {
31813191
Foo bytesUnmershalerWithMapAlias `yaml:"foo"`
@@ -3186,6 +3196,100 @@ foo:
31863196
}
31873197
}
31883198

3199+
func TestIssue650(t *testing.T) {
3200+
type Disk struct {
3201+
Name string `yaml:"name"`
3202+
Format *bool `yaml:"format"`
3203+
}
3204+
3205+
type Sample struct {
3206+
Disks []Disk `yaml:"disks"`
3207+
}
3208+
3209+
unmarshalDisk := func(dst *Disk, b []byte) error {
3210+
var s string
3211+
if err := yaml.Unmarshal(b, &s); err == nil {
3212+
*dst = Disk{Name: s}
3213+
return nil
3214+
}
3215+
return yaml.Unmarshal(b, dst)
3216+
}
3217+
3218+
data := []byte(`
3219+
disks:
3220+
- name: foo
3221+
format: true
3222+
`)
3223+
3224+
var sample Sample
3225+
if err := yaml.UnmarshalWithOptions(data, &sample, yaml.CustomUnmarshaler[Disk](unmarshalDisk)); err != nil {
3226+
t.Fatal(err)
3227+
}
3228+
}
3229+
3230+
func TestBytesUnmarshalerWithLiteral(t *testing.T) {
3231+
t.Run("map value", func(t *testing.T) {
3232+
type Literal string
3233+
3234+
unmarshalLit := func(dst *Literal, b []byte) error {
3235+
var s string
3236+
if err := yaml.Unmarshal(b, &s); err != nil {
3237+
return err
3238+
}
3239+
*dst = Literal(s)
3240+
return nil
3241+
}
3242+
3243+
data := []byte(`
3244+
- name: |
3245+
foo
3246+
bar
3247+
- name:
3248+
|
3249+
foo
3250+
bar
3251+
`)
3252+
3253+
var v []map[string]Literal
3254+
if err := yaml.UnmarshalWithOptions(data, &v, yaml.CustomUnmarshaler[Literal](unmarshalLit)); err != nil {
3255+
t.Fatal(err)
3256+
}
3257+
if !reflect.DeepEqual(v, []map[string]Literal{{"name": "foo\n bar\n"}, {"name": "foo\nbar\n"}}) {
3258+
t.Fatalf("failed to get decoded value. got: %q", v)
3259+
}
3260+
})
3261+
t.Run("sequence value", func(t *testing.T) {
3262+
type Literal string
3263+
3264+
unmarshalLit := func(dst *Literal, b []byte) error {
3265+
var s string
3266+
if err := yaml.Unmarshal(b, &s); err != nil {
3267+
return err
3268+
}
3269+
*dst = Literal(s)
3270+
return nil
3271+
}
3272+
3273+
data := []byte(`
3274+
- |
3275+
foo
3276+
bar
3277+
-
3278+
|
3279+
foo
3280+
bar
3281+
`)
3282+
3283+
var v []Literal
3284+
if err := yaml.UnmarshalWithOptions(data, &v, yaml.CustomUnmarshaler[Literal](unmarshalLit)); err != nil {
3285+
t.Fatal(err)
3286+
}
3287+
if !reflect.DeepEqual(v, []Literal{"foo\n bar\n", "foo\nbar\n"}) {
3288+
t.Fatalf("failed to get decoded value. got: %q", v)
3289+
}
3290+
})
3291+
}
3292+
31893293
func TestDecoderPreservesDefaultValues(t *testing.T) {
31903294
type nested struct {
31913295
Val string `yaml:"val"`

internal/format/format.go

+108-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
func FormatNodeWithResolvedAlias(n ast.Node, anchorNodeMap map[string]ast.Node) string {
11-
tk := n.GetToken()
11+
tk := getFirstToken(n)
1212
if tk == nil {
1313
return ""
1414
}
@@ -18,7 +18,7 @@ func FormatNodeWithResolvedAlias(n ast.Node, anchorNodeMap map[string]ast.Node)
1818
}
1919

2020
func FormatNode(n ast.Node) string {
21-
tk := n.GetToken()
21+
tk := getFirstToken(n)
2222
if tk == nil {
2323
return ""
2424
}
@@ -29,7 +29,7 @@ func FormatFile(file *ast.File) string {
2929
if len(file.Docs) == 0 {
3030
return ""
3131
}
32-
tk := file.Docs[0].GetToken()
32+
tk := getFirstToken(file.Docs[0])
3333
if tk == nil {
3434
return ""
3535
}
@@ -131,6 +131,58 @@ func hasComment(n ast.Node) bool {
131131
return false
132132
}
133133

134+
func getFirstToken(n ast.Node) *token.Token {
135+
if n == nil {
136+
return nil
137+
}
138+
switch nn := n.(type) {
139+
case *ast.DocumentNode:
140+
if nn.Start != nil {
141+
return nn.Start
142+
}
143+
return getFirstToken(nn.Body)
144+
case *ast.NullNode:
145+
return nn.Token
146+
case *ast.BoolNode:
147+
return nn.Token
148+
case *ast.IntegerNode:
149+
return nn.Token
150+
case *ast.FloatNode:
151+
return nn.Token
152+
case *ast.StringNode:
153+
return nn.Token
154+
case *ast.InfinityNode:
155+
return nn.Token
156+
case *ast.NanNode:
157+
return nn.Token
158+
case *ast.LiteralNode:
159+
return nn.Start
160+
case *ast.DirectiveNode:
161+
return nn.Start
162+
case *ast.TagNode:
163+
return nn.Start
164+
case *ast.MappingNode:
165+
if nn.IsFlowStyle {
166+
return nn.Start
167+
}
168+
if len(nn.Values) == 0 {
169+
return nn.Start
170+
}
171+
return getFirstToken(nn.Values[0].Key)
172+
case *ast.MappingKeyNode:
173+
return nn.Start
174+
case *ast.MergeKeyNode:
175+
return nn.Token
176+
case *ast.SequenceNode:
177+
return nn.Start
178+
case *ast.AnchorNode:
179+
return nn.Start
180+
case *ast.AliasNode:
181+
return nn.Start
182+
}
183+
return nil
184+
}
185+
134186
type Formatter struct {
135187
existsComment bool
136188
tokenToOriginMap map[*token.Token]string
@@ -155,17 +207,66 @@ func newFormatter(tk *token.Token, existsComment bool) *Formatter {
155207
tokenToOriginMap[tk] = origin
156208
origin = ""
157209
}
158-
159210
return &Formatter{
160211
existsComment: existsComment,
161212
tokenToOriginMap: tokenToOriginMap,
162213
}
163214
}
164215

216+
func getIndentNumByFirstLineToken(tk *token.Token) int {
217+
defaultIndent := tk.Position.Column - 1
218+
219+
// key: value
220+
// ^
221+
// next
222+
if tk.Type == token.SequenceEntryType {
223+
// If the current token is the sequence entry.
224+
// the indent is calculated from the column value of the current token.
225+
return defaultIndent
226+
}
227+
228+
// key: value
229+
// ^
230+
// next
231+
if tk.Next != nil && tk.Next.Type == token.MappingValueType {
232+
// If the current token is the key in the mapping-value,
233+
// the indent is calculated from the column value of the current token.
234+
return defaultIndent
235+
}
236+
237+
if tk.Prev == nil {
238+
return defaultIndent
239+
}
240+
prev := tk.Prev
241+
242+
// key: value
243+
// ^
244+
// prev
245+
if prev.Type == token.MappingValueType {
246+
// If the current token is the value in the mapping-value,
247+
// the indent is calculated from the column value of the key two steps back.
248+
if prev.Prev == nil {
249+
return defaultIndent
250+
}
251+
return prev.Prev.Position.Column - 1
252+
}
253+
254+
// - value
255+
// ^
256+
// prev
257+
if prev.Type == token.SequenceEntryType {
258+
// If the value is not a mapping-value and the previous token was a sequence entry,
259+
// the indent is calculated using the column value of the sequence entry token.
260+
return prev.Position.Column - 1
261+
}
262+
263+
return defaultIndent
264+
}
265+
165266
func (f *Formatter) format(n ast.Node) string {
166267
return f.trimSpacePrefix(
167268
f.trimIndentSpace(
168-
n.GetToken().Position.IndentNum,
269+
getIndentNumByFirstLineToken(getFirstToken(n)),
169270
f.trimNewLineCharPrefix(f.formatNode(n)),
170271
),
171272
)
@@ -306,7 +407,8 @@ func (f *Formatter) formatAnchor(n *ast.AnchorNode) string {
306407

307408
func (f *Formatter) formatAlias(n *ast.AliasNode) string {
308409
if f.anchorNodeMap != nil {
309-
node := f.anchorNodeMap[n.Value.GetToken().Value]
410+
anchorName := n.Value.GetToken().Value
411+
node := f.anchorNodeMap[anchorName]
310412
if node != nil {
311413
formatted := f.formatNode(node)
312414
// If formatted text contains newline characters, indentation needs to be considered.

parser/context.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ func (c *context) addNullValueToken(tk *Token) *Token {
132132
rawTk := nullToken.RawToken()
133133

134134
// add space for map or sequence value.
135-
rawTk.Origin = " null"
136135
rawTk.Position.Column++
137136

138137
c.addToken(nullToken)
@@ -144,7 +143,7 @@ func (c *context) addNullValueToken(tk *Token) *Token {
144143
func (c *context) createNullToken(base *Token) *Token {
145144
pos := *(base.RawToken().Position)
146145
pos.Column++
147-
return &Token{Token: token.New("null", "null", &pos)}
146+
return &Token{Token: token.New("null", " null", &pos)}
148147
}
149148

150149
func (c *context) insertToken(tk *Token) {

0 commit comments

Comments
 (0)