From ff93f9579f3ff1a2db6ea3a41c4c81c080cacdc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 29 Feb 2024 13:43:46 +0100 Subject: [PATCH] append to wrapped arrays --- .../source-edits/add-transition.test.ts | 444 ++++++++++++++++-- new-packages/ts-project/src/codeChanges.ts | 90 ++-- 2 files changed, 468 insertions(+), 66 deletions(-) diff --git a/new-packages/ts-project/__tests__/source-edits/add-transition.test.ts b/new-packages/ts-project/__tests__/source-edits/add-transition.test.ts index 8dcd1a5c..2b1cbe2e 100644 --- a/new-packages/ts-project/__tests__/source-edits/add-transition.test.ts +++ b/new-packages/ts-project/__tests__/source-edits/add-transition.test.ts @@ -1696,46 +1696,422 @@ test('should be possible to add a transition at the end of an upgraded array (ex `); }); -test.todo( - 'should be possible to add a transition at the start of an upgraded array', - async () => { - const tmpPath = await testdir({ - 'tsconfig.json': JSON.stringify({}), - 'index.ts': ts` - import { createMachine } from "xstate"; - - createMachine({ - initial: "foo", - states: { - foo: { - on: { - NEXT: "bar", - }, +test('should be possible to add a transition at the start of an upgraded array (single line, comma directly after it)', async () => { + const tmpPath = await testdir({ + 'tsconfig.json': JSON.stringify({}), + 'index.ts': ts` + import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: "bar", }, - bar: {}, - baz: {}, }, - }); - `, - }); + bar: {}, + baz: {}, + }, + }); + `, + }); - const project = await createTestProject(tmpPath); + const project = await createTestProject(tmpPath); - const textEdits = project.editDigraph( - { - fileName: 'index.ts', - machineIndex: 0, + const textEdits = project.editDigraph( + { + fileName: 'index.ts', + machineIndex: 0, + }, + { + type: 'add_transition', + sourcePath: ['foo'], + targetPath: ['baz'], + transitionPath: ['on', 'NEXT', 0], + }, + ); + expect(await project.applyTextEdits(textEdits)).toMatchInlineSnapshot(` + { + "index.ts": "import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: [ + "baz", + "bar" + ], + }, + }, + bar: {}, + baz: {}, }, - { - type: 'add_transition', - sourcePath: ['foo'], - targetPath: ['baz'], - transitionPath: ['on', 'NEXT', 0], + });", + } + `); +}); + +test('should be possible to add a transition at the start of an upgraded array (single line, comma directly after it, comment before it)', async () => { + const tmpPath = await testdir({ + 'tsconfig.json': JSON.stringify({}), + 'index.ts': ts` + import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: /* comm */ "bar", + }, + }, + bar: {}, + baz: {}, + }, + }); + `, + }); + + const project = await createTestProject(tmpPath); + + const textEdits = project.editDigraph( + { + fileName: 'index.ts', + machineIndex: 0, + }, + { + type: 'add_transition', + sourcePath: ['foo'], + targetPath: ['baz'], + transitionPath: ['on', 'NEXT', 0], + }, + ); + expect(await project.applyTextEdits(textEdits)).toMatchInlineSnapshot(` + { + "index.ts": "import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: [ + "baz", + /* comm */ "bar" + ], + }, + }, + bar: {}, + baz: {}, }, - ); - expect(await project.applyTextEdits(textEdits)).toMatchInlineSnapshot(); - }, -); + });", + } + `); +}); + +test('should be possible to add a transition at the start of an upgraded array (existing value on subsequent line)', async () => { + const tmpPath = await testdir({ + 'tsconfig.json': JSON.stringify({}), + 'index.ts': outdent` + import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: + "bar", + }, + }, + bar: {}, + baz: {}, + }, + }); + `, + }); + + const project = await createTestProject(tmpPath); + + const textEdits = project.editDigraph( + { + fileName: 'index.ts', + machineIndex: 0, + }, + { + type: 'add_transition', + sourcePath: ['foo'], + targetPath: ['baz'], + transitionPath: ['on', 'NEXT', 0], + }, + ); + expect(await project.applyTextEdits(textEdits)).toMatchInlineSnapshot(` + { + "index.ts": "import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: [ + "baz", + "bar" + ], + }, + }, + bar: {}, + baz: {}, + }, + });", + } + `); +}); + +test('should be possible to add a transition at the start of an upgraded array (existing value on subsequent line, trailing comma on yet another line)', async () => { + const tmpPath = await testdir({ + 'tsconfig.json': JSON.stringify({}), + 'index.ts': outdent` + import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: + "bar" + + , + }, + }, + bar: {}, + baz: {}, + }, + }); + `, + }); + + const project = await createTestProject(tmpPath); + + const textEdits = project.editDigraph( + { + fileName: 'index.ts', + machineIndex: 0, + }, + { + type: 'add_transition', + sourcePath: ['foo'], + targetPath: ['baz'], + transitionPath: ['on', 'NEXT', 0], + }, + ); + expect(await project.applyTextEdits(textEdits)).toMatchInlineSnapshot(` + { + "index.ts": "import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: [ + "baz", + "bar" + ] + + , + }, + }, + bar: {}, + baz: {}, + }, + });", + } + `); +}); + +test('should be possible to add a transition at the start of an upgraded array (existing value on subsequent line, with trailing comment)', async () => { + const tmpPath = await testdir({ + 'tsconfig.json': JSON.stringify({}), + 'index.ts': outdent` + import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: + "bar" // comment + }, + }, + bar: {}, + baz: {}, + }, + }); + `, + }); + + const project = await createTestProject(tmpPath); + + const textEdits = project.editDigraph( + { + fileName: 'index.ts', + machineIndex: 0, + }, + { + type: 'add_transition', + sourcePath: ['foo'], + targetPath: ['baz'], + transitionPath: ['on', 'NEXT', 0], + }, + ); + expect(await project.applyTextEdits(textEdits)).toMatchInlineSnapshot(` + { + "index.ts": "import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: [ + "baz", + "bar" // comment + ] + }, + }, + bar: {}, + baz: {}, + }, + });", + } + `); +}); + +test('should be possible to add a transition at the start of an upgraded array (existing value on subsequent line, multi-line comment below it)', async () => { + const tmpPath = await testdir({ + 'tsconfig.json': JSON.stringify({}), + 'index.ts': outdent` + import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: + "bar" + /* comment */ + }, + }, + bar: {}, + baz: {}, + }, + }); + `, + }); + + const project = await createTestProject(tmpPath); + + const textEdits = project.editDigraph( + { + fileName: 'index.ts', + machineIndex: 0, + }, + { + type: 'add_transition', + sourcePath: ['foo'], + targetPath: ['baz'], + transitionPath: ['on', 'NEXT', 0], + }, + ); + expect(await project.applyTextEdits(textEdits)).toMatchInlineSnapshot(` + { + "index.ts": "import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: [ + "baz", + "bar" + ] + /* comment */ + }, + }, + bar: {}, + baz: {}, + }, + });", + } + `); +}); + +test('should be possible to add a transition at the start of an upgraded array (existing value on subsequent line, with trailing comment and comma after it)', async () => { + const tmpPath = await testdir({ + 'tsconfig.json': JSON.stringify({}), + 'index.ts': outdent` + import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: + "bar" /* comment */ , + }, + }, + bar: {}, + baz: {}, + }, + }); + `, + }); + + const project = await createTestProject(tmpPath); + + const textEdits = project.editDigraph( + { + fileName: 'index.ts', + machineIndex: 0, + }, + { + type: 'add_transition', + sourcePath: ['foo'], + targetPath: ['baz'], + transitionPath: ['on', 'NEXT', 0], + }, + ); + expect(await project.applyTextEdits(textEdits)).toMatchInlineSnapshot(` + { + "index.ts": "import { createMachine } from "xstate"; + + createMachine({ + initial: "foo", + states: { + foo: { + on: { + NEXT: [ + "baz", + "bar" /* comment */ + ] , + }, + }, + bar: {}, + baz: {}, + }, + });", + } + `); +}); test("should be possible to add a transition to invoke's onDone", async () => { const tmpPath = await testdir({ diff --git a/new-packages/ts-project/src/codeChanges.ts b/new-packages/ts-project/src/codeChanges.ts index 63e2d22a..f470a322 100644 --- a/new-packages/ts-project/src/codeChanges.ts +++ b/new-packages/ts-project/src/codeChanges.ts @@ -304,11 +304,11 @@ export function createCodeChanges(ts: typeof import('typescript')) { const existing = prop.initializer; if (nextSegment === 0) { - codeChanges.wrapIntoArrayWith(existing, 'append', value); + codeChanges.wrapIntoArrayWith(existing, 'prepend', value); return; } if (nextSegment === 1) { - codeChanges.wrapIntoArrayWith(existing, 'prepend', value); + codeChanges.wrapIntoArrayWith(existing, 'append', value); return; } @@ -861,40 +861,59 @@ export function createCodeChanges(ts: typeof import('typescript')) { change.current.getStart(), ); const hasNewLine = change.current.getFullText().includes('\n'); - const trailingComment = last( - ts.getTrailingCommentRanges( - change.sourceFile.text, - change.current.getEnd(), - ), - ); const newElementIndentation = hasNewLine ? currentIdentation : currentIdentation + formattingOptions.singleIndentation; + let beforePosition; + let beforeText = ''; + if (hasNewLine) { - edits.push({ - type: 'insert', - fileName: change.sourceFile.fileName, - position: change.current.getFullStart(), - newText: ` [`, - }); + beforePosition = change.current.getFullStart(); + beforeText += ` [`; + if (change.insertionType === 'prepend') { + beforeText += `\n` + newElementIndentation; + } } else { const leadingTrivia = change.sourceFile.text.slice( change.current.getFullStart(), change.current.getStart(), ); - edits.push({ - type: 'insert', - fileName: change.sourceFile.fileName, - position: - change.current.getFullStart() + - getLeadingWhitespaceLength(leadingTrivia), - newText: `[\n` + newElementIndentation, - }); + beforePosition = + change.current.getFullStart() + + getLeadingWhitespaceLength(leadingTrivia); + beforeText += `[\n` + newElementIndentation; } - if (trailingComment) { + if (change.insertionType === 'prepend') { + beforeText += + insertionToText( + ts, + change.sourceFile, + change.newElement, + formattingOptions, + ) + ','; + if (!hasNewLine) { + beforeText += '\n' + newElementIndentation; + } + } + + edits.push({ + type: 'insert', + fileName: change.sourceFile.fileName, + position: beforePosition, + newText: beforeText, + }); + + const trailingComment = last( + ts.getTrailingCommentRanges( + change.sourceFile.text, + change.current.getEnd(), + ), + ); + + if (change.insertionType === 'append' && trailingComment) { edits.push({ type: 'insert', fileName: change.sourceFile.fileName, @@ -903,20 +922,28 @@ export function createCodeChanges(ts: typeof import('typescript')) { }); } - edits.push({ - type: 'insert', - fileName: change.sourceFile.fileName, - position: trailingComment?.end ?? change.current.getEnd(), - newText: - (trailingComment ? '' : `,`) + - `\n` + + let afterPosition = trailingComment?.end ?? change.current.getEnd(); + let afterText = ''; + + if (change.insertionType === 'append') { + afterText += + (trailingComment ? '' : ',') + + '\n' + newElementIndentation + insertionToText( ts, change.sourceFile, change.newElement, formattingOptions, - ) + + ); + } + + edits.push({ + type: 'insert', + fileName: change.sourceFile.fileName, + position: afterPosition, + newText: + afterText + '\n' + getIndentationBeforePosition( change.sourceFile.text, @@ -924,7 +951,6 @@ export function createCodeChanges(ts: typeof import('typescript')) { ) + `]`, }); - break; } }