@openrewrite/rewrite 8.69.0-20251211-160325 → 8.69.0-20251212-112414
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/cli-utils.d.ts.map +1 -1
- package/dist/cli/cli-utils.js +110 -71
- package/dist/cli/cli-utils.js.map +1 -1
- package/dist/javascript/package-json-parser.d.ts +0 -5
- package/dist/javascript/package-json-parser.d.ts.map +1 -1
- package/dist/javascript/package-json-parser.js +2 -12
- package/dist/javascript/package-json-parser.js.map +1 -1
- package/dist/javascript/package-manager.d.ts +72 -1
- package/dist/javascript/package-manager.d.ts.map +1 -1
- package/dist/javascript/package-manager.js +173 -2
- package/dist/javascript/package-manager.js.map +1 -1
- package/dist/javascript/recipes/add-dependency.d.ts.map +1 -1
- package/dist/javascript/recipes/add-dependency.js +11 -8
- package/dist/javascript/recipes/add-dependency.js.map +1 -1
- package/dist/javascript/recipes/upgrade-dependency-version.d.ts.map +1 -1
- package/dist/javascript/recipes/upgrade-dependency-version.js +11 -8
- package/dist/javascript/recipes/upgrade-dependency-version.js.map +1 -1
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.d.ts.map +1 -1
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.js +11 -8
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.js.map +1 -1
- package/dist/json/parser.d.ts.map +1 -1
- package/dist/json/parser.js +355 -123
- package/dist/json/parser.js.map +1 -1
- package/dist/json/tree.d.ts +5 -1
- package/dist/json/tree.d.ts.map +1 -1
- package/dist/json/tree.js +157 -7
- package/dist/json/tree.js.map +1 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +5 -1
- package/dist/print.js.map +1 -1
- package/dist/rpc/request/get-languages.d.ts.map +1 -1
- package/dist/rpc/request/get-languages.js +1 -0
- package/dist/rpc/request/get-languages.js.map +1 -1
- package/dist/rpc/server.d.ts +1 -0
- package/dist/rpc/server.d.ts.map +1 -1
- package/dist/rpc/server.js +1 -0
- package/dist/rpc/server.js.map +1 -1
- package/dist/version.txt +1 -1
- package/dist/yaml/index.d.ts +6 -0
- package/dist/yaml/index.d.ts.map +1 -0
- package/dist/yaml/index.js +37 -0
- package/dist/yaml/index.js.map +1 -0
- package/dist/yaml/parser.d.ts +6 -0
- package/dist/yaml/parser.d.ts.map +1 -0
- package/dist/yaml/parser.js +803 -0
- package/dist/yaml/parser.js.map +1 -0
- package/dist/yaml/print.d.ts +2 -0
- package/dist/yaml/print.d.ts.map +1 -0
- package/dist/yaml/print.js +234 -0
- package/dist/yaml/print.js.map +1 -0
- package/dist/yaml/rpc.d.ts +2 -0
- package/dist/yaml/rpc.d.ts.map +1 -0
- package/dist/yaml/rpc.js +264 -0
- package/dist/yaml/rpc.js.map +1 -0
- package/dist/yaml/tree.d.ts +188 -0
- package/dist/yaml/tree.d.ts.map +1 -0
- package/dist/yaml/tree.js +117 -0
- package/dist/yaml/tree.js.map +1 -0
- package/dist/yaml/visitor.d.ts +19 -0
- package/dist/yaml/visitor.d.ts.map +1 -0
- package/dist/yaml/visitor.js +170 -0
- package/dist/yaml/visitor.js.map +1 -0
- package/package.json +6 -1
- package/src/cli/cli-utils.ts +112 -35
- package/src/javascript/package-json-parser.ts +2 -12
- package/src/javascript/package-manager.ts +179 -3
- package/src/javascript/recipes/add-dependency.ts +16 -10
- package/src/javascript/recipes/upgrade-dependency-version.ts +16 -10
- package/src/javascript/recipes/upgrade-transitive-dependency-version.ts +15 -9
- package/src/json/parser.ts +443 -146
- package/src/json/tree.ts +159 -7
- package/src/print.ts +6 -2
- package/src/rpc/request/get-languages.ts +1 -0
- package/src/rpc/server.ts +1 -0
- package/src/yaml/index.ts +21 -0
- package/src/yaml/parser.ts +850 -0
- package/src/yaml/print.ts +212 -0
- package/src/yaml/rpc.ts +248 -0
- package/src/yaml/tree.ts +281 -0
- package/src/yaml/visitor.ts +146 -0
|
@@ -0,0 +1,850 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 the original author or authors.
|
|
3
|
+
* <p>
|
|
4
|
+
* Licensed under the Moderne Source Available License (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
* <p>
|
|
8
|
+
* https://docs.moderne.io/licensing/moderne-source-available-license
|
|
9
|
+
* <p>
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import {emptyMarkers, markers, MarkersKind, ParseExceptionResult} from "../markers";
|
|
17
|
+
import {Parser, ParserInput, parserInputRead} from "../parser";
|
|
18
|
+
import {randomId} from "../uuid";
|
|
19
|
+
import {SourceFile} from "../tree";
|
|
20
|
+
import {Yaml} from "./tree";
|
|
21
|
+
import {ParseError, ParseErrorKind} from "../parse-error";
|
|
22
|
+
import {Parser as YamlCstParser, CST} from "yaml";
|
|
23
|
+
|
|
24
|
+
// Types from yaml package CST
|
|
25
|
+
type CstToken = CST.Token;
|
|
26
|
+
type CstSourceToken = CST.SourceToken;
|
|
27
|
+
type CstDocument = CST.Document;
|
|
28
|
+
type CstDocumentEnd = CST.DocumentEnd;
|
|
29
|
+
type CstBlockMap = CST.BlockMap;
|
|
30
|
+
type CstBlockSequence = CST.BlockSequence;
|
|
31
|
+
type CstFlowCollection = CST.FlowCollection;
|
|
32
|
+
type CstFlowScalar = CST.FlowScalar;
|
|
33
|
+
type CstBlockScalar = CST.BlockScalar;
|
|
34
|
+
type CstCollectionItem = CST.CollectionItem;
|
|
35
|
+
|
|
36
|
+
export class YamlParser extends Parser {
|
|
37
|
+
|
|
38
|
+
async *parse(...sourcePaths: ParserInput[]): AsyncGenerator<SourceFile> {
|
|
39
|
+
for (const sourcePath of sourcePaths) {
|
|
40
|
+
const text = parserInputRead(sourcePath);
|
|
41
|
+
try {
|
|
42
|
+
yield {
|
|
43
|
+
...new YamlCstReader(text).parse(),
|
|
44
|
+
sourcePath: this.relativePath(sourcePath)
|
|
45
|
+
};
|
|
46
|
+
} catch (e: any) {
|
|
47
|
+
// Return a ParseError for files that can't be parsed
|
|
48
|
+
const parseError: ParseError = {
|
|
49
|
+
kind: ParseErrorKind,
|
|
50
|
+
id: randomId(),
|
|
51
|
+
markers: markers({
|
|
52
|
+
kind: MarkersKind.ParseExceptionResult,
|
|
53
|
+
id: randomId(),
|
|
54
|
+
parserType: "YamlParser",
|
|
55
|
+
exceptionType: e.name || "Error",
|
|
56
|
+
message: e.message || "Unknown parse error"
|
|
57
|
+
} satisfies ParseExceptionResult as ParseExceptionResult),
|
|
58
|
+
sourcePath: this.relativePath(sourcePath),
|
|
59
|
+
text
|
|
60
|
+
};
|
|
61
|
+
yield parseError;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Converts YAML CST from the 'yaml' package to our Yaml AST.
|
|
69
|
+
* The CST preserves all whitespace, comments, and formatting.
|
|
70
|
+
*/
|
|
71
|
+
class YamlCstReader {
|
|
72
|
+
private readonly cstTokens: CstToken[];
|
|
73
|
+
|
|
74
|
+
constructor(source: string) {
|
|
75
|
+
const parser = new YamlCstParser();
|
|
76
|
+
this.cstTokens = [...parser.parse(source)];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
parse(): Omit<Yaml.Documents, "sourcePath"> {
|
|
80
|
+
const documents: Yaml.Document[] = [];
|
|
81
|
+
let pendingPrefix = "";
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i < this.cstTokens.length; i++) {
|
|
84
|
+
const token = this.cstTokens[i];
|
|
85
|
+
|
|
86
|
+
if (token.type === 'document') {
|
|
87
|
+
const cstDoc = token as CstDocument;
|
|
88
|
+
// Check if next token is doc-end
|
|
89
|
+
const nextToken = this.cstTokens[i + 1];
|
|
90
|
+
let docEnd: CstDocumentEnd | undefined;
|
|
91
|
+
if (nextToken?.type === 'doc-end') {
|
|
92
|
+
docEnd = nextToken as CstDocumentEnd;
|
|
93
|
+
i++; // Skip the doc-end in the main loop
|
|
94
|
+
}
|
|
95
|
+
const {doc, afterEnd} = this.convertDocument(cstDoc, docEnd, pendingPrefix);
|
|
96
|
+
documents.push(doc);
|
|
97
|
+
pendingPrefix = afterEnd; // Content after ... becomes next doc's prefix
|
|
98
|
+
} else if (token.type === 'doc-end') {
|
|
99
|
+
// Standalone doc-end without preceding document
|
|
100
|
+
const docEnd = token as CstDocumentEnd;
|
|
101
|
+
pendingPrefix += this.concatenateSources(docEnd.end || []);
|
|
102
|
+
} else if (token.type === 'comment' || token.type === 'newline' || token.type === 'space') {
|
|
103
|
+
// Content before document (comments, whitespace) becomes document prefix
|
|
104
|
+
pendingPrefix += (token as CstSourceToken).source;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
kind: Yaml.Kind.Documents,
|
|
110
|
+
id: randomId(),
|
|
111
|
+
prefix: "",
|
|
112
|
+
markers: emptyMarkers,
|
|
113
|
+
documents,
|
|
114
|
+
suffix: undefined
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private convertDocument(cstDoc: CstDocument, cstDocEnd?: CstDocumentEnd, pendingPrefix: string = ""): {doc: Yaml.Document, afterEnd: string} {
|
|
119
|
+
// Extract prefix from document start tokens
|
|
120
|
+
// The document prefix is content BEFORE the --- marker
|
|
121
|
+
// Content AFTER --- goes into the block's first entry
|
|
122
|
+
const startTokens = cstDoc.start || [];
|
|
123
|
+
let prefix = pendingPrefix; // Content before this document
|
|
124
|
+
let explicit = false;
|
|
125
|
+
let afterDocStart = ""; // Content after ---
|
|
126
|
+
let seenDocStart = false;
|
|
127
|
+
|
|
128
|
+
for (const token of startTokens) {
|
|
129
|
+
if (token.type === 'doc-start') {
|
|
130
|
+
explicit = true;
|
|
131
|
+
seenDocStart = true;
|
|
132
|
+
} else if (seenDocStart) {
|
|
133
|
+
// Content after --- goes into block's prefix
|
|
134
|
+
afterDocStart += token.source;
|
|
135
|
+
} else {
|
|
136
|
+
// Content before --- goes into document prefix
|
|
137
|
+
prefix += token.source;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Convert the document body and get trailing content
|
|
142
|
+
let block: Yaml.Block;
|
|
143
|
+
let trailing = "";
|
|
144
|
+
if (cstDoc.value) {
|
|
145
|
+
const result = this.convertTokenWithTrailing(cstDoc.value);
|
|
146
|
+
block = result.node as Yaml.Block;
|
|
147
|
+
trailing = result.trailing;
|
|
148
|
+
|
|
149
|
+
// Prepend the whitespace after --- to the block
|
|
150
|
+
if (afterDocStart) {
|
|
151
|
+
block = this.prependWhitespaceToValue(block, afterDocStart);
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
block = this.createEmptyScalar(afterDocStart);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Convert document end, passing the trailing content from block
|
|
158
|
+
const {end, afterEnd} = this.convertDocumentEnd(cstDoc.end, cstDocEnd, trailing);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
doc: {
|
|
162
|
+
kind: Yaml.Kind.Document,
|
|
163
|
+
id: randomId(),
|
|
164
|
+
prefix,
|
|
165
|
+
markers: emptyMarkers,
|
|
166
|
+
explicit,
|
|
167
|
+
block,
|
|
168
|
+
end
|
|
169
|
+
},
|
|
170
|
+
afterEnd
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private convertDocumentEnd(docEndTokens?: CstSourceToken[], cstDocEnd?: CstDocumentEnd, trailing: string = ""): {end: Yaml.DocumentEnd, afterEnd: string} {
|
|
175
|
+
// Prefix is the trailing content from the block, plus any content before ...
|
|
176
|
+
let prefix = trailing;
|
|
177
|
+
let explicit = false;
|
|
178
|
+
let afterEnd = "";
|
|
179
|
+
|
|
180
|
+
// Content after the document body (before ... if present)
|
|
181
|
+
if (docEndTokens) {
|
|
182
|
+
prefix += this.concatenateSources(docEndTokens);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Explicit document end marker (...)
|
|
186
|
+
if (cstDocEnd) {
|
|
187
|
+
explicit = true;
|
|
188
|
+
// cstDocEnd.end contains tokens AFTER the ... marker - these become next doc's prefix
|
|
189
|
+
if (cstDocEnd.end) {
|
|
190
|
+
afterEnd = this.concatenateSources(cstDocEnd.end);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
end: {
|
|
196
|
+
kind: Yaml.Kind.DocumentEnd,
|
|
197
|
+
id: randomId(),
|
|
198
|
+
prefix,
|
|
199
|
+
markers: emptyMarkers,
|
|
200
|
+
explicit
|
|
201
|
+
},
|
|
202
|
+
afterEnd
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private convertTokenWithTrailing(token: CstToken): {node: Yaml, trailing: string} {
|
|
207
|
+
switch (token.type) {
|
|
208
|
+
case 'block-map':
|
|
209
|
+
return this.convertBlockMapWithTrailing(token as CstBlockMap);
|
|
210
|
+
case 'block-seq':
|
|
211
|
+
return this.convertBlockSequenceWithTrailing(token as CstBlockSequence);
|
|
212
|
+
case 'flow-collection':
|
|
213
|
+
return this.convertFlowCollectionWithTrailing(token as CstFlowCollection);
|
|
214
|
+
case 'scalar':
|
|
215
|
+
case 'single-quoted-scalar':
|
|
216
|
+
case 'double-quoted-scalar':
|
|
217
|
+
return this.convertFlowScalarWithTrailing(token as CstFlowScalar);
|
|
218
|
+
case 'block-scalar':
|
|
219
|
+
return this.convertBlockScalarWithTrailing(token as CstBlockScalar);
|
|
220
|
+
case 'alias':
|
|
221
|
+
return this.convertAliasWithTrailing(token as CstFlowScalar);
|
|
222
|
+
default:
|
|
223
|
+
// For unknown types, create an empty scalar
|
|
224
|
+
return {node: this.createEmptyScalar(), trailing: ""};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private convertBlockMapWithTrailing(cst: CstBlockMap): {node: Yaml.Mapping, trailing: string} {
|
|
229
|
+
const entries: Yaml.MappingEntry[] = [];
|
|
230
|
+
let pendingPrefix = "";
|
|
231
|
+
|
|
232
|
+
for (const item of cst.items) {
|
|
233
|
+
if (item.key !== undefined || item.value !== undefined) {
|
|
234
|
+
const entry = this.convertMappingEntry(item, pendingPrefix);
|
|
235
|
+
entries.push(entry.entry);
|
|
236
|
+
pendingPrefix = entry.trailingContent;
|
|
237
|
+
} else {
|
|
238
|
+
// Entry with no key or value - capture start tokens (comments, whitespace)
|
|
239
|
+
// into pending prefix for the next entry or as trailing content
|
|
240
|
+
pendingPrefix += this.concatenateSources(item.start || []);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
node: {
|
|
246
|
+
kind: Yaml.Kind.Mapping,
|
|
247
|
+
id: randomId(),
|
|
248
|
+
prefix: "",
|
|
249
|
+
markers: emptyMarkers,
|
|
250
|
+
openingBracePrefix: undefined, // Block map has no braces
|
|
251
|
+
entries,
|
|
252
|
+
closingBracePrefix: undefined,
|
|
253
|
+
anchor: undefined,
|
|
254
|
+
tag: undefined
|
|
255
|
+
},
|
|
256
|
+
trailing: pendingPrefix // Trailing content from last entry
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private convertMappingEntry(item: CstCollectionItem, pendingPrefix: string = ""): {entry: Yaml.MappingEntry, trailingContent: string} {
|
|
261
|
+
// Prefix comes from start tokens plus any pending prefix from previous entry's trailing content
|
|
262
|
+
const prefix = pendingPrefix + this.concatenateSources(item.start || []);
|
|
263
|
+
|
|
264
|
+
// Convert key and get its trailing content
|
|
265
|
+
let keyTrailing = "";
|
|
266
|
+
let key: Yaml.YamlKey;
|
|
267
|
+
if (item.key) {
|
|
268
|
+
const keyResult = this.convertTokenWithTrailing(item.key);
|
|
269
|
+
key = keyResult.node as Yaml.YamlKey;
|
|
270
|
+
keyTrailing = keyResult.trailing;
|
|
271
|
+
} else {
|
|
272
|
+
key = this.createEmptyScalar();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Extract whitespace before ':' and any anchor/tag after it
|
|
276
|
+
let beforeMappingValueIndicator = keyTrailing;
|
|
277
|
+
let anchorForValue: Yaml.Anchor | undefined;
|
|
278
|
+
let tagForValue: Yaml.Tag | undefined;
|
|
279
|
+
let afterColonWhitespace = "";
|
|
280
|
+
let seenColon = false;
|
|
281
|
+
|
|
282
|
+
if (item.sep) {
|
|
283
|
+
const sepTokens = item.sep;
|
|
284
|
+
for (let i = 0; i < sepTokens.length; i++) {
|
|
285
|
+
const sepToken = sepTokens[i];
|
|
286
|
+
if (sepToken.type === 'map-value-ind') {
|
|
287
|
+
seenColon = true;
|
|
288
|
+
} else if (!seenColon) {
|
|
289
|
+
// Before the ':'
|
|
290
|
+
beforeMappingValueIndicator += sepToken.source;
|
|
291
|
+
} else if (sepToken.type === 'anchor') {
|
|
292
|
+
// Collect all remaining tokens after anchor as postfix
|
|
293
|
+
let postfix = "";
|
|
294
|
+
for (let j = i + 1; j < sepTokens.length; j++) {
|
|
295
|
+
const nextToken = sepTokens[j];
|
|
296
|
+
if (nextToken.type === 'tag') {
|
|
297
|
+
tagForValue = this.parseTagTokenWithSuffix(nextToken.source, postfix, sepTokens, j + 1);
|
|
298
|
+
postfix = "";
|
|
299
|
+
break; // Tag handler consumed remaining tokens
|
|
300
|
+
} else {
|
|
301
|
+
postfix += nextToken.source;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
anchorForValue = {
|
|
305
|
+
kind: Yaml.Kind.Anchor,
|
|
306
|
+
id: randomId(),
|
|
307
|
+
prefix: afterColonWhitespace,
|
|
308
|
+
markers: emptyMarkers,
|
|
309
|
+
postfix,
|
|
310
|
+
key: sepToken.source.substring(1) // Remove &
|
|
311
|
+
};
|
|
312
|
+
afterColonWhitespace = "";
|
|
313
|
+
break; // We've consumed all remaining tokens
|
|
314
|
+
} else if (sepToken.type === 'tag') {
|
|
315
|
+
// Parse tag with its suffix (whitespace after the tag)
|
|
316
|
+
tagForValue = this.parseTagTokenWithSuffix(sepToken.source, afterColonWhitespace, sepTokens, i + 1);
|
|
317
|
+
afterColonWhitespace = ""; // Remaining whitespace is in tag suffix, not value prefix
|
|
318
|
+
break; // Tag handler consumed remaining tokens
|
|
319
|
+
} else {
|
|
320
|
+
// After the ':'
|
|
321
|
+
afterColonWhitespace += sepToken.source;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Convert value
|
|
327
|
+
let value: Yaml.Block;
|
|
328
|
+
let valueTrailing = "";
|
|
329
|
+
if (item.value) {
|
|
330
|
+
const valueResult = this.convertTokenWithTrailing(item.value);
|
|
331
|
+
value = valueResult.node as Yaml.Block;
|
|
332
|
+
valueTrailing = valueResult.trailing;
|
|
333
|
+
|
|
334
|
+
// Apply anchor and tag if found in separator
|
|
335
|
+
if (anchorForValue && 'anchor' in value) {
|
|
336
|
+
value = {...value, anchor: anchorForValue} as Yaml.Block;
|
|
337
|
+
}
|
|
338
|
+
if (tagForValue && 'tag' in value) {
|
|
339
|
+
value = {...value, tag: tagForValue} as Yaml.Block;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Prepend accumulated whitespace based on value type
|
|
343
|
+
if (afterColonWhitespace) {
|
|
344
|
+
value = this.prependWhitespaceToValue(value, afterColonWhitespace);
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
value = this.createEmptyScalar(afterColonWhitespace);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
entry: {
|
|
352
|
+
kind: Yaml.Kind.MappingEntry,
|
|
353
|
+
id: randomId(),
|
|
354
|
+
prefix,
|
|
355
|
+
markers: emptyMarkers,
|
|
356
|
+
key,
|
|
357
|
+
beforeMappingValueIndicator,
|
|
358
|
+
value
|
|
359
|
+
},
|
|
360
|
+
trailingContent: valueTrailing
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private convertBlockSequenceWithTrailing(cst: CstBlockSequence): {node: Yaml.Sequence, trailing: string} {
|
|
365
|
+
const entries: Yaml.SequenceEntry[] = [];
|
|
366
|
+
let pendingPrefix = "";
|
|
367
|
+
|
|
368
|
+
for (const item of cst.items) {
|
|
369
|
+
const result = this.convertSequenceEntry(item, true, pendingPrefix);
|
|
370
|
+
entries.push(result.entry);
|
|
371
|
+
pendingPrefix = result.trailingContent;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
node: {
|
|
376
|
+
kind: Yaml.Kind.Sequence,
|
|
377
|
+
id: randomId(),
|
|
378
|
+
prefix: "",
|
|
379
|
+
markers: emptyMarkers,
|
|
380
|
+
openingBracketPrefix: undefined, // Block sequence has no brackets
|
|
381
|
+
entries,
|
|
382
|
+
closingBracketPrefix: undefined,
|
|
383
|
+
anchor: undefined,
|
|
384
|
+
tag: undefined
|
|
385
|
+
},
|
|
386
|
+
trailing: pendingPrefix // Trailing content from last entry
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private convertSequenceEntry(item: CstCollectionItem, dash: boolean, pendingPrefix: string = ""): {entry: Yaml.SequenceEntry, trailingContent: string} {
|
|
391
|
+
// Build prefix from start tokens, but exclude the dash indicator itself
|
|
392
|
+
let prefix = pendingPrefix;
|
|
393
|
+
let afterDashSpace = "";
|
|
394
|
+
let seenDash = false;
|
|
395
|
+
|
|
396
|
+
for (const token of item.start || []) {
|
|
397
|
+
if (token.type === 'seq-item-ind') {
|
|
398
|
+
seenDash = true;
|
|
399
|
+
} else if (seenDash) {
|
|
400
|
+
afterDashSpace += token.source;
|
|
401
|
+
} else {
|
|
402
|
+
prefix += token.source;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Convert value
|
|
407
|
+
let block: Yaml.Block;
|
|
408
|
+
let trailing = "";
|
|
409
|
+
if (item.value) {
|
|
410
|
+
const valueResult = this.convertTokenWithTrailing(item.value);
|
|
411
|
+
block = valueResult.node as Yaml.Block;
|
|
412
|
+
trailing = valueResult.trailing;
|
|
413
|
+
// Prepend the space after dash to the block's prefix
|
|
414
|
+
block = {...block, prefix: afterDashSpace + block.prefix} as Yaml.Block;
|
|
415
|
+
} else {
|
|
416
|
+
block = this.createEmptyScalar(afterDashSpace);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return {
|
|
420
|
+
entry: {
|
|
421
|
+
kind: Yaml.Kind.SequenceEntry,
|
|
422
|
+
id: randomId(),
|
|
423
|
+
prefix,
|
|
424
|
+
markers: emptyMarkers,
|
|
425
|
+
block,
|
|
426
|
+
dash,
|
|
427
|
+
trailingCommaPrefix: undefined // Block sequence has no commas
|
|
428
|
+
},
|
|
429
|
+
trailingContent: trailing
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private convertFlowCollectionWithTrailing(cst: CstFlowCollection): {node: Yaml.Mapping | Yaml.Sequence, trailing: string} {
|
|
434
|
+
const isMap = cst.start.source === '{';
|
|
435
|
+
const openingPrefix = "";
|
|
436
|
+
|
|
437
|
+
// End tokens include closing bracket/brace plus any trailing content
|
|
438
|
+
// We need to separate the closing delimiter from trailing whitespace
|
|
439
|
+
const endTokens = cst.end || [];
|
|
440
|
+
let closingPrefix = "";
|
|
441
|
+
let trailing = "";
|
|
442
|
+
let seenClosing = false;
|
|
443
|
+
|
|
444
|
+
for (const token of endTokens) {
|
|
445
|
+
if (token.type === 'flow-map-end' || token.type === 'flow-seq-end') {
|
|
446
|
+
seenClosing = true;
|
|
447
|
+
} else if (seenClosing) {
|
|
448
|
+
trailing += token.source;
|
|
449
|
+
} else {
|
|
450
|
+
closingPrefix += token.source;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (isMap) {
|
|
455
|
+
const entries: Yaml.MappingEntry[] = [];
|
|
456
|
+
let pendingPrefix = "";
|
|
457
|
+
|
|
458
|
+
for (const item of cst.items) {
|
|
459
|
+
if (item.key !== undefined || item.value !== undefined) {
|
|
460
|
+
const result = this.convertFlowMappingEntry(item, pendingPrefix);
|
|
461
|
+
entries.push(result.entry);
|
|
462
|
+
pendingPrefix = result.trailingContent;
|
|
463
|
+
} else {
|
|
464
|
+
// Empty item (trailing comma) - capture start tokens including the comma
|
|
465
|
+
pendingPrefix += this.concatenateSources(item.start || []);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Trailing content from last entry becomes part of closing brace prefix
|
|
470
|
+
const finalClosingPrefix = pendingPrefix + closingPrefix;
|
|
471
|
+
|
|
472
|
+
return {
|
|
473
|
+
node: {
|
|
474
|
+
kind: Yaml.Kind.Mapping,
|
|
475
|
+
id: randomId(),
|
|
476
|
+
prefix: "",
|
|
477
|
+
markers: emptyMarkers,
|
|
478
|
+
openingBracePrefix: openingPrefix,
|
|
479
|
+
entries,
|
|
480
|
+
closingBracePrefix: finalClosingPrefix,
|
|
481
|
+
anchor: undefined,
|
|
482
|
+
tag: undefined
|
|
483
|
+
},
|
|
484
|
+
trailing
|
|
485
|
+
};
|
|
486
|
+
} else {
|
|
487
|
+
// Flow sequence [a, b, c]
|
|
488
|
+
const entries: Yaml.SequenceEntry[] = [];
|
|
489
|
+
let pendingPrefix = "";
|
|
490
|
+
|
|
491
|
+
for (let i = 0; i < cst.items.length; i++) {
|
|
492
|
+
const item = cst.items[i];
|
|
493
|
+
const isLast = i === cst.items.length - 1;
|
|
494
|
+
const result = this.convertFlowSequenceEntry(item, isLast, pendingPrefix);
|
|
495
|
+
entries.push(result.entry);
|
|
496
|
+
pendingPrefix = result.trailingContent;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Trailing content from last entry becomes part of closing bracket prefix
|
|
500
|
+
const finalClosingPrefix = pendingPrefix + closingPrefix;
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
node: {
|
|
504
|
+
kind: Yaml.Kind.Sequence,
|
|
505
|
+
id: randomId(),
|
|
506
|
+
prefix: "",
|
|
507
|
+
markers: emptyMarkers,
|
|
508
|
+
openingBracketPrefix: openingPrefix,
|
|
509
|
+
entries,
|
|
510
|
+
closingBracketPrefix: finalClosingPrefix,
|
|
511
|
+
anchor: undefined,
|
|
512
|
+
tag: undefined
|
|
513
|
+
},
|
|
514
|
+
trailing
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
private convertFlowMappingEntry(item: CstCollectionItem, pendingPrefix: string): {entry: Yaml.MappingEntry, trailingContent: string} {
|
|
520
|
+
const prefix = pendingPrefix + this.concatenateSources(item.start || []);
|
|
521
|
+
|
|
522
|
+
let keyTrailing = "";
|
|
523
|
+
let key: Yaml.YamlKey;
|
|
524
|
+
if (item.key) {
|
|
525
|
+
const keyResult = this.convertTokenWithTrailing(item.key);
|
|
526
|
+
key = keyResult.node as Yaml.YamlKey;
|
|
527
|
+
keyTrailing = keyResult.trailing;
|
|
528
|
+
} else {
|
|
529
|
+
key = this.createEmptyScalar();
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
let beforeMappingValueIndicator = keyTrailing;
|
|
533
|
+
let afterColonSpace = "";
|
|
534
|
+
let seenColon = false;
|
|
535
|
+
|
|
536
|
+
if (item.sep) {
|
|
537
|
+
for (const token of item.sep) {
|
|
538
|
+
if (token.type === 'map-value-ind') {
|
|
539
|
+
seenColon = true;
|
|
540
|
+
} else if (seenColon) {
|
|
541
|
+
afterColonSpace += token.source;
|
|
542
|
+
} else if (token.type === 'comma') {
|
|
543
|
+
// This is the comma after the entry, not before
|
|
544
|
+
// Skip it as we handle comma separately
|
|
545
|
+
} else {
|
|
546
|
+
beforeMappingValueIndicator += token.source;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
let value: Yaml.Block;
|
|
552
|
+
let valueTrailing = "";
|
|
553
|
+
if (item.value) {
|
|
554
|
+
const valueResult = this.convertTokenWithTrailing(item.value);
|
|
555
|
+
value = valueResult.node as Yaml.Block;
|
|
556
|
+
valueTrailing = valueResult.trailing;
|
|
557
|
+
value = {...value, prefix: afterColonSpace + value.prefix} as Yaml.Block;
|
|
558
|
+
} else {
|
|
559
|
+
value = this.createEmptyScalar(afterColonSpace);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return {
|
|
563
|
+
entry: {
|
|
564
|
+
kind: Yaml.Kind.MappingEntry,
|
|
565
|
+
id: randomId(),
|
|
566
|
+
prefix,
|
|
567
|
+
markers: emptyMarkers,
|
|
568
|
+
key,
|
|
569
|
+
beforeMappingValueIndicator,
|
|
570
|
+
value
|
|
571
|
+
},
|
|
572
|
+
trailingContent: valueTrailing
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
private convertFlowSequenceEntry(item: CstCollectionItem, isLast: boolean, pendingPrefix: string): {entry: Yaml.SequenceEntry, trailingContent: string} {
|
|
577
|
+
// Start tokens may include comma from previous item
|
|
578
|
+
let prefix = pendingPrefix;
|
|
579
|
+
let hasComma = false;
|
|
580
|
+
|
|
581
|
+
for (const token of item.start || []) {
|
|
582
|
+
if (token.type === 'comma') {
|
|
583
|
+
hasComma = true;
|
|
584
|
+
} else {
|
|
585
|
+
prefix += token.source;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
let block: Yaml.Block;
|
|
590
|
+
let trailing = "";
|
|
591
|
+
|
|
592
|
+
if (item.value) {
|
|
593
|
+
const valueResult = this.convertTokenWithTrailing(item.value);
|
|
594
|
+
block = valueResult.node as Yaml.Block;
|
|
595
|
+
trailing = valueResult.trailing;
|
|
596
|
+
} else if (item.key !== undefined) {
|
|
597
|
+
// Flow sequence can contain mapping entries: [a: 1, b: 2]
|
|
598
|
+
const entryResult = this.convertFlowMappingEntry(item, "");
|
|
599
|
+
block = {
|
|
600
|
+
kind: Yaml.Kind.Mapping,
|
|
601
|
+
id: randomId(),
|
|
602
|
+
prefix: "",
|
|
603
|
+
markers: emptyMarkers,
|
|
604
|
+
openingBracePrefix: undefined,
|
|
605
|
+
entries: [entryResult.entry],
|
|
606
|
+
closingBracePrefix: undefined,
|
|
607
|
+
anchor: undefined,
|
|
608
|
+
tag: undefined
|
|
609
|
+
};
|
|
610
|
+
trailing = entryResult.trailingContent;
|
|
611
|
+
} else {
|
|
612
|
+
block = this.createEmptyScalar();
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Trailing comma prefix captures space after value before comma
|
|
616
|
+
let trailingCommaPrefix: string | undefined;
|
|
617
|
+
if (!isLast) {
|
|
618
|
+
trailingCommaPrefix = trailing;
|
|
619
|
+
trailing = "";
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return {
|
|
623
|
+
entry: {
|
|
624
|
+
kind: Yaml.Kind.SequenceEntry,
|
|
625
|
+
id: randomId(),
|
|
626
|
+
prefix,
|
|
627
|
+
markers: emptyMarkers,
|
|
628
|
+
block,
|
|
629
|
+
dash: false, // Flow sequences don't use dashes
|
|
630
|
+
trailingCommaPrefix
|
|
631
|
+
},
|
|
632
|
+
trailingContent: trailing
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
private convertFlowScalarWithTrailing(cst: CstFlowScalar): {node: Yaml.Scalar, trailing: string} {
|
|
637
|
+
const resolved = CST.resolveAsScalar(cst);
|
|
638
|
+
const value = resolved?.value ?? "";
|
|
639
|
+
|
|
640
|
+
// Determine scalar style
|
|
641
|
+
let style: Yaml.ScalarStyle;
|
|
642
|
+
switch (cst.type) {
|
|
643
|
+
case 'single-quoted-scalar':
|
|
644
|
+
style = Yaml.ScalarStyle.SINGLE_QUOTED;
|
|
645
|
+
break;
|
|
646
|
+
case 'double-quoted-scalar':
|
|
647
|
+
style = Yaml.ScalarStyle.DOUBLE_QUOTED;
|
|
648
|
+
break;
|
|
649
|
+
default:
|
|
650
|
+
style = Yaml.ScalarStyle.PLAIN;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// End tokens contain trailing whitespace/newlines
|
|
654
|
+
const trailing = this.concatenateSources(cst.end || []);
|
|
655
|
+
|
|
656
|
+
return {
|
|
657
|
+
node: {
|
|
658
|
+
kind: Yaml.Kind.Scalar,
|
|
659
|
+
id: randomId(),
|
|
660
|
+
prefix: "",
|
|
661
|
+
markers: emptyMarkers,
|
|
662
|
+
style,
|
|
663
|
+
anchor: undefined,
|
|
664
|
+
tag: undefined,
|
|
665
|
+
value
|
|
666
|
+
},
|
|
667
|
+
trailing
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
private convertBlockScalarWithTrailing(cst: CstBlockScalar): {node: Yaml.Scalar, trailing: string} {
|
|
672
|
+
// Use CST.stringify to get the exact original source including header
|
|
673
|
+
const fullSource = CST.stringify(cst);
|
|
674
|
+
|
|
675
|
+
// Determine style from the first character (| or >)
|
|
676
|
+
const headerChar = fullSource.charAt(0);
|
|
677
|
+
const style = headerChar === '|' ? Yaml.ScalarStyle.LITERAL : Yaml.ScalarStyle.FOLDED;
|
|
678
|
+
|
|
679
|
+
// The value is everything after the header indicator (| or >)
|
|
680
|
+
const value = fullSource.substring(1);
|
|
681
|
+
|
|
682
|
+
// Extract anchor and tag from props if present
|
|
683
|
+
let anchor: Yaml.Anchor | undefined;
|
|
684
|
+
let tag: Yaml.Tag | undefined;
|
|
685
|
+
for (const prop of cst.props || []) {
|
|
686
|
+
if ('type' in prop) {
|
|
687
|
+
const propTyped = prop as CstSourceToken;
|
|
688
|
+
if (propTyped.type === 'anchor') {
|
|
689
|
+
anchor = this.parseAnchorToken(propTyped.source, "");
|
|
690
|
+
} else if (propTyped.type === 'tag') {
|
|
691
|
+
tag = this.parseTagToken(propTyped.source, "");
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
return {
|
|
697
|
+
node: {
|
|
698
|
+
kind: Yaml.Kind.Scalar,
|
|
699
|
+
id: randomId(),
|
|
700
|
+
prefix: "",
|
|
701
|
+
markers: emptyMarkers,
|
|
702
|
+
style,
|
|
703
|
+
anchor,
|
|
704
|
+
tag,
|
|
705
|
+
value
|
|
706
|
+
},
|
|
707
|
+
trailing: "" // Block scalars don't have separate end tokens
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
private convertAliasWithTrailing(cst: CstFlowScalar): {node: Yaml.Alias, trailing: string} {
|
|
712
|
+
// Alias source is like "*anchorName"
|
|
713
|
+
const key = cst.source.substring(1); // Remove the *
|
|
714
|
+
|
|
715
|
+
const anchor: Yaml.Anchor = {
|
|
716
|
+
kind: Yaml.Kind.Anchor,
|
|
717
|
+
id: randomId(),
|
|
718
|
+
prefix: "",
|
|
719
|
+
markers: emptyMarkers,
|
|
720
|
+
postfix: "",
|
|
721
|
+
key
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
// End tokens contain trailing whitespace
|
|
725
|
+
const trailing = this.concatenateSources(cst.end || []);
|
|
726
|
+
|
|
727
|
+
return {
|
|
728
|
+
node: {
|
|
729
|
+
kind: Yaml.Kind.Alias,
|
|
730
|
+
id: randomId(),
|
|
731
|
+
prefix: "",
|
|
732
|
+
markers: emptyMarkers,
|
|
733
|
+
anchor
|
|
734
|
+
},
|
|
735
|
+
trailing
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
private parseAnchorToken(source: string, prefix: string): Yaml.Anchor {
|
|
740
|
+
// Anchor source is like "&anchorName"
|
|
741
|
+
const key = source.substring(1); // Remove the &
|
|
742
|
+
|
|
743
|
+
return {
|
|
744
|
+
kind: Yaml.Kind.Anchor,
|
|
745
|
+
id: randomId(),
|
|
746
|
+
prefix,
|
|
747
|
+
markers: emptyMarkers,
|
|
748
|
+
postfix: "",
|
|
749
|
+
key
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
private parseTagToken(source: string, prefix: string): Yaml.Tag {
|
|
754
|
+
return this.parseTagTokenWithSuffix(source, prefix, [], 0);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
private parseTagTokenWithSuffix(source: string, prefix: string, remainingTokens: CstSourceToken[], startIndex: number): Yaml.Tag {
|
|
758
|
+
let name: string;
|
|
759
|
+
let tagKind: Yaml.TagKind;
|
|
760
|
+
|
|
761
|
+
if (source.startsWith('!<') && source.endsWith('>')) {
|
|
762
|
+
// Explicit global tag: !<tag:yaml.org,2002:str>
|
|
763
|
+
name = source.substring(2, source.length - 1);
|
|
764
|
+
tagKind = Yaml.TagKind.EXPLICIT_GLOBAL;
|
|
765
|
+
} else if (source.startsWith('!!')) {
|
|
766
|
+
// Implicit global tag: !!str
|
|
767
|
+
name = source.substring(2);
|
|
768
|
+
tagKind = Yaml.TagKind.IMPLICIT_GLOBAL;
|
|
769
|
+
} else {
|
|
770
|
+
// Local tag: !custom
|
|
771
|
+
name = source.substring(1);
|
|
772
|
+
tagKind = Yaml.TagKind.LOCAL;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Collect remaining tokens as suffix (whitespace after the tag)
|
|
776
|
+
let suffix = "";
|
|
777
|
+
for (let i = startIndex; i < remainingTokens.length; i++) {
|
|
778
|
+
suffix += remainingTokens[i].source;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
return {
|
|
782
|
+
kind: Yaml.Kind.Tag,
|
|
783
|
+
id: randomId(),
|
|
784
|
+
prefix,
|
|
785
|
+
markers: emptyMarkers,
|
|
786
|
+
name,
|
|
787
|
+
suffix,
|
|
788
|
+
tagKind
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
private createEmptyScalar(prefix: string = ""): Yaml.Scalar {
|
|
793
|
+
return {
|
|
794
|
+
kind: Yaml.Kind.Scalar,
|
|
795
|
+
id: randomId(),
|
|
796
|
+
prefix,
|
|
797
|
+
markers: emptyMarkers,
|
|
798
|
+
style: Yaml.ScalarStyle.PLAIN,
|
|
799
|
+
anchor: undefined,
|
|
800
|
+
tag: undefined,
|
|
801
|
+
value: ""
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Prepends whitespace to a value node in the appropriate location based on its type.
|
|
807
|
+
* - For Scalars and Aliases: prepend to prefix
|
|
808
|
+
* - For flow Mappings (with braces): prepend to openingBracePrefix
|
|
809
|
+
* - For flow Sequences (with brackets): prepend to openingBracketPrefix
|
|
810
|
+
* - For block Mappings/Sequences: prepend to first entry's prefix
|
|
811
|
+
*/
|
|
812
|
+
private prependWhitespaceToValue(value: Yaml.Block, whitespace: string): Yaml.Block {
|
|
813
|
+
if (value.kind === Yaml.Kind.Scalar || value.kind === Yaml.Kind.Alias) {
|
|
814
|
+
return {...value, prefix: whitespace + value.prefix};
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if (value.kind === Yaml.Kind.Mapping) {
|
|
818
|
+
const mapping = value as Yaml.Mapping;
|
|
819
|
+
if (mapping.openingBracePrefix !== undefined) {
|
|
820
|
+
// Flow mapping: prepend to opening brace prefix
|
|
821
|
+
return {...mapping, openingBracePrefix: whitespace + mapping.openingBracePrefix};
|
|
822
|
+
} else if (mapping.entries.length > 0) {
|
|
823
|
+
// Block mapping: prepend to first entry's prefix
|
|
824
|
+
const firstEntry = mapping.entries[0];
|
|
825
|
+
const updatedFirstEntry = {...firstEntry, prefix: whitespace + firstEntry.prefix};
|
|
826
|
+
return {...mapping, entries: [updatedFirstEntry, ...mapping.entries.slice(1)]};
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
if (value.kind === Yaml.Kind.Sequence) {
|
|
831
|
+
const sequence = value as Yaml.Sequence;
|
|
832
|
+
if (sequence.openingBracketPrefix !== undefined) {
|
|
833
|
+
// Flow sequence: prepend to opening bracket prefix
|
|
834
|
+
return {...sequence, openingBracketPrefix: whitespace + sequence.openingBracketPrefix};
|
|
835
|
+
} else if (sequence.entries.length > 0) {
|
|
836
|
+
// Block sequence: prepend to first entry's prefix
|
|
837
|
+
const firstEntry = sequence.entries[0];
|
|
838
|
+
const updatedFirstEntry = {...firstEntry, prefix: whitespace + firstEntry.prefix};
|
|
839
|
+
return {...sequence, entries: [updatedFirstEntry, ...sequence.entries.slice(1)]};
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// Fallback: prepend to prefix (shouldn't normally reach here)
|
|
844
|
+
return {...value, prefix: whitespace + value.prefix} as Yaml.Block;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
private concatenateSources(tokens: CstSourceToken[]): string {
|
|
848
|
+
return tokens.map(t => t.source).join('');
|
|
849
|
+
}
|
|
850
|
+
}
|