diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 1f1ee7a694861..5af03481e731a 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 function expression": {
+ "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
}
}
diff --git a/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts
new file mode 100644
index 0000000000000..9bf16775f6c43
--- /dev/null
+++ b/src/services/refactors/convertArrowFunctionOrFunctionExpression.ts
@@ -0,0 +1,192 @@
+/* @internal */
+namespace ts.refactor.convertArrowFunctionOrFunctionExpression {
+ const refactorName = "Convert arrow function or function expression";
+ 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 = 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 FunctionInfo {
+ readonly selectedVariableDeclaration: boolean;
+ readonly func: FunctionExpression | ArrowFunction;
+ }
+
+ interface VariableInfo {
+ readonly variableDeclaration: VariableDeclaration;
+ readonly variableDeclarationList: VariableDeclarationList;
+ readonly statement: VariableStatement;
+ readonly name: Identifier;
+ }
+
+ function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined {
+ const { file, startPosition } = context;
+ const info = getFunctionInfo(file, startPosition);
+
+ if (!info) return undefined;
+ const { selectedVariableDeclaration, func } = info;
+ const possibleActions: RefactorActionInfo[] = [];
+
+ if (selectedVariableDeclaration || (isArrowFunction(func) && isVariableDeclaration(func.parent))) {
+ possibleActions.push({
+ name: toNamedFunctionActionName,
+ description: toNamedFunctionActionDescription
+ });
+ }
+
+ if (!selectedVariableDeclaration && isArrowFunction(func)) {
+ possibleActions.push({
+ name: toAnonymousFunctionActionName,
+ description: toAnonymousFunctionActionDescription
+ });
+ }
+
+ if (isFunctionExpression(func)) {
+ possibleActions.push({
+ name: toArrowFunctionActionName,
+ description: toArrowFunctionActionDescription
+ });
+ }
+
+ return [{
+ name: refactorName,
+ description: refactorDescription,
+ actions: possibleActions
+ }];
+ }
+
+ function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined {
+ const { file, startPosition } = context;
+ const info = getFunctionInfo(file, startPosition);
+
+ if (!info) return undefined;
+ const { func } = info;
+
+ switch (actionName) {
+ case toAnonymousFunctionActionName:
+ return getEditInfoForConvertToAnonymousFunction(context, func);
+
+ case toNamedFunctionActionName:
+ return getEditInfoForConvertToNamedFunction(context, func);
+
+ case toArrowFunctionActionName:
+ return getEditInfoForConvertToArrowFunction(context, func);
+
+ default:
+ Debug.fail("invalid action");
+ break;
+ }
+
+ return undefined;
+ }
+
+ function getFunctionInfo(file: SourceFile, startPosition: number): FunctionInfo | undefined {
+ const token = getTokenAtPosition(file, startPosition);
+ let 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 { selectedVariableDeclaration: false, func: maybeFunc };
+ }
+
+ return undefined;
+ }
+
+ function isSingleVariableDeclaration(parent: Node): parent is VariableDeclarationList {
+ return isVariableDeclaration(parent) || (isVariableDeclarationList(parent) && parent.declarations.length === 1);
+ }
+
+ function getArrowFunctionFromVariableDeclaration(parent: Node): ArrowFunction | undefined {
+ if (!isSingleVariableDeclaration(parent)) return undefined;
+ const variableDeclaration = isVariableDeclaration(parent) ? parent : parent.declarations[0];
+
+ const initializer = variableDeclaration.initializer;
+ if (!initializer || !isArrowFunction(initializer)) return undefined;
+ return initializer;
+ }
+
+ function convertToBlock(body: ConciseBody): Block {
+ if (isExpression(body)) {
+ return createBlock([createReturn(body)], /* multiLine */ true);
+ }
+ else {
+ return body;
+ }
+ }
+
+ function getVariableInfo(func: FunctionExpression | ArrowFunction): VariableInfo | undefined {
+ const variableDeclaration = func.parent;
+ if (!isVariableDeclaration(variableDeclaration) || !isVariableDeclarationInVariableStatement(variableDeclaration)) return undefined;
+
+ 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.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 };
+ }
+
+ function getEditInfoForConvertToNamedFunction(context: RefactorContext, func: FunctionExpression | ArrowFunction): RefactorEditInfo | undefined {
+ const { file } = context;
+ const body = convertToBlock(func.body);
+ 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 (canBeConvertedToExpression(func.body, 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 };
+ }
+
+ function canBeConvertedToExpression(body: Block, head: Statement): head is ReturnStatement | ExpressionStatement {
+ return body.statements.length === 1 && ((isReturnStatement(head) && !!head.expression) || isExpressionStatement(head));
+ }
+}
diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json
index 66b1977ddc573..134b28f854f64 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/convertArrowFunctionOrFunctionExpression.ts",
"services.ts",
"breakpoints.ts",
"transform.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..0fa3222617c1b
--- /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");
diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnArgument.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnArgument.ts
new file mode 100644
index 0000000000000..6c017aeeb739a
--- /dev/null
+++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Anon_FnArgument.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");
diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow.ts
new file mode 100644
index 0000000000000..619b2af822c5d
--- /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");
diff --git a/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnArgument.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnArgument.ts
new file mode 100644
index 0000000000000..6d309494da9f1
--- /dev/null
+++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_Availability_Arrow_FnArgument.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");
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..1e87f8bc0c30a
--- /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");
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_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_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_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;
+};`,
+});
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_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_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) { }
+};`,
+});
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_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.ts b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl.ts
new file mode 100644
index 0000000000000..789eb98141db7
--- /dev/null
+++ b/tests/cases/fourslash/refactorConvertArrowFunctionOrFunctionExpression_ToNamed_MultiDecl.ts
@@ -0,0 +1,15 @@
+///
+
+//// 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, magicNo;
+function bar() {
+ return 1 + 1;
+}
+`,
+});
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;
+}
+`,
+});
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_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;
+}`,
+});
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;
+}`,
+});