Skip to content

Commit 79af840

Browse files
datho7561eemeli
authored andcommitted
Add trailingComma ToString option for multiline flow formatting (#670)
Disabled by default. When enabled, the last entry in a flow map or flow sequence will have a `,` after it if the collection is split across multiple lines. --------- Signed-off-by: David Thompson <davthomp@redhat.com>
1 parent 01f09cd commit 79af840

5 files changed

Lines changed: 203 additions & 3 deletions

File tree

docs/03_options.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,5 @@ Used by: `stringify()` and `doc.toString()`
166166
| nullStr | `string` | `'null'` | String representation for `null` values. |
167167
| simpleKeys | `boolean` | `false` | Require keys to be scalars and always use implicit rather than explicit notation. |
168168
| singleQuote | `boolean ⎮ null` | `null` | Use 'single quote' rather than "double quote" where applicable. Set to `false` to disable single quotes completely. |
169+
| trailingComma | `boolean` | `false` | Add a trailing comma after the last entry in a flow collection that's split across multiple lines. |
169170
| trueStr | `string` | `'true'` | String representation for `true` values. |

src/options.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,13 @@ export type ToStringOptions = {
371371
*/
372372
singleQuote?: boolean | null
373373

374+
/**
375+
* Add a trailing comma after the last entry in a flow map or flow sequence that's split across multiple lines.
376+
*
377+
* Default: `'false'`
378+
*/
379+
trailingComma?: boolean
380+
374381
/**
375382
* String representation for `true`.
376383
* With the core schema, use `'true'`, `'True'`, or `'TRUE'`.

src/stringify/stringify.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export function createStringifyContext(
5050
nullStr: 'null',
5151
simpleKeys: false,
5252
singleQuote: null,
53+
trailingComma: false,
5354
trueStr: 'true',
5455
verifyAliasOrder: true
5556
},

src/stringify/stringifyCollection.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,21 @@ function stringifyFlowCollection(
129129

130130
if (comment) reqNewline = true
131131
let str = stringify(item, itemCtx, () => (comment = null))
132-
if (i < items.length - 1) str += ','
132+
reqNewline ||= lines.length > linesAtValue || str.includes('\n')
133+
if (i < items.length - 1) {
134+
str += ','
135+
} else if (ctx.options.trailingComma) {
136+
if (ctx.options.lineWidth > 0) {
137+
reqNewline ||=
138+
lines.reduce((sum, line) => sum + line.length + 2, 2) +
139+
(str.length + 2) >
140+
ctx.options.lineWidth
141+
}
142+
if (reqNewline) {
143+
str += ','
144+
}
145+
}
133146
if (comment) str += lineComment(str, itemIndent, commentString(comment))
134-
if (!reqNewline && (lines.length > linesAtValue || str.includes('\n')))
135-
reqNewline = true
136147
lines.push(str)
137148
linesAtValue = lines.length
138149
}

tests/doc/stringify.ts

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,186 @@ z:
546546
expect(String(doc)).toBe(src)
547547
})
548548
})
549+
550+
describe('trailing comma (maps)', () => {
551+
test('no trailing comma is added when the flow map is shorter than the word wrap length', () => {
552+
const doc = YAML.parseDocument(source`
553+
{
554+
a: aaa,
555+
b: bbb,
556+
c: ccc
557+
}
558+
`)
559+
expect(doc.toString({ trailingComma: true, lineWidth: 40 })).toBe(
560+
'{ a: aaa, b: bbb, c: ccc }\n'
561+
)
562+
})
563+
test('no trailing comma is added when the flow map is exactly the word wrap length', () => {
564+
const doc = YAML.parseDocument(source`
565+
{
566+
a: aaa,
567+
b: bbb,
568+
c: ccc,
569+
d: dddddd
570+
}
571+
`)
572+
expect(doc.toString({ trailingComma: true, lineWidth: 40 })).toBe(
573+
'{ a: aaa, b: bbb, c: ccc, d: dddddd }\n'
574+
)
575+
})
576+
test('a trailing comma is added when the flow map is exactly one more than the word wrap length', () => {
577+
const doc = YAML.parseDocument(source`
578+
{
579+
a: aaa,
580+
b: bbb,
581+
c: ccc,
582+
d: ddddddd
583+
}
584+
`)
585+
expect(doc.toString({ trailingComma: true, lineWidth: 40 })).toBe(
586+
'{\n a: aaa,\n b: bbb,\n c: ccc,\n d: ddddddd,\n}\n'
587+
)
588+
})
589+
test('no trailing comma is added when the word wrap length is 0', () => {
590+
const doc = YAML.parseDocument(source`
591+
{
592+
a: aaa,
593+
b: bbb,
594+
c: ccc
595+
}
596+
`)
597+
expect(doc.toString({ trailingComma: true, lineWidth: 0 })).toBe(
598+
'{ a: aaa, b: bbb, c: ccc }\n'
599+
)
600+
})
601+
test('a trailing comma is added when a comment is present in the flow map', () => {
602+
const doc = YAML.parseDocument(source`
603+
{
604+
a: aaa, # my cool comment
605+
b: bbb,
606+
c: ccc
607+
}
608+
`)
609+
expect(doc.toString({ trailingComma: true })).toBe(
610+
'{\n a: aaa, # my cool comment\n b: bbb,\n c: ccc,\n}\n'
611+
)
612+
})
613+
test('a trailing comma is added when a newline is present in the flow map', () => {
614+
const doc = YAML.parseDocument(source`
615+
{
616+
a: aaa,
617+
618+
b: bbb
619+
}
620+
`)
621+
expect(doc.toString({ trailingComma: true })).toBe(
622+
'{\n a: aaa,\n\n b: bbb,\n}\n'
623+
)
624+
})
625+
test('a trailing comma is added when one of the entries includes a newline', () => {
626+
const doc = YAML.parseDocument(source`
627+
{
628+
a: {
629+
a: a # a
630+
},
631+
b: bbb
632+
}
633+
`)
634+
expect(doc.toString({ trailingComma: true })).toBe(
635+
'{\n a:\n {\n a: a, # a\n },\n b: bbb,\n}\n'
636+
)
637+
})
638+
})
639+
640+
describe('trailing comma (arrays)', () => {
641+
test('no trailing comma is added when the flow array is shorter than the word wrap length', () => {
642+
const doc = YAML.parseDocument(source`
643+
[
644+
aaa,
645+
bbb,
646+
ccc
647+
]
648+
`)
649+
expect(doc.toString({ trailingComma: true, lineWidth: 40 })).toBe(
650+
'[ aaa, bbb, ccc ]\n'
651+
)
652+
})
653+
test('no trailing comma is added when the flow array is exactly the word wrap length', () => {
654+
const doc = YAML.parseDocument(source`
655+
[
656+
aaaaaa,
657+
bbbbbb,
658+
cccccc,
659+
ddddddddd
660+
]
661+
`)
662+
expect(doc.toString({ trailingComma: true, lineWidth: 40 })).toBe(
663+
'[ aaaaaa, bbbbbb, cccccc, ddddddddd ]\n'
664+
)
665+
})
666+
test('a trailing comma is added when the flow array is exactly one more than the word wrap length', () => {
667+
const doc = YAML.parseDocument(source`
668+
[
669+
aaaaaa,
670+
bbbbbb,
671+
cccccc,
672+
dddddddddd
673+
]
674+
`)
675+
expect(doc.toString({ trailingComma: true, lineWidth: 40 })).toBe(
676+
'[\n aaaaaa,\n bbbbbb,\n cccccc,\n dddddddddd,\n]\n'
677+
)
678+
})
679+
test('no trailing comma is added when the word wrap length is 0', () => {
680+
const doc = YAML.parseDocument(source`
681+
[
682+
aaa,
683+
bbb,
684+
ccc
685+
]
686+
`)
687+
expect(doc.toString({ trailingComma: true, lineWidth: 0 })).toBe(
688+
'[ aaa, bbb, ccc ]\n'
689+
)
690+
})
691+
test('a trailing comma is added when a comment is present in the flow array', () => {
692+
const doc = YAML.parseDocument(source`
693+
[
694+
aaa, # my cool comment
695+
bbb,
696+
ccc
697+
]
698+
`)
699+
expect(doc.toString({ trailingComma: true })).toBe(
700+
'[\n aaa, # my cool comment\n bbb,\n ccc,\n]\n'
701+
)
702+
})
703+
test('a trailing comma is added when a newline is present in the flow array', () => {
704+
const doc = YAML.parseDocument(source`
705+
[
706+
aaa,
707+
708+
bbb
709+
]
710+
`)
711+
expect(doc.toString({ trailingComma: true })).toBe(
712+
'[\n aaa,\n\n bbb,\n]\n'
713+
)
714+
})
715+
test('a trailing comma is added when one of the entries includes a newline', () => {
716+
const doc = YAML.parseDocument(source`
717+
[
718+
{
719+
a: a # a
720+
},
721+
bbb
722+
]
723+
`)
724+
expect(doc.toString({ trailingComma: true })).toBe(
725+
'[\n {\n a: a, # a\n },\n bbb,\n]\n'
726+
)
727+
})
728+
})
549729
})
550730

551731
test('Quoting colons (#43)', () => {

0 commit comments

Comments
 (0)