prettier-plugin-multiline-arrays-2 1.0.0
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/LICENSE +3 -0
- package/LICENSE-CC0 +121 -0
- package/LICENSE-MIT +21 -0
- package/README.md +91 -0
- package/dist/augments/array.d.ts +3 -0
- package/dist/augments/array.js +21 -0
- package/dist/augments/array.test.d.ts +1 -0
- package/dist/augments/array.test.js +34 -0
- package/dist/augments/doc.d.ts +5 -0
- package/dist/augments/doc.js +30 -0
- package/dist/augments/string.d.ts +1 -0
- package/dist/augments/string.js +6 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +46 -0
- package/dist/options.d.ts +32 -0
- package/dist/options.js +71 -0
- package/dist/options.test.d.ts +1 -0
- package/dist/options.test.js +48 -0
- package/dist/plugin-marker.d.ts +1 -0
- package/dist/plugin-marker.js +2 -0
- package/dist/preprocessing.d.ts +2 -0
- package/dist/preprocessing.js +94 -0
- package/dist/printer/child-docs.d.ts +18 -0
- package/dist/printer/child-docs.js +89 -0
- package/dist/printer/comment-triggers.d.ts +25 -0
- package/dist/printer/comment-triggers.js +327 -0
- package/dist/printer/comments.d.ts +2 -0
- package/dist/printer/comments.js +34 -0
- package/dist/printer/insert-new-lines.d.ts +15 -0
- package/dist/printer/insert-new-lines.js +580 -0
- package/dist/printer/insert-new-lines.test.d.ts +1 -0
- package/dist/printer/insert-new-lines.test.js +36 -0
- package/dist/printer/leading-new-line.d.ts +7 -0
- package/dist/printer/leading-new-line.js +31 -0
- package/dist/printer/multiline-array-printer.d.ts +4 -0
- package/dist/printer/multiline-array-printer.js +42 -0
- package/dist/printer/original-printer.d.ts +3 -0
- package/dist/printer/original-printer.js +10 -0
- package/dist/printer/supported-node-types.d.ts +9 -0
- package/dist/printer/supported-node-types.js +16 -0
- package/dist/printer/trailing-comma.d.ts +6 -0
- package/dist/printer/trailing-comma.js +28 -0
- package/dist/readme-examples/formatted-with-comments.example.d.ts +3 -0
- package/dist/readme-examples/formatted-with-comments.example.js +16 -0
- package/dist/readme-examples/formatted.example.d.ts +2 -0
- package/dist/readme-examples/formatted.example.js +12 -0
- package/dist/readme-examples/not-formatted.example.d.ts +2 -0
- package/dist/readme-examples/not-formatted.example.js +6 -0
- package/dist/readme-examples/prettier-options.example.d.ts +4 -0
- package/dist/readme-examples/prettier-options.example.js +5 -0
- package/dist/test/babel-ts.test.d.ts +1 -0
- package/dist/test/babel-ts.test.js +10 -0
- package/dist/test/javascript.test.d.ts +1 -0
- package/dist/test/javascript.test.js +702 -0
- package/dist/test/json.test.d.ts +1 -0
- package/dist/test/json.test.js +403 -0
- package/dist/test/json5.test.d.ts +1 -0
- package/dist/test/json5.test.js +210 -0
- package/dist/test/prettier-config.d.ts +2 -0
- package/dist/test/prettier-config.js +12 -0
- package/dist/test/prettier-versions.mock.script.d.ts +1 -0
- package/dist/test/prettier-versions.mock.script.js +123 -0
- package/dist/test/run-tests.mock.d.ts +17 -0
- package/dist/test/run-tests.mock.js +95 -0
- package/dist/test/typescript-tests.mock.d.ts +2 -0
- package/dist/test/typescript-tests.mock.js +1575 -0
- package/dist/test/typescript.test.d.ts +1 -0
- package/dist/test/typescript.test.js +10 -0
- package/package.json +117 -0
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
import { getObjectTypedKeys, stringify, } from "@augment-vir/common";
|
|
2
|
+
import { doc } from "prettier";
|
|
3
|
+
import { isDocCommand } from "../augments/doc.js";
|
|
4
|
+
import {} from "../options.js";
|
|
5
|
+
import { walkDoc } from "./child-docs.js";
|
|
6
|
+
import { getCommentTriggers, parseNextLineCounts, } from "./comment-triggers.js";
|
|
7
|
+
import { containsLeadingNewline } from "./leading-new-line.js";
|
|
8
|
+
import { getArrayLikeElements, isArrayLikeNode, } from "./supported-node-types.js";
|
|
9
|
+
import { containsTrailingComma } from "./trailing-comma.js";
|
|
10
|
+
const nestingSyntaxOpen = "[{(`";
|
|
11
|
+
const nestingSyntaxClose = "]})`";
|
|
12
|
+
const matchingNestingSyntaxClose = {
|
|
13
|
+
"[": "]",
|
|
14
|
+
"{": "}",
|
|
15
|
+
"(": ")",
|
|
16
|
+
"`": "`",
|
|
17
|
+
};
|
|
18
|
+
const found = 'Found "[" but:';
|
|
19
|
+
function isMatchingNestingSyntaxClose({ maybeClose, openingSyntax, }) {
|
|
20
|
+
const closingSyntax = matchingNestingSyntaxClose[openingSyntax];
|
|
21
|
+
const firstCharacter = maybeClose.trimStart()[0];
|
|
22
|
+
return !!firstCharacter && closingSyntax === firstCharacter;
|
|
23
|
+
}
|
|
24
|
+
function extractIndentDoc({ maybeIndentDoc, }) {
|
|
25
|
+
if (isDocCommand(maybeIndentDoc) && maybeIndentDoc.type === "indent") {
|
|
26
|
+
return maybeIndentDoc;
|
|
27
|
+
}
|
|
28
|
+
else if (Array.isArray(maybeIndentDoc)) {
|
|
29
|
+
const firstDoc = maybeIndentDoc[0];
|
|
30
|
+
if (isDocCommand(firstDoc) && firstDoc.type === "indent") {
|
|
31
|
+
return firstDoc;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
export function insertLinesIntoArray({ inputDoc, manualWrap, lineCounts, wrapThreshold, debug, }) {
|
|
37
|
+
/**
|
|
38
|
+
* Each call to `insertLinesIntoArray` corresponds to a single array-like
|
|
39
|
+
* AST node, but its Doc can contain additional `[` characters that belong
|
|
40
|
+
* to sibling nodes (for example, the tuple type annotation on a
|
|
41
|
+
* destructuring pattern). Only the first `[` is our own array; skip the
|
|
42
|
+
* rest so we don't rewrite unrelated brackets.
|
|
43
|
+
*/
|
|
44
|
+
let hasProcessedOwnArray = false;
|
|
45
|
+
walkDoc({
|
|
46
|
+
startDoc: inputDoc,
|
|
47
|
+
debug,
|
|
48
|
+
callback: (currentDoc, parentDocs, childIndex) => {
|
|
49
|
+
const currentParent = parentDocs[0];
|
|
50
|
+
const parentDoc = currentParent?.parent;
|
|
51
|
+
if (typeof currentDoc === "string" && currentDoc.trim() === "[") {
|
|
52
|
+
if (hasProcessedOwnArray) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
const undoMutations = [];
|
|
56
|
+
let finalLineBreakExists = false;
|
|
57
|
+
function undoAllMutations() {
|
|
58
|
+
undoMutations.toReversed().forEach((undoMutation) => {
|
|
59
|
+
undoMutation();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (!Array.isArray(parentDoc)) {
|
|
63
|
+
if (debug) {
|
|
64
|
+
console.error({
|
|
65
|
+
brokenParent: parentDoc,
|
|
66
|
+
currentDoc,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
throw new Error(`${found} parentDoc is not an array.`);
|
|
70
|
+
}
|
|
71
|
+
if (debug) {
|
|
72
|
+
console.info({
|
|
73
|
+
currentDoc,
|
|
74
|
+
parentDoc,
|
|
75
|
+
});
|
|
76
|
+
console.info(stringify(parentDoc));
|
|
77
|
+
}
|
|
78
|
+
if (childIndex !== 0) {
|
|
79
|
+
/**
|
|
80
|
+
* This happens in some situations which we don't want to
|
|
81
|
+
* format in this plugin, like in type accessors:
|
|
82
|
+
*
|
|
83
|
+
* ```typescript
|
|
84
|
+
* type mockType = exampleObject["property"];
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
const maybeBreak = parentDoc[childIndex + 2];
|
|
90
|
+
if (isDocCommand(maybeBreak) &&
|
|
91
|
+
maybeBreak.type === "if-break") {
|
|
92
|
+
undoMutations.push(() => {
|
|
93
|
+
parentDoc[childIndex + 2] = maybeBreak;
|
|
94
|
+
});
|
|
95
|
+
parentDoc[childIndex + 2] = maybeBreak.breakContents;
|
|
96
|
+
}
|
|
97
|
+
const indentIndex = childIndex + 1;
|
|
98
|
+
const maybeBracketSibling = parentDoc[indentIndex] === ""
|
|
99
|
+
? parentDoc[indentIndex + 1]
|
|
100
|
+
: parentDoc[indentIndex];
|
|
101
|
+
const bracketSibling = extractIndentDoc({
|
|
102
|
+
maybeIndentDoc: maybeBracketSibling,
|
|
103
|
+
});
|
|
104
|
+
if (debug) {
|
|
105
|
+
console.info({
|
|
106
|
+
bracketSibling,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (maybeBracketSibling === "]") {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
else if (!bracketSibling) {
|
|
113
|
+
throw new Error(`${found} its sibling was not an indent Doc.: ${stringify(maybeBracketSibling)}`);
|
|
114
|
+
}
|
|
115
|
+
const indentContents = bracketSibling.contents;
|
|
116
|
+
if (debug) {
|
|
117
|
+
console.info({
|
|
118
|
+
indentContents,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (!Array.isArray(indentContents)) {
|
|
122
|
+
throw new TypeError(`${found} indent didn't have array contents.`);
|
|
123
|
+
}
|
|
124
|
+
else if (indentContents.length < 2) {
|
|
125
|
+
if (debug) {
|
|
126
|
+
console.error(stringify(indentContents));
|
|
127
|
+
}
|
|
128
|
+
throw new Error(`${found} indent contents did not have at least 2 children`);
|
|
129
|
+
}
|
|
130
|
+
const startingLine = indentContents[0];
|
|
131
|
+
if (debug) {
|
|
132
|
+
console.info({
|
|
133
|
+
firstIndentContentsChild: startingLine,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (!isDocCommand(startingLine) ||
|
|
137
|
+
startingLine.type !== "line") {
|
|
138
|
+
if (Array.isArray(startingLine)) {
|
|
139
|
+
undoAllMutations();
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
throw new TypeError(`${found} first indent child was not a line.`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
indentContents[0] = "";
|
|
147
|
+
undoMutations.push(() => {
|
|
148
|
+
indentContents[0] = startingLine;
|
|
149
|
+
});
|
|
150
|
+
const indentedContent = indentContents[1];
|
|
151
|
+
if (debug) {
|
|
152
|
+
console.info({
|
|
153
|
+
secondIndentContentsChild: indentedContent,
|
|
154
|
+
itsFirstChild: indentedContent[0],
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (!indentedContent) {
|
|
158
|
+
console.error("second indent child (indentedContent) is not defined:", {
|
|
159
|
+
indentContents,
|
|
160
|
+
indentedContent,
|
|
161
|
+
});
|
|
162
|
+
throw new Error(`${found} second indent child is not a fill.`);
|
|
163
|
+
}
|
|
164
|
+
else if (!Array.isArray(indentedContent) &&
|
|
165
|
+
!(isDocCommand(indentedContent) &&
|
|
166
|
+
indentedContent.type === "fill")) {
|
|
167
|
+
console.error("second indent child (indentCode) is not a fill doc or an array:", {
|
|
168
|
+
indentContents,
|
|
169
|
+
indentCode: indentedContent,
|
|
170
|
+
});
|
|
171
|
+
throw new Error(`${found} second indent child is not a fill doc or an array.`);
|
|
172
|
+
}
|
|
173
|
+
else if (Array.isArray(indentedContent)
|
|
174
|
+
? indentedContent.length === 0
|
|
175
|
+
: indentedContent.parts.length === 0) {
|
|
176
|
+
throw new Error(`${found} indentedContent has no length.`);
|
|
177
|
+
}
|
|
178
|
+
// lineIndex is 0 indexed
|
|
179
|
+
let lineIndex = 0;
|
|
180
|
+
// columnCount is 1 indexed
|
|
181
|
+
let columnCount = 1;
|
|
182
|
+
if (debug) {
|
|
183
|
+
console.info(">>>>>>>>>>>>>> Walking children for commas");
|
|
184
|
+
}
|
|
185
|
+
let arrayChildCount = 0;
|
|
186
|
+
let forceFinalLineBreakExists = false;
|
|
187
|
+
if (!finalLineBreakExists) {
|
|
188
|
+
walkDoc({
|
|
189
|
+
startDoc: indentedContent,
|
|
190
|
+
debug,
|
|
191
|
+
callback: (currentDoc, commaParents, commaChildIndex) => {
|
|
192
|
+
finalLineBreakExists = false;
|
|
193
|
+
const innerCurrentParent = commaParents[0];
|
|
194
|
+
const innerCurrentParentDoc = innerCurrentParent?.parent;
|
|
195
|
+
if (isDocCommand(currentDoc) &&
|
|
196
|
+
currentDoc.type === "if-break" &&
|
|
197
|
+
currentDoc.breakContents === ",") {
|
|
198
|
+
/**
|
|
199
|
+
* Only treat a trailing-comma if-break (whose
|
|
200
|
+
* `breakContents` is `,`) as the array's final
|
|
201
|
+
* line-break marker. Other if-break commands —
|
|
202
|
+
* like the leading `| ` Prettier emits when a
|
|
203
|
+
* union type wraps — appear inside element docs
|
|
204
|
+
* (e.g. `[A | B | C]`) and must not be forced
|
|
205
|
+
* open, otherwise the inner union breaks
|
|
206
|
+
* unnecessarily.
|
|
207
|
+
*/
|
|
208
|
+
if (debug) {
|
|
209
|
+
console.info("found final line break inside of if-break");
|
|
210
|
+
}
|
|
211
|
+
finalLineBreakExists = true;
|
|
212
|
+
if (!innerCurrentParentDoc) {
|
|
213
|
+
throw new Error("Found if-break without a parent");
|
|
214
|
+
}
|
|
215
|
+
else if (!Array.isArray(innerCurrentParentDoc)) {
|
|
216
|
+
throw new TypeError("if-break parent is not an array");
|
|
217
|
+
}
|
|
218
|
+
else if (commaChildIndex == undefined) {
|
|
219
|
+
throw new Error("if-break child index is undefined");
|
|
220
|
+
}
|
|
221
|
+
innerCurrentParentDoc[commaChildIndex] =
|
|
222
|
+
currentDoc.breakContents;
|
|
223
|
+
innerCurrentParentDoc.splice(commaChildIndex + 1, 0, doc.builders.breakParent);
|
|
224
|
+
undoMutations.push(() => {
|
|
225
|
+
innerCurrentParentDoc.splice(commaChildIndex + 1, 1);
|
|
226
|
+
innerCurrentParentDoc[commaChildIndex] =
|
|
227
|
+
currentDoc;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
else if (typeof currentDoc === "string") {
|
|
231
|
+
if (!innerCurrentParentDoc) {
|
|
232
|
+
console.error({
|
|
233
|
+
innerParentDoc: innerCurrentParentDoc,
|
|
234
|
+
});
|
|
235
|
+
throw new Error("Found string but innerParentDoc is not defined.");
|
|
236
|
+
}
|
|
237
|
+
else if (currentDoc === "<" &&
|
|
238
|
+
Array.isArray(innerCurrentParentDoc) &&
|
|
239
|
+
commaChildIndex != undefined &&
|
|
240
|
+
innerCurrentParentDoc
|
|
241
|
+
.slice(commaChildIndex + 1)
|
|
242
|
+
.includes(">")) {
|
|
243
|
+
/**
|
|
244
|
+
* A paired `<...>` in the same Doc array is
|
|
245
|
+
* a generic type-argument group (e.g.
|
|
246
|
+
* `Omit<MyType, 'field'>`). Skip walking
|
|
247
|
+
* its siblings so commas inside the generic
|
|
248
|
+
* aren't mistaken for array-element
|
|
249
|
+
* separators. A lone `<` or `>` without a
|
|
250
|
+
* match in the same array is a comparison
|
|
251
|
+
* operator and handled by the default
|
|
252
|
+
* walk.
|
|
253
|
+
*/
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
else if (currentDoc &&
|
|
257
|
+
nestingSyntaxOpen.includes(currentDoc)) {
|
|
258
|
+
if (!Array.isArray(innerCurrentParentDoc)) {
|
|
259
|
+
throw new TypeError("Found opening syntax but parent is not an array.");
|
|
260
|
+
}
|
|
261
|
+
const closingIndex = innerCurrentParentDoc.findIndex((sibling) => typeof sibling === "string" &&
|
|
262
|
+
sibling &&
|
|
263
|
+
isMatchingNestingSyntaxClose({
|
|
264
|
+
maybeClose: sibling,
|
|
265
|
+
openingSyntax: currentDoc,
|
|
266
|
+
}));
|
|
267
|
+
if (closingIndex < 0) {
|
|
268
|
+
throw new Error(`Could not find closing match for ${currentDoc}`);
|
|
269
|
+
}
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
else if (currentDoc &&
|
|
273
|
+
nestingSyntaxClose.includes(currentDoc)) {
|
|
274
|
+
throw new Error("Found closing syntax which shouldn't be walked");
|
|
275
|
+
}
|
|
276
|
+
else if (currentDoc === ",") {
|
|
277
|
+
if (debug) {
|
|
278
|
+
console.info({
|
|
279
|
+
foundCommaIn: innerCurrentParentDoc,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
if (!Array.isArray(innerCurrentParentDoc)) {
|
|
283
|
+
console.error({
|
|
284
|
+
innerParentDoc: innerCurrentParentDoc,
|
|
285
|
+
});
|
|
286
|
+
throw new Error("Found comma but innerParentDoc is not an array.");
|
|
287
|
+
}
|
|
288
|
+
else if (commaChildIndex == undefined) {
|
|
289
|
+
throw new Error("Found comma but childIndex is undefined.");
|
|
290
|
+
}
|
|
291
|
+
let siblingIndex = commaChildIndex + 1;
|
|
292
|
+
let parentToMutate = innerCurrentParentDoc;
|
|
293
|
+
if (commaChildIndex ===
|
|
294
|
+
innerCurrentParentDoc.length - 1) {
|
|
295
|
+
const commaGrandParent = commaParents[1];
|
|
296
|
+
if (commaGrandParent == undefined) {
|
|
297
|
+
throw new Error("Could not find grandparent of comma group.");
|
|
298
|
+
}
|
|
299
|
+
else if (commaGrandParent.childIndexInThisParent ==
|
|
300
|
+
undefined) {
|
|
301
|
+
throw new Error("Could not find index of comma group parent");
|
|
302
|
+
}
|
|
303
|
+
else if (!Array.isArray(commaGrandParent.parent)) {
|
|
304
|
+
throw new TypeError("Comma group grandparent is not an array.");
|
|
305
|
+
}
|
|
306
|
+
siblingIndex =
|
|
307
|
+
commaGrandParent.childIndexInThisParent +
|
|
308
|
+
1;
|
|
309
|
+
parentToMutate =
|
|
310
|
+
commaGrandParent.parent;
|
|
311
|
+
}
|
|
312
|
+
if (debug) {
|
|
313
|
+
console.info({
|
|
314
|
+
commaParentDoc: innerCurrentParentDoc,
|
|
315
|
+
parentToMutate,
|
|
316
|
+
siblingIndex,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
let maybeCommaSibling = parentToMutate[siblingIndex];
|
|
320
|
+
if (debug) {
|
|
321
|
+
console.info(`Trying to find comma sibling at index ${siblingIndex}`, stringify({
|
|
322
|
+
parentToMutate,
|
|
323
|
+
maybeCommaSibling,
|
|
324
|
+
}));
|
|
325
|
+
}
|
|
326
|
+
function isCommaSibling(maybe) {
|
|
327
|
+
return ((isDocCommand(maybe) &&
|
|
328
|
+
maybe.type === "line") ||
|
|
329
|
+
(Array.isArray(maybe) &&
|
|
330
|
+
isDocCommand(maybe[0]) &&
|
|
331
|
+
maybe[0].type === "line"));
|
|
332
|
+
}
|
|
333
|
+
while (!isCommaSibling(maybeCommaSibling) &&
|
|
334
|
+
siblingIndex < parentToMutate.length) {
|
|
335
|
+
siblingIndex++;
|
|
336
|
+
maybeCommaSibling =
|
|
337
|
+
parentToMutate[siblingIndex];
|
|
338
|
+
if (debug) {
|
|
339
|
+
console.info(`Trying to find comma sibling at index ${siblingIndex}`, parentToMutate, maybeCommaSibling);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (debug) {
|
|
343
|
+
console.info(`Found comma sibling at index ${siblingIndex}`, parentToMutate, maybeCommaSibling);
|
|
344
|
+
}
|
|
345
|
+
if (!isCommaSibling(maybeCommaSibling)) {
|
|
346
|
+
throw new Error(`Found comma but its following sibling is not a line: ${stringify(maybeCommaSibling)}`);
|
|
347
|
+
}
|
|
348
|
+
const commaSibling = maybeCommaSibling;
|
|
349
|
+
const currentLineCountIndex = lineIndex % lineCounts.length;
|
|
350
|
+
const currentLineCount = lineCounts.length
|
|
351
|
+
? lineCounts[currentLineCountIndex]
|
|
352
|
+
: undefined;
|
|
353
|
+
arrayChildCount++;
|
|
354
|
+
if ((currentLineCount &&
|
|
355
|
+
columnCount === currentLineCount) ||
|
|
356
|
+
!currentLineCount) {
|
|
357
|
+
// if we're on the last element of the line then increment to the next line
|
|
358
|
+
lineIndex++;
|
|
359
|
+
columnCount = 1;
|
|
360
|
+
/**
|
|
361
|
+
* Don't use doc.builders.hardline here.
|
|
362
|
+
* It causes "invalid size error" which
|
|
363
|
+
* I don't understand and which has no
|
|
364
|
+
* other useful information or stack
|
|
365
|
+
* trace.
|
|
366
|
+
*/
|
|
367
|
+
if (debug) {
|
|
368
|
+
console.info({
|
|
369
|
+
breakingAfter: parentToMutate[siblingIndex - 1],
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
parentToMutate[siblingIndex] =
|
|
373
|
+
doc.builders.hardlineWithoutBreakParent;
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
parentToMutate[siblingIndex] = " ";
|
|
377
|
+
columnCount++;
|
|
378
|
+
}
|
|
379
|
+
undoMutations.push(() => {
|
|
380
|
+
parentToMutate[siblingIndex] =
|
|
381
|
+
commaSibling;
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
else if (Array.isArray(currentDoc)) {
|
|
386
|
+
const firstPart = currentDoc[0];
|
|
387
|
+
const secondPart = currentDoc[1];
|
|
388
|
+
if (debug) {
|
|
389
|
+
console.info("got concat child doc");
|
|
390
|
+
console.info(currentDoc, {
|
|
391
|
+
firstPart,
|
|
392
|
+
secondPart,
|
|
393
|
+
});
|
|
394
|
+
console.info(isDocCommand(firstPart), isDocCommand(secondPart), firstPart?.type === "line", firstPart?.hard, secondPart?.type ===
|
|
395
|
+
"break-parent");
|
|
396
|
+
}
|
|
397
|
+
if (isDocCommand(firstPart) &&
|
|
398
|
+
isDocCommand(secondPart) &&
|
|
399
|
+
firstPart.type === "line" &&
|
|
400
|
+
firstPart.hard &&
|
|
401
|
+
secondPart.type === "break-parent") {
|
|
402
|
+
if (debug) {
|
|
403
|
+
console.info("concat child was indeed a line break");
|
|
404
|
+
}
|
|
405
|
+
forceFinalLineBreakExists = true;
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
else if (debug) {
|
|
409
|
+
console.info("concat child doc was not a line break");
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return true;
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
if (forceFinalLineBreakExists) {
|
|
417
|
+
finalLineBreakExists = true;
|
|
418
|
+
}
|
|
419
|
+
if (!finalLineBreakExists) {
|
|
420
|
+
if (debug) {
|
|
421
|
+
console.info(`Parsed all array children but finalBreakHappened = ${finalLineBreakExists}`);
|
|
422
|
+
}
|
|
423
|
+
const closingBracketIndex = parentDoc.indexOf("]");
|
|
424
|
+
const preBracketChild = parentDoc[closingBracketIndex - 1];
|
|
425
|
+
if (isDocCommand(preBracketChild) &&
|
|
426
|
+
preBracketChild.type === "line") {
|
|
427
|
+
parentDoc.splice(closingBracketIndex - 1, 1);
|
|
428
|
+
undoMutations.push(() => {
|
|
429
|
+
parentDoc.splice(closingBracketIndex - 1, 0, preBracketChild);
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
parentDoc.splice(closingBracketIndex - 1, 0, doc.builders.hardlineWithoutBreakParent);
|
|
433
|
+
undoMutations.push(() => {
|
|
434
|
+
parentDoc.splice(closingBracketIndex - 1, 1);
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
if (Array.isArray(indentedContent)) {
|
|
438
|
+
const oldIndentContentChild = indentContents[1];
|
|
439
|
+
indentContents.splice(1, 1, doc.builders.group([
|
|
440
|
+
doc.builders.hardlineWithoutBreakParent,
|
|
441
|
+
...indentedContent,
|
|
442
|
+
]));
|
|
443
|
+
undoMutations.push(() => {
|
|
444
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
445
|
+
indentContents[1] = oldIndentContentChild;
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
const oldParts = indentedContent.parts;
|
|
450
|
+
indentedContent.parts = [
|
|
451
|
+
doc.builders.group([
|
|
452
|
+
doc.builders.hardlineWithoutBreakParent,
|
|
453
|
+
...indentedContent.parts,
|
|
454
|
+
]),
|
|
455
|
+
];
|
|
456
|
+
undoMutations.push(() => {
|
|
457
|
+
indentedContent.parts = oldParts;
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
if (arrayChildCount < wrapThreshold &&
|
|
461
|
+
!lineCounts.length &&
|
|
462
|
+
!manualWrap) {
|
|
463
|
+
undoAllMutations();
|
|
464
|
+
}
|
|
465
|
+
hasProcessedOwnArray = true;
|
|
466
|
+
// don't walk any deeper
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
else if (debug) {
|
|
470
|
+
console.info({
|
|
471
|
+
ignoring: currentDoc,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
return true;
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
if (debug) {
|
|
478
|
+
console.info("final doc:", stringify(inputDoc));
|
|
479
|
+
}
|
|
480
|
+
// return what is input because we perform mutations on it
|
|
481
|
+
return inputDoc;
|
|
482
|
+
}
|
|
483
|
+
function getLatestSetValue(currentLine, triggers) {
|
|
484
|
+
const relevantSetLineCountsKey = getObjectTypedKeys(triggers)
|
|
485
|
+
.sort()
|
|
486
|
+
.reduce((closestKey, currentKey) => {
|
|
487
|
+
if (Number(currentKey) < currentLine) {
|
|
488
|
+
const currentData = triggers[currentKey];
|
|
489
|
+
if (currentData && currentData.lineEnd > currentLine) {
|
|
490
|
+
return currentKey;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return closestKey;
|
|
494
|
+
}, "");
|
|
495
|
+
const relevantSetLineCount = triggers[relevantSetLineCountsKey]?.data;
|
|
496
|
+
return relevantSetLineCount;
|
|
497
|
+
}
|
|
498
|
+
export function printWithMultilineArrays({ originalFormattedOutput, path, inputOptions, debug, }) {
|
|
499
|
+
const rootNode = path.stack[0];
|
|
500
|
+
if (!rootNode) {
|
|
501
|
+
throw new Error(`Could not find valid root node in ${path.stack.map((entry) => entry.type).join(",")}`);
|
|
502
|
+
}
|
|
503
|
+
const node = path.getNode();
|
|
504
|
+
if (debug) {
|
|
505
|
+
console.info("[multiline-arrays] printWithMultilineArrays node type:", node?.type);
|
|
506
|
+
}
|
|
507
|
+
if (node && isArrayLikeNode(node)) {
|
|
508
|
+
if (!node.loc) {
|
|
509
|
+
throw new Error(`Could not find location of node ${node.type}`);
|
|
510
|
+
}
|
|
511
|
+
const currentLineNumber = node.loc.start.line;
|
|
512
|
+
const commentTriggers = getCommentTriggers(rootNode, debug);
|
|
513
|
+
const originalText = inputOptions.originalText;
|
|
514
|
+
const splitOriginalText = originalText.split("\n");
|
|
515
|
+
/**
|
|
516
|
+
* ArrayPattern nodes in Babel-TS include their `typeAnnotation` in the
|
|
517
|
+
* node's `loc`, so the node's end location can be well past the array's
|
|
518
|
+
* `]`. Use the typeAnnotation's start (when present) as the effective
|
|
519
|
+
* end so trailing-comma / leading-newline detection only inspects text
|
|
520
|
+
* inside the actual array brackets.
|
|
521
|
+
*/
|
|
522
|
+
const typeAnnotationStart = node.typeAnnotation?.loc?.start;
|
|
523
|
+
const arrayLoc = typeAnnotationStart
|
|
524
|
+
? {
|
|
525
|
+
start: node.loc.start,
|
|
526
|
+
end: typeAnnotationStart,
|
|
527
|
+
}
|
|
528
|
+
: node.loc;
|
|
529
|
+
const elements = getArrayLikeElements(node);
|
|
530
|
+
const includesLeadingNewline = containsLeadingNewline({
|
|
531
|
+
nodeLocation: arrayLoc,
|
|
532
|
+
children: elements,
|
|
533
|
+
originalLines: splitOriginalText,
|
|
534
|
+
debug,
|
|
535
|
+
});
|
|
536
|
+
const includesTrailingComma = containsTrailingComma({
|
|
537
|
+
nodeLocation: arrayLoc,
|
|
538
|
+
children: elements,
|
|
539
|
+
originalLines: splitOriginalText,
|
|
540
|
+
});
|
|
541
|
+
const relevantSetLineCount = getLatestSetValue(currentLineNumber, commentTriggers.setLineCounts);
|
|
542
|
+
const lineCounts = commentTriggers.nextLineCounts[currentLineNumber] ??
|
|
543
|
+
relevantSetLineCount ??
|
|
544
|
+
parseNextLineCounts({
|
|
545
|
+
input: inputOptions.multilineArraysLinePattern,
|
|
546
|
+
nextOnly: false,
|
|
547
|
+
debug,
|
|
548
|
+
});
|
|
549
|
+
const relevantSetWrapCommentThreshold = getLatestSetValue(currentLineNumber, commentTriggers.setWrapThresholds);
|
|
550
|
+
const wrapThreshold = commentTriggers.nextWrapThresholds[currentLineNumber] ??
|
|
551
|
+
relevantSetWrapCommentThreshold ??
|
|
552
|
+
(inputOptions.multilineArraysWrapThreshold < 0
|
|
553
|
+
? Infinity
|
|
554
|
+
: inputOptions.multilineArraysWrapThreshold);
|
|
555
|
+
const includesCommentTrigger = (commentTriggers.nextWrapThresholds[currentLineNumber] ??
|
|
556
|
+
relevantSetWrapCommentThreshold) != undefined ||
|
|
557
|
+
!!lineCounts.length;
|
|
558
|
+
if (debug) {
|
|
559
|
+
console.info(`======= Starting call to ${insertLinesIntoArray.name}: =======`);
|
|
560
|
+
console.info({
|
|
561
|
+
options: {
|
|
562
|
+
lineCounts,
|
|
563
|
+
wrapThreshold,
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
const manualWrap = includesCommentTrigger
|
|
568
|
+
? false
|
|
569
|
+
: includesTrailingComma || includesLeadingNewline;
|
|
570
|
+
const newDoc = insertLinesIntoArray({
|
|
571
|
+
inputDoc: originalFormattedOutput,
|
|
572
|
+
manualWrap,
|
|
573
|
+
lineCounts,
|
|
574
|
+
wrapThreshold,
|
|
575
|
+
debug,
|
|
576
|
+
});
|
|
577
|
+
return newDoc;
|
|
578
|
+
}
|
|
579
|
+
return originalFormattedOutput;
|
|
580
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { assert } from '@augment-vir/assert';
|
|
2
|
+
import { describe, it } from '@augment-vir/test';
|
|
3
|
+
import { doc } from 'prettier';
|
|
4
|
+
import { insertLinesIntoArray } from './insert-new-lines.js';
|
|
5
|
+
describe(insertLinesIntoArray.name, () => {
|
|
6
|
+
it('handles comment-only array doc emitted by Prettier GitHub Issue #75', () => {
|
|
7
|
+
const commentOnlyArrayDoc = [
|
|
8
|
+
'[',
|
|
9
|
+
[
|
|
10
|
+
doc.builders.indent([
|
|
11
|
+
doc.builders.softline,
|
|
12
|
+
[
|
|
13
|
+
'// "source.fixAll.eslint",',
|
|
14
|
+
[
|
|
15
|
+
doc.builders.hardline,
|
|
16
|
+
doc.builders.breakParent,
|
|
17
|
+
],
|
|
18
|
+
'// "source.fixAll.prettier",',
|
|
19
|
+
],
|
|
20
|
+
]),
|
|
21
|
+
[
|
|
22
|
+
doc.builders.hardline,
|
|
23
|
+
doc.builders.breakParent,
|
|
24
|
+
],
|
|
25
|
+
],
|
|
26
|
+
']',
|
|
27
|
+
];
|
|
28
|
+
assert.strictEquals(insertLinesIntoArray({
|
|
29
|
+
inputDoc: commentOnlyArrayDoc,
|
|
30
|
+
manualWrap: false,
|
|
31
|
+
lineCounts: [],
|
|
32
|
+
wrapThreshold: Infinity,
|
|
33
|
+
debug: false,
|
|
34
|
+
}), commentOnlyArrayDoc);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BaseNode } from "estree";
|
|
2
|
+
export declare function containsLeadingNewline({ nodeLocation, children, originalLines, debug, }: Readonly<{
|
|
3
|
+
nodeLocation: BaseNode["loc"];
|
|
4
|
+
children: (BaseNode | null)[];
|
|
5
|
+
originalLines: string[];
|
|
6
|
+
debug: boolean;
|
|
7
|
+
}>): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { extractTextBetweenRanges } from "../augments/array.js";
|
|
2
|
+
export function containsLeadingNewline({ nodeLocation, children, originalLines, debug, }) {
|
|
3
|
+
const firstElement = children[0];
|
|
4
|
+
if (firstElement) {
|
|
5
|
+
const startLocation = nodeLocation?.start;
|
|
6
|
+
if (!startLocation) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const endLocation = firstElement.loc?.start;
|
|
10
|
+
if (!endLocation) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const textBeforeFirstElement = extractTextBetweenRanges(originalLines, {
|
|
14
|
+
start: {
|
|
15
|
+
column: startLocation.column - 1,
|
|
16
|
+
line: startLocation.line - 1,
|
|
17
|
+
},
|
|
18
|
+
end: {
|
|
19
|
+
column: endLocation.column - 1,
|
|
20
|
+
line: endLocation.line - 1,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
if (debug) {
|
|
24
|
+
console.info(`containsLeadingNewline textBeforeFirstElement: ${textBeforeFirstElement}`);
|
|
25
|
+
}
|
|
26
|
+
if (textBeforeFirstElement.includes("\n")) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|