diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 5e932050bfd7f..c8cb9de72ec29 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2652,10 +2652,10 @@ Actual: ${stringify(fullActual)}`); public verifyImportFixAtPosition(expectedTextArray: string[], errorCode: number | undefined, preferences: ts.UserPreferences | undefined) { const { fileName } = this.activeFile; const ranges = this.getRanges().filter(r => r.fileName === fileName); - if (ranges.length !== 1) { + if (ranges.length > 1) { this.raiseError("Exactly one range should be specified in the testfile."); } - const range = ts.first(ranges); + const range = ts.firstOrUndefined(ranges); const codeFixes = this.getCodeFixes(fileName, errorCode, preferences).filter(f => f.fixId === undefined); // TODO: GH#20315 filter out those that use the import fix ID; @@ -2674,7 +2674,7 @@ Actual: ${stringify(fullActual)}`); const change = ts.first(codeFix.changes); ts.Debug.assert(change.fileName === fileName); this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false); - const text = this.rangeText(range); + const text = range ? this.rangeText(range) : this.getFileContent(this.activeFile.fileName); actualTextArray.push(text); scriptInfo.updateContent(originalContent); } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 648ab77a922e9..d5ef90ff3ef1f 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -177,10 +177,10 @@ namespace ts.textChanges { } function getAdjustedEndPosition(sourceFile: SourceFile, node: Node, options: ConfigurableEnd) { + const { end } = node; if (options.useNonAdjustedEndPosition || isExpression(node)) { - return node.getEnd(); + return end; } - const end = node.getEnd(); const newEnd = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true); return newEnd !== end && isLineBreak(sourceFile.text.charCodeAt(newEnd - 1)) ? newEnd @@ -466,7 +466,11 @@ namespace ts.textChanges { } } const endPosition = getAdjustedEndPosition(sourceFile, after, {}); - return this.replaceRange(sourceFile, createTextRange(endPosition), newNode, this.getInsertNodeAfterOptions(after)); + const options = this.getInsertNodeAfterOptions(after); + return this.replaceRange(sourceFile, createTextRange(endPosition), newNode, { + ...options, + prefix: after.end === sourceFile.end && isStatement(after) ? (options.prefix ? `\n${options.prefix}` : "\n") : options.prefix, + }); } private getInsertNodeAfterOptions(node: Node): InsertNodeOptions { diff --git a/tests/baselines/reference/textChanges/insertNodeAfter2.js b/tests/baselines/reference/textChanges/insertNodeAfter2.js index fbd24e2b75e8e..47907322fe9d2 100644 --- a/tests/baselines/reference/textChanges/insertNodeAfter2.js +++ b/tests/baselines/reference/textChanges/insertNodeAfter2.js @@ -20,6 +20,7 @@ namespace M { // comment 6 var a = 4; // comment 7 } + public class class1 implements interface1 { property1: boolean; diff --git a/tests/cases/fourslash/codeFixAddMissingMember5.ts b/tests/cases/fourslash/codeFixAddMissingMember5.ts index 64a62b8268a4c..363a8e2b38e0d 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember5.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember5.ts @@ -18,6 +18,7 @@ verify.codeFix({ ()=>{ this.foo === 10 }; } } + C.foo = undefined; ` }); diff --git a/tests/cases/fourslash/codeFixAddMissingMember7.ts b/tests/cases/fourslash/codeFixAddMissingMember7.ts index 6b80901a7bbc6..95fbb4be18cba 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember7.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember7.ts @@ -14,6 +14,7 @@ verify.codeFix({ newFileContent: `class C { static p = ()=>{ this.foo === 10 }; } + C.foo = undefined; ` }); diff --git a/tests/cases/fourslash/convertFunctionToEs6Class_commentOnVariableDeclaration.ts b/tests/cases/fourslash/convertFunctionToEs6Class_commentOnVariableDeclaration.ts index 417bf0bc9053d..07ca6ea70d65b 100644 --- a/tests/cases/fourslash/convertFunctionToEs6Class_commentOnVariableDeclaration.ts +++ b/tests/cases/fourslash/convertFunctionToEs6Class_commentOnVariableDeclaration.ts @@ -8,7 +8,8 @@ verify.codeFix({ description: "Convert function to an ES2015 class", newFileContent: -`/** Doc */ +` +/** Doc */ class C { constructor() { this.x = 0; } } diff --git a/tests/cases/fourslash/importNameCodeFix_fileWithNoTrailingNewline.ts b/tests/cases/fourslash/importNameCodeFix_fileWithNoTrailingNewline.ts new file mode 100644 index 0000000000000..89d6398a5538b --- /dev/null +++ b/tests/cases/fourslash/importNameCodeFix_fileWithNoTrailingNewline.ts @@ -0,0 +1,19 @@ +/// + +// @Filename: /a.ts +////export const foo = 0; + +// @Filename: /b.ts +////export const bar = 0; + +// @Filename: /c.ts +////foo; +////import { bar } from "./b"; + +goTo.file("/c.ts"); +verify.importFixAtPosition([ +`foo; +import { bar } from "./b"; +import { foo } from "./a"; +`, +]); diff --git a/tests/cases/fourslash/importNameCodeFix_jsExtension.ts b/tests/cases/fourslash/importNameCodeFix_jsExtension.ts index 98c7c9e6cad28..e719f1aeee889 100644 --- a/tests/cases/fourslash/importNameCodeFix_jsExtension.ts +++ b/tests/cases/fourslash/importNameCodeFix_jsExtension.ts @@ -13,10 +13,13 @@ ////import * as g from "global"; // Global imports skipped ////import { a } from "./a.js"; ////import { a as a2 } from "./a"; // Ignored, only the first relative import is considered -////[|b;|] +////b; goTo.file("/c.ts"); verify.importFixAtPosition([ -`import { b } from "./b.js"; +`import * as g from "global"; // Global imports skipped +import { a } from "./a.js"; +import { a as a2 } from "./a"; // Ignored, only the first relative import is considered +import { b } from "./b.js"; b;`, ]); diff --git a/tests/cases/fourslash/importNameCodeFix_jsx.ts b/tests/cases/fourslash/importNameCodeFix_jsx.ts index f16f1f1bd769c..5e09074ba1c75 100644 --- a/tests/cases/fourslash/importNameCodeFix_jsx.ts +++ b/tests/cases/fourslash/importNameCodeFix_jsx.ts @@ -13,11 +13,11 @@ // @Filename: /c.tsx ////import { React } from "react"; -////[|;|] +////; // @Filename: /d.tsx -////[|import { Foo } from "./Foo"; -////;|] +////import { Foo } from "./Foo"; +////; // Tests that we don't crash at non-identifier location. goTo.file("/a.tsx"); @@ -26,7 +26,8 @@ verify.importFixAtPosition([]); // When constructor is missing, provide fix for that goTo.file("/c.tsx"); verify.importFixAtPosition([ -`import { Foo } from "./Foo"; +`import { React } from "react"; +import { Foo } from "./Foo"; ;`]); // When JSX namespace is missing, provide fix for that