Skip to content

Commit 0892d17

Browse files
committed
feat: rewrite the mappingItem logic
* feat: indent sequence in mapping * feat(doc): add ifBreak#groupId and group#id
1 parent 7ca4e69 commit 0892d17

11 files changed

Lines changed: 462 additions & 362 deletions

File tree

src/doc/doc-builders.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ function group(contents, opts) {
5050

5151
return {
5252
type: "group",
53+
id: opts.id,
5354
contents: contents,
5455
break: !!opts.shouldBreak,
5556
expandedStates: opts.expandedStates
@@ -83,7 +84,9 @@ function fill(parts) {
8384
return { type: "fill", parts };
8485
}
8586

86-
function ifBreak(breakContents, flatContents) {
87+
function ifBreak(breakContents, flatContents, opts) {
88+
opts = opts || {};
89+
8790
if (process.env.NODE_ENV !== "production") {
8891
if (breakContents) {
8992
assertDoc(breakContents);
@@ -93,7 +96,12 @@ function ifBreak(breakContents, flatContents) {
9396
}
9497
}
9598

96-
return { type: "if-break", breakContents, flatContents };
99+
return {
100+
type: "if-break",
101+
breakContents,
102+
flatContents,
103+
groupId: opts.groupId
104+
};
97105
}
98106

99107
function lineSuffix(contents) {

src/doc/doc-printer.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
const util = require("../common/util");
44
const { concat, fill, cursor } = require("./doc-builders");
55

6+
/** @type {{[groupId: PropertyKey]: MODE}} */
7+
let groupModeMap;
8+
69
const MODE_BREAK = 1;
710
const MODE_FLAT = 2;
811

@@ -150,26 +153,31 @@ function fits(next, restCommands, width, options, mustBeFlat) {
150153
}
151154
cmds.push([ind, doc.break ? MODE_BREAK : mode, doc.contents]);
152155

156+
if (doc.id) {
157+
groupModeMap[doc.id] = cmds[cmds.length - 1][1];
158+
}
153159
break;
154160
case "fill":
155161
for (let i = doc.parts.length - 1; i >= 0; i--) {
156162
cmds.push([ind, mode, doc.parts[i]]);
157163
}
158164

159165
break;
160-
case "if-break":
161-
if (mode === MODE_BREAK) {
166+
case "if-break": {
167+
const groupMode = doc.groupId ? groupModeMap[doc.groupId] : mode;
168+
if (groupMode === MODE_BREAK) {
162169
if (doc.breakContents) {
163170
cmds.push([ind, mode, doc.breakContents]);
164171
}
165172
}
166-
if (mode === MODE_FLAT) {
173+
if (groupMode === MODE_FLAT) {
167174
if (doc.flatContents) {
168175
cmds.push([ind, mode, doc.flatContents]);
169176
}
170177
}
171178

172179
break;
180+
}
173181
case "line":
174182
switch (mode) {
175183
// fallthrough
@@ -194,6 +202,8 @@ function fits(next, restCommands, width, options, mustBeFlat) {
194202
}
195203

196204
function printDocToString(doc, options) {
205+
groupModeMap = {};
206+
197207
const width = options.printWidth;
198208
const newLine = options.newLine || "\n";
199209
let pos = 0;
@@ -299,6 +309,10 @@ function printDocToString(doc, options) {
299309
break;
300310
}
301311
}
312+
313+
if (doc.id) {
314+
groupModeMap[doc.id] = cmds[cmds.length - 1][1];
315+
}
302316
break;
303317
// Fills each line with as much code as possible before moving to a new
304318
// line with the same indentation.
@@ -395,19 +409,21 @@ function printDocToString(doc, options) {
395409
}
396410
break;
397411
}
398-
case "if-break":
399-
if (mode === MODE_BREAK) {
412+
case "if-break": {
413+
const groupMode = doc.groupId ? groupModeMap[doc.groupId] : mode;
414+
if (groupMode === MODE_BREAK) {
400415
if (doc.breakContents) {
401416
cmds.push([ind, mode, doc.breakContents]);
402417
}
403418
}
404-
if (mode === MODE_FLAT) {
419+
if (groupMode === MODE_FLAT) {
405420
if (doc.flatContents) {
406421
cmds.push([ind, mode, doc.flatContents]);
407422
}
408423
}
409424

410425
break;
426+
}
411427
case "line-suffix":
412428
lineSuffix.push([ind, mode, doc.contents]);
413429
break;

src/language-yaml/printer-yaml.js

Lines changed: 80 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const {
1818
} = require("./utils");
1919
const docBuilders = require("../doc").builders;
2020
const {
21+
conditionalGroup,
2122
breakParent,
2223
concat,
2324
dedent,
@@ -58,44 +59,15 @@ function genericPrint(path, options, print) {
5859
: "";
5960

6061
return concat([
61-
hasLeadingComments(node)
62-
? concat([
63-
(node.type === "sequence" || node.type === "mapping") &&
64-
(parentParentNode =>
65-
parentParentNode.type === "mappingItem" &&
66-
isImplicitMappingItem(parentParentNode, path.getParentNode(2)))(
67-
path.getParentNode(1)
68-
) &&
69-
!(tag || anchor)
70-
? hardline
71-
: "",
72-
join(hardline, path.map(print, "leadingComments")),
73-
hardline
74-
])
62+
node.type !== "mappingValue" && hasLeadingComments(node)
63+
? concat([join(hardline, path.map(print, "leadingComments")), hardline])
7564
: "",
7665
tag,
7766
tag && anchor ? " " : "",
7867
anchor,
7968
(node.type === "sequence" || node.type === "mapping") &&
8069
node.middleComments.length === 0
81-
? tag ||
82-
anchor ||
83-
(parentNode.type !== "documentBody" &&
84-
parentNode.type !== "sequenceItem" &&
85-
node.leadingComments.length === 0 &&
86-
!(
87-
parentNode.type === "mappingValue" &&
88-
(parentParentNode =>
89-
parentParentNode.type === "mappingItem" &&
90-
hasTrailingComments(parentParentNode.key.node))(
91-
path.getParentNode(1)
92-
)
93-
) &&
94-
!(parentParentNode =>
95-
parentParentNode.type === "mappingItem" &&
96-
!isImplicitMappingItem(parentParentNode, path.getParentNode(2)))(
97-
path.getParentNode(1)
98-
))
70+
? tag || anchor
9971
? hardline
10072
: ""
10173
: tag || anchor
@@ -118,9 +90,9 @@ function genericPrint(path, options, print) {
11890
? lineSuffix(
11991
concat([
12092
" ",
121-
isSingleLineNode(node) &&
122-
parentNode.type === "mappingValue" &&
123-
path.getParentNode(1).type === "mappingItem"
93+
parentNode.type === "mappingKey" &&
94+
path.getParentNode(2).type === "mapping" &&
95+
isInlineNode(node)
12496
? ""
12597
: breakParent,
12698
join(hardline, path.map(print, "trailingComments"))
@@ -281,50 +253,84 @@ function _print(node, parentNode, path, options, print) {
281253
case "sequenceItem":
282254
return concat(["- ", align(2, path.call(print, "node"))]);
283255
case "mappingKey":
284-
return isImplicitMappingItem(parentNode, path.getParentNode(1))
285-
? path.call(print, "node")
286-
: concat(["? ", align(2, path.call(print, "node"))]);
256+
return path.call(print, "node");
287257
case "mappingValue":
288-
return isImplicitMappingItem(parentNode, path.getParentNode(1))
289-
? concat([
290-
concat([
291-
needsSpaceInFrontOfMappingValue(parentNode) ? " " : "",
292-
":"
293-
]),
294-
(doc => (node.node.type === "sequence" ? doc : indent(doc)))(
295-
concat([
296-
node.node.type === "null"
297-
? ""
298-
: hasTrailingComments(parentNode.key.node) &&
299-
!isBlockValue(node.node)
300-
? hardline
301-
: isInlineNode(node.node)
302-
? line
303-
: " ",
304-
path.call(print, "node")
305-
])
306-
)
307-
])
308-
: concat([
309-
":",
310-
node.node.type === "null" ? "" : " ",
311-
align(2, path.call(print, "node"))
312-
]);
258+
return path.call(print, "node");
313259
case "mapping":
314260
return join(hardline, path.map(print, "children"));
315261
case "mappingItem":
316262
case "flowMappingItem": {
317-
return (node.type === "flowMappingItem" || node.type === "mappingItem") &&
318-
node.key.type === "null" &&
319-
node.value.type === "null"
320-
? concat([":", line])
321-
: join(
322-
isImplicitMappingItem(node, parentNode) ? "" : hardline,
323-
[
324-
node.key.type === "null" ? "" : path.call(print, "key"),
325-
node.value.type === "null" ? "" : path.call(print, "value")
326-
].filter(doc => doc !== "")
327-
);
263+
if (node.key.type === "null" && node.value.type === "null") {
264+
return concat([":", line]);
265+
}
266+
267+
const key = path.call(print, "key");
268+
const value = path.call(print, "value");
269+
270+
if (node.value.type === "null") {
271+
return node.type === "flowMappingItem" &&
272+
path.getParentNode().type !== "flowSequence"
273+
? key
274+
: concat(["? ", align(2, key)]);
275+
}
276+
277+
if (node.key.type === "null") {
278+
return concat([
279+
":",
280+
node.value.node.type === "null" ? "" : " ",
281+
align(2, value)
282+
]);
283+
}
284+
285+
const groupId = Symbol("mappingKey");
286+
287+
const forceExplicitKey =
288+
hasLeadingComments(node.value) ||
289+
(node.key.type !== "null" && !isInlineNode(node.key.node));
290+
return forceExplicitKey
291+
? concat([
292+
"? ",
293+
align(2, key),
294+
hardline,
295+
join(
296+
"",
297+
path
298+
.map(print, "value", "leadingComments")
299+
.map(comment => concat([comment, hardline]))
300+
),
301+
": ",
302+
align(2, value)
303+
])
304+
: conditionalGroup([
305+
concat([
306+
group(
307+
concat([ifBreak("? "), group(align(2, key), { id: groupId })])
308+
),
309+
ifBreak(
310+
concat([hardline, ": ", align(2, value)]),
311+
indent(
312+
concat([
313+
needsSpaceInFrontOfMappingValue(node) ? " " : "",
314+
":",
315+
hasLeadingComments(node.value.node) ||
316+
(parentNode.type === "mapping" &&
317+
hasTrailingComments(node.key.node) &&
318+
isInlineNode(node.value.node)) ||
319+
((node.value.node.type === "mapping" ||
320+
node.value.node.type === "sequence") &&
321+
node.value.node.tag.type === "null" &&
322+
node.value.node.anchor.type === "null")
323+
? hardline
324+
: node.value.node.type === "null"
325+
? ""
326+
: line,
327+
value
328+
])
329+
),
330+
{ groupId }
331+
)
332+
])
333+
]);
328334
}
329335
case "flowMapping":
330336
case "flowSequence": {
@@ -394,82 +400,6 @@ function _print(node, parentNode, path, options, print) {
394400
}
395401
}
396402

397-
/**
398-
* (implicit: indent)
399-
*
400-
* key:
401-
* value
402-
*
403-
* (explicit: align 2)
404-
*
405-
* ? key
406-
* : value
407-
*/
408-
function isImplicitMappingItem(node, parentNode) {
409-
/**
410-
* { : value }
411-
*/
412-
if (node.key.type === "null") {
413-
return false;
414-
}
415-
416-
/**
417-
* ? key
418-
*
419-
* [ ? key ]
420-
*
421-
* { key }
422-
*/
423-
if (node.value.type === "null") {
424-
return parentNode.type === "flowMapping";
425-
}
426-
427-
/**
428-
* ? !!tag key
429-
* : value
430-
*/
431-
if (
432-
"tag" in node.key.node &&
433-
(node.key.node.tag.type !== "null" || node.key.node.anchor.type !== "null")
434-
) {
435-
return false;
436-
}
437-
438-
/**
439-
* ? # comment
440-
* key
441-
* : value
442-
*/
443-
if (hasLeadingComments(node.key.node)) {
444-
return false;
445-
}
446-
447-
/**
448-
* ? key
449-
* # comment
450-
* : value
451-
*/
452-
if (hasLeadingComments(node.value)) {
453-
return false;
454-
}
455-
456-
return isSingleLineNode(node.key.node);
457-
}
458-
459-
function isSingleLineNode(node) {
460-
switch (node.type) {
461-
case "alias":
462-
return true;
463-
case "plain":
464-
return !node.value.includes("\n");
465-
case "quoteDouble":
466-
case "quoteSingle":
467-
return node.position.start.line === node.position.end.line;
468-
default:
469-
return false;
470-
}
471-
}
472-
473403
function isInlineNode(node) {
474404
switch (node.type) {
475405
case "plain":

0 commit comments

Comments
 (0)