• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import * as ts from "../../_namespaces/ts";
2import * as Harness from "../../_namespaces/Harness";
3
4// Some tests have trailing whitespace
5
6describe("unittests:: services:: textChanges", () => {
7    function findChild(name: string, n: ts.Node) {
8        return find(n)!;
9
10        function find(node: ts.Node): ts.Node | undefined {
11            if (ts.isDeclaration(node) && node.name && ts.isIdentifier(node.name) && node.name.escapedText === name) {
12                return node;
13            }
14            else {
15                return ts.forEachChild(node, find);
16            }
17        }
18    }
19
20    const printerOptions = { newLine: ts.NewLineKind.LineFeed };
21    const newLineCharacter = ts.getNewLineCharacter(printerOptions);
22
23    function getRuleProvider(placeOpenBraceOnNewLineForFunctions: boolean): ts.formatting.FormatContext {
24        return ts.formatting.getFormatContext(placeOpenBraceOnNewLineForFunctions ? { ...ts.testFormatSettings, placeOpenBraceOnNewLineForFunctions: true } : ts.testFormatSettings, ts.notImplementedHost);
25    }
26
27    // validate that positions that were recovered from the printed text actually match positions that will be created if the same text is parsed.
28    function verifyPositions(node: ts.Node, text: string): void {
29        const nodeList = flattenNodes(node);
30        const sourceFile = ts.createSourceFile("f.ts", text, ts.ScriptTarget.ES2015);
31        const parsedNodeList = flattenNodes(sourceFile.statements[0]);
32        ts.zipWith(nodeList, parsedNodeList, (left, right) => {
33            ts.Debug.assert(left.pos === right.pos);
34            ts.Debug.assert(left.end === right.end);
35        });
36
37        function flattenNodes(n: ts.Node) {
38            const data: (ts.Node | ts.NodeArray<ts.Node>)[] = [];
39            walk(n);
40            return data;
41
42            function walk(n: ts.Node | ts.NodeArray<ts.Node>): void {
43                data.push(n);
44                return ts.isArray(n) ? ts.forEach(n, walk) : ts.forEachChild(n, walk, walk);
45            }
46        }
47    }
48
49    function runSingleFileTest(caption: string, placeOpenBraceOnNewLineForFunctions: boolean, text: string, validateNodes: boolean, testBlock: (sourceFile: ts.SourceFile, changeTracker: ts.textChanges.ChangeTracker) => void) {
50        it(caption, () => {
51            const sourceFile = ts.createSourceFile("source.ts", text, ts.ScriptTarget.ES2015, /*setParentNodes*/ true);
52            const rulesProvider = getRuleProvider(placeOpenBraceOnNewLineForFunctions);
53            const changeTracker = new ts.textChanges.ChangeTracker(newLineCharacter, rulesProvider);
54            testBlock(sourceFile, changeTracker);
55            const changes = changeTracker.getChanges(validateNodes ? verifyPositions : undefined);
56            assert.equal(changes.length, 1);
57            assert.equal(changes[0].fileName, sourceFile.fileName);
58            const modified = ts.textChanges.applyChanges(sourceFile.text, changes[0].textChanges);
59            Harness.Baseline.runBaseline(`textChanges/${caption}.js`, `===ORIGINAL===${newLineCharacter}${text}${newLineCharacter}===MODIFIED===${newLineCharacter}${modified}`);
60        });
61    }
62
63    {
64        const text = `
65namespace M
66{
67    namespace M2
68    {
69        function foo() {
70            // comment 1
71            const x = 1;
72
73            /**
74             * comment 2 line 1
75             * comment 2 line 2
76             */
77            function f() {
78                return 100;
79            }
80            const y = 2; // comment 3
81            return 1;
82        }
83    }
84}`;
85        runSingleFileTest("extractMethodLike", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
86            const statements = (findChild("foo", sourceFile) as ts.FunctionDeclaration).body!.statements.slice(1);
87            const newFunction = ts.factory.createFunctionDeclaration(
88                /*modifiers*/ undefined,
89                /*asteriskToken*/ undefined,
90                /*name*/ "bar",
91                /*typeParameters*/ undefined,
92                /*parameters*/ ts.emptyArray,
93                /*type*/ ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
94                /*body */ ts.factory.createBlock(statements)
95            );
96
97            changeTracker.insertNodeBefore(sourceFile, /*before*/findChild("M2", sourceFile), newFunction);
98
99            // replace statements with return statement
100            const newStatement = ts.factory.createReturnStatement(
101                ts.factory.createCallExpression(
102                    /*expression*/ newFunction.name!,
103                    /*typeArguments*/ undefined,
104                    /*argumentsArray*/ ts.emptyArray
105                ));
106            changeTracker.replaceNodeRange(sourceFile, statements[0], ts.last(statements), newStatement, { suffix: newLineCharacter });
107        });
108    }
109    {
110        const text = `
111function foo() {
112    return 1;
113}
114
115function bar() {
116    return 2;
117}
118`;
119        runSingleFileTest("deleteRange1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
120            changeTracker.deleteRange(sourceFile, { pos: text.indexOf("function foo"), end: text.indexOf("function bar") });
121        });
122    }
123    function findVariableStatementContaining(name: string, sourceFile: ts.SourceFile): ts.VariableStatement {
124        return ts.cast(findVariableDeclarationContaining(name, sourceFile).parent.parent, ts.isVariableStatement);
125    }
126    function findVariableDeclarationContaining(name: string, sourceFile: ts.SourceFile): ts.VariableDeclaration {
127        return ts.cast(findChild(name, sourceFile), ts.isVariableDeclaration);
128    }
129    const { deleteNode } = ts.textChanges;
130    {
131        const text = `
132var x = 1; // some comment - 1
133/**
134 * comment 2
135 */
136var y = 2; // comment 3
137var z = 3; // comment 4
138`;
139        runSingleFileTest("deleteNode1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
140            deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile));
141        });
142        runSingleFileTest("deleteNode2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
143            deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude });
144        });
145        runSingleFileTest("deleteNode3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
146            deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude });
147        });
148        runSingleFileTest("deleteNode4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
149            deleteNode(changeTracker, sourceFile, findVariableStatementContaining("y", sourceFile), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude });
150        });
151        runSingleFileTest("deleteNode5", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
152            deleteNode(changeTracker, sourceFile, findVariableStatementContaining("x", sourceFile));
153        });
154    }
155    {
156        const text = `
157// comment 1
158var x = 1; // comment 2
159// comment 3
160var y = 2; // comment 4
161var z = 3; // comment 5
162// comment 6
163var a = 4; // comment 7
164`;
165        runSingleFileTest("deleteNodeRange1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
166            changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile));
167        });
168        runSingleFileTest("deleteNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
169            changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
170                { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude });
171        });
172        runSingleFileTest("deleteNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
173            changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
174                { trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude });
175        });
176        runSingleFileTest("deleteNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
177            changeTracker.deleteNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile),
178                { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude });
179        });
180    }
181    function createTestVariableDeclaration(name: string) {
182        return ts.factory.createVariableDeclaration(name, /*exclamationToken*/ undefined, /*type*/ undefined, ts.factory.createObjectLiteralExpression([ts.factory.createPropertyAssignment("p1", ts.factory.createNumericLiteral(1))], /*multiline*/ true));
183    }
184    function createTestClass() {
185        return ts.factory.createClassDeclaration(
186            [
187                ts.factory.createToken(ts.SyntaxKind.PublicKeyword)
188            ],
189            "class1",
190            /*typeParameters*/ undefined,
191            [
192                ts.factory.createHeritageClause(
193                    ts.SyntaxKind.ImplementsKeyword,
194                    [
195                        ts.factory.createExpressionWithTypeArguments(ts.factory.createIdentifier("interface1"), /*typeArguments*/ undefined)
196                    ]
197                )
198            ],
199            [
200                ts.factory.createPropertyDeclaration(
201                    /*modifiers*/ undefined,
202                    "property1",
203                    /*questionToken*/ undefined,
204                    ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword),
205                    /*initializer*/ undefined
206                )
207            ]
208        );
209    }
210    {
211        const text = `
212// comment 1
213var x = 1; // comment 2
214// comment 3
215var y = 2; // comment 4
216var z = 3; // comment 5
217// comment 6
218var a = 4; // comment 7`;
219        runSingleFileTest("replaceRange", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
220            changeTracker.replaceRange(sourceFile, { pos: text.indexOf("var y"), end: text.indexOf("var a") }, createTestClass(), { suffix: newLineCharacter });
221        });
222        runSingleFileTest("replaceRangeWithForcedIndentation", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
223            changeTracker.replaceRange(sourceFile, { pos: text.indexOf("var y"), end: text.indexOf("var a") }, createTestClass(), { suffix: newLineCharacter, indentation: 8, delta: 0 });
224        });
225
226        runSingleFileTest("replaceRangeNoLineBreakBefore", /*placeOpenBraceOnNewLineForFunctions*/ true, `const x = 1, y = "2";`, /*validateNodes*/ false, (sourceFile, changeTracker) => {
227            const newNode = createTestVariableDeclaration("z1");
228            changeTracker.replaceRange(sourceFile, { pos: sourceFile.text.indexOf("y"), end: sourceFile.text.indexOf(";") }, newNode);
229        });
230    }
231    {
232        const text = `
233namespace A {
234    const x = 1, y = "2";
235}
236`;
237        runSingleFileTest("replaceNode1NoLineBreakBefore", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
238            const newNode = createTestVariableDeclaration("z1");
239            changeTracker.replaceNode(sourceFile, findChild("y", sourceFile), newNode);
240        });
241    }
242    {
243        const text = `
244// comment 1
245var x = 1; // comment 2
246// comment 3
247var y = 2; // comment 4
248var z = 3; // comment 5
249// comment 6
250var a = 4; // comment 7`;
251        runSingleFileTest("replaceNode1", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
252            changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { suffix: newLineCharacter });
253        });
254        runSingleFileTest("replaceNode2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
255            changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter });
256        });
257        runSingleFileTest("replaceNode3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
258            changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter });
259        });
260        runSingleFileTest("replaceNode4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
261            changeTracker.replaceNode(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude });
262        });
263        runSingleFileTest("replaceNode5", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
264            changeTracker.replaceNode(sourceFile, findVariableStatementContaining("x", sourceFile), createTestClass(), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude });
265        });
266    }
267    {
268        const text = `
269// comment 1
270var x = 1; // comment 2
271// comment 3
272var y = 2; // comment 4
273var z = 3; // comment 5
274// comment 6
275var a = 4; // comment 7`;
276        runSingleFileTest("replaceNodeRange1", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
277            changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { suffix: newLineCharacter });
278        });
279        runSingleFileTest("replaceNodeRange2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
280            changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, suffix: newLineCharacter, prefix: newLineCharacter });
281        });
282        runSingleFileTest("replaceNodeRange3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
283            changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude, suffix: newLineCharacter });
284        });
285        runSingleFileTest("replaceNodeRange4", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
286            changeTracker.replaceNodeRange(sourceFile, findVariableStatementContaining("y", sourceFile), findVariableStatementContaining("z", sourceFile), createTestClass(), { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude, trailingTriviaOption: ts.textChanges.TrailingTriviaOption.Exclude });
287        });
288    }
289    {
290        const text = `
291// comment 1
292var x = 1; // comment 2
293// comment 3
294var y; // comment 4
295var z = 3; // comment 5
296// comment 6
297var a = 4; // comment 7`;
298        runSingleFileTest("insertNodeBefore3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
299            changeTracker.insertNodeBefore(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass());
300        });
301        runSingleFileTest("insertNodeAfterVariableDeclaration", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
302            changeTracker.insertNodeAfter(sourceFile, findVariableDeclarationContaining("y", sourceFile), createTestVariableDeclaration("z1"));
303        });
304    }
305    {
306        const text = `
307namespace M {
308    // comment 1
309    var x = 1; // comment 2
310    // comment 3
311    var y; // comment 4
312    var z = 3; // comment 5
313    // comment 6
314    var a = 4; // comment 7
315}`;
316        runSingleFileTest("insertNodeBefore1", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
317            changeTracker.insertNodeBefore(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass());
318        });
319        runSingleFileTest("insertNodeBefore2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
320            changeTracker.insertNodeBefore(sourceFile, findChild("M", sourceFile), createTestClass());
321        });
322        runSingleFileTest("insertNodeAfter1", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
323            changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass());
324        });
325        runSingleFileTest("insertNodeAfter2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
326            changeTracker.insertNodeAfter(sourceFile, findChild("M", sourceFile), createTestClass());
327        });
328    }
329
330    function findConstructor(sourceFile: ts.SourceFile): ts.ConstructorDeclaration {
331        const classDecl = sourceFile.statements[0] as ts.ClassDeclaration;
332        return ts.find<ts.ClassElement, ts.ConstructorDeclaration>(classDecl.members, (m): m is ts.ConstructorDeclaration => ts.isConstructorDeclaration(m) && !!m.body)!;
333    }
334    function createTestSuperCall() {
335        const superCall = ts.factory.createCallExpression(
336            ts.factory.createSuper(),
337            /*typeArguments*/ undefined,
338            /*argumentsArray*/ ts.emptyArray
339        );
340        return ts.factory.createExpressionStatement(superCall);
341    }
342
343    {
344        const text1 = `
345class A {
346    constructor() {
347    }
348}
349`;
350        runSingleFileTest("insertNodeAtConstructorStart", /*placeOpenBraceOnNewLineForFunctions*/ false, text1, /*validateNodes*/ false, (sourceFile, changeTracker) => {
351            changeTracker.insertNodeAtConstructorStart(sourceFile, findConstructor(sourceFile), createTestSuperCall());
352        });
353        const text2 = `
354class A {
355    constructor() {
356        var x = 1;
357    }
358}
359`;
360        runSingleFileTest("insertNodeAfter4", /*placeOpenBraceOnNewLineForFunctions*/ false, text2, /*validateNodes*/ false, (sourceFile, changeTracker) => {
361            changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("x", sourceFile), createTestSuperCall());
362        });
363        const text3 = `
364class A {
365    constructor() {
366
367    }
368}
369`;
370        runSingleFileTest("insertNodeAtConstructorStart-block with newline", /*placeOpenBraceOnNewLineForFunctions*/ false, text3, /*validateNodes*/ false, (sourceFile, changeTracker) => {
371            changeTracker.insertNodeAtConstructorStart(sourceFile, findConstructor(sourceFile), createTestSuperCall());
372        });
373    }
374    {
375        const text = `var a = 1, b = 2, c = 3;`;
376        runSingleFileTest("deleteNodeInList1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
377            changeTracker.delete(sourceFile, findChild("a", sourceFile));
378        });
379        runSingleFileTest("deleteNodeInList2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
380            changeTracker.delete(sourceFile, findChild("b", sourceFile));
381        });
382        runSingleFileTest("deleteNodeInList3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
383            changeTracker.delete(sourceFile, findChild("c", sourceFile));
384        });
385    }
386    {
387        const text = `var a = 1,b = 2,c = 3;`;
388        runSingleFileTest("deleteNodeInList1_1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
389            changeTracker.delete(sourceFile, findChild("a", sourceFile));
390        });
391        runSingleFileTest("deleteNodeInList2_1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
392            changeTracker.delete(sourceFile, findChild("b", sourceFile));
393        });
394        runSingleFileTest("deleteNodeInList3_1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
395            changeTracker.delete(sourceFile, findChild("c", sourceFile));
396        });
397    }
398    {
399        const text = `
400namespace M {
401    var a = 1,
402        b = 2,
403        c = 3;
404}`;
405        runSingleFileTest("deleteNodeInList4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
406            changeTracker.delete(sourceFile, findChild("a", sourceFile));
407        });
408        runSingleFileTest("deleteNodeInList5", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
409            changeTracker.delete(sourceFile, findChild("b", sourceFile));
410        });
411        runSingleFileTest("deleteNodeInList6", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
412            changeTracker.delete(sourceFile, findChild("c", sourceFile));
413        });
414    }
415    {
416        const text = `
417namespace M {
418    var a = 1, // comment 1
419        // comment 2
420        b = 2, // comment 3
421        // comment 4
422        c = 3; // comment 5
423}`;
424        runSingleFileTest("deleteNodeInList4_1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
425            changeTracker.delete(sourceFile, findChild("a", sourceFile));
426        });
427        runSingleFileTest("deleteNodeInList5_1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
428            changeTracker.delete(sourceFile, findChild("b", sourceFile));
429        });
430        runSingleFileTest("deleteNodeInList6_1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
431            changeTracker.delete(sourceFile, findChild("c", sourceFile));
432        });
433    }
434    {
435        const text = `
436function foo(a: number, b: string, c = true) {
437    return 1;
438}`;
439        runSingleFileTest("deleteNodeInList7", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
440            changeTracker.delete(sourceFile, findChild("a", sourceFile));
441        });
442        runSingleFileTest("deleteNodeInList8", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
443            changeTracker.delete(sourceFile, findChild("b", sourceFile));
444        });
445        runSingleFileTest("deleteNodeInList9", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
446            changeTracker.delete(sourceFile, findChild("c", sourceFile));
447        });
448    }
449    {
450        const text = `
451function foo(a: number,b: string,c = true) {
452    return 1;
453}`;
454        runSingleFileTest("deleteNodeInList10", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
455            changeTracker.delete(sourceFile, findChild("a", sourceFile));
456        });
457        runSingleFileTest("deleteNodeInList11", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
458            changeTracker.delete(sourceFile, findChild("b", sourceFile));
459        });
460        runSingleFileTest("deleteNodeInList12", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
461            changeTracker.delete(sourceFile, findChild("c", sourceFile));
462        });
463    }
464    {
465        const text = `
466function foo(
467    a: number,
468    b: string,
469    c = true) {
470    return 1;
471}`;
472        runSingleFileTest("deleteNodeInList13", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
473            changeTracker.delete(sourceFile, findChild("a", sourceFile));
474        });
475        runSingleFileTest("deleteNodeInList14", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
476            changeTracker.delete(sourceFile, findChild("b", sourceFile));
477        });
478        runSingleFileTest("deleteNodeInList15", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
479            changeTracker.delete(sourceFile, findChild("c", sourceFile));
480        });
481    }
482    {
483        const text = `
484const x = 1, y = 2;`;
485        runSingleFileTest("insertNodeInListAfter1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
486            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createVariableDeclaration("z", /*exclamationToken*/ undefined, /*type*/ undefined, ts.factory.createNumericLiteral(1)));
487        });
488        runSingleFileTest("insertNodeInListAfter2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
489            changeTracker.insertNodeInListAfter(sourceFile, findChild("y", sourceFile), ts.factory.createVariableDeclaration("z", /*exclamationToken*/ undefined, /*type*/ undefined, ts.factory.createNumericLiteral(1)));
490        });
491    }
492    {
493        const text = `
494const /*x*/ x = 1, /*y*/ y = 2;`;
495        runSingleFileTest("insertNodeInListAfter3", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
496            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createVariableDeclaration("z", /*exclamationToken*/ undefined, /*type*/ undefined, ts.factory.createNumericLiteral(1)));
497        });
498        runSingleFileTest("insertNodeInListAfter4", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
499            changeTracker.insertNodeInListAfter(sourceFile, findChild("y", sourceFile), ts.factory.createVariableDeclaration("z", /*exclamationToken*/ undefined, /*type*/ undefined, ts.factory.createNumericLiteral(1)));
500        });
501    }
502    {
503        const text = `
504const x = 1;`;
505        runSingleFileTest("insertNodeInListAfter5", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
506            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createVariableDeclaration("z", /*exclamationToken*/ undefined, /*type*/ undefined, ts.factory.createNumericLiteral(1)));
507        });
508    }
509    {
510        const text = `
511const x = 1,
512    y = 2;`;
513        runSingleFileTest("insertNodeInListAfter6", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
514            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createVariableDeclaration("z", /*exclamationToken*/ undefined, /*type*/ undefined, ts.factory.createNumericLiteral(1)));
515        });
516        runSingleFileTest("insertNodeInListAfter7", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
517            changeTracker.insertNodeInListAfter(sourceFile, findChild("y", sourceFile), ts.factory.createVariableDeclaration("z", /*exclamationToken*/ undefined, /*type*/ undefined, ts.factory.createNumericLiteral(1)));
518        });
519    }
520    {
521        const text = `
522const /*x*/ x = 1,
523    /*y*/ y = 2;`;
524        runSingleFileTest("insertNodeInListAfter8", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
525            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createVariableDeclaration("z", /*exclamationToken*/ undefined, /*type*/ undefined, ts.factory.createNumericLiteral(1)));
526        });
527        runSingleFileTest("insertNodeInListAfter9", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
528            changeTracker.insertNodeInListAfter(sourceFile, findChild("y", sourceFile), ts.factory.createVariableDeclaration("z", /*exclamationToken*/ undefined, /*type*/ undefined, ts.factory.createNumericLiteral(1)));
529        });
530    }
531    {
532        const text = `
533import {
534    x
535} from "bar"`;
536        runSingleFileTest("insertNodeInListAfter10", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
537            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createImportSpecifier(/*isTypeOnly*/ false, ts.factory.createIdentifier("b"), ts.factory.createIdentifier("a")));
538        });
539    }
540    {
541        const text = `
542import {
543    x // this is x
544} from "bar"`;
545        runSingleFileTest("insertNodeInListAfter11", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
546            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createImportSpecifier(/*isTypeOnly*/ false, ts.factory.createIdentifier("b"), ts.factory.createIdentifier("a")));
547        });
548    }
549    {
550        const text = `
551import {
552    x
553} from "bar"`;
554        runSingleFileTest("insertNodeInListAfter12", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
555            // eslint-disable-next-line local/boolean-trivia
556            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createImportSpecifier(/*isTypeOnly*/ false, undefined, ts.factory.createIdentifier("a")));
557        });
558    }
559    {
560        const text = `
561import {
562    x // this is x
563} from "bar"`;
564        runSingleFileTest("insertNodeInListAfter13", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
565            // eslint-disable-next-line local/boolean-trivia
566            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createImportSpecifier(/*isTypeOnly*/ false, undefined, ts.factory.createIdentifier("a")));
567        });
568    }
569    {
570        const text = `
571import {
572    x0,
573    x
574} from "bar"`;
575        runSingleFileTest("insertNodeInListAfter14", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
576            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createImportSpecifier(/*isTypeOnly*/ false, ts.factory.createIdentifier("b"), ts.factory.createIdentifier("a")));
577        });
578    }
579    {
580        const text = `
581import {
582    x0,
583    x // this is x
584} from "bar"`;
585        runSingleFileTest("insertNodeInListAfter15", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
586            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createImportSpecifier(/*isTypeOnly*/ false, ts.factory.createIdentifier("b"), ts.factory.createIdentifier("a")));
587        });
588    }
589    {
590        const text = `
591import {
592    x0,
593    x
594} from "bar"`;
595        runSingleFileTest("insertNodeInListAfter16", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
596            // eslint-disable-next-line local/boolean-trivia
597            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createImportSpecifier(/*isTypeOnly*/ false, undefined, ts.factory.createIdentifier("a")));
598        });
599    }
600    {
601        const text = `
602import {
603    x0,
604    x // this is x
605} from "bar"`;
606        runSingleFileTest("insertNodeInListAfter17", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
607            // eslint-disable-next-line local/boolean-trivia
608            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createImportSpecifier(/*isTypeOnly*/ false, undefined, ts.factory.createIdentifier("a")));
609        });
610    }
611    {
612        const text = `
613import {
614    x0, x
615} from "bar"`;
616        runSingleFileTest("insertNodeInListAfter18", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
617            // eslint-disable-next-line local/boolean-trivia
618            changeTracker.insertNodeInListAfter(sourceFile, findChild("x", sourceFile), ts.factory.createImportSpecifier(/*isTypeOnly*/ false, undefined, ts.factory.createIdentifier("a")));
619        });
620    }
621    {
622        const runTest = (name: string, text: string) => runSingleFileTest(name, /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
623            for (const specifier of ["x3", "x4", "x5"]) {
624                // eslint-disable-next-line local/boolean-trivia
625                changeTracker.insertNodeInListAfter(sourceFile, findChild("x2", sourceFile), ts.factory.createImportSpecifier(/*isTypeOnly*/ false, undefined, ts.factory.createIdentifier(specifier)));
626            }
627        });
628
629        const crlfText = "import {\r\nx1,\r\nx2\r\n} from \"bar\";";
630        runTest("insertNodeInListAfter19", crlfText);
631
632        const lfText = "import {\nx1,\nx2\n} from \"bar\";";
633        runTest("insertNodeInListAfter20", lfText);
634    }
635    {
636        const text = `
637class A {
638    x;
639}`;
640        runSingleFileTest("insertNodeAfterMultipleNodes", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
641            const newNodes = [];
642            for (let i = 0; i < 11 /*error doesn't occur with fewer nodes*/; ++i) {
643                newNodes.push(
644                    // eslint-disable-next-line local/boolean-trivia
645                    ts.factory.createPropertyDeclaration(undefined, i + "", undefined, undefined, undefined));
646            }
647            const insertAfter = findChild("x", sourceFile);
648            for (const newNode of newNodes) {
649                changeTracker.insertNodeAfter(sourceFile, insertAfter, newNode);
650            }
651        });
652    }
653    {
654        const text = `
655class A {
656    x
657}
658`;
659        runSingleFileTest("insertNodeAfterInClass1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
660            // eslint-disable-next-line local/boolean-trivia
661            changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), ts.factory.createPropertyDeclaration(undefined, "a", undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword), undefined));
662        });
663    }
664    {
665        const text = `
666class A {
667    x;
668}
669`;
670        runSingleFileTest("insertNodeAfterInClass2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
671            // eslint-disable-next-line local/boolean-trivia
672            changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), ts.factory.createPropertyDeclaration(undefined, "a", undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword), undefined));
673        });
674    }
675    {
676        const text = `
677class A {
678    x;
679    y = 1;
680}
681`;
682        runSingleFileTest("deleteNodeAfterInClass1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
683            deleteNode(changeTracker, sourceFile, findChild("x", sourceFile));
684        });
685    }
686    {
687        const text = `
688class A {
689    x
690    y = 1;
691}
692`;
693        runSingleFileTest("deleteNodeAfterInClass2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
694            deleteNode(changeTracker, sourceFile, findChild("x", sourceFile));
695        });
696    }
697    {
698        const text = `
699class A {
700    x = foo
701}
702`;
703        runSingleFileTest("insertNodeInClassAfterNodeWithoutSeparator1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
704            const newNode = ts.factory.createPropertyDeclaration(
705                /*modifiers*/ undefined,
706                ts.factory.createComputedPropertyName(ts.factory.createNumericLiteral(1)),
707                /*questionToken*/ undefined,
708                ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
709                /*initializer*/ undefined);
710            changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode);
711        });
712    }
713    {
714        const text = `
715class A {
716    x() {
717    }
718}
719`;
720        runSingleFileTest("insertNodeInClassAfterNodeWithoutSeparator2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
721            const newNode = ts.factory.createPropertyDeclaration(
722                /*modifiers*/ undefined,
723                ts.factory.createComputedPropertyName(ts.factory.createNumericLiteral(1)),
724                /*questionToken*/ undefined,
725                ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
726                /*initializer*/ undefined);
727            changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode);
728        });
729    }
730    {
731        const text = `
732interface A {
733    x
734}
735`;
736        runSingleFileTest("insertNodeInInterfaceAfterNodeWithoutSeparator1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
737            const newNode = ts.factory.createPropertyDeclaration(
738                /*modifiers*/ undefined,
739                ts.factory.createComputedPropertyName(ts.factory.createNumericLiteral(1)),
740                /*questionToken*/ undefined,
741                ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
742                /*initializer*/ undefined);
743            changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode);
744        });
745    }
746    {
747        const text = `
748interface A {
749    x()
750}
751`;
752        runSingleFileTest("insertNodeInInterfaceAfterNodeWithoutSeparator2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
753            const newNode = ts.factory.createPropertyDeclaration(
754                /*modifiers*/ undefined,
755                ts.factory.createComputedPropertyName(ts.factory.createNumericLiteral(1)),
756                /*questionToken*/ undefined,
757                ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
758                /*initializer*/ undefined);
759            changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode);
760        });
761    }
762    {
763        const text = `
764let x = foo
765`;
766        runSingleFileTest("insertNodeInStatementListAfterNodeWithoutSeparator1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
767            const newNode = ts.factory.createExpressionStatement(ts.factory.createParenthesizedExpression(ts.factory.createNumericLiteral(1)));
768            changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("x", sourceFile), newNode);
769        });
770    }
771});
772