apply feedback from pr
This commit is contained in:
parent
3e7dcad726
commit
649b53c8de
|
@ -13,12 +13,12 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression {
|
|||
|
||||
registerRefactor(refactorName, { getEditsForAction, getAvailableActions });
|
||||
|
||||
interface BasicInfo {
|
||||
fromDeclaration: boolean;
|
||||
interface FunctionInfo {
|
||||
selectedVariableDeclaration: boolean;
|
||||
func: FunctionExpression | ArrowFunction;
|
||||
}
|
||||
|
||||
interface HeaderInfo {
|
||||
interface VariableInfo {
|
||||
variableDeclaration: VariableDeclaration;
|
||||
variableDeclarationList: VariableDeclarationList;
|
||||
statement: VariableStatement;
|
||||
|
@ -26,22 +26,21 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression {
|
|||
}
|
||||
|
||||
function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined {
|
||||
|
||||
const { file, startPosition } = context;
|
||||
const info = getInfo(file, startPosition);
|
||||
const info = getFunctionInfo(file, startPosition);
|
||||
|
||||
if (!info) return undefined;
|
||||
const { fromDeclaration, func } = info;
|
||||
const { selectedVariableDeclaration, func } = info;
|
||||
const possibleActions: RefactorActionInfo[] = [];
|
||||
|
||||
if (fromDeclaration || (isArrowFunction(func) && isVariableDeclaration(func.parent))) {
|
||||
if (selectedVariableDeclaration || (isArrowFunction(func) && isVariableDeclaration(func.parent))) {
|
||||
possibleActions.push({
|
||||
name: toNamedFunctionActionName,
|
||||
description: toNamedFunctionActionDescription
|
||||
});
|
||||
}
|
||||
|
||||
if (!fromDeclaration && isArrowFunction(func)) {
|
||||
if (!selectedVariableDeclaration && isArrowFunction(func)) {
|
||||
possibleActions.push({
|
||||
name: toAnonymousFunctionActionName,
|
||||
description: toAnonymousFunctionActionDescription
|
||||
|
@ -64,122 +63,126 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression {
|
|||
|
||||
function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined {
|
||||
const { file, startPosition } = context;
|
||||
const info = getInfo(file, startPosition);
|
||||
const info = getFunctionInfo(file, startPosition);
|
||||
|
||||
if (!info) return undefined;
|
||||
const { func } = info;
|
||||
|
||||
let body: Block | ConciseBody;
|
||||
let newNode: ArrowFunction | FunctionExpression | FunctionDeclaration;
|
||||
let edits: FileTextChanges[] = [];
|
||||
|
||||
switch (actionName) {
|
||||
case toAnonymousFunctionActionName:
|
||||
|
||||
body = makeBlock(func);
|
||||
newNode = createFunctionExpression(func.modifiers, func.asteriskToken, /* name */ undefined, func.typeParameters, func.parameters, func.type, body);
|
||||
edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, newNode));
|
||||
break;
|
||||
return getEditInfoForConvertToAnonymousFunction(context, func);
|
||||
|
||||
case toNamedFunctionActionName:
|
||||
|
||||
body = makeBlock(func);
|
||||
const headInfo = getHeaderInfo(func);
|
||||
if (!headInfo) return undefined;
|
||||
|
||||
const { variableDeclaration, variableDeclarationList, statement, name } = headInfo;
|
||||
newNode = makeFunctionDeclaration(func, statement, name, body);
|
||||
|
||||
if (variableDeclarationList.declarations.length === 1) {
|
||||
edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement, newNode));
|
||||
}
|
||||
else {
|
||||
edits = textChanges.ChangeTracker.with(context, t => {
|
||||
t.delete(file, variableDeclaration);
|
||||
t.insertNodeAfter(file, statement, newNode);
|
||||
});
|
||||
}
|
||||
break;
|
||||
return getEditInfoForConvertToNamedFunction(context, func);
|
||||
|
||||
case toArrowFunctionActionName:
|
||||
if (!isFunctionExpression(func)) return undefined;
|
||||
|
||||
const statements = func.body.statements;
|
||||
const head = statements[0];
|
||||
if (func.body.statements.length === 1 && (isReturnStatement(head) || isExpressionStatement(head))) {
|
||||
body = head.expression!;
|
||||
suppressLeadingAndTrailingTrivia(body);
|
||||
copyComments(head, body, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false);
|
||||
}
|
||||
else {
|
||||
body = func.body;
|
||||
}
|
||||
|
||||
newNode = createArrowFunction(func.modifiers, func.typeParameters, func.parameters, func.type, /* equalsGreaterThanToken */ undefined, body);
|
||||
edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, newNode));
|
||||
break;
|
||||
return getEditInfoForConvertToArrowFunction(context, func);
|
||||
|
||||
default:
|
||||
Debug.fail("invalid action");
|
||||
break;
|
||||
}
|
||||
|
||||
return { renameFilename: undefined, renameLocation: undefined, edits };
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getInfo(file: SourceFile, startPosition: number): BasicInfo | undefined {
|
||||
function getFunctionInfo(file: SourceFile, startPosition: number): FunctionInfo | undefined {
|
||||
const token = getTokenAtPosition(file, startPosition);
|
||||
let maybeFunc;
|
||||
|
||||
maybeFunc = getArrowFunctionFromDeclaration(token.parent);
|
||||
if (!!maybeFunc) return { fromDeclaration: true, func: maybeFunc };
|
||||
maybeFunc = getArrowFunctionFromVariableDeclaration(token.parent);
|
||||
if (!!maybeFunc) return { selectedVariableDeclaration: true, func: maybeFunc };
|
||||
|
||||
maybeFunc = getContainingFunction(token);
|
||||
if (!!maybeFunc && (isFunctionExpression(maybeFunc) || isArrowFunction(maybeFunc)) && !rangeContainsRange(maybeFunc.body, token)) {
|
||||
return { fromDeclaration: false, func: maybeFunc };
|
||||
return { selectedVariableDeclaration: false, func: maybeFunc };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getArrowFunctionFromDeclaration(parent: Node): ArrowFunction | undefined {
|
||||
if (!(isVariableDeclaration(parent) || (isVariableDeclarationList(parent) && parent.declarations.length === 1))) return undefined;
|
||||
const variableDeclaration = isVariableDeclaration(parent) ? parent : parent.declarations[0];
|
||||
|
||||
if (!variableDeclaration.initializer || !isArrowFunction(variableDeclaration.initializer)) return undefined;
|
||||
return variableDeclaration.initializer;
|
||||
function isSingleVariableDeclaration(parent: Node): parent is VariableDeclarationList {
|
||||
return isVariableDeclarationList(parent) && parent.declarations.length === 1;
|
||||
}
|
||||
|
||||
function makeBlock(func: ArrowFunction | FunctionExpression): Block {
|
||||
function getArrowFunctionFromVariableDeclaration(parent: Node): ArrowFunction | undefined {
|
||||
if (!(isVariableDeclaration(parent) || isSingleVariableDeclaration(parent))) return undefined;
|
||||
const variableDeclaration = isVariableDeclaration(parent) ? parent : parent.declarations[0];
|
||||
|
||||
const initializer = variableDeclaration.initializer;
|
||||
if (!initializer || !isArrowFunction(initializer)) return undefined;
|
||||
return initializer;
|
||||
}
|
||||
|
||||
function convertToBlock(func: ArrowFunction | FunctionExpression): Block {
|
||||
if (isExpression(func.body)) {
|
||||
const statements: Statement[] = [createReturn(func.body)];
|
||||
return createBlock(statements, /* multiLine */ true);
|
||||
return createBlock([createReturn(func.body)], /* multiLine */ true);
|
||||
}
|
||||
else {
|
||||
return func.body;
|
||||
}
|
||||
}
|
||||
|
||||
function makeFunctionDeclaration(func: FunctionExpression | ArrowFunction, statement: VariableStatement, name: Identifier, body: Block) {
|
||||
return createFunctionDeclaration(
|
||||
func.decorators,
|
||||
statement.modifiers,
|
||||
func.asteriskToken,
|
||||
name,
|
||||
func.typeParameters,
|
||||
func.parameters,
|
||||
func.type,
|
||||
body);
|
||||
}
|
||||
|
||||
function getHeaderInfo(func: FunctionExpression | ArrowFunction): HeaderInfo | undefined {
|
||||
function getVariableInfo(func: FunctionExpression | ArrowFunction): VariableInfo | undefined {
|
||||
const variableDeclaration = func.parent;
|
||||
if (!isVariableDeclaration(variableDeclaration) || !isVariableDeclarationInVariableStatement(variableDeclaration)) return undefined;
|
||||
|
||||
const variableDeclarationList = findAncestor(variableDeclaration, n => isVariableDeclarationList(n))!;
|
||||
const statement = findAncestor(variableDeclaration, n => isVariableStatement(n))!;
|
||||
const variableDeclarationList = variableDeclaration.parent;
|
||||
const statement = variableDeclarationList.parent;
|
||||
if (!isVariableDeclarationList(variableDeclarationList) || !isVariableStatement(statement) || !isIdentifier(variableDeclaration.name)) return undefined;
|
||||
|
||||
return { variableDeclaration, variableDeclarationList, statement, name: variableDeclaration.name };
|
||||
}
|
||||
|
||||
function getEditInfoForConvertToAnonymousFunction(context: RefactorContext, func: FunctionExpression | ArrowFunction): RefactorEditInfo {
|
||||
const { file } = context;
|
||||
const body = convertToBlock(func);
|
||||
const newNode = createFunctionExpression(func.modifiers, func.asteriskToken, /* name */ undefined, func.typeParameters, func.parameters, func.type, body);
|
||||
const edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, newNode));
|
||||
return { renameFilename: undefined, renameLocation: undefined, edits };
|
||||
}
|
||||
|
||||
function getEditInfoForConvertToNamedFunction(context: RefactorContext, func: FunctionExpression | ArrowFunction): RefactorEditInfo | undefined {
|
||||
const { file } = context;
|
||||
const body = convertToBlock(func);
|
||||
const variableInfo = getVariableInfo(func);
|
||||
if (!variableInfo) return undefined;
|
||||
|
||||
const { variableDeclaration, variableDeclarationList, statement, name } = variableInfo;
|
||||
const newNode = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, name, func.typeParameters, func.parameters, func.type, body);
|
||||
let edits: FileTextChanges[];
|
||||
|
||||
if (variableDeclarationList.declarations.length === 1) {
|
||||
edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement, newNode));
|
||||
}
|
||||
else {
|
||||
edits = textChanges.ChangeTracker.with(context, t => {
|
||||
t.delete(file, variableDeclaration);
|
||||
t.insertNodeAfter(file, statement, newNode);
|
||||
});
|
||||
}
|
||||
return { renameFilename: undefined, renameLocation: undefined, edits };
|
||||
}
|
||||
|
||||
function getEditInfoForConvertToArrowFunction(context: RefactorContext, func: FunctionExpression | ArrowFunction): RefactorEditInfo | undefined {
|
||||
const { file } = context;
|
||||
if (!isFunctionExpression(func)) return undefined;
|
||||
|
||||
const statements = func.body.statements;
|
||||
const head = statements[0];
|
||||
let body: ConciseBody;
|
||||
|
||||
if (func.body.statements.length === 1 && (isReturnStatement(head) || isExpressionStatement(head))) {
|
||||
body = head.expression!;
|
||||
suppressLeadingAndTrailingTrivia(body);
|
||||
copyComments(head, body, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false);
|
||||
}
|
||||
else {
|
||||
body = func.body;
|
||||
}
|
||||
|
||||
const newNode = createArrowFunction(func.modifiers, func.typeParameters, func.parameters, func.type, /* equalsGreaterThanToken */ undefined, body);
|
||||
const edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, newNode));
|
||||
return { renameFilename: undefined, renameLocation: undefined, edits };
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue