apply feedback from pr

This commit is contained in:
BigAru 2018-10-22 06:23:05 +02:00
parent 3e7dcad726
commit 649b53c8de

View file

@ -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 };
}
}