@speclynx/apidom-parser-adapter-yaml-1-2 5.0.2 → 5.1.1
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/CHANGELOG.md +10 -0
- package/dist/apidom-parser-adapter-yaml-1-2.browser.js +162 -71
- package/dist/apidom-parser-adapter-yaml-1-2.browser.min.js +1 -1
- package/package.json +6 -6
- package/src/tree-sitter/lexical-analysis/index.cjs +1 -1
- package/src/tree-sitter/lexical-analysis/index.mjs +1 -1
- package/src/tree-sitter/syntactic-analysis/CstTransformer.cjs +46 -41
- package/src/tree-sitter/syntactic-analysis/CstTransformer.mjs +46 -41
- package/src/tree-sitter/syntactic-analysis/YamlAstTransformer.cjs +28 -2
- package/src/tree-sitter/syntactic-analysis/YamlAstTransformer.mjs +28 -2
|
@@ -103,8 +103,11 @@ const processChildren = (cursor, ctx, transformerMap) => {
|
|
|
103
103
|
return results;
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
-
// strip leading '#
|
|
107
|
-
|
|
106
|
+
// strip leading '#' from each line of comment text; the text after '#' is kept
|
|
107
|
+
// verbatim (including leading whitespace) so serializers can round-trip it exactly;
|
|
108
|
+
// a bare '#' becomes a single space — the yaml library's encoding for an empty
|
|
109
|
+
// comment, which its stringifier turns back into '#' (plain '' would be dropped)
|
|
110
|
+
const stripCommentHash = text => text.split('\n').map(line => line === '#' ? ' ' : line.replace(/^#/, '')).join('\n');
|
|
108
111
|
|
|
109
112
|
// find the first YamlNode in a TransformResult (which may be an array from block_node)
|
|
110
113
|
const findYamlNode = result => {
|
|
@@ -147,7 +150,10 @@ const processKeyValuePairChildren = (cursor, ctx, transformerMap) => {
|
|
|
147
150
|
if (seenValue) {
|
|
148
151
|
commentsAfterValue.push(commentText);
|
|
149
152
|
} else if (seenKey) {
|
|
150
|
-
commentsBetweenKeyValue.push(
|
|
153
|
+
commentsBetweenKeyValue.push({
|
|
154
|
+
text: commentText,
|
|
155
|
+
startLine: info.startPosition.row
|
|
156
|
+
});
|
|
151
157
|
}
|
|
152
158
|
continue;
|
|
153
159
|
}
|
|
@@ -176,6 +182,35 @@ const processKeyValuePairChildren = (cursor, ctx, transformerMap) => {
|
|
|
176
182
|
commentsAfterValue
|
|
177
183
|
};
|
|
178
184
|
};
|
|
185
|
+
|
|
186
|
+
// attach comments collected inside a pair: comments on the key's own line belong
|
|
187
|
+
// after the key (`key: # comment`), the rest before the value node; comments
|
|
188
|
+
// found after the value become the value's trailing comment
|
|
189
|
+
const attachPairComments = ({
|
|
190
|
+
key,
|
|
191
|
+
value,
|
|
192
|
+
commentsBetweenKeyValue,
|
|
193
|
+
commentsAfterValue
|
|
194
|
+
}) => {
|
|
195
|
+
if (commentsBetweenKeyValue.length > 0) {
|
|
196
|
+
const keyNode = findYamlNode(key);
|
|
197
|
+
const valueNode = findYamlNode(value);
|
|
198
|
+
const onKeyLine = commentsBetweenKeyValue.filter(comment => keyNode !== null && comment.startLine === keyNode.endLine);
|
|
199
|
+
const onOwnLine = commentsBetweenKeyValue.filter(comment => !onKeyLine.includes(comment));
|
|
200
|
+
if (onKeyLine.length > 0 && keyNode) {
|
|
201
|
+
keyNode.comment = onKeyLine.map(comment => comment.text).join('\n');
|
|
202
|
+
}
|
|
203
|
+
if (onOwnLine.length > 0 && valueNode) {
|
|
204
|
+
valueNode.commentBefore = onOwnLine.map(comment => comment.text).join('\n');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (commentsAfterValue.length > 0) {
|
|
208
|
+
const valueNode = findYamlNode(value);
|
|
209
|
+
if (valueNode) {
|
|
210
|
+
valueNode.comment = commentsAfterValue.join('\n');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
179
214
|
const transform = (cursor, ctx, transformerMap, siblings = {}) => {
|
|
180
215
|
const info = getCursorInfo(cursor);
|
|
181
216
|
|
|
@@ -347,13 +382,12 @@ const createTransformers = transformerMap => ({
|
|
|
347
382
|
},
|
|
348
383
|
block_mapping_pair(cursor, ctx) {
|
|
349
384
|
const info = getCursorInfo(cursor);
|
|
385
|
+
const pairResult = processKeyValuePairChildren(cursor, ctx, transformerMap);
|
|
350
386
|
const {
|
|
351
387
|
key,
|
|
352
388
|
value,
|
|
353
|
-
errors
|
|
354
|
-
|
|
355
|
-
commentsAfterValue
|
|
356
|
-
} = processKeyValuePairChildren(cursor, ctx, transformerMap);
|
|
389
|
+
errors
|
|
390
|
+
} = pairResult;
|
|
357
391
|
const children = [];
|
|
358
392
|
|
|
359
393
|
// Handle empty key
|
|
@@ -399,21 +433,7 @@ const createTransformers = transformerMap => ({
|
|
|
399
433
|
} else {
|
|
400
434
|
children.push(value);
|
|
401
435
|
}
|
|
402
|
-
|
|
403
|
-
// attach comments found between key and value to the value node
|
|
404
|
-
if (commentsBetweenKeyValue.length > 0) {
|
|
405
|
-
const valueNode = findYamlNode(value);
|
|
406
|
-
if (valueNode) {
|
|
407
|
-
valueNode.commentBefore = commentsBetweenKeyValue.join('\n');
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
// attach comments found after value to the value node
|
|
411
|
-
if (commentsAfterValue.length > 0) {
|
|
412
|
-
const valueNode = findYamlNode(value);
|
|
413
|
-
if (valueNode) {
|
|
414
|
-
valueNode.comment = commentsAfterValue.join('\n');
|
|
415
|
-
}
|
|
416
|
-
}
|
|
436
|
+
attachPairComments(pairResult);
|
|
417
437
|
children.push(...errors);
|
|
418
438
|
return new _YamlKeyValuePair.default({
|
|
419
439
|
children: children.flat().filter(c => c !== null),
|
|
@@ -441,13 +461,12 @@ const createTransformers = transformerMap => ({
|
|
|
441
461
|
},
|
|
442
462
|
flow_pair(cursor, ctx) {
|
|
443
463
|
const info = getCursorInfo(cursor);
|
|
464
|
+
const pairResult = processKeyValuePairChildren(cursor, ctx, transformerMap);
|
|
444
465
|
const {
|
|
445
466
|
key,
|
|
446
467
|
value,
|
|
447
|
-
errors
|
|
448
|
-
|
|
449
|
-
commentsAfterValue
|
|
450
|
-
} = processKeyValuePairChildren(cursor, ctx, transformerMap);
|
|
468
|
+
errors
|
|
469
|
+
} = pairResult;
|
|
451
470
|
const children = [];
|
|
452
471
|
|
|
453
472
|
// Handle empty key
|
|
@@ -493,21 +512,7 @@ const createTransformers = transformerMap => ({
|
|
|
493
512
|
} else {
|
|
494
513
|
children.push(value);
|
|
495
514
|
}
|
|
496
|
-
|
|
497
|
-
// attach comments found between key and value to the value node
|
|
498
|
-
if (commentsBetweenKeyValue.length > 0) {
|
|
499
|
-
const valueNode = findYamlNode(value);
|
|
500
|
-
if (valueNode) {
|
|
501
|
-
valueNode.commentBefore = commentsBetweenKeyValue.join('\n');
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
// attach comments found after value to the value node
|
|
505
|
-
if (commentsAfterValue.length > 0) {
|
|
506
|
-
const valueNode = findYamlNode(value);
|
|
507
|
-
if (valueNode) {
|
|
508
|
-
valueNode.comment = commentsAfterValue.join('\n');
|
|
509
|
-
}
|
|
510
|
-
}
|
|
515
|
+
attachPairComments(pairResult);
|
|
511
516
|
children.push(...errors);
|
|
512
517
|
return new _YamlKeyValuePair.default({
|
|
513
518
|
children: children.flat().filter(c => c !== null),
|
|
@@ -95,8 +95,11 @@ const processChildren = (cursor, ctx, transformerMap) => {
|
|
|
95
95
|
return results;
|
|
96
96
|
};
|
|
97
97
|
|
|
98
|
-
// strip leading '#
|
|
99
|
-
|
|
98
|
+
// strip leading '#' from each line of comment text; the text after '#' is kept
|
|
99
|
+
// verbatim (including leading whitespace) so serializers can round-trip it exactly;
|
|
100
|
+
// a bare '#' becomes a single space — the yaml library's encoding for an empty
|
|
101
|
+
// comment, which its stringifier turns back into '#' (plain '' would be dropped)
|
|
102
|
+
const stripCommentHash = text => text.split('\n').map(line => line === '#' ? ' ' : line.replace(/^#/, '')).join('\n');
|
|
100
103
|
|
|
101
104
|
// find the first YamlNode in a TransformResult (which may be an array from block_node)
|
|
102
105
|
const findYamlNode = result => {
|
|
@@ -139,7 +142,10 @@ const processKeyValuePairChildren = (cursor, ctx, transformerMap) => {
|
|
|
139
142
|
if (seenValue) {
|
|
140
143
|
commentsAfterValue.push(commentText);
|
|
141
144
|
} else if (seenKey) {
|
|
142
|
-
commentsBetweenKeyValue.push(
|
|
145
|
+
commentsBetweenKeyValue.push({
|
|
146
|
+
text: commentText,
|
|
147
|
+
startLine: info.startPosition.row
|
|
148
|
+
});
|
|
143
149
|
}
|
|
144
150
|
continue;
|
|
145
151
|
}
|
|
@@ -168,6 +174,35 @@ const processKeyValuePairChildren = (cursor, ctx, transformerMap) => {
|
|
|
168
174
|
commentsAfterValue
|
|
169
175
|
};
|
|
170
176
|
};
|
|
177
|
+
|
|
178
|
+
// attach comments collected inside a pair: comments on the key's own line belong
|
|
179
|
+
// after the key (`key: # comment`), the rest before the value node; comments
|
|
180
|
+
// found after the value become the value's trailing comment
|
|
181
|
+
const attachPairComments = ({
|
|
182
|
+
key,
|
|
183
|
+
value,
|
|
184
|
+
commentsBetweenKeyValue,
|
|
185
|
+
commentsAfterValue
|
|
186
|
+
}) => {
|
|
187
|
+
if (commentsBetweenKeyValue.length > 0) {
|
|
188
|
+
const keyNode = findYamlNode(key);
|
|
189
|
+
const valueNode = findYamlNode(value);
|
|
190
|
+
const onKeyLine = commentsBetweenKeyValue.filter(comment => keyNode !== null && comment.startLine === keyNode.endLine);
|
|
191
|
+
const onOwnLine = commentsBetweenKeyValue.filter(comment => !onKeyLine.includes(comment));
|
|
192
|
+
if (onKeyLine.length > 0 && keyNode) {
|
|
193
|
+
keyNode.comment = onKeyLine.map(comment => comment.text).join('\n');
|
|
194
|
+
}
|
|
195
|
+
if (onOwnLine.length > 0 && valueNode) {
|
|
196
|
+
valueNode.commentBefore = onOwnLine.map(comment => comment.text).join('\n');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (commentsAfterValue.length > 0) {
|
|
200
|
+
const valueNode = findYamlNode(value);
|
|
201
|
+
if (valueNode) {
|
|
202
|
+
valueNode.comment = commentsAfterValue.join('\n');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
};
|
|
171
206
|
const transform = (cursor, ctx, transformerMap, siblings = {}) => {
|
|
172
207
|
const info = getCursorInfo(cursor);
|
|
173
208
|
|
|
@@ -339,13 +374,12 @@ const createTransformers = transformerMap => ({
|
|
|
339
374
|
},
|
|
340
375
|
block_mapping_pair(cursor, ctx) {
|
|
341
376
|
const info = getCursorInfo(cursor);
|
|
377
|
+
const pairResult = processKeyValuePairChildren(cursor, ctx, transformerMap);
|
|
342
378
|
const {
|
|
343
379
|
key,
|
|
344
380
|
value,
|
|
345
|
-
errors
|
|
346
|
-
|
|
347
|
-
commentsAfterValue
|
|
348
|
-
} = processKeyValuePairChildren(cursor, ctx, transformerMap);
|
|
381
|
+
errors
|
|
382
|
+
} = pairResult;
|
|
349
383
|
const children = [];
|
|
350
384
|
|
|
351
385
|
// Handle empty key
|
|
@@ -391,21 +425,7 @@ const createTransformers = transformerMap => ({
|
|
|
391
425
|
} else {
|
|
392
426
|
children.push(value);
|
|
393
427
|
}
|
|
394
|
-
|
|
395
|
-
// attach comments found between key and value to the value node
|
|
396
|
-
if (commentsBetweenKeyValue.length > 0) {
|
|
397
|
-
const valueNode = findYamlNode(value);
|
|
398
|
-
if (valueNode) {
|
|
399
|
-
valueNode.commentBefore = commentsBetweenKeyValue.join('\n');
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
// attach comments found after value to the value node
|
|
403
|
-
if (commentsAfterValue.length > 0) {
|
|
404
|
-
const valueNode = findYamlNode(value);
|
|
405
|
-
if (valueNode) {
|
|
406
|
-
valueNode.comment = commentsAfterValue.join('\n');
|
|
407
|
-
}
|
|
408
|
-
}
|
|
428
|
+
attachPairComments(pairResult);
|
|
409
429
|
children.push(...errors);
|
|
410
430
|
return new YamlKeyValuePair({
|
|
411
431
|
children: children.flat().filter(c => c !== null),
|
|
@@ -433,13 +453,12 @@ const createTransformers = transformerMap => ({
|
|
|
433
453
|
},
|
|
434
454
|
flow_pair(cursor, ctx) {
|
|
435
455
|
const info = getCursorInfo(cursor);
|
|
456
|
+
const pairResult = processKeyValuePairChildren(cursor, ctx, transformerMap);
|
|
436
457
|
const {
|
|
437
458
|
key,
|
|
438
459
|
value,
|
|
439
|
-
errors
|
|
440
|
-
|
|
441
|
-
commentsAfterValue
|
|
442
|
-
} = processKeyValuePairChildren(cursor, ctx, transformerMap);
|
|
460
|
+
errors
|
|
461
|
+
} = pairResult;
|
|
443
462
|
const children = [];
|
|
444
463
|
|
|
445
464
|
// Handle empty key
|
|
@@ -485,21 +504,7 @@ const createTransformers = transformerMap => ({
|
|
|
485
504
|
} else {
|
|
486
505
|
children.push(value);
|
|
487
506
|
}
|
|
488
|
-
|
|
489
|
-
// attach comments found between key and value to the value node
|
|
490
|
-
if (commentsBetweenKeyValue.length > 0) {
|
|
491
|
-
const valueNode = findYamlNode(value);
|
|
492
|
-
if (valueNode) {
|
|
493
|
-
valueNode.commentBefore = commentsBetweenKeyValue.join('\n');
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
// attach comments found after value to the value node
|
|
497
|
-
if (commentsAfterValue.length > 0) {
|
|
498
|
-
const valueNode = findYamlNode(value);
|
|
499
|
-
if (valueNode) {
|
|
500
|
-
valueNode.comment = commentsAfterValue.join('\n');
|
|
501
|
-
}
|
|
502
|
-
}
|
|
507
|
+
attachPairComments(pairResult);
|
|
503
508
|
children.push(...errors);
|
|
504
509
|
return new YamlKeyValuePair({
|
|
505
510
|
children: children.flat().filter(c => c !== null),
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.transformYamlAstToApiDOM = exports.default = void 0;
|
|
7
7
|
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
8
|
+
var _predicates = require("./ast/nodes/predicates.cjs");
|
|
8
9
|
var _YamlStyle = require("./ast/nodes/YamlStyle.cjs");
|
|
9
10
|
// Transform context passed through transformation
|
|
10
11
|
|
|
@@ -39,8 +40,11 @@ const detectIndent = node => {
|
|
|
39
40
|
return 2;
|
|
40
41
|
};
|
|
41
42
|
|
|
42
|
-
// strip leading '#' from each line of comment text; yaml library adds '#' during
|
|
43
|
-
|
|
43
|
+
// strip leading '#' from each line of comment text; yaml library adds '#' during
|
|
44
|
+
// stringification; a bare '#' becomes a single space — the yaml library's encoding
|
|
45
|
+
// for an empty comment, which its stringifier turns back into '#' (plain '' would
|
|
46
|
+
// be dropped)
|
|
47
|
+
const stripCommentHash = text => text.split('\n').map(line => line === '#' ? ' ' : line.replace(/^#/, '')).join('\n');
|
|
44
48
|
|
|
45
49
|
// collect comment texts from raw AST children, indexed by position relative to non-comment siblings
|
|
46
50
|
|
|
@@ -110,6 +114,7 @@ const setYamlStyleProp = (element, key, value) => {
|
|
|
110
114
|
yaml[key] = value;
|
|
111
115
|
element.style.yaml = yaml;
|
|
112
116
|
};
|
|
117
|
+
const getYamlStyleProp = (element, key) => element.style?.yaml?.[key];
|
|
113
118
|
|
|
114
119
|
// apply collected comments to transformed ApiDOM elements
|
|
115
120
|
const applyComments = (elements, comments) => {
|
|
@@ -240,6 +245,17 @@ const transformChildren = (children, ctx) => {
|
|
|
240
245
|
return results;
|
|
241
246
|
};
|
|
242
247
|
|
|
248
|
+
// stream comments that precede the first document, joined; null when there are none
|
|
249
|
+
// (stream children are in source order, so everything before the document node qualifies)
|
|
250
|
+
const collectPreDocumentComments = node => {
|
|
251
|
+
const texts = [];
|
|
252
|
+
for (const child of node.children) {
|
|
253
|
+
if ((0, _predicates.isDocument)(child)) break;
|
|
254
|
+
if ((0, _predicates.isComment)(child)) texts.push(stripCommentHash(child.content));
|
|
255
|
+
}
|
|
256
|
+
return texts.length > 0 ? texts.join('\n') : null;
|
|
257
|
+
};
|
|
258
|
+
|
|
243
259
|
// Stream: Wraps transformed children in ParseResultElement
|
|
244
260
|
const transformStream = (node, ctx) => {
|
|
245
261
|
// detect indent from the stream structure (only needed for style preservation)
|
|
@@ -261,6 +277,16 @@ const transformStream = (node, ctx) => {
|
|
|
261
277
|
if (elements.length > 0) {
|
|
262
278
|
const resultElement = elements[0];
|
|
263
279
|
resultElement.classes.push('result');
|
|
280
|
+
|
|
281
|
+
// stream comments before the first document belong above the result element;
|
|
282
|
+
// capture them as commentBefore so style round-trips can re-emit them
|
|
283
|
+
if (ctx.style) {
|
|
284
|
+
const preDocumentComments = collectPreDocumentComments(node);
|
|
285
|
+
if (preDocumentComments !== null) {
|
|
286
|
+
const existing = getYamlStyleProp(resultElement, 'commentBefore');
|
|
287
|
+
setYamlStyleProp(resultElement, 'commentBefore', typeof existing === 'string' ? `${preDocumentComments}\n${existing}` : preDocumentComments);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
264
290
|
}
|
|
265
291
|
|
|
266
292
|
// Add collected annotations
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ParseResultElement, AnnotationElement, CommentElement, MemberElement, ObjectElement, ArrayElement, isPrimitiveElement, Namespace, SourceMapElement } from '@speclynx/apidom-datamodel';
|
|
2
|
+
import { isComment, isDocument } from "./ast/nodes/predicates.mjs";
|
|
2
3
|
import { YamlStyle, YamlStyleGroup } from "./ast/nodes/YamlStyle.mjs"; // Transform context passed through transformation
|
|
3
4
|
// Node with type property and flat position properties
|
|
4
5
|
// Helper to add source map to element
|
|
@@ -30,8 +31,11 @@ const detectIndent = node => {
|
|
|
30
31
|
return 2;
|
|
31
32
|
};
|
|
32
33
|
|
|
33
|
-
// strip leading '#' from each line of comment text; yaml library adds '#' during
|
|
34
|
-
|
|
34
|
+
// strip leading '#' from each line of comment text; yaml library adds '#' during
|
|
35
|
+
// stringification; a bare '#' becomes a single space — the yaml library's encoding
|
|
36
|
+
// for an empty comment, which its stringifier turns back into '#' (plain '' would
|
|
37
|
+
// be dropped)
|
|
38
|
+
const stripCommentHash = text => text.split('\n').map(line => line === '#' ? ' ' : line.replace(/^#/, '')).join('\n');
|
|
35
39
|
|
|
36
40
|
// collect comment texts from raw AST children, indexed by position relative to non-comment siblings
|
|
37
41
|
|
|
@@ -101,6 +105,7 @@ const setYamlStyleProp = (element, key, value) => {
|
|
|
101
105
|
yaml[key] = value;
|
|
102
106
|
element.style.yaml = yaml;
|
|
103
107
|
};
|
|
108
|
+
const getYamlStyleProp = (element, key) => element.style?.yaml?.[key];
|
|
104
109
|
|
|
105
110
|
// apply collected comments to transformed ApiDOM elements
|
|
106
111
|
const applyComments = (elements, comments) => {
|
|
@@ -231,6 +236,17 @@ const transformChildren = (children, ctx) => {
|
|
|
231
236
|
return results;
|
|
232
237
|
};
|
|
233
238
|
|
|
239
|
+
// stream comments that precede the first document, joined; null when there are none
|
|
240
|
+
// (stream children are in source order, so everything before the document node qualifies)
|
|
241
|
+
const collectPreDocumentComments = node => {
|
|
242
|
+
const texts = [];
|
|
243
|
+
for (const child of node.children) {
|
|
244
|
+
if (isDocument(child)) break;
|
|
245
|
+
if (isComment(child)) texts.push(stripCommentHash(child.content));
|
|
246
|
+
}
|
|
247
|
+
return texts.length > 0 ? texts.join('\n') : null;
|
|
248
|
+
};
|
|
249
|
+
|
|
234
250
|
// Stream: Wraps transformed children in ParseResultElement
|
|
235
251
|
const transformStream = (node, ctx) => {
|
|
236
252
|
// detect indent from the stream structure (only needed for style preservation)
|
|
@@ -252,6 +268,16 @@ const transformStream = (node, ctx) => {
|
|
|
252
268
|
if (elements.length > 0) {
|
|
253
269
|
const resultElement = elements[0];
|
|
254
270
|
resultElement.classes.push('result');
|
|
271
|
+
|
|
272
|
+
// stream comments before the first document belong above the result element;
|
|
273
|
+
// capture them as commentBefore so style round-trips can re-emit them
|
|
274
|
+
if (ctx.style) {
|
|
275
|
+
const preDocumentComments = collectPreDocumentComments(node);
|
|
276
|
+
if (preDocumentComments !== null) {
|
|
277
|
+
const existing = getYamlStyleProp(resultElement, 'commentBefore');
|
|
278
|
+
setYamlStyleProp(resultElement, 'commentBefore', typeof existing === 'string' ? `${preDocumentComments}\n${existing}` : preDocumentComments);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
255
281
|
}
|
|
256
282
|
|
|
257
283
|
// Add collected annotations
|