From 649b53c8de762e09bd93caac6ec1348d1b1260fa Mon Sep 17 00:00:00 2001 From: BigAru Date: Mon, 22 Oct 2018 06:23:05 +0200 Subject: [PATCH] apply feedback from pr --- ...onvertArrowFunctionOrFunctionExpression.ts | 165 +++++++++--------- 1 file changed, 84 insertions(+), 81 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index a0d6677ce2d..3098fe3c6a4 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -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 }; + } }