Skip to content

Commit 39d6f93

Browse files
committed
Add keepIndentStep option to parse
1 parent 79af840 commit 39d6f93

8 files changed

Lines changed: 211 additions & 8 deletions

File tree

src/compose/resolve-block-map.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ export function resolveBlockMap(
116116
offset = valueNode.range![2]
117117
const pair = new Pair(keyNode, valueNode)
118118
if (ctx.options.keepSourceTokens) pair.srcToken = collItem
119+
if (ctx.options.keepIndentStep) {
120+
pair.indentStep =
121+
valueProps.hasNewline && value && 'indent' in value
122+
? ' '.repeat(value.indent - bm.indent)
123+
: ''
124+
}
119125
map.items.push(pair)
120126
} else {
121127
// key with no value

src/compose/resolve-flow-collection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,4 @@ export function resolveFlowCollection(
229229
}
230230

231231
return coll
232-
}
232+
}

src/nodes/Pair.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ export class Pair<
1818
/** The CST token that was composed into this pair. */
1919
declare srcToken?: CollectionItem
2020

21+
/**
22+
* The indentStep between the key and the value.
23+
*
24+
* If the value is in flow / on the same line as the key,
25+
* the indentStep will be an empty string.
26+
*/
27+
indentStep?: string
28+
2129
constructor(key: NodeOf<K>, value: NodeOf<V> | null = null) {
2230
this.key = key
2331
this.value = value
@@ -46,4 +54,4 @@ export class Pair<
4654
? stringifyPair(this, ctx, onComment, onChompKeep)
4755
: JSON.stringify(this)
4856
}
49-
}
57+
}

src/options.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@ export type ParseOptions = {
6868
* Default: `true`
6969
*/
7070
uniqueKeys?: boolean | ((a: Node, b: Node) => boolean)
71+
72+
/**
73+
* Include a `indentStep` value on each parsed `Pair`.
74+
*
75+
* This helps keep the original indentation and formatting after making
76+
* transformations to the document. Currently, this only adds formatting
77+
* information to Pairs found in block maps.
78+
*
79+
* Default: `false`
80+
*/
81+
keepIndentStep?: boolean
7182
}
7283

7384
export type DocumentOptions = {

src/stringify/stringify.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,4 @@ export function stringify(
168168
return node instanceof Scalar || str[0] === '{' || str[0] === '['
169169
? `${props} ${str}`
170170
: `${props}\n${ctx.indent}${str}`
171-
}
171+
}

src/stringify/stringifyPair.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,21 @@ import { stringify } from './stringify.ts'
77
import { indentComment, lineComment } from './stringifyComment.ts'
88

99
export function stringifyPair(
10-
{ key, value }: Readonly<Pair>,
10+
{ key, value, indentStep }: Readonly<Pair>,
1111
ctx: StringifyContext,
1212
onComment?: () => void,
1313
onChompKeep?: () => void
1414
): string {
1515
const {
1616
indent,
17-
indentStep,
1817
noValues,
1918
options: { commentString, indentSeq, simpleKeys }
2019
} = ctx
20+
21+
// Use the indentStep that is on the Node by default,
22+
// since that is preserved from the original document.
23+
indentStep ??= ctx.indentStep
24+
2125
if (simpleKeys) {
2226
if (key.comment) {
2327
throw new Error('With simple keys, key nodes cannot have comments')
@@ -153,7 +157,8 @@ export function stringifyPair(
153157
}
154158
if (sp0 === -1 || nl0 < sp0) hasPropsLine = true
155159
}
156-
if (!hasPropsLine) ws = `\n${ctx.indent}`
160+
if (!hasPropsLine && (indentStep.length > 0 || !value.flow))
161+
ws = `\n${ctx.indent}`
157162
}
158163
} else if (valueStr === '' || valueStr[0] === '\n') {
159164
ws = ''
@@ -169,4 +174,4 @@ export function stringifyPair(
169174
}
170175

171176
return str
172-
}
177+
}

tests/doc/stringify.ts

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,179 @@ describe('custom indent', () => {
10361036
})
10371037
})
10381038

1039+
describe('keepIndentStep: true', () => {
1040+
test('keep object indentation', () => {
1041+
const src = source`
1042+
key:
1043+
super: indented
1044+
`
1045+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1046+
expect(doc.toString()).toBe(src)
1047+
})
1048+
1049+
test('keep array indentation', () => {
1050+
const src = source`
1051+
key:
1052+
- name: foo
1053+
- name: bar
1054+
- name: bam
1055+
- name: baz
1056+
`
1057+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1058+
expect(doc.toString()).toBe(src)
1059+
})
1060+
1061+
test('keep complex indentation', () => {
1062+
const src = source`
1063+
key:
1064+
super:
1065+
- name: foo
1066+
- name: bar
1067+
- name: bam
1068+
- name: baz
1069+
metadata:
1070+
foo: why
1071+
`
1072+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1073+
expect(doc.toString()).toBe(src)
1074+
})
1075+
1076+
test('handles multiline seq flow on same line', () => {
1077+
const src = source`
1078+
key: [
1079+
aaaaaaaa,
1080+
bbbbbbbb,
1081+
cccccccc,
1082+
dddddddd,
1083+
eeeeeeee,
1084+
ffffffff,
1085+
gggggggg,
1086+
hhhhhhhh
1087+
]
1088+
`
1089+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1090+
expect(doc.toString()).toBe(src)
1091+
})
1092+
test('handles multiline flow on same line', () => {
1093+
const src = source`
1094+
key: !tag {
1095+
one: aaaaaaaa,
1096+
two: bbbbbbbb,
1097+
three: cccccccc,
1098+
four: dddddddd,
1099+
five: eeeeeeee
1100+
}
1101+
`
1102+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1103+
expect(doc.toString()).toBe(src)
1104+
})
1105+
1106+
test('handles multiline flow on next line', () => {
1107+
const src = source`
1108+
key:
1109+
!tag {
1110+
one: aaaaaaaa,
1111+
two: bbbbbbbb,
1112+
three: cccccccc,
1113+
four: dddddddd,
1114+
five: eeeeeeee
1115+
}
1116+
`
1117+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1118+
expect(doc.toString()).toBe(src)
1119+
})
1120+
1121+
test('handles multiline flow on next line', () => {
1122+
const src = source`
1123+
key:
1124+
!tag {
1125+
one: aaaaaaaa,
1126+
two: bbbbbbbb,
1127+
three: cccccccc,
1128+
four: dddddddd,
1129+
five: eeeeeeee
1130+
}
1131+
`
1132+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1133+
expect(doc.toString()).toBe(src)
1134+
})
1135+
1136+
test('handles explicit key map with no indent', () => {
1137+
const src = source`
1138+
foo:
1139+
? [ key ]
1140+
: !tag
1141+
- foo
1142+
- bar
1143+
`
1144+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1145+
expect(doc.toString()).toBe(src)
1146+
})
1147+
1148+
test('handles explicit key map with indent', () => {
1149+
const src = source`
1150+
foo:
1151+
? [ key ]
1152+
: !tag
1153+
- foo
1154+
- bar
1155+
`
1156+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1157+
expect(doc.toString()).toBe(src)
1158+
})
1159+
1160+
test('comment after key, value on next line', () => {
1161+
const src = source`
1162+
key: # comment
1163+
value: indented
1164+
`
1165+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1166+
expect(doc.toString()).toBe(
1167+
source`
1168+
key:
1169+
# comment
1170+
value: indented
1171+
`
1172+
)
1173+
})
1174+
1175+
test('comment on value line', () => {
1176+
const src = source`
1177+
key:
1178+
value: indented # comment
1179+
`
1180+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1181+
expect(doc.toString()).toBe(src)
1182+
})
1183+
1184+
test('comment before nested key at value indent level', () => {
1185+
const src = source`
1186+
key:
1187+
# comment
1188+
super: indented
1189+
`
1190+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1191+
expect(doc.toString()).toBe(src)
1192+
})
1193+
1194+
test('nesting parsed node under a new key preserves inner indentation', () => {
1195+
const src = source`
1196+
a:
1197+
b: 1
1198+
c: 2
1199+
`
1200+
const doc = YAML.parseDocument(src, { keepIndentStep: true })
1201+
const wrapper = YAML.parseDocument('outer: null')
1202+
wrapper.set('outer', doc.value)
1203+
expect(wrapper.toString()).toBe(source`
1204+
outer:
1205+
a:
1206+
b: 1
1207+
c: 2
1208+
`)
1209+
})
1210+
})
1211+
10391212
describe('indentSeq: false', () => {
10401213
let obj: unknown
10411214
beforeEach(() => {

0 commit comments

Comments
 (0)