From 8cb019d79319a09438e4031828c4655c2976a7d6 Mon Sep 17 00:00:00 2001 From: BigAru Date: Wed, 17 Oct 2018 09:27:08 +0200 Subject: [PATCH 01/24] add skeleton --- src/compiler/diagnosticMessages.json | 18 +++++++- .../convertArrowFunctionOrFunction.ts | 41 +++++++++++++++++++ src/services/tsconfig.json | 1 + 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/services/refactors/convertArrowFunctionOrFunction.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1f1ee7a694861..8b713557f322a 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4720,5 +4720,21 @@ "Generate types for all packages without types": { "category": "Message", "code": 95068 + }, + "Convert arrow function or anonymous function": { + "category": "Message", + "code": 95069 + }, + "Convert to anonymous function": { + "category": "Message", + "code": 95070 + }, + "Convert to named function": { + "category": "Message", + "code": 95071 + }, + "Convert to arrow function": { + "category": "Message", + "code": 95072 } -} +} \ No newline at end of file diff --git a/src/services/refactors/convertArrowFunctionOrFunction.ts b/src/services/refactors/convertArrowFunctionOrFunction.ts new file mode 100644 index 0000000000000..77fd7b0cdb43b --- /dev/null +++ b/src/services/refactors/convertArrowFunctionOrFunction.ts @@ -0,0 +1,41 @@ +/* @internal */ +namespace ts.refactor.convertArrowFunctionOrFunction { + const refactorName = "Convert arrow function or anonymous function"; + const refactorDescription = Diagnostics.Convert_arrow_function_or_anonymous_function.message; + + const toAnonymousFunctionActionName = "Convert to anonymous function"; + const toNamedFunctionActionName = "Convert to named function"; + const toArrowFunctionActionName = "Convert to arrow function"; + + const toAnonymousFunctionActionDescription = Diagnostics.Convert_to_anonymous_function.message; + const toNamedFunctionActionDescription = Diagnostics.Convert_to_named_function.message; + const toArrowFunctionActionDescription = Diagnostics.Convert_to_arrow_function.message; + + registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); + + function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { + return [{ + name: refactorName, + description: refactorDescription, + actions: [ + { + name: toAnonymousFunctionActionName, + description: toAnonymousFunctionActionDescription + }, + { + name: toNamedFunctionActionName, + description: toNamedFunctionActionDescription + }, + { + name: toArrowFunctionActionName, + description: toArrowFunctionActionDescription + } + ] + }]; + } + + function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { + return undefined; + } + +} diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 66b1977ddc573..0caa9f8e901b3 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -80,6 +80,7 @@ "refactors/generateGetAccessorAndSetAccessor.ts", "refactors/moveToNewFile.ts", "refactors/addOrRemoveBracesToArrowFunction.ts", + "refactors/convertArrowFunctionOrFunction.ts", "services.ts", "breakpoints.ts", "transform.ts", From 6cfbee7f30dfb4debc0b469c17ff0a0b40644082 Mon Sep 17 00:00:00 2001 From: BigAru Date: Wed, 17 Oct 2018 12:05:42 +0200 Subject: [PATCH 02/24] add getAvailableActions --- .../convertArrowFunctionOrFunction.ts | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunction.ts b/src/services/refactors/convertArrowFunctionOrFunction.ts index 77fd7b0cdb43b..b9ede66fe5c63 100644 --- a/src/services/refactors/convertArrowFunctionOrFunction.ts +++ b/src/services/refactors/convertArrowFunctionOrFunction.ts @@ -14,27 +14,45 @@ namespace ts.refactor.convertArrowFunctionOrFunction { registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { + + const { file, startPosition } = context; + + const node = getTokenAtPosition(file, startPosition); + const func = getContainingFunction(node); + if (!func || !(isFunctionExpression(func) || isArrowFunction(func)) || rangeContainsRange(func.body, node)) return undefined; + + const possibleActions: RefactorActionInfo[] = []; + + if (isArrowFunction(func)) { + if (isVariableDeclaration(func.parent)) { + possibleActions.push({ + name: toNamedFunctionActionName, + description: toNamedFunctionActionDescription + }); + } + + possibleActions.push({ + name: toAnonymousFunctionActionName, + description: toAnonymousFunctionActionDescription + }); + } + else { + possibleActions.push({ + name: toArrowFunctionActionName, + description: toArrowFunctionActionDescription + }); + } + return [{ name: refactorName, description: refactorDescription, - actions: [ - { - name: toAnonymousFunctionActionName, - description: toAnonymousFunctionActionDescription - }, - { - name: toNamedFunctionActionName, - description: toNamedFunctionActionDescription - }, - { - name: toArrowFunctionActionName, - description: toArrowFunctionActionDescription - } - ] + actions: possibleActions }]; } function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { + context; + actionName; return undefined; } From f558f2f6cdfcd416cc2672d8ddb6432e617f5bb9 Mon Sep 17 00:00:00 2001 From: BigAru Date: Wed, 17 Oct 2018 15:13:03 +0200 Subject: [PATCH 03/24] add working getEditsForAction --- .../convertArrowFunctionOrFunction.ts | 105 ++++++++++++++++-- 1 file changed, 93 insertions(+), 12 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunction.ts b/src/services/refactors/convertArrowFunctionOrFunction.ts index b9ede66fe5c63..5ee7a3918dc84 100644 --- a/src/services/refactors/convertArrowFunctionOrFunction.ts +++ b/src/services/refactors/convertArrowFunctionOrFunction.ts @@ -13,28 +13,32 @@ namespace ts.refactor.convertArrowFunctionOrFunction { registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); + interface Info { + func: FunctionExpression | ArrowFunction; + } + function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { const { file, startPosition } = context; + const info = getInfo(file, startPosition); - const node = getTokenAtPosition(file, startPosition); - const func = getContainingFunction(node); - if (!func || !(isFunctionExpression(func) || isArrowFunction(func)) || rangeContainsRange(func.body, node)) return undefined; + if (!info) return undefined; + const { func } = info; const possibleActions: RefactorActionInfo[] = []; if (isArrowFunction(func)) { if (isVariableDeclaration(func.parent)) { possibleActions.push({ - name: toNamedFunctionActionName, - description: toNamedFunctionActionDescription - }); + name: toNamedFunctionActionName, + description: toNamedFunctionActionDescription + }); } possibleActions.push({ - name: toAnonymousFunctionActionName, - description: toAnonymousFunctionActionDescription - }); + name: toAnonymousFunctionActionName, + description: toAnonymousFunctionActionDescription + }); } else { possibleActions.push({ @@ -51,9 +55,86 @@ namespace ts.refactor.convertArrowFunctionOrFunction { } function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { - context; - actionName; - return undefined; + const { file, startPosition } = context; + const info = getInfo(file, startPosition); + + if (!info) return undefined; + const { func } = info; + + switch (actionName) { + case toAnonymousFunctionActionName: + let body: Block; + + if (isExpression(func.body)) { + const statements: Statement[] = [createReturn(func.body)]; + body = createBlock(statements, /* multiLine */ true); + } + else { + body = func.body; + } + + 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 }; + + case toNamedFunctionActionName: + let body2: Block; + + if (isExpression(func.body)) { + const statements: Statement[] = [createReturn(func.body)]; + body2 = createBlock(statements, /* multiLine */ true); + } + else { + body2 = func.body; + } + + const variableDeclaration = func.parent; + if (isVariableDeclaration(variableDeclaration) && isVariableDeclarationInVariableStatement(variableDeclaration) && isIdentifier(variableDeclaration.name)) { + const statement = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableStatement); + + const newNode1 = createFunctionDeclaration(func.decorators, func.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body2); + const edits1 = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement!, newNode1)); + return { renameFilename: undefined, renameLocation: undefined, edits: edits1 }; + } + + return undefined; + + case toArrowFunctionActionName: + let body1: ConciseBody; + + if (isFunctionExpression(func)) { + const statements = func.body.statements; + const head = statements[0]; + if (func.body.statements.length === 1 && (isReturnStatement(head) || isExpressionStatement(head))) { + body1 = head.expression!; + + suppressLeadingAndTrailingTrivia(body1); + copyComments(head, body1, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false); + } + else { + body1 = func.body; + } + + const newNode = createArrowFunction(func.modifiers, func.typeParameters, func.parameters, func.type, /* equalsGreaterThanToken */ undefined, body1); + const edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, newNode)); + return { renameFilename: undefined, renameLocation: undefined, edits }; + } + + return undefined; + + default: + Debug.fail("invalid action"); + break; + } + + } + + function getInfo(file: SourceFile, startPosition: number): Info | undefined { + const node = getTokenAtPosition(file, startPosition); + const func = getContainingFunction(node); + if (!func || !(isFunctionExpression(func) || isArrowFunction(func)) || rangeContainsRange(func.body, node)) return undefined; + + return { func }; } } From 4c80de907800ae366527f0c57a96b467e322906f Mon Sep 17 00:00:00 2001 From: BigAru Date: Thu, 18 Oct 2018 08:23:37 +0200 Subject: [PATCH 04/24] add multi vardecl --- .../convertArrowFunctionOrFunction.ts | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunction.ts b/src/services/refactors/convertArrowFunctionOrFunction.ts index 5ee7a3918dc84..cae45c188803b 100644 --- a/src/services/refactors/convertArrowFunctionOrFunction.ts +++ b/src/services/refactors/convertArrowFunctionOrFunction.ts @@ -14,6 +14,7 @@ namespace ts.refactor.convertArrowFunctionOrFunction { registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); interface Info { + token: Node; func: FunctionExpression | ArrowFunction; } @@ -23,11 +24,22 @@ namespace ts.refactor.convertArrowFunctionOrFunction { const info = getInfo(file, startPosition); if (!info) return undefined; - const { func } = info; + const { token, func } = info; const possibleActions: RefactorActionInfo[] = []; - if (isArrowFunction(func)) { + const parent = token.parent; + + if (isVariableDeclaration(parent) || (isVariableDeclarationList(parent) && parent.declarations.length === 1)) { + const variableDeclaration = isVariableDeclaration(parent) ? parent : parent.declarations[0]; + if (isArrowFunction(variableDeclaration.initializer!)) { + possibleActions.push({ + name: toNamedFunctionActionName, + description: toNamedFunctionActionDescription + }); + } + } + else if (isArrowFunction(func)) { if (isVariableDeclaration(func.parent)) { possibleActions.push({ name: toNamedFunctionActionName, @@ -130,11 +142,28 @@ namespace ts.refactor.convertArrowFunctionOrFunction { } function getInfo(file: SourceFile, startPosition: number): Info | undefined { - const node = getTokenAtPosition(file, startPosition); - const func = getContainingFunction(node); - if (!func || !(isFunctionExpression(func) || isArrowFunction(func)) || rangeContainsRange(func.body, node)) return undefined; + const token = getTokenAtPosition(file, startPosition); + let func: FunctionExpression | ArrowFunction; + const parent = token.parent; + + if (isVariableDeclaration(parent) || (isVariableDeclarationList(parent) && parent.declarations.length === 1)) { + const variableDeclaration = isVariableDeclaration(parent) ? parent : parent.declarations[0]; + + if (!variableDeclaration.initializer) return undefined; + const initializer = variableDeclaration.initializer; + + if (!isArrowFunction(initializer)) return undefined; + func = initializer; + } + else { + const tmpFunc = getContainingFunction(token); + if (!tmpFunc || !(isFunctionExpression(tmpFunc) || isArrowFunction(tmpFunc)) || rangeContainsRange(tmpFunc.body, token)) return undefined; + func = tmpFunc; + } + + - return { func }; + return { token, func }; } } From 367f47e75501c616626facff002fcc4aa2c274aa Mon Sep 17 00:00:00 2001 From: BigAru Date: Thu, 18 Oct 2018 08:45:19 +0200 Subject: [PATCH 05/24] fix multi decl bug --- .../convertArrowFunctionOrFunction.ts | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunction.ts b/src/services/refactors/convertArrowFunctionOrFunction.ts index cae45c188803b..4d05dbaac1e1c 100644 --- a/src/services/refactors/convertArrowFunctionOrFunction.ts +++ b/src/services/refactors/convertArrowFunctionOrFunction.ts @@ -102,11 +102,29 @@ namespace ts.refactor.convertArrowFunctionOrFunction { const variableDeclaration = func.parent; if (isVariableDeclaration(variableDeclaration) && isVariableDeclarationInVariableStatement(variableDeclaration) && isIdentifier(variableDeclaration.name)) { - const statement = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableStatement); - const newNode1 = createFunctionDeclaration(func.decorators, func.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body2); - const edits1 = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement!, newNode1)); - return { renameFilename: undefined, renameLocation: undefined, edits: edits1 }; + const varDeclList = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableDeclarationList)!; + if (!isVariableDeclarationList(varDeclList)) return undefined; + + if (varDeclList.declarations.length === 0) return undefined; + if (varDeclList.declarations.length === 1) { + const statement = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableStatement); + const newNode1 = createFunctionDeclaration(func.decorators, func.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body2); + const edits1 = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement!, newNode1)); + return { renameFilename: undefined, renameLocation: undefined, edits: edits1 }; + } + else { + const statement = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableStatement); + const newNode1 = createFunctionDeclaration(func.decorators, func.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body2); + + const edits1 = textChanges.ChangeTracker.with(context, t => { + t.delete(file, variableDeclaration); + t.insertNodeAfter(file, statement!, newNode1); + }); + return { renameFilename: undefined, renameLocation: undefined, edits: edits1 }; + } + + } return undefined; From e697856a7d663118ce0895a3cd97b3cd0a81f10a Mon Sep 17 00:00:00 2001 From: BigAru Date: Thu, 18 Oct 2018 10:01:06 +0200 Subject: [PATCH 06/24] change refactor name --- src/compiler/diagnosticMessages.json | 2 +- ...ction.ts => convertArrowFunctionOrFunctionExpression.ts} | 6 +++--- src/services/tsconfig.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/services/refactors/{convertArrowFunctionOrFunction.ts => convertArrowFunctionOrFunctionExpression.ts} (95%) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8b713557f322a..3a4d0c7becca2 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4721,7 +4721,7 @@ "category": "Message", "code": 95068 }, - "Convert arrow function or anonymous function": { + "Convert arrow function or function expression": { "category": "Message", "code": 95069 }, diff --git a/src/services/refactors/convertArrowFunctionOrFunction.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts similarity index 95% rename from src/services/refactors/convertArrowFunctionOrFunction.ts rename to src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 4d05dbaac1e1c..c296493a66908 100644 --- a/src/services/refactors/convertArrowFunctionOrFunction.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -1,7 +1,7 @@ /* @internal */ -namespace ts.refactor.convertArrowFunctionOrFunction { - const refactorName = "Convert arrow function or anonymous function"; - const refactorDescription = Diagnostics.Convert_arrow_function_or_anonymous_function.message; +namespace ts.refactor.convertArrowFunctionOrFunctionExpression { + const refactorName = "Convert arrow function or function expression"; + const refactorDescription = Diagnostics.Convert_arrow_function_or_function_expression.message; const toAnonymousFunctionActionName = "Convert to anonymous function"; const toNamedFunctionActionName = "Convert to named function"; diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 0caa9f8e901b3..134b28f854f64 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -80,7 +80,7 @@ "refactors/generateGetAccessorAndSetAccessor.ts", "refactors/moveToNewFile.ts", "refactors/addOrRemoveBracesToArrowFunction.ts", - "refactors/convertArrowFunctionOrFunction.ts", + "refactors/convertArrowFunctionOrFunctionExpression.ts", "services.ts", "breakpoints.ts", "transform.ts", From cc07d682105cf74682a59e4adbb14a61123f86cd Mon Sep 17 00:00:00 2001 From: BigAru Date: Thu, 18 Oct 2018 11:52:00 +0200 Subject: [PATCH 07/24] add tests for ToAnon, ToArrow and available arrow --- ...OrFunctionExpression_Availability_Arrow.ts | 33 +++++++++++++++++++ ...tionOrFunctionExpression_ToAnon_Comment.ts | 23 +++++++++++++ ...onOrFunctionExpression_ToAnon_MultiLine.ts | 17 ++++++++++ ...nOrFunctionExpression_ToAnon_MultiParam.ts | 13 ++++++++ ...nOrFunctionExpression_ToAnon_SingleLine.ts | 13 ++++++++ ...nOrFunctionExpression_ToArrow_MultiLine.ts | 17 ++++++++++ ...ionExpression_ToArrow_MultiLine_Comment.ts | 25 ++++++++++++++ ...ctionOrFunctionExpression_ToArrow_Param.ts | 13 ++++++++ ...unctionExpression_ToArrow_Single_Assign.ts | 15 +++++++++ ...rFunctionExpression_ToArrow_Single_Decl.ts | 15 +++++++++ ...unctionExpression_ToArrow_Single_FnCall.ts | 15 +++++++++ ...OrFunctionExpression_ToArrow_Single_For.ts | 15 +++++++++ ...nOrFunctionExpression_ToArrow_Single_If.ts | 15 +++++++++ ...unctionExpression_ToArrow_Single_Return.ts | 13 ++++++++ ...FunctionExpression_ToArrow_Single_While.ts | 15 +++++++++ 15 files changed, 257 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_Comment.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_MultiLine.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_MultiParam.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_SingleLine.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_MultiLine.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_MultiLine_Comment.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Param.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Assign.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Decl.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_FnCall.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_For.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_If.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Return.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_While.ts diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow.ts new file mode 100644 index 0000000000000..673b49ef703d2 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow.ts @@ -0,0 +1,33 @@ +/// + +//// /*z*/c/*y*/onst /*x*/f/*w*/oo = /*v*/(/*u*//*t*/a/*s*/, b) /*r*/=/*q*/> /*p*/4/*o*/2; + +goTo.select("z", "y"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("x", "w"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("v", "u"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("t", "s"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("r", "q"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("p", "o"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_Comment.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_Comment.ts new file mode 100644 index 0000000000000..c3a65cbf0550a --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_Comment.ts @@ -0,0 +1,23 @@ +/// + +//// const foo = /*x*/a/*y*/ => { +//// // secret word +//// return a + 1; +//// /* +//// hidden msg +//// */ +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to anonymous function", + actionDescription: "Convert to anonymous function", + newContent: `const foo = function(a) { + // secret word + return a + 1; + /* + hidden msg + */ +};`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_MultiLine.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_MultiLine.ts new file mode 100644 index 0000000000000..cbaadbf6a8587 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_MultiLine.ts @@ -0,0 +1,17 @@ +/// + +//// const foo = /*x*/a/*y*/ => { +//// let b = 1; +//// return a + b; +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to anonymous function", + actionDescription: "Convert to anonymous function", + newContent: `const foo = function(a) { + let b = 1; + return a + b; +};`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_MultiParam.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_MultiParam.ts new file mode 100644 index 0000000000000..38953dbf248e7 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_MultiParam.ts @@ -0,0 +1,13 @@ +/// + +//// const foo = /*x*/(/*y*/a,b,c) => a + 1; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to anonymous function", + actionDescription: "Convert to anonymous function", + newContent: `const foo = function(a, b, c) { + return a + 1; +};`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_SingleLine.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_SingleLine.ts new file mode 100644 index 0000000000000..9870104eb162d --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_SingleLine.ts @@ -0,0 +1,13 @@ +/// + +//// const foo = /*x*/(/*y*/) => 1 + 1; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to anonymous function", + actionDescription: "Convert to anonymous function", + newContent: `const foo = function() { + return 1 + 1; +};`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_MultiLine.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_MultiLine.ts new file mode 100644 index 0000000000000..71855947b36cd --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_MultiLine.ts @@ -0,0 +1,17 @@ +/// + +//// const foo = /*x*/f/*y*/unction() { +//// let a = 41; +//// return a + 1; +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `const foo = () => { + let a = 41; + return a + 1; +};`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_MultiLine_Comment.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_MultiLine_Comment.ts new file mode 100644 index 0000000000000..470cae1c446c1 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_MultiLine_Comment.ts @@ -0,0 +1,25 @@ +/// + +//// const foo = /*x*/f/*y*/unction() { +//// // secret +//// let a = 41; +//// /* +//// msg +//// */ +//// return a + 1; +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `const foo = () => { + // secret + let a = 41; + /* + msg + */ + return a + 1; +};`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Param.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Param.ts new file mode 100644 index 0000000000000..a7659080998a6 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Param.ts @@ -0,0 +1,13 @@ +/// + +//// const foo = /*x*/f/*y*/unction(a, b, c) { +//// return a + b + c; +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `const foo = (a, b, c) => a + b + c;`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Assign.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Assign.ts new file mode 100644 index 0000000000000..453a3f701bc06 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Assign.ts @@ -0,0 +1,15 @@ +/// + +//// let bar; +//// const foo = /*x*/f/*y*/unction() { +//// bar = 42; +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `let bar; +const foo = () => bar = 42;`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Decl.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Decl.ts new file mode 100644 index 0000000000000..e1f15b78f3dd6 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Decl.ts @@ -0,0 +1,15 @@ +/// + +//// const foo = /*x*/f/*y*/unction() { +//// let bar; +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `const foo = () => { + let bar; +};`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_FnCall.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_FnCall.ts new file mode 100644 index 0000000000000..e9cced11640de --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_FnCall.ts @@ -0,0 +1,15 @@ +/// + +//// function s(){} +//// const foo = /*x*/f/*y*/unction() { +//// s(); +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `function s(){} +const foo = () => s();`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_For.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_For.ts new file mode 100644 index 0000000000000..a0da4868b0ec1 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_For.ts @@ -0,0 +1,15 @@ +/// + +//// const foo = /*x*/f/*y*/unction() { +//// for (let i = 0; i < 5; i++) { } +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `const foo = () => { + for (let i = 0; i < 5; i++) { } +};`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_If.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_If.ts new file mode 100644 index 0000000000000..65084f0f3a973 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_If.ts @@ -0,0 +1,15 @@ +/// + +//// const foo = /*x*/f/*y*/unction() { +//// if (true) { } +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `const foo = () => { + if (true) { } +};`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Return.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Return.ts new file mode 100644 index 0000000000000..c901046de3e7f --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_Return.ts @@ -0,0 +1,13 @@ +/// + +//// const foo = /*x*/f/*y*/unction() { +//// return 42; +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `const foo = () => 42;`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_While.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_While.ts new file mode 100644 index 0000000000000..830a9b4933b58 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_Single_While.ts @@ -0,0 +1,15 @@ +/// + +//// const foo = /*x*/f/*y*/unction() { +//// while (true) { } +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `const foo = () => { + while (true) { } +};`, +}); From 05c44f123e7b6cf166aef1df9041938a24f09131 Mon Sep 17 00:00:00 2001 From: BigAru Date: Thu, 18 Oct 2018 13:10:04 +0200 Subject: [PATCH 08/24] add tests for ToNamed and available anon --- ...nOrFunctionExpression_Availability_Anon.ts | 28 +++++++++++++++ ...ionExpression_Availability_Anon_FnParam.ts | 34 +++++++++++++++++++ ...Expression_Availability_Arrow_MultiDecl.ts | 23 +++++++++++++ ...ionOrFunctionExpression_ToNamed_Comment.ts | 23 +++++++++++++ ...nOrFunctionExpression_ToNamed_MultiDecl.ts | 15 ++++++++ ...pression_ToNamed_MultiDecl_VarSelection.ts | 15 ++++++++ ...nOrFunctionExpression_ToNamed_MultiLine.ts | 17 ++++++++++ ...OrFunctionExpression_ToNamed_MultiParam.ts | 13 +++++++ ...OrFunctionExpression_ToNamed_SingleLine.ts | 13 +++++++ 9 files changed, 181 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnParam.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_MultiDecl.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_Comment.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl_VarSelection.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiLine.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiParam.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_SingleLine.ts diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon.ts new file mode 100644 index 0000000000000..452d2ecd043d5 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon.ts @@ -0,0 +1,28 @@ +/// + +//// /*z*/c/*y*/onst /*x*/f/*w*/oo = /*v*/f/*u*/unction() /*t*/{/*s*/ /*r*/r/*q*/eturn 42;}; + +goTo.select("z", "y"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("v", "u"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("t", "s"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("r", "q"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnParam.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnParam.ts new file mode 100644 index 0000000000000..8285478a57410 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnParam.ts @@ -0,0 +1,34 @@ +/// + +//// function foo(a){} +//// /*z*/f/*y*/oo/*x*/(/*w*//*v*/f/*u*/unction/*t*/(/*s*//*r*/b/*q*/,c){/*p*/r/*o*/eturn 42;}) + +goTo.select("z", "y"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("v", "u"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("t", "s"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("r", "q"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("p", "o"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_MultiDecl.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_MultiDecl.ts new file mode 100644 index 0000000000000..1cff8b487d529 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_MultiDecl.ts @@ -0,0 +1,23 @@ +/// + +//// /*z*/l/*y*/et /*x*/f/*w*/oo, /*v*/b/*u*/ar = /*t*/(/*s*/) => 42; + +goTo.select("z", "y"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("v", "u"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("t", "s"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_Comment.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_Comment.ts new file mode 100644 index 0000000000000..8f615da85ef45 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_Comment.ts @@ -0,0 +1,23 @@ +/// + +//// const foo = /*x*/a/*y*/ => { +//// // secret word +//// return a + 1; +//// /* +//// hidden msg +//// */ +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to named function", + actionDescription: "Convert to named function", + newContent: `function foo(a) { + // secret word + return a + 1; + /* + hidden msg + */ +}`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl.ts new file mode 100644 index 0000000000000..605026f2e52bd --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl.ts @@ -0,0 +1,15 @@ +/// + +//// let foo, bar = /*x*/(/*y*/) => 1 + 1; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to named function", + actionDescription: "Convert to named function", + newContent: `let foo; +function bar() { + return 1 + 1; +} +`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl_VarSelection.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl_VarSelection.ts new file mode 100644 index 0000000000000..6757cb15609c4 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl_VarSelection.ts @@ -0,0 +1,15 @@ +/// + +//// let foo, /*x*/b/*y*/ar = a => 1 + a; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to named function", + actionDescription: "Convert to named function", + newContent: `let foo; +function bar(a) { + return 1 + a; +} +`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiLine.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiLine.ts new file mode 100644 index 0000000000000..ef2866f783e36 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiLine.ts @@ -0,0 +1,17 @@ +/// + +//// let foo = /*x*/a/*y*/ => { +//// let b = 1; +//// return a + b; +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to named function", + actionDescription: "Convert to named function", + newContent: `function foo(a) { + let b = 1; + return a + b; +}`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiParam.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiParam.ts new file mode 100644 index 0000000000000..0036f11136d9e --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiParam.ts @@ -0,0 +1,13 @@ +/// + +//// let foo = /*x*/(/*y*/a,b,c) => a + 1; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to named function", + actionDescription: "Convert to named function", + newContent: `function foo(a, b, c) { + return a + 1; +}`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_SingleLine.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_SingleLine.ts new file mode 100644 index 0000000000000..4952856d87f11 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_SingleLine.ts @@ -0,0 +1,13 @@ +/// + +//// let foo = /*x*/(/*y*/) => 1 + 1; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to named function", + actionDescription: "Convert to named function", + newContent: `function foo() { + return 1 + 1; +}`, +}); From d77186589495c770a52effee141feb0451ef93a9 Mon Sep 17 00:00:00 2001 From: BigAru Date: Thu, 18 Oct 2018 13:38:27 +0200 Subject: [PATCH 09/24] add tests for ReturnType and available Arrow as FnParam --- ...onExpression_Availability_Arrow_FnParam.ts | 30 +++++++++++++++++++ ...tionOrFunctionExpression_ToAnon_RetType.ts | 13 ++++++++ ...ionOrFunctionExpression_ToArrow_RetType.ts | 13 ++++++++ ...ionOrFunctionExpression_ToNamed_RetType.ts | 13 ++++++++ 4 files changed, 69 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnParam.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_RetType.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_RetType.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_RetType.ts diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnParam.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnParam.ts new file mode 100644 index 0000000000000..75ffddeb39abc --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnParam.ts @@ -0,0 +1,30 @@ +/// + +//// function foo(a){} +//// /*z*/f/*y*/oo/*x*/(/*w*//*v*/(/*u*//*t*/a/*s*/, b) => /*r*/a/*q*/ + b) + + +goTo.select("z", "y"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("x", "w"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("v", "u"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("t", "s"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); + +goTo.select("r", "q"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_RetType.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_RetType.ts new file mode 100644 index 0000000000000..d8f6a591d17ee --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToAnon_RetType.ts @@ -0,0 +1,13 @@ +/// + +//// const foo = /*x*/(/*y*/): number => a + 1; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to anonymous function", + actionDescription: "Convert to anonymous function", + newContent: `const foo = function(): number { + return a + 1; +};`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_RetType.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_RetType.ts new file mode 100644 index 0000000000000..ce814984236a6 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_RetType.ts @@ -0,0 +1,13 @@ +/// + +//// const foo = /*x*/f/*y*/unction(): string { +//// return "foobar"; +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `const foo = (): string => "foobar";`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_RetType.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_RetType.ts new file mode 100644 index 0000000000000..9fffa04149979 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_RetType.ts @@ -0,0 +1,13 @@ +/// + +//// let foo = /*x*/(/*y*/): number => 42; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to named function", + actionDescription: "Convert to named function", + newContent: `function foo(): number { + return 42; +}`, +}); From 62b9fccb981e2ed133d05dc4071da5c2c795684a Mon Sep 17 00:00:00 2001 From: BigAru Date: Thu, 18 Oct 2018 14:13:58 +0200 Subject: [PATCH 10/24] fix bug modifiers by toNamed --- .../convertArrowFunctionOrFunctionExpression.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index c296493a66908..5229372558aec 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -106,16 +106,18 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const varDeclList = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableDeclarationList)!; if (!isVariableDeclarationList(varDeclList)) return undefined; + const statement = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableStatement)!; + if (!isVariableStatement(statement)) return undefined; + if (varDeclList.declarations.length === 0) return undefined; if (varDeclList.declarations.length === 1) { - const statement = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableStatement); - const newNode1 = createFunctionDeclaration(func.decorators, func.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body2); - const edits1 = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement!, newNode1)); + + const newNode1 = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body2); + const edits1 = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement, newNode1)); return { renameFilename: undefined, renameLocation: undefined, edits: edits1 }; } else { - const statement = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableStatement); - const newNode1 = createFunctionDeclaration(func.decorators, func.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body2); + const newNode1 = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body2); const edits1 = textChanges.ChangeTracker.with(context, t => { t.delete(file, variableDeclaration); From c25726e7649ab6b71da8bce7b97503d558d09f1e Mon Sep 17 00:00:00 2001 From: BigAru Date: Thu, 18 Oct 2018 14:17:50 +0200 Subject: [PATCH 11/24] add tests for modifiers --- ...tionOrFunctionExpression_ToNamed_Modifier.ts | 17 +++++++++++++++++ ...tionExpression_ToNamed_MultiDecl_Modifier.ts | 15 +++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_Modifier.ts create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl_Modifier.ts diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_Modifier.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_Modifier.ts new file mode 100644 index 0000000000000..9d60d172397be --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_Modifier.ts @@ -0,0 +1,17 @@ +/// + +//// export let foo = /*x*/a/*y*/ => { +//// let b = 1; +//// return a + b; +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to named function", + actionDescription: "Convert to named function", + newContent: `export function foo(a) { + let b = 1; + return a + b; +}`, +}); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl_Modifier.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl_Modifier.ts new file mode 100644 index 0000000000000..4044d76ad2aff --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl_Modifier.ts @@ -0,0 +1,15 @@ +/// + +//// export let foo, bar = /*x*/(/*y*/) => 1 + 1; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to named function", + actionDescription: "Convert to named function", + newContent: `export let foo; +export function bar() { + return 1 + 1; +} +`, +}); From 7c78cd5c6f20047b0d238e9960bc67aaecb3bca1 Mon Sep 17 00:00:00 2001 From: BigAru Date: Thu, 18 Oct 2018 14:47:39 +0200 Subject: [PATCH 12/24] fix for tslint error --- .../refactors/convertArrowFunctionOrFunctionExpression.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 5229372558aec..29b205a63580a 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -121,7 +121,7 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const edits1 = textChanges.ChangeTracker.with(context, t => { t.delete(file, variableDeclaration); - t.insertNodeAfter(file, statement!, newNode1); + t.insertNodeAfter(file, statement, newNode1); }); return { renameFilename: undefined, renameLocation: undefined, edits: edits1 }; } From 152e3621756f22c424508fec61316d2d6e27076a Mon Sep 17 00:00:00 2001 From: BigAru Date: Fri, 19 Oct 2018 08:08:57 +0200 Subject: [PATCH 13/24] adapt one test case --- ...vertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl.ts index 605026f2e52bd..789eb98141db7 100644 --- a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl.ts +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl.ts @@ -1,13 +1,13 @@ /// -//// let foo, bar = /*x*/(/*y*/) => 1 + 1; +//// let foo, bar = /*x*/(/*y*/) => 1 + 1, magicNo; goTo.select("x", "y"); edit.applyRefactor({ refactorName: "Convert arrow function or function expression", actionName: "Convert to named function", actionDescription: "Convert to named function", - newContent: `let foo; + newContent: `let foo, magicNo; function bar() { return 1 + 1; } From 6922f6c4cae774cdb9f3b5446600ca53be50fb95 Mon Sep 17 00:00:00 2001 From: BigAru Date: Fri, 19 Oct 2018 10:50:33 +0200 Subject: [PATCH 14/24] refactor getInfo getAvailableActions --- ...onvertArrowFunctionOrFunctionExpression.ts | 59 +++++++------------ 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 29b205a63580a..5b3e00fa478c7 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -14,7 +14,7 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); interface Info { - token: Node; + fromVarDecl: boolean; func: FunctionExpression | ArrowFunction; } @@ -24,35 +24,24 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const info = getInfo(file, startPosition); if (!info) return undefined; - const { token, func } = info; - + const { fromVarDecl, func } = info; const possibleActions: RefactorActionInfo[] = []; - const parent = token.parent; - - if (isVariableDeclaration(parent) || (isVariableDeclarationList(parent) && parent.declarations.length === 1)) { - const variableDeclaration = isVariableDeclaration(parent) ? parent : parent.declarations[0]; - if (isArrowFunction(variableDeclaration.initializer!)) { - possibleActions.push({ - name: toNamedFunctionActionName, - description: toNamedFunctionActionDescription - }); - } + if (fromVarDecl || (isArrowFunction(func) && isVariableDeclaration(func.parent))) { + possibleActions.push({ + name: toNamedFunctionActionName, + description: toNamedFunctionActionDescription + }); } - else if (isArrowFunction(func)) { - if (isVariableDeclaration(func.parent)) { - possibleActions.push({ - name: toNamedFunctionActionName, - description: toNamedFunctionActionDescription - }); - } + if (isArrowFunction(func) && !fromVarDecl) { possibleActions.push({ name: toAnonymousFunctionActionName, description: toAnonymousFunctionActionDescription }); } - else { + + if (isFunctionExpression(func)) { possibleActions.push({ name: toArrowFunctionActionName, description: toArrowFunctionActionDescription @@ -163,27 +152,23 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { function getInfo(file: SourceFile, startPosition: number): Info | undefined { const token = getTokenAtPosition(file, startPosition); - let func: FunctionExpression | ArrowFunction; - const parent = token.parent; - if (isVariableDeclaration(parent) || (isVariableDeclarationList(parent) && parent.declarations.length === 1)) { - const variableDeclaration = isVariableDeclaration(parent) ? parent : parent.declarations[0]; + const declFunc = extractArrowFnFromDecl(token.parent); + if (!!declFunc) return { fromVarDecl: true, func: declFunc }; - if (!variableDeclaration.initializer) return undefined; - const initializer = variableDeclaration.initializer; - - if (!isArrowFunction(initializer)) return undefined; - func = initializer; - } - else { - const tmpFunc = getContainingFunction(token); - if (!tmpFunc || !(isFunctionExpression(tmpFunc) || isArrowFunction(tmpFunc)) || rangeContainsRange(tmpFunc.body, token)) return undefined; - func = tmpFunc; + const maybeFunc = getContainingFunction(token); + if (!!maybeFunc && (isFunctionExpression(maybeFunc) || isArrowFunction(maybeFunc)) && !rangeContainsRange(maybeFunc.body, token)) { + return { fromVarDecl: false, func: maybeFunc }; } + return undefined; + } + function extractArrowFnFromDecl(parent: Node): ArrowFunction | undefined { + if (!(isVariableDeclaration(parent) || (isVariableDeclarationList(parent) && parent.declarations.length === 1))) return undefined; + const varDecl = isVariableDeclaration(parent) ? parent : parent.declarations[0]; - return { token, func }; + if (!varDecl.initializer || !isArrowFunction(varDecl.initializer)) return undefined; + return varDecl.initializer; } - } From 39c3928e5d488b55fd889861f95dfe454fe9f752 Mon Sep 17 00:00:00 2001 From: BigAru Date: Fri, 19 Oct 2018 11:58:10 +0200 Subject: [PATCH 15/24] refactor small progress --- ...onvertArrowFunctionOrFunctionExpression.ts | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 5b3e00fa478c7..e67a793e9fdc9 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -34,7 +34,7 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { }); } - if (isArrowFunction(func) && !fromVarDecl) { + if (!fromVarDecl && isArrowFunction(func)) { possibleActions.push({ name: toAnonymousFunctionActionName, description: toAnonymousFunctionActionDescription @@ -62,9 +62,12 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { if (!info) return undefined; const { func } = info; + let body: Block | ConciseBody; + let newNode: ArrowFunction | FunctionExpression; + let edits: FileTextChanges[] = []; + switch (actionName) { case toAnonymousFunctionActionName: - let body: Block; if (isExpression(func.body)) { const statements: Statement[] = [createReturn(func.body)]; @@ -74,19 +77,18 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { body = func.body; } - 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 }; + 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; case toNamedFunctionActionName: - let body2: Block; if (isExpression(func.body)) { const statements: Statement[] = [createReturn(func.body)]; - body2 = createBlock(statements, /* multiLine */ true); + body = createBlock(statements, /* multiLine */ true); } else { - body2 = func.body; + body = func.body; } const variableDeclaration = func.parent; @@ -101,12 +103,12 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { if (varDeclList.declarations.length === 0) return undefined; if (varDeclList.declarations.length === 1) { - const newNode1 = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body2); + const newNode1 = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body); const edits1 = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement, newNode1)); return { renameFilename: undefined, renameLocation: undefined, edits: edits1 }; } else { - const newNode1 = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body2); + const newNode1 = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body); const edits1 = textChanges.ChangeTracker.with(context, t => { t.delete(file, variableDeclaration); @@ -121,33 +123,33 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { return undefined; case toArrowFunctionActionName: - let body1: ConciseBody; - if (isFunctionExpression(func)) { - const statements = func.body.statements; - const head = statements[0]; - if (func.body.statements.length === 1 && (isReturnStatement(head) || isExpressionStatement(head))) { - body1 = head.expression!; + if (!isFunctionExpression(func)) return undefined; - suppressLeadingAndTrailingTrivia(body1); - copyComments(head, body1, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false); - } - else { - body1 = func.body; - } + const statements = func.body.statements; + const head = statements[0]; + if (func.body.statements.length === 1 && (isReturnStatement(head) || isExpressionStatement(head))) { + body = head.expression!; - const newNode = createArrowFunction(func.modifiers, func.typeParameters, func.parameters, func.type, /* equalsGreaterThanToken */ undefined, body1); - const edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, newNode)); - return { renameFilename: undefined, renameLocation: undefined, edits }; + suppressLeadingAndTrailingTrivia(body); + copyComments(head, body, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false); + } + else { + body = func.body; } - return undefined; + 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; default: Debug.fail("invalid action"); break; } + return { renameFilename: undefined, renameLocation: undefined, edits }; + + } function getInfo(file: SourceFile, startPosition: number): Info | undefined { From a9cb62379579510e1a17386be47009f07dc91c09 Mon Sep 17 00:00:00 2001 From: BigAru Date: Fri, 19 Oct 2018 12:09:34 +0200 Subject: [PATCH 16/24] extract creation of block --- ...onvertArrowFunctionOrFunctionExpression.ts | 68 +++++++++---------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index e67a793e9fdc9..37a89454a31a3 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -63,19 +63,13 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const { func } = info; let body: Block | ConciseBody; - let newNode: ArrowFunction | FunctionExpression; + let newNode: ArrowFunction | FunctionExpression | FunctionDeclaration; let edits: FileTextChanges[] = []; switch (actionName) { case toAnonymousFunctionActionName: - if (isExpression(func.body)) { - const statements: Statement[] = [createReturn(func.body)]; - body = createBlock(statements, /* multiLine */ true); - } - else { - body = func.body; - } + 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)); @@ -83,44 +77,34 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { case toNamedFunctionActionName: - if (isExpression(func.body)) { - const statements: Statement[] = [createReturn(func.body)]; - body = createBlock(statements, /* multiLine */ true); - } - else { - body = func.body; - } + body = makeBlock(func); const variableDeclaration = func.parent; - if (isVariableDeclaration(variableDeclaration) && isVariableDeclarationInVariableStatement(variableDeclaration) && isIdentifier(variableDeclaration.name)) { + if (!isVariableDeclaration(variableDeclaration) || !isVariableDeclarationInVariableStatement(variableDeclaration) || !isIdentifier(variableDeclaration.name)) return undefined; - const varDeclList = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableDeclarationList)!; - if (!isVariableDeclarationList(varDeclList)) return undefined; + const varDeclList = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableDeclarationList)!; + if (!isVariableDeclarationList(varDeclList)) return undefined; - const statement = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableStatement)!; - if (!isVariableStatement(statement)) return undefined; + const statement = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableStatement)!; + if (!isVariableStatement(statement)) return undefined; - if (varDeclList.declarations.length === 0) return undefined; - if (varDeclList.declarations.length === 1) { - - const newNode1 = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body); - const edits1 = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement, newNode1)); - return { renameFilename: undefined, renameLocation: undefined, edits: edits1 }; - } - else { - const newNode1 = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body); - - const edits1 = textChanges.ChangeTracker.with(context, t => { - t.delete(file, variableDeclaration); - t.insertNodeAfter(file, statement, newNode1); - }); - return { renameFilename: undefined, renameLocation: undefined, edits: edits1 }; - } + if (varDeclList.declarations.length === 0) return undefined; + if (varDeclList.declarations.length === 1) { + const newNode = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body); + const edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement, newNode)); + return { renameFilename: undefined, renameLocation: undefined, edits }; + } + else { + const newNode = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body); + const edits = textChanges.ChangeTracker.with(context, t => { + t.delete(file, variableDeclaration); + t.insertNodeAfter(file, statement, newNode); + }); + return { renameFilename: undefined, renameLocation: undefined, edits }; } - return undefined; case toArrowFunctionActionName: @@ -173,4 +157,14 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { if (!varDecl.initializer || !isArrowFunction(varDecl.initializer)) return undefined; return varDecl.initializer; } + + function makeBlock(func: ArrowFunction | FunctionExpression): Block { + if (isExpression(func.body)) { + const statements: Statement[] = [createReturn(func.body)]; + return createBlock(statements, /* multiLine */ true); + } + else { + return func.body; + } + } } From 6bd26cd2e5c424963d4b8c03240986184f155f56 Mon Sep 17 00:00:00 2001 From: BigAru Date: Fri, 19 Oct 2018 12:43:12 +0200 Subject: [PATCH 17/24] extract creation of funcDeclaration --- ...onvertArrowFunctionOrFunctionExpression.ts | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 37a89454a31a3..ae70320b1b5f8 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -82,29 +82,24 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const variableDeclaration = func.parent; if (!isVariableDeclaration(variableDeclaration) || !isVariableDeclarationInVariableStatement(variableDeclaration) || !isIdentifier(variableDeclaration.name)) return undefined; - const varDeclList = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableDeclarationList)!; + const varDeclList = findAncestor(variableDeclaration, n => isVariableDeclarationList(n))!; if (!isVariableDeclarationList(varDeclList)) return undefined; - const statement = findAncestor(variableDeclaration, n => n.kind === SyntaxKind.VariableStatement)!; + const statement = findAncestor(variableDeclaration, n => isVariableStatement(n))!; if (!isVariableStatement(statement)) return undefined; - if (varDeclList.declarations.length === 0) return undefined; - if (varDeclList.declarations.length === 1) { + newNode = makeFuncDecl(func, statement, variableDeclaration.name, body); - const newNode = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body); - const edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement, newNode)); - return { renameFilename: undefined, renameLocation: undefined, edits }; + if (varDeclList.declarations.length === 1) { + edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement, newNode)); } else { - const newNode = createFunctionDeclaration(func.decorators, statement.modifiers, func.asteriskToken, variableDeclaration.name, func.typeParameters, func.parameters, func.type, body); - - const edits = textChanges.ChangeTracker.with(context, t => { + edits = textChanges.ChangeTracker.with(context, t => { t.delete(file, variableDeclaration); t.insertNodeAfter(file, statement, newNode); }); - return { renameFilename: undefined, renameLocation: undefined, edits }; } - + break; case toArrowFunctionActionName: @@ -132,8 +127,6 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { } return { renameFilename: undefined, renameLocation: undefined, edits }; - - } function getInfo(file: SourceFile, startPosition: number): Info | undefined { @@ -167,4 +160,16 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { return func.body; } } + + function makeFuncDecl(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); + } } From 3e7dcad726033cc38ad3e844f52990cb29fa50c5 Mon Sep 17 00:00:00 2001 From: BigAru Date: Fri, 19 Oct 2018 14:09:20 +0200 Subject: [PATCH 18/24] make guideline compliant --- ...onvertArrowFunctionOrFunctionExpression.ts | 76 +++++++++++-------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index ae70320b1b5f8..a0d6677ce2df8 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -1,40 +1,47 @@ /* @internal */ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const refactorName = "Convert arrow function or function expression"; - const refactorDescription = Diagnostics.Convert_arrow_function_or_function_expression.message; + const refactorDescription = getLocaleSpecificMessage(Diagnostics.Convert_arrow_function_or_function_expression); const toAnonymousFunctionActionName = "Convert to anonymous function"; const toNamedFunctionActionName = "Convert to named function"; const toArrowFunctionActionName = "Convert to arrow function"; - const toAnonymousFunctionActionDescription = Diagnostics.Convert_to_anonymous_function.message; - const toNamedFunctionActionDescription = Diagnostics.Convert_to_named_function.message; - const toArrowFunctionActionDescription = Diagnostics.Convert_to_arrow_function.message; + const toAnonymousFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_anonymous_function); + const toNamedFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_named_function); + const toArrowFunctionActionDescription = getLocaleSpecificMessage(Diagnostics.Convert_to_arrow_function); registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); - interface Info { - fromVarDecl: boolean; + interface BasicInfo { + fromDeclaration: boolean; func: FunctionExpression | ArrowFunction; } + interface HeaderInfo { + variableDeclaration: VariableDeclaration; + variableDeclarationList: VariableDeclarationList; + statement: VariableStatement; + name: Identifier; + } + function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { const { file, startPosition } = context; const info = getInfo(file, startPosition); if (!info) return undefined; - const { fromVarDecl, func } = info; + const { fromDeclaration, func } = info; const possibleActions: RefactorActionInfo[] = []; - if (fromVarDecl || (isArrowFunction(func) && isVariableDeclaration(func.parent))) { + if (fromDeclaration || (isArrowFunction(func) && isVariableDeclaration(func.parent))) { possibleActions.push({ name: toNamedFunctionActionName, description: toNamedFunctionActionDescription }); } - if (!fromVarDecl && isArrowFunction(func)) { + if (!fromDeclaration && isArrowFunction(func)) { possibleActions.push({ name: toAnonymousFunctionActionName, description: toAnonymousFunctionActionDescription @@ -70,7 +77,6 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { 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; @@ -78,19 +84,13 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { case toNamedFunctionActionName: body = makeBlock(func); + const headInfo = getHeaderInfo(func); + if (!headInfo) return undefined; - const variableDeclaration = func.parent; - if (!isVariableDeclaration(variableDeclaration) || !isVariableDeclarationInVariableStatement(variableDeclaration) || !isIdentifier(variableDeclaration.name)) return undefined; - - const varDeclList = findAncestor(variableDeclaration, n => isVariableDeclarationList(n))!; - if (!isVariableDeclarationList(varDeclList)) return undefined; - - const statement = findAncestor(variableDeclaration, n => isVariableStatement(n))!; - if (!isVariableStatement(statement)) return undefined; - - newNode = makeFuncDecl(func, statement, variableDeclaration.name, body); + const { variableDeclaration, variableDeclarationList, statement, name } = headInfo; + newNode = makeFunctionDeclaration(func, statement, name, body); - if (varDeclList.declarations.length === 1) { + if (variableDeclarationList.declarations.length === 1) { edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, statement, newNode)); } else { @@ -102,14 +102,12 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { break; 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); } @@ -129,26 +127,27 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { return { renameFilename: undefined, renameLocation: undefined, edits }; } - function getInfo(file: SourceFile, startPosition: number): Info | undefined { + function getInfo(file: SourceFile, startPosition: number): BasicInfo | undefined { const token = getTokenAtPosition(file, startPosition); + let maybeFunc; - const declFunc = extractArrowFnFromDecl(token.parent); - if (!!declFunc) return { fromVarDecl: true, func: declFunc }; + maybeFunc = getArrowFunctionFromDeclaration(token.parent); + if (!!maybeFunc) return { fromDeclaration: true, func: maybeFunc }; - const maybeFunc = getContainingFunction(token); + maybeFunc = getContainingFunction(token); if (!!maybeFunc && (isFunctionExpression(maybeFunc) || isArrowFunction(maybeFunc)) && !rangeContainsRange(maybeFunc.body, token)) { - return { fromVarDecl: false, func: maybeFunc }; + return { fromDeclaration: false, func: maybeFunc }; } return undefined; } - function extractArrowFnFromDecl(parent: Node): ArrowFunction | undefined { + function getArrowFunctionFromDeclaration(parent: Node): ArrowFunction | undefined { if (!(isVariableDeclaration(parent) || (isVariableDeclarationList(parent) && parent.declarations.length === 1))) return undefined; - const varDecl = isVariableDeclaration(parent) ? parent : parent.declarations[0]; + const variableDeclaration = isVariableDeclaration(parent) ? parent : parent.declarations[0]; - if (!varDecl.initializer || !isArrowFunction(varDecl.initializer)) return undefined; - return varDecl.initializer; + if (!variableDeclaration.initializer || !isArrowFunction(variableDeclaration.initializer)) return undefined; + return variableDeclaration.initializer; } function makeBlock(func: ArrowFunction | FunctionExpression): Block { @@ -161,7 +160,7 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { } } - function makeFuncDecl(func: FunctionExpression | ArrowFunction, statement: VariableStatement, name: Identifier, body: Block) { + function makeFunctionDeclaration(func: FunctionExpression | ArrowFunction, statement: VariableStatement, name: Identifier, body: Block) { return createFunctionDeclaration( func.decorators, statement.modifiers, @@ -172,4 +171,15 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { func.type, body); } + + function getHeaderInfo(func: FunctionExpression | ArrowFunction): HeaderInfo | 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))!; + if (!isVariableDeclarationList(variableDeclarationList) || !isVariableStatement(statement) || !isIdentifier(variableDeclaration.name)) return undefined; + + return { variableDeclaration, variableDeclarationList, statement, name: variableDeclaration.name }; + } } From 649b53c8de762e09bd93caac6ec1348d1b1260fa Mon Sep 17 00:00:00 2001 From: BigAru Date: Mon, 22 Oct 2018 06:23:05 +0200 Subject: [PATCH 19/24] apply feedback from pr --- ...onvertArrowFunctionOrFunctionExpression.ts | 161 +++++++++--------- 1 file changed, 82 insertions(+), 79 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index a0d6677ce2df8..3098fe3c6a4a9 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; + function isSingleVariableDeclaration(parent: Node): parent is VariableDeclarationList { + return isVariableDeclarationList(parent) && parent.declarations.length === 1; + } + + function getArrowFunctionFromVariableDeclaration(parent: Node): ArrowFunction | undefined { + if (!(isVariableDeclaration(parent) || isSingleVariableDeclaration(parent))) return undefined; const variableDeclaration = isVariableDeclaration(parent) ? parent : parent.declarations[0]; - if (!variableDeclaration.initializer || !isArrowFunction(variableDeclaration.initializer)) return undefined; - return variableDeclaration.initializer; + const initializer = variableDeclaration.initializer; + if (!initializer || !isArrowFunction(initializer)) return undefined; + return initializer; } - function makeBlock(func: ArrowFunction | FunctionExpression): Block { + 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 }; + } } From 595131868f6cea6fc0db05b39ac7e76846f1662d Mon Sep 17 00:00:00 2001 From: BigAru Date: Mon, 22 Oct 2018 06:43:21 +0200 Subject: [PATCH 20/24] add testcase and apply feedback from pr --- .../convertArrowFunctionOrFunctionExpression.ts | 2 +- ...ctionOrFunctionExpression_Availability_Anon.ts | 2 +- ...unctionExpression_Availability_Anon_FnParam.ts | 2 +- ...tionOrFunctionExpression_Availability_Arrow.ts | 2 +- ...nctionExpression_Availability_Arrow_FnParam.ts | 2 +- ...tionExpression_Availability_Arrow_MultiDecl.ts | 2 +- ...ionOrFunctionExpression_ToArrow_EmptyReturn.ts | 15 +++++++++++++++ 7 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_EmptyReturn.ts diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 3098fe3c6a4a9..0843ddea79b49 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -172,7 +172,7 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const head = statements[0]; let body: ConciseBody; - if (func.body.statements.length === 1 && (isReturnStatement(head) || isExpressionStatement(head))) { + if (func.body.statements.length === 1 && ((isReturnStatement(head) && !!head.expression) || isExpressionStatement(head))) { body = head.expression!; suppressLeadingAndTrailingTrivia(body); copyComments(head, body, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon.ts index 452d2ecd043d5..0fa3222617c1b 100644 --- a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon.ts +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon.ts @@ -25,4 +25,4 @@ verify.not.refactorAvailable("Convert arrow function or function expression", "C goTo.select("r", "q"); verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); -verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); \ No newline at end of file +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnParam.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnParam.ts index 8285478a57410..6c017aeeb739a 100644 --- a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnParam.ts +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnParam.ts @@ -31,4 +31,4 @@ verify.refactorAvailable("Convert arrow function or function expression", "Conve goTo.select("p", "o"); verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); -verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); \ No newline at end of file +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow.ts index 673b49ef703d2..619b2af822c5d 100644 --- a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow.ts +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow.ts @@ -30,4 +30,4 @@ verify.not.refactorAvailable("Convert arrow function or function expression", "C goTo.select("p", "o"); verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); -verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); \ No newline at end of file +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnParam.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnParam.ts index 75ffddeb39abc..6d309494da9f1 100644 --- a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnParam.ts +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnParam.ts @@ -27,4 +27,4 @@ verify.not.refactorAvailable("Convert arrow function or function expression", "C goTo.select("r", "q"); verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); -verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); \ No newline at end of file +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_MultiDecl.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_MultiDecl.ts index 1cff8b487d529..1e87f8bc0c30a 100644 --- a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_MultiDecl.ts +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_MultiDecl.ts @@ -20,4 +20,4 @@ verify.not.refactorAvailable("Convert arrow function or function expression", "C goTo.select("t", "s"); verify.refactorAvailable("Convert arrow function or function expression", "Convert to named function"); verify.refactorAvailable("Convert arrow function or function expression", "Convert to anonymous function"); -verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); \ No newline at end of file +verify.not.refactorAvailable("Convert arrow function or function expression", "Convert to arrow function"); diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_EmptyReturn.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_EmptyReturn.ts new file mode 100644 index 0000000000000..e77adca0eb8ae --- /dev/null +++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToArrow_EmptyReturn.ts @@ -0,0 +1,15 @@ +/// + +//// const foo = /*x*/f/*y*/unction() { +//// return; +//// }; + +goTo.select("x", "y"); +edit.applyRefactor({ + refactorName: "Convert arrow function or function expression", + actionName: "Convert to arrow function", + actionDescription: "Convert to arrow function", + newContent: `const foo = () => { + return; +};`, +}); From d97e07360cdcf382467cecaeba87011c776eb0eb Mon Sep 17 00:00:00 2001 From: BigAru Date: Mon, 22 Oct 2018 06:57:26 +0200 Subject: [PATCH 21/24] apply feedback from pr --- ...onvertArrowFunctionOrFunctionExpression.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 0843ddea79b49..6658199cd6931 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -14,15 +14,15 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); interface FunctionInfo { - selectedVariableDeclaration: boolean; - func: FunctionExpression | ArrowFunction; + readonly selectedVariableDeclaration: boolean; + readonly func: FunctionExpression | ArrowFunction; } interface VariableInfo { - variableDeclaration: VariableDeclaration; - variableDeclarationList: VariableDeclarationList; - statement: VariableStatement; - name: Identifier; + readonly variableDeclaration: VariableDeclaration; + readonly variableDeclarationList: VariableDeclarationList; + readonly statement: VariableStatement; + readonly name: Identifier; } function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { @@ -114,12 +114,12 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { return initializer; } - function convertToBlock(func: ArrowFunction | FunctionExpression): Block { - if (isExpression(func.body)) { - return createBlock([createReturn(func.body)], /* multiLine */ true); + function convertToBlock(body: ConciseBody): Block { + if (isExpression(body)) { + return createBlock([createReturn(body)], /* multiLine */ true); } else { - return func.body; + return body; } } @@ -136,7 +136,7 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { function getEditInfoForConvertToAnonymousFunction(context: RefactorContext, func: FunctionExpression | ArrowFunction): RefactorEditInfo { const { file } = context; - const body = convertToBlock(func); + const body = convertToBlock(func.body); 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 }; @@ -144,7 +144,7 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { function getEditInfoForConvertToNamedFunction(context: RefactorContext, func: FunctionExpression | ArrowFunction): RefactorEditInfo | undefined { const { file } = context; - const body = convertToBlock(func); + const body = convertToBlock(func.body); const variableInfo = getVariableInfo(func); if (!variableInfo) return undefined; From 0fa2fadeca756093ea3a7824ebbd31d54718d398 Mon Sep 17 00:00:00 2001 From: BigAru Date: Tue, 23 Oct 2018 07:53:40 +0200 Subject: [PATCH 22/24] add newline --- src/compiler/diagnosticMessages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 3a4d0c7becca2..5af03481e731a 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4737,4 +4737,4 @@ "category": "Message", "code": 95072 } -} \ No newline at end of file +} From 1c74d0eef402e9737ffc9532d541d1a0c51d03b1 Mon Sep 17 00:00:00 2001 From: BigAru Date: Tue, 23 Oct 2018 08:07:27 +0200 Subject: [PATCH 23/24] rename testcases --- ...wFunctionOrFunctionExpression_Availability_Anon_FnArgument.ts} | 0 ...FunctionOrFunctionExpression_Availability_Arrow_FnArgument.ts} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/cases/fourslash/{refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnParam.ts => refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnArgument.ts} (100%) rename tests/cases/fourslash/{refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnParam.ts => refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnArgument.ts} (100%) diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnParam.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnArgument.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnParam.ts rename to tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnArgument.ts diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnParam.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnArgument.ts similarity index 100% rename from tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnParam.ts rename to tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnArgument.ts From 8bc7f43665a29c3d84f23be542b999bd7a560262 Mon Sep 17 00:00:00 2001 From: Giovanni Heilmann Date: Thu, 25 Oct 2018 14:48:54 +0200 Subject: [PATCH 24/24] Make conditions more expressive --- .../convertArrowFunctionOrFunctionExpression.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts index 6658199cd6931..9bf16775f6c43 100644 --- a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts +++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts @@ -95,18 +95,18 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { maybeFunc = getContainingFunction(token); if (!!maybeFunc && (isFunctionExpression(maybeFunc) || isArrowFunction(maybeFunc)) && !rangeContainsRange(maybeFunc.body, token)) { - return { selectedVariableDeclaration: false, func: maybeFunc }; + return { selectedVariableDeclaration: false, func: maybeFunc }; } return undefined; } function isSingleVariableDeclaration(parent: Node): parent is VariableDeclarationList { - return isVariableDeclarationList(parent) && parent.declarations.length === 1; + return isVariableDeclaration(parent) || (isVariableDeclarationList(parent) && parent.declarations.length === 1); } function getArrowFunctionFromVariableDeclaration(parent: Node): ArrowFunction | undefined { - if (!(isVariableDeclaration(parent) || isSingleVariableDeclaration(parent))) return undefined; + if (!isSingleVariableDeclaration(parent)) return undefined; const variableDeclaration = isVariableDeclaration(parent) ? parent : parent.declarations[0]; const initializer = variableDeclaration.initializer; @@ -172,7 +172,7 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const head = statements[0]; let body: ConciseBody; - if (func.body.statements.length === 1 && ((isReturnStatement(head) && !!head.expression) || isExpressionStatement(head))) { + if (canBeConvertedToExpression(func.body, head)) { body = head.expression!; suppressLeadingAndTrailingTrivia(body); copyComments(head, body, file, SyntaxKind.MultiLineCommentTrivia, /* hasTrailingNewLine */ false); @@ -185,4 +185,8 @@ namespace ts.refactor.convertArrowFunctionOrFunctionExpression { const edits = textChanges.ChangeTracker.with(context, t => t.replaceNode(file, func, newNode)); return { renameFilename: undefined, renameLocation: undefined, edits }; } + + function canBeConvertedToExpression(body: Block, head: Statement): head is ReturnStatement | ExpressionStatement { + return body.statements.length === 1 && ((isReturnStatement(head) && !!head.expression) || isExpressionStatement(head)); + } }