mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-08 12:55:49 -05:00
Make ChangeTracker#newLineCharacter public, to avoid having to pass newLineCharacter around as a parameter (#20574)
* Make ChangeTracker#newLineCharacter public, to avoid having to pass newLineCharacter around as a parameter * Don't require newLineCharacter as input to ChangeTracker methods, and make it private again
This commit is contained in:
@@ -288,6 +288,8 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function findLast<T, U extends T>(array: ReadonlyArray<T>, predicate: (element: T, index: number) => element is U): U | undefined;
|
||||
export function findLast<T>(array: ReadonlyArray<T>, predicate: (element: T, index: number) => boolean): T | undefined;
|
||||
export function findLast<T>(array: ReadonlyArray<T>, predicate: (element: T, index: number) => boolean): T | undefined {
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
const value = array[i];
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace M
|
||||
/*body */ createBlock(statements)
|
||||
);
|
||||
|
||||
changeTracker.insertNodeBefore(sourceFile, /*before*/findChild("M2", sourceFile), newFunction, { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeBefore(sourceFile, /*before*/findChild("M2", sourceFile), newFunction);
|
||||
|
||||
// replace statements with return statement
|
||||
const newStatement = createReturn(
|
||||
@@ -129,12 +129,11 @@ function bar() {
|
||||
changeTracker.deleteRange(sourceFile, { pos: text.indexOf("function foo"), end: text.indexOf("function bar") });
|
||||
});
|
||||
}
|
||||
function findVariableStatementContaining(name: string, sourceFile: SourceFile) {
|
||||
const varDecl = findChild(name, sourceFile);
|
||||
assert.equal(varDecl.kind, SyntaxKind.VariableDeclaration);
|
||||
const varStatement = varDecl.parent.parent;
|
||||
assert.equal(varStatement.kind, SyntaxKind.VariableStatement);
|
||||
return varStatement;
|
||||
function findVariableStatementContaining(name: string, sourceFile: SourceFile): VariableStatement {
|
||||
return cast(findVariableDeclarationContaining(name, sourceFile).parent.parent, isVariableStatement);
|
||||
}
|
||||
function findVariableDeclarationContaining(name: string, sourceFile: SourceFile): VariableDeclaration {
|
||||
return cast(findChild(name, sourceFile), isVariableDeclaration);
|
||||
}
|
||||
{
|
||||
const text = `
|
||||
@@ -306,11 +305,11 @@ var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
var a = 4; // comment 7`;
|
||||
runSingleFileTest("insertNodeAt1", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAt(sourceFile, text.indexOf("var y"), createTestClass(), { suffix: newLineCharacter });
|
||||
runSingleFileTest("insertNodeBefore3", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeBefore(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass());
|
||||
});
|
||||
runSingleFileTest("insertNodeAt2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAt(sourceFile, text.indexOf("; // comment 4"), createTestVariableDeclaration("z1"));
|
||||
runSingleFileTest("insertNodeAfterVariableDeclaration", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findVariableDeclarationContaining("y", sourceFile), createTestVariableDeclaration("z1"));
|
||||
});
|
||||
}
|
||||
{
|
||||
@@ -325,23 +324,22 @@ namespace M {
|
||||
var a = 4; // comment 7
|
||||
}`;
|
||||
runSingleFileTest("insertNodeBefore1", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeBefore(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeBefore(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass());
|
||||
});
|
||||
runSingleFileTest("insertNodeBefore2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeBefore(sourceFile, findChild("M", sourceFile), createTestClass(), { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeBefore(sourceFile, findChild("M", sourceFile), createTestClass());
|
||||
});
|
||||
runSingleFileTest("insertNodeAfter1", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass(), { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("y", sourceFile), createTestClass());
|
||||
});
|
||||
runSingleFileTest("insertNodeAfter2", /*placeOpenBraceOnNewLineForFunctions*/ true, text, /*validateNodes*/ true, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("M", sourceFile), createTestClass(), { prefix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("M", sourceFile), createTestClass());
|
||||
});
|
||||
}
|
||||
|
||||
function findOpenBraceForConstructor(sourceFile: SourceFile) {
|
||||
function findConstructor(sourceFile: SourceFile): ConstructorDeclaration {
|
||||
const classDecl = <ClassDeclaration>sourceFile.statements[0];
|
||||
const constructorDecl = forEach(classDecl.members, m => m.kind === SyntaxKind.Constructor && (<ConstructorDeclaration>m).body && <ConstructorDeclaration>m);
|
||||
return constructorDecl.body.getFirstToken();
|
||||
return find<ClassElement, ConstructorDeclaration>(classDecl.members, (m): m is ConstructorDeclaration => isConstructorDeclaration(m) && !!m.body)!;
|
||||
}
|
||||
function createTestSuperCall() {
|
||||
const superCall = createCall(
|
||||
@@ -359,8 +357,8 @@ class A {
|
||||
}
|
||||
}
|
||||
`;
|
||||
runSingleFileTest("insertNodeAfter3", /*placeOpenBraceOnNewLineForFunctions*/ false, text1, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findOpenBraceForConstructor(sourceFile), createTestSuperCall(), { suffix: newLineCharacter });
|
||||
runSingleFileTest("insertNodeAtConstructorStart", /*placeOpenBraceOnNewLineForFunctions*/ false, text1, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAtConstructorStart(sourceFile, findConstructor(sourceFile), createTestSuperCall());
|
||||
});
|
||||
const text2 = `
|
||||
class A {
|
||||
@@ -370,7 +368,7 @@ class A {
|
||||
}
|
||||
`;
|
||||
runSingleFileTest("insertNodeAfter4", /*placeOpenBraceOnNewLineForFunctions*/ false, text2, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("x", sourceFile), createTestSuperCall(), { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("x", sourceFile), createTestSuperCall());
|
||||
});
|
||||
const text3 = `
|
||||
class A {
|
||||
@@ -379,8 +377,8 @@ class A {
|
||||
}
|
||||
}
|
||||
`;
|
||||
runSingleFileTest("insertNodeAfter3-block with newline", /*placeOpenBraceOnNewLineForFunctions*/ false, text3, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findOpenBraceForConstructor(sourceFile), createTestSuperCall(), { suffix: newLineCharacter });
|
||||
runSingleFileTest("insertNodeAtConstructorStart-block with newline", /*placeOpenBraceOnNewLineForFunctions*/ false, text3, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAtConstructorStart(sourceFile, findConstructor(sourceFile), createTestSuperCall());
|
||||
});
|
||||
}
|
||||
{
|
||||
@@ -638,7 +636,7 @@ class A {
|
||||
}
|
||||
const insertAfter = findChild("x", sourceFile);
|
||||
for (const newNode of newNodes) {
|
||||
changeTracker.insertNodeAfter(sourceFile, insertAfter, newNode, { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, insertAfter, newNode);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -649,7 +647,7 @@ class A {
|
||||
}
|
||||
`;
|
||||
runSingleFileTest("insertNodeAfterInClass1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), createProperty(undefined, undefined, "a", undefined, createKeywordTypeNode(SyntaxKind.BooleanKeyword), undefined), { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), createProperty(undefined, undefined, "a", undefined, createKeywordTypeNode(SyntaxKind.BooleanKeyword), undefined));
|
||||
});
|
||||
}
|
||||
{
|
||||
@@ -659,7 +657,7 @@ class A {
|
||||
}
|
||||
`;
|
||||
runSingleFileTest("insertNodeAfterInClass2", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), createProperty(undefined, undefined, "a", undefined, createKeywordTypeNode(SyntaxKind.BooleanKeyword), undefined), { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), createProperty(undefined, undefined, "a", undefined, createKeywordTypeNode(SyntaxKind.BooleanKeyword), undefined));
|
||||
});
|
||||
}
|
||||
{
|
||||
@@ -698,7 +696,7 @@ class A {
|
||||
/*questionToken*/ undefined,
|
||||
createKeywordTypeNode(SyntaxKind.AnyKeyword),
|
||||
/*initializer*/ undefined);
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode, { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode);
|
||||
});
|
||||
}
|
||||
{
|
||||
@@ -716,7 +714,7 @@ class A {
|
||||
/*questionToken*/ undefined,
|
||||
createKeywordTypeNode(SyntaxKind.AnyKeyword),
|
||||
/*initializer*/ undefined);
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode, { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode);
|
||||
});
|
||||
}
|
||||
{
|
||||
@@ -733,7 +731,7 @@ interface A {
|
||||
/*questionToken*/ undefined,
|
||||
createKeywordTypeNode(SyntaxKind.AnyKeyword),
|
||||
/*initializer*/ undefined);
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode, { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode);
|
||||
});
|
||||
}
|
||||
{
|
||||
@@ -750,7 +748,7 @@ interface A {
|
||||
/*questionToken*/ undefined,
|
||||
createKeywordTypeNode(SyntaxKind.AnyKeyword),
|
||||
/*initializer*/ undefined);
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode, { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, findChild("x", sourceFile), newNode);
|
||||
});
|
||||
}
|
||||
{
|
||||
@@ -759,7 +757,7 @@ let x = foo
|
||||
`;
|
||||
runSingleFileTest("insertNodeInStatementListAfterNodeWithoutSeparator1", /*placeOpenBraceOnNewLineForFunctions*/ false, text, /*validateNodes*/ false, (sourceFile, changeTracker) => {
|
||||
const newNode = createStatement(createParen(createLiteral(1)));
|
||||
changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("x", sourceFile), newNode, { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, findVariableStatementContaining("x", sourceFile), newNode);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace ts.codefix {
|
||||
getAllCodeActions: context => {
|
||||
const seenNames = createMap<true>();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const { newLineCharacter, program } = context;
|
||||
const info = getInfo(diag.file!, diag.start!, context.program.getTypeChecker());
|
||||
const { program } = context;
|
||||
const info = getInfo(diag.file!, diag.start!, program.getTypeChecker());
|
||||
if (!info) return;
|
||||
const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info;
|
||||
if (!addToSeen(seenNames, token.text)) {
|
||||
@@ -31,15 +31,15 @@ namespace ts.codefix {
|
||||
|
||||
// Always prefer to add a method declaration if possible.
|
||||
if (call) {
|
||||
addMethodDeclaration(changes, classDeclarationSourceFile, classDeclaration, token, call, newLineCharacter, makeStatic, inJs);
|
||||
addMethodDeclaration(changes, classDeclarationSourceFile, classDeclaration, token, call, makeStatic, inJs);
|
||||
}
|
||||
else {
|
||||
if (inJs) {
|
||||
addMissingMemberInJs(changes, classDeclarationSourceFile, classDeclaration, token.text, makeStatic, newLineCharacter);
|
||||
addMissingMemberInJs(changes, classDeclarationSourceFile, classDeclaration, token.text, makeStatic);
|
||||
}
|
||||
else {
|
||||
const typeNode = getTypeNode(program.getTypeChecker(), classDeclaration, token);
|
||||
addPropertyDeclaration(changes, classDeclarationSourceFile, classDeclaration, token.text, typeNode, makeStatic, newLineCharacter);
|
||||
addPropertyDeclaration(changes, classDeclarationSourceFile, classDeclaration, token.text, typeNode, makeStatic);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -96,20 +96,20 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function getActionsForAddMissingMemberInJavaScriptFile(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): CodeFixAction | undefined {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic, context.newLineCharacter));
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic));
|
||||
if (changes.length === 0) return undefined;
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Initialize_static_property_0 : Diagnostics.Initialize_property_0_in_the_constructor), [tokenName]);
|
||||
return { description, changes, fixId };
|
||||
}
|
||||
|
||||
function addMissingMemberInJs(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean, newLineCharacter: string): void {
|
||||
function addMissingMemberInJs(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): void {
|
||||
if (makeStatic) {
|
||||
if (classDeclaration.kind === SyntaxKind.ClassExpression) {
|
||||
return;
|
||||
}
|
||||
const className = classDeclaration.name.getText();
|
||||
const staticInitialization = initializePropertyToUndefined(createIdentifier(className), tokenName);
|
||||
changeTracker.insertNodeAfter(classDeclarationSourceFile, classDeclaration, staticInitialization, { prefix: newLineCharacter, suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(classDeclarationSourceFile, classDeclaration, staticInitialization);
|
||||
}
|
||||
else {
|
||||
const classConstructor = getFirstConstructorWithBody(classDeclaration);
|
||||
@@ -117,7 +117,7 @@ namespace ts.codefix {
|
||||
return;
|
||||
}
|
||||
const propertyInitialization = initializePropertyToUndefined(createThis(), tokenName);
|
||||
changeTracker.insertNodeBefore(classDeclarationSourceFile, classConstructor.body.getLastToken(), propertyInitialization, { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAtConstructorEnd(classDeclarationSourceFile, classConstructor, propertyInitialization);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,13 +142,13 @@ namespace ts.codefix {
|
||||
return typeNode || createKeywordTypeNode(SyntaxKind.AnyKeyword);
|
||||
}
|
||||
|
||||
function createAddPropertyDeclarationAction(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction {
|
||||
function createAddPropertyDeclarationAction(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction {
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0), [tokenName]);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic, context.newLineCharacter));
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic));
|
||||
return { description, changes, fixId };
|
||||
}
|
||||
|
||||
function addPropertyDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode, makeStatic: boolean, newLineCharacter: string): void {
|
||||
function addPropertyDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode, makeStatic: boolean): void {
|
||||
const property = createProperty(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ makeStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined,
|
||||
@@ -156,10 +156,10 @@ namespace ts.codefix {
|
||||
/*questionToken*/ undefined,
|
||||
typeNode,
|
||||
/*initializer*/ undefined);
|
||||
changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, property, newLineCharacter);
|
||||
changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, property);
|
||||
}
|
||||
|
||||
function createAddIndexSignatureAction(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode): CodeFixAction {
|
||||
function createAddIndexSignatureAction(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode): CodeFixAction {
|
||||
// Index signatures cannot have the static modifier.
|
||||
const stringTypeNode = createKeywordTypeNode(SyntaxKind.StringKeyword);
|
||||
const indexingParameter = createParameter(
|
||||
@@ -176,19 +176,19 @@ namespace ts.codefix {
|
||||
[indexingParameter],
|
||||
typeNode);
|
||||
|
||||
const changes = textChanges.ChangeTracker.with(context, t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature, context.newLineCharacter));
|
||||
const changes = textChanges.ChangeTracker.with(context, t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature));
|
||||
// No fixId here because code-fix-all currently only works on adding individual named properties.
|
||||
return { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_property_0), [tokenName]), changes, fixId: undefined };
|
||||
}
|
||||
|
||||
function getActionForMethodDeclaration(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined {
|
||||
function getActionForMethodDeclaration(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined {
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0), [token.text]);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, context.newLineCharacter, makeStatic, inJs));
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs));
|
||||
return { description, changes, fixId };
|
||||
}
|
||||
|
||||
function addMethodDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, newLineCharacter: string, makeStatic: boolean, inJs: boolean) {
|
||||
function addMethodDeclaration(changeTracker: textChanges.ChangeTracker, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean) {
|
||||
const methodDeclaration = createMethodFromCallExpression(callExpression, token.text, inJs, makeStatic);
|
||||
changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, methodDeclaration, newLineCharacter);
|
||||
changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, methodDeclaration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ namespace ts.codefix {
|
||||
getCodeActions(context) {
|
||||
const { program, sourceFile, span } = context;
|
||||
const changes = textChanges.ChangeTracker.with(context, t =>
|
||||
addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), context.newLineCharacter, t));
|
||||
addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t));
|
||||
return changes.length === 0 ? undefined : [{ description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class), changes, fixId }];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
addMissingMembers(getClass(diag.file!, diag.start!), context.sourceFile, context.program.getTypeChecker(), context.newLineCharacter, changes);
|
||||
addMissingMembers(getClass(diag.file!, diag.start!), context.sourceFile, context.program.getTypeChecker(), changes);
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace ts.codefix {
|
||||
return classDeclaration as ClassLikeDeclaration;
|
||||
}
|
||||
|
||||
function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, checker: TypeChecker, newLineCharacter: string, changeTracker: textChanges.ChangeTracker): void {
|
||||
function addMissingMembers(classDeclaration: ClassLikeDeclaration, sourceFile: SourceFile, checker: TypeChecker, changeTracker: textChanges.ChangeTracker): void {
|
||||
const extendsNode = getClassExtendsHeritageClauseElement(classDeclaration);
|
||||
const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode);
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace ts.codefix {
|
||||
// so duplicates cannot occur.
|
||||
const abstractAndNonPrivateExtendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType).filter(symbolPointsToNonPrivateAndAbstractMember);
|
||||
|
||||
createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member, newLineCharacter));
|
||||
createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member));
|
||||
}
|
||||
|
||||
function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean {
|
||||
|
||||
@@ -5,11 +5,11 @@ namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions(context) {
|
||||
const { newLineCharacter, program, sourceFile, span } = context;
|
||||
const { program, sourceFile, span } = context;
|
||||
const classDeclaration = getClass(sourceFile, span.start);
|
||||
const checker = program.getTypeChecker();
|
||||
return mapDefined<ExpressionWithTypeArguments, CodeFixAction>(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, newLineCharacter, t));
|
||||
const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t));
|
||||
if (changes.length === 0) return undefined;
|
||||
const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]);
|
||||
return { description, changes, fixId };
|
||||
@@ -22,7 +22,7 @@ namespace ts.codefix {
|
||||
const classDeclaration = getClass(diag.file!, diag.start!);
|
||||
if (addToSeen(seenClassDeclarations, getNodeId(classDeclaration))) {
|
||||
for (const implementedTypeNode of getClassImplementsHeritageClauseElements(classDeclaration)) {
|
||||
addMissingDeclarations(context.program.getTypeChecker(), implementedTypeNode, diag.file!, classDeclaration, context.newLineCharacter, changes);
|
||||
addMissingDeclarations(context.program.getTypeChecker(), implementedTypeNode, diag.file!, classDeclaration, changes);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -40,7 +40,6 @@ namespace ts.codefix {
|
||||
implementedTypeNode: ExpressionWithTypeArguments,
|
||||
sourceFile: SourceFile,
|
||||
classDeclaration: ClassLikeDeclaration,
|
||||
newLineCharacter: string,
|
||||
changeTracker: textChanges.ChangeTracker
|
||||
): void {
|
||||
// Note that this is ultimately derived from a map indexed by symbol names,
|
||||
@@ -58,12 +57,12 @@ namespace ts.codefix {
|
||||
createMissingIndexSignatureDeclaration(implementedType, IndexKind.String);
|
||||
}
|
||||
|
||||
createMissingMemberNodes(classDeclaration, nonPrivateMembers, checker, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member, newLineCharacter));
|
||||
createMissingMemberNodes(classDeclaration, nonPrivateMembers, checker, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member));
|
||||
|
||||
function createMissingIndexSignatureDeclaration(type: InterfaceType, kind: IndexKind): void {
|
||||
const indexInfoOfKind = checker.getIndexInfoOfType(type, kind);
|
||||
if (indexInfoOfKind) {
|
||||
changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, checker.indexInfoToIndexSignatureDeclaration(indexInfoOfKind, kind, classDeclaration), newLineCharacter);
|
||||
changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, checker.indexInfoToIndexSignatureDeclaration(indexInfoOfKind, kind, classDeclaration));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,30 +5,30 @@ namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions(context) {
|
||||
const { sourceFile } = context;
|
||||
const nodes = getNodes(sourceFile, context.span.start);
|
||||
const { sourceFile, span } = context;
|
||||
const nodes = getNodes(sourceFile, span.start);
|
||||
if (!nodes) return undefined;
|
||||
const { constructor, superCall } = nodes;
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, constructor, superCall, context.newLineCharacter));
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, constructor, superCall));
|
||||
return [{ description: getLocaleSpecificMessage(Diagnostics.Make_super_call_the_first_statement_in_the_constructor), changes, fixId }];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions(context) {
|
||||
const { newLineCharacter, sourceFile } = context;
|
||||
const { sourceFile } = context;
|
||||
const seenClasses = createMap<true>(); // Ensure we only do this once per class.
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const nodes = getNodes(diag.file!, diag.start!);
|
||||
if (!nodes) return;
|
||||
const { constructor, superCall } = nodes;
|
||||
if (addToSeen(seenClasses, getNodeId(constructor.parent))) {
|
||||
doChange(changes, sourceFile, constructor, superCall, newLineCharacter);
|
||||
doChange(changes, sourceFile, constructor, superCall);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, constructor: ConstructorDeclaration, superCall: ExpressionStatement, newLineCharacter: string): void {
|
||||
changes.insertNodeAtConstructorStart(sourceFile, constructor, superCall, newLineCharacter);
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, constructor: ConstructorDeclaration, superCall: ExpressionStatement): void {
|
||||
changes.insertNodeAtConstructorStart(sourceFile, constructor, superCall);
|
||||
changes.deleteNode(sourceFile, superCall);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,14 @@ namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions(context) {
|
||||
const { sourceFile } = context;
|
||||
const ctr = getNode(sourceFile, context.span.start);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, ctr, context.newLineCharacter));
|
||||
const { sourceFile, span } = context;
|
||||
const ctr = getNode(sourceFile, span.start);
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, ctr));
|
||||
return [{ description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call), changes, fixId }];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) =>
|
||||
doChange(changes, context.sourceFile, getNode(diag.file, diag.start!), context.newLineCharacter)),
|
||||
doChange(changes, context.sourceFile, getNode(diag.file, diag.start!))),
|
||||
});
|
||||
|
||||
function getNode(sourceFile: SourceFile, pos: number): ConstructorDeclaration {
|
||||
@@ -21,8 +21,8 @@ namespace ts.codefix {
|
||||
return token.parent as ConstructorDeclaration;
|
||||
}
|
||||
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, ctr: ConstructorDeclaration, newLineCharacter: string) {
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, ctr: ConstructorDeclaration) {
|
||||
const superCall = createStatement(createCall(createSuper(), /*typeArguments*/ undefined, /*argumentsArray*/ emptyArray));
|
||||
changes.insertNodeAtConstructorStart(sourceFile, ctr, superCall, newLineCharacter);
|
||||
changes.insertNodeAtConstructorStart(sourceFile, ctr, superCall);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
function getCodeActionForNewImport(context: SymbolContext & { kind: ImportKind }, moduleSpecifier: string): ImportCodeAction {
|
||||
const { kind, sourceFile, newLineCharacter, symbolName } = context;
|
||||
const { kind, sourceFile, symbolName } = context;
|
||||
const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax);
|
||||
|
||||
const moduleSpecifierWithoutQuotes = stripQuotes(moduleSpecifier);
|
||||
@@ -276,10 +276,10 @@ namespace ts.codefix {
|
||||
|
||||
const changes = ChangeTracker.with(context, changeTracker => {
|
||||
if (lastImportDeclaration) {
|
||||
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: newLineCharacter });
|
||||
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl);
|
||||
}
|
||||
else {
|
||||
changeTracker.insertNodeAt(sourceFile, getSourceFileImportLocation(sourceFile), importDecl, { suffix: `${newLineCharacter}${newLineCharacter}` });
|
||||
changeTracker.insertNodeAtTopOfFile(sourceFile, importDecl);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ namespace ts.refactor.convertFunctionToES6Class {
|
||||
|
||||
const { file: sourceFile } = context;
|
||||
const ctorSymbol = getConstructorSymbol(context);
|
||||
const newLine = context.formatContext.options.newLineCharacter;
|
||||
|
||||
const deletedNodes: Node[] = [];
|
||||
const deletes: (() => any)[] = [];
|
||||
@@ -88,7 +87,7 @@ namespace ts.refactor.convertFunctionToES6Class {
|
||||
}
|
||||
|
||||
// Because the preceding node could be touched, we need to insert nodes before delete nodes.
|
||||
changeTracker.insertNodeAfter(sourceFile, precedingNode, newClassDeclaration, { suffix: newLine });
|
||||
changeTracker.insertNodeAfter(sourceFile, precedingNode, newClassDeclaration);
|
||||
for (const deleteCallback of deletes) {
|
||||
deleteCallback();
|
||||
}
|
||||
|
||||
@@ -810,13 +810,10 @@ namespace ts.refactor.extractSymbol {
|
||||
const minInsertionPos = (isReadonlyArray(range.range) ? last(range.range) : range.range).end;
|
||||
const nodeToInsertBefore = getNodeToInsertFunctionBefore(minInsertionPos, scope);
|
||||
if (nodeToInsertBefore) {
|
||||
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newFunction, { suffix: context.newLineCharacter + context.newLineCharacter });
|
||||
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newFunction, /*blankLineBetween*/ true);
|
||||
}
|
||||
else {
|
||||
changeTracker.insertNodeBefore(context.file, scope.getLastToken(), newFunction, {
|
||||
prefix: isLineBreak(file.text.charCodeAt(scope.getLastToken().pos)) ? context.newLineCharacter : context.newLineCharacter + context.newLineCharacter,
|
||||
suffix: context.newLineCharacter
|
||||
});
|
||||
changeTracker.insertNodeAtEndOfScope(context.file, scope, newFunction);
|
||||
}
|
||||
|
||||
const newNodes: Node[] = [];
|
||||
@@ -959,10 +956,12 @@ namespace ts.refactor.extractSymbol {
|
||||
}
|
||||
}
|
||||
|
||||
const replacementRange = isReadonlyArray(range.range)
|
||||
? { pos: first(range.range).getStart(), end: last(range.range).end }
|
||||
: { pos: range.range.getStart(), end: range.range.end };
|
||||
changeTracker.replaceRangeWithNodes(context.file, replacementRange, newNodes, { nodeSeparator: context.newLineCharacter });
|
||||
if (isReadonlyArray(range.range)) {
|
||||
changeTracker.replaceNodesWithNodes(context.file, range.range, newNodes);
|
||||
}
|
||||
else {
|
||||
changeTracker.replaceNodeWithNodes(context.file, range.range, newNodes);
|
||||
}
|
||||
|
||||
const edits = changeTracker.getChanges();
|
||||
const renameRange = isReadonlyArray(range.range) ? first(range.range) : range.range;
|
||||
@@ -1040,7 +1039,7 @@ namespace ts.refactor.extractSymbol {
|
||||
// Declare
|
||||
const maxInsertionPos = node.pos;
|
||||
const nodeToInsertBefore = getNodeToInsertPropertyBefore(maxInsertionPos, scope);
|
||||
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, { suffix: context.newLineCharacter + context.newLineCharacter });
|
||||
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, /*blankLineBetween*/ true);
|
||||
|
||||
// Consume
|
||||
changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference);
|
||||
@@ -1055,8 +1054,8 @@ namespace ts.refactor.extractSymbol {
|
||||
const oldVariableDeclaration = getContainingVariableDeclarationIfInList(node, scope);
|
||||
if (oldVariableDeclaration) {
|
||||
// Declare
|
||||
// CONSIDER: could detect that each is on a separate line
|
||||
changeTracker.insertNodeAt(context.file, oldVariableDeclaration.getStart(), newVariableDeclaration, { suffix: ", " });
|
||||
// CONSIDER: could detect that each is on a separate line (See `extractConstant_VariableList_MultipleLines` in `extractConstants.ts`)
|
||||
changeTracker.insertNodeBefore(context.file, oldVariableDeclaration, newVariableDeclaration);
|
||||
|
||||
// Consume
|
||||
const localReference = createIdentifier(localNameText);
|
||||
@@ -1078,17 +1077,10 @@ namespace ts.refactor.extractSymbol {
|
||||
// Declare
|
||||
const nodeToInsertBefore = getNodeToInsertConstantBefore(node, scope);
|
||||
if (nodeToInsertBefore.pos === 0) {
|
||||
// If we're at the beginning of the file, we need to take care not to insert before header comments
|
||||
// (e.g. copyright, triple-slash references). Fortunately, this problem has already been solved
|
||||
// for imports.
|
||||
const insertionPos = getSourceFileImportLocation(file);
|
||||
changeTracker.insertNodeAt(context.file, insertionPos, newVariableStatement, {
|
||||
prefix: insertionPos === 0 ? undefined : context.newLineCharacter,
|
||||
suffix: isLineBreak(file.text.charCodeAt(insertionPos)) ? context.newLineCharacter : context.newLineCharacter + context.newLineCharacter
|
||||
});
|
||||
changeTracker.insertNodeAtTopOfFile(context.file, newVariableStatement);
|
||||
}
|
||||
else {
|
||||
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariableStatement, { suffix: context.newLineCharacter + context.newLineCharacter });
|
||||
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariableStatement, /*blankLineBetween*/ true);
|
||||
}
|
||||
|
||||
// Consume
|
||||
@@ -1285,12 +1277,12 @@ namespace ts.refactor.extractSymbol {
|
||||
* If `scope` contains a function after `minPos`, then return the first such function.
|
||||
* Otherwise, return `undefined`.
|
||||
*/
|
||||
function getNodeToInsertFunctionBefore(minPos: number, scope: Scope): Node | undefined {
|
||||
function getNodeToInsertFunctionBefore(minPos: number, scope: Scope): Statement | ClassElement | undefined {
|
||||
return find<Statement | ClassElement>(getStatementsOrClassElements(scope), child =>
|
||||
child.pos >= minPos && isFunctionLikeDeclaration(child) && !isConstructorDeclaration(child));
|
||||
}
|
||||
|
||||
function getNodeToInsertPropertyBefore(maxPos: number, scope: ClassLikeDeclaration): Node {
|
||||
function getNodeToInsertPropertyBefore(maxPos: number, scope: ClassLikeDeclaration): ClassElement {
|
||||
const members = scope.members;
|
||||
Debug.assert(members.length > 0); // There must be at least one child, since we extracted from one.
|
||||
|
||||
@@ -1316,7 +1308,7 @@ namespace ts.refactor.extractSymbol {
|
||||
return prevMember;
|
||||
}
|
||||
|
||||
function getNodeToInsertConstantBefore(node: Node, scope: Scope): Node {
|
||||
function getNodeToInsertConstantBefore(node: Node, scope: Scope): Statement {
|
||||
Debug.assert(!isClassLike(scope));
|
||||
|
||||
let prevScope: Scope | undefined = undefined;
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace ts.textChanges {
|
||||
readonly options?: never;
|
||||
}
|
||||
|
||||
export interface ChangeMultipleNodesOptions extends ChangeNodeOptions {
|
||||
interface ChangeMultipleNodesOptions extends ChangeNodeOptions {
|
||||
nodeSeparator: string;
|
||||
}
|
||||
interface ReplaceWithMultipleNodes extends BaseChange {
|
||||
@@ -192,7 +192,7 @@ namespace ts.textChanges {
|
||||
}
|
||||
|
||||
export class ChangeTracker {
|
||||
private changes: Change[] = [];
|
||||
private readonly changes: Change[] = [];
|
||||
private readonly newLineCharacter: string;
|
||||
private readonly deletedNodesInLists: true[] = []; // Stores ids of nodes in lists that we already deleted. Used to avoid deleting `, ` twice in `a, b`.
|
||||
// Map from class id to nodes to insert at the start
|
||||
@@ -319,49 +319,75 @@ namespace ts.textChanges {
|
||||
return this;
|
||||
}
|
||||
|
||||
public replaceNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: ReadonlyArray<Node>, options: ChangeMultipleNodesOptions) {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, oldNode, options, Position.Start);
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, oldNode, options);
|
||||
return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options);
|
||||
public replaceNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: ReadonlyArray<Node>): void {
|
||||
this.replaceWithMultiple(sourceFile, oldNode.getStart(sourceFile), oldNode.getEnd(), newNodes, { nodeSeparator: this.newLineCharacter });
|
||||
}
|
||||
|
||||
public replaceNodesWithNodes(sourceFile: SourceFile, oldNodes: ReadonlyArray<Node>, newNodes: ReadonlyArray<Node>, options: ChangeMultipleNodesOptions) {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, oldNodes[0], options, Position.Start);
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, lastOrUndefined(oldNodes), options);
|
||||
return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options);
|
||||
public replaceNodesWithNodes(sourceFile: SourceFile, oldNodes: ReadonlyArray<Node>, newNodes: ReadonlyArray<Node>): void {
|
||||
this.replaceWithMultiple(sourceFile, first(oldNodes).getStart(sourceFile), last(oldNodes).getEnd(), newNodes, { nodeSeparator: this.newLineCharacter });
|
||||
}
|
||||
|
||||
public replaceRangeWithNodes(sourceFile: SourceFile, range: TextRange, newNodes: ReadonlyArray<Node>, options: ChangeMultipleNodesOptions) {
|
||||
return this.replaceWithMultiple(sourceFile, range.pos, range.end, newNodes, options);
|
||||
}
|
||||
|
||||
public replaceNodeRangeWithNodes(sourceFile: SourceFile, startNode: Node, endNode: Node, newNodes: ReadonlyArray<Node>, options: ChangeMultipleNodesOptions) {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.Start);
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, endNode, options);
|
||||
return this.replaceWithMultiple(sourceFile, startPosition, endPosition, newNodes, options);
|
||||
}
|
||||
|
||||
public insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}) {
|
||||
private insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}) {
|
||||
this.changes.push({ kind: ChangeKind.ReplaceWithSingleNode, sourceFile, options, node: newNode, range: { pos, end: pos } });
|
||||
return this;
|
||||
}
|
||||
|
||||
public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, options: InsertNodeOptions & ConfigurableStart = {}) {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, before, options, Position.Start);
|
||||
return this.replaceWithSingle(sourceFile, startPosition, startPosition, newNode, options);
|
||||
public insertNodeAtTopOfFile(sourceFile: SourceFile, newNode: Statement): void {
|
||||
const pos = getInsertionPositionAtSourceFileTop(sourceFile);
|
||||
this.insertNodeAt(sourceFile, pos, newNode, {
|
||||
prefix: pos === 0 ? undefined : this.newLineCharacter,
|
||||
suffix: isLineBreak(sourceFile.text.charCodeAt(pos)) ? this.newLineCharacter : this.newLineCharacter + this.newLineCharacter,
|
||||
});
|
||||
}
|
||||
|
||||
public insertNodeAtConstructorStart(sourceFile: SourceFile, ctr: ConstructorDeclaration, newStatement: Statement, newLineCharacter: string): void {
|
||||
public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, blankLineBetween = false) {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, before, {}, Position.Start);
|
||||
return this.replaceWithSingle(sourceFile, startPosition, startPosition, newNode, this.getOptionsForInsertNodeBefore(before, blankLineBetween));
|
||||
}
|
||||
|
||||
private getOptionsForInsertNodeBefore(before: Node, doubleNewlines: boolean): ChangeNodeOptions {
|
||||
if (isStatement(before) || isClassElement(before)) {
|
||||
return { suffix: doubleNewlines ? this.newLineCharacter + this.newLineCharacter : this.newLineCharacter };
|
||||
}
|
||||
else if (isVariableDeclaration(before)) { // insert `x = 1, ` into `const x = 1, y = 2;
|
||||
return { suffix: ", " };
|
||||
}
|
||||
throw Debug.failBadSyntaxKind(before); // We haven't handled this kind of node yet -- add it
|
||||
}
|
||||
|
||||
public insertNodeAtConstructorStart(sourceFile: SourceFile, ctr: ConstructorDeclaration, newStatement: Statement): void {
|
||||
const firstStatement = firstOrUndefined(ctr.body.statements);
|
||||
if (!firstStatement || !ctr.body.multiLine) {
|
||||
this.replaceNode(sourceFile, ctr.body, createBlock([newStatement, ...ctr.body.statements], /*multiLine*/ true), { useNonAdjustedEndPosition: true });
|
||||
this.replaceConstructorBody(sourceFile, ctr, [newStatement, ...ctr.body.statements]);
|
||||
}
|
||||
else {
|
||||
this.insertNodeBefore(sourceFile, firstStatement, newStatement, { suffix: newLineCharacter });
|
||||
this.insertNodeBefore(sourceFile, firstStatement, newStatement);
|
||||
}
|
||||
}
|
||||
|
||||
public insertNodeAtClassStart(sourceFile: SourceFile, cls: ClassLikeDeclaration, newElement: ClassElement, newLineCharacter: string): void {
|
||||
public insertNodeAtConstructorEnd(sourceFile: SourceFile, ctr: ConstructorDeclaration, newStatement: Statement): void {
|
||||
const lastStatement = lastOrUndefined(ctr.body.statements);
|
||||
if (!lastStatement || !ctr.body.multiLine) {
|
||||
this.replaceConstructorBody(sourceFile, ctr, [...ctr.body.statements, newStatement]);
|
||||
}
|
||||
else {
|
||||
this.insertNodeAfter(sourceFile, lastStatement, newStatement);
|
||||
}
|
||||
}
|
||||
|
||||
private replaceConstructorBody(sourceFile: SourceFile, ctr: ConstructorDeclaration, statements: ReadonlyArray<Statement>): void {
|
||||
this.replaceNode(sourceFile, ctr.body, createBlock(statements, /*multiLine*/ true), { useNonAdjustedEndPosition: true });
|
||||
}
|
||||
|
||||
public insertNodeAtEndOfScope(sourceFile: SourceFile, scope: Node, newNode: Node): void {
|
||||
const startPosition = getAdjustedStartPosition(sourceFile, scope.getLastToken(), {}, Position.Start);
|
||||
this.replaceWithSingle(sourceFile, startPosition, startPosition, newNode, {
|
||||
prefix: isLineBreak(sourceFile.text.charCodeAt(scope.getLastToken().pos)) ? this.newLineCharacter : this.newLineCharacter + this.newLineCharacter,
|
||||
suffix: this.newLineCharacter
|
||||
});
|
||||
}
|
||||
|
||||
public insertNodeAtClassStart(sourceFile: SourceFile, cls: ClassLikeDeclaration, newElement: ClassElement): void {
|
||||
const firstMember = firstOrUndefined(cls.members);
|
||||
if (!firstMember) {
|
||||
const id = getNodeId(cls).toString();
|
||||
@@ -375,11 +401,11 @@ namespace ts.textChanges {
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.insertNodeBefore(sourceFile, firstMember, newElement, { suffix: newLineCharacter });
|
||||
this.insertNodeBefore(sourceFile, firstMember, newElement);
|
||||
}
|
||||
}
|
||||
|
||||
public insertNodeAfter(sourceFile: SourceFile, after: Node, newNode: Node, options: InsertNodeOptions & ConfigurableEnd = {}): this {
|
||||
public insertNodeAfter(sourceFile: SourceFile, after: Node, newNode: Node): this {
|
||||
if (isStatementButNotDeclaration(after) ||
|
||||
after.kind === SyntaxKind.PropertyDeclaration ||
|
||||
after.kind === SyntaxKind.PropertySignature ||
|
||||
@@ -396,8 +422,21 @@ namespace ts.textChanges {
|
||||
});
|
||||
}
|
||||
}
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, after, options);
|
||||
return this.replaceWithSingle(sourceFile, endPosition, endPosition, newNode, options);
|
||||
const endPosition = getAdjustedEndPosition(sourceFile, after, {});
|
||||
return this.replaceWithSingle(sourceFile, endPosition, endPosition, newNode, this.getInsertNodeAfterOptions(after));
|
||||
}
|
||||
|
||||
private getInsertNodeAfterOptions(node: Node): InsertNodeOptions {
|
||||
if (isClassDeclaration(node) || isModuleDeclaration(node)) {
|
||||
return { prefix: this.newLineCharacter, suffix: this.newLineCharacter };
|
||||
}
|
||||
else if (isStatement(node) || isClassElement(node) || isTypeElement(node)) {
|
||||
return { suffix: this.newLineCharacter };
|
||||
}
|
||||
else if (isVariableDeclaration(node)) {
|
||||
return { prefix: ", " };
|
||||
}
|
||||
throw Debug.failBadSyntaxKind(node); // We haven't handled this kind of node yet -- add it
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -804,4 +843,46 @@ namespace ts.textChanges {
|
||||
this.lastNonTriviaPosition = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function getInsertionPositionAtSourceFileTop({ text }: SourceFile): number {
|
||||
const shebang = getShebang(text);
|
||||
let position = 0;
|
||||
if (shebang !== undefined) {
|
||||
position = shebang.length;
|
||||
advancePastLineBreak();
|
||||
}
|
||||
|
||||
// For a source file, it is possible there are detached comments we should not skip
|
||||
let ranges = getLeadingCommentRanges(text, position);
|
||||
if (!ranges) return position;
|
||||
// However we should still skip a pinned comment at the top
|
||||
if (ranges.length && ranges[0].kind === SyntaxKind.MultiLineCommentTrivia && isPinnedComment(text, ranges[0])) {
|
||||
position = ranges[0].end;
|
||||
advancePastLineBreak();
|
||||
ranges = ranges.slice(1);
|
||||
}
|
||||
// As well as any triple slash references
|
||||
for (const range of ranges) {
|
||||
if (range.kind === SyntaxKind.SingleLineCommentTrivia && isRecognizedTripleSlashComment(text, range.pos, range.end)) {
|
||||
position = range.end;
|
||||
advancePastLineBreak();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return position;
|
||||
|
||||
function advancePastLineBreak() {
|
||||
if (position < text.length) {
|
||||
const charCode = text.charCodeAt(position);
|
||||
if (isLineBreak(charCode)) {
|
||||
position++;
|
||||
|
||||
if (position < text.length && charCode === CharacterCodes.carriageReturn && text.charCodeAt(position) === CharacterCodes.lineFeed) {
|
||||
position++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1332,48 +1332,6 @@ namespace ts {
|
||||
return position;
|
||||
}
|
||||
|
||||
export function getSourceFileImportLocation({ text }: SourceFile) {
|
||||
const shebang = getShebang(text);
|
||||
let position = 0;
|
||||
if (shebang !== undefined) {
|
||||
position = shebang.length;
|
||||
advancePastLineBreak();
|
||||
}
|
||||
|
||||
// For a source file, it is possible there are detached comments we should not skip
|
||||
let ranges = getLeadingCommentRanges(text, position);
|
||||
if (!ranges) return position;
|
||||
// However we should still skip a pinned comment at the top
|
||||
if (ranges.length && ranges[0].kind === SyntaxKind.MultiLineCommentTrivia && isPinnedComment(text, ranges[0])) {
|
||||
position = ranges[0].end;
|
||||
advancePastLineBreak();
|
||||
ranges = ranges.slice(1);
|
||||
}
|
||||
// As well as any triple slash references
|
||||
for (const range of ranges) {
|
||||
if (range.kind === SyntaxKind.SingleLineCommentTrivia && isRecognizedTripleSlashComment(text, range.pos, range.end)) {
|
||||
position = range.end;
|
||||
advancePastLineBreak();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return position;
|
||||
|
||||
function advancePastLineBreak() {
|
||||
if (position < text.length) {
|
||||
const charCode = text.charCodeAt(position);
|
||||
if (isLineBreak(charCode)) {
|
||||
position++;
|
||||
|
||||
if (position < text.length && charCode === CharacterCodes.carriageReturn && text.charCodeAt(position) === CharacterCodes.lineFeed) {
|
||||
position++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a deep, memberwise clone of a node with no source map location.
|
||||
*
|
||||
|
||||
@@ -3,4 +3,4 @@ const /*About A*/a = 1,
|
||||
/*About B*/b = /*[#|*/a + 1/*|]*/;
|
||||
// ==SCOPE::Extract to constant in enclosing scope==
|
||||
const /*About A*/a = 1,
|
||||
/*About B*/newLocal = a + 1, b = /*RENAME*/newLocal;
|
||||
newLocal = a + 1, /*About B*/b = /*RENAME*/newLocal;
|
||||
@@ -3,4 +3,4 @@ const /*About A*/a = 1,
|
||||
/*About B*/b = /*[#|*/a + 1/*|]*/;
|
||||
// ==SCOPE::Extract to constant in enclosing scope==
|
||||
const /*About A*/a = 1,
|
||||
/*About B*/newLocal = a + 1, b = /*RENAME*/newLocal;
|
||||
newLocal = a + 1, /*About B*/b = /*RENAME*/newLocal;
|
||||
@@ -23,4 +23,4 @@ namespace M {
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ var a = 4; // comment 7
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
var yz1 = {
|
||||
var y, z1 = {
|
||||
p1: 1
|
||||
}; // comment 4
|
||||
var z = 3; // comment 5
|
||||
@@ -11,6 +11,5 @@ class A {
|
||||
class A {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,11 @@ var a = 4; // comment 7
|
||||
|
||||
// comment 1
|
||||
var x = 1; // comment 2
|
||||
// comment 3
|
||||
public class class1 implements interface1
|
||||
{
|
||||
property1: boolean;
|
||||
}
|
||||
// comment 3
|
||||
var y; // comment 4
|
||||
var z = 3; // comment 5
|
||||
// comment 6
|
||||
@@ -17,7 +17,7 @@ verify.codeFix({
|
||||
index: 0,
|
||||
// TODO: GH#18445
|
||||
newFileContent: `class C {
|
||||
constructor() {
|
||||
constructor() {\r
|
||||
this.foo = undefined;\r
|
||||
}
|
||||
method() {
|
||||
|
||||
@@ -15,7 +15,7 @@ verify.codeFix({
|
||||
index: 0,
|
||||
// TODO: GH#18445
|
||||
newFileContent: `class C {
|
||||
constructor() {
|
||||
constructor() {\r
|
||||
this.foo = undefined;\r
|
||||
}
|
||||
prop = ()=>{ this.foo === 10 };
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
verify.codeFixAll({
|
||||
fixId: "addMissingMember",
|
||||
newFileContent:
|
||||
// TODO: GH#18445 GH#20073
|
||||
// TODO: GH#18445
|
||||
`class C {
|
||||
y() {\r
|
||||
throw new Error("Method not implemented.");\r
|
||||
}\r
|
||||
constructor() {this.x = undefined;\r
|
||||
}
|
||||
constructor() {\r
|
||||
this.x = undefined;\r
|
||||
}
|
||||
method() {
|
||||
this.x;
|
||||
this.y();
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
////// newFunction
|
||||
/////*start*/1 + 1/*end*/;
|
||||
|
||||
// NOTE: '// newFunction' should be included, but due to incorrect handling of trivia,
|
||||
// it's omitted right now.
|
||||
goTo.select('start', 'end')
|
||||
edit.applyRefactor({
|
||||
refactorName: "Extract Symbol",
|
||||
|
||||
@@ -15,6 +15,7 @@ verify.importFixAtPosition([
|
||||
`/*!
|
||||
* I'm a license or something
|
||||
*/
|
||||
|
||||
import { f1 } from "ambient-module";
|
||||
|
||||
f1();`
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
verify.importFixAtPosition([
|
||||
`/// <reference path="./tripleSlashReference.ts" />
|
||||
|
||||
import { f1 } from "./Module";
|
||||
|
||||
f1();`
|
||||
|
||||
@@ -24,6 +24,7 @@ verify.importFixAtPosition([
|
||||
/// <reference types="node" />
|
||||
/// <reference path="./a.ts" />
|
||||
/// <amd-dependency path="./b.ts" />
|
||||
|
||||
import { f1 } from "./module";
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ goTo.file("/b.ts");
|
||||
|
||||
verify.importFixAtPosition([
|
||||
`#!/usr/bin/env node
|
||||
|
||||
import { foo } from "./a";
|
||||
|
||||
foo`,
|
||||
|
||||
Reference in New Issue
Block a user