diff --git a/src/services/refactors/convertFunctionToEs6Class.ts b/src/services/refactors/convertFunctionToEs6Class.ts index d275924b76a..745ba6d15bf 100644 --- a/src/services/refactors/convertFunctionToEs6Class.ts +++ b/src/services/refactors/convertFunctionToEs6Class.ts @@ -164,12 +164,15 @@ namespace ts.refactor { } switch (assignmentBinaryExpression.right.kind) { - case SyntaxKind.FunctionExpression: + case SyntaxKind.FunctionExpression: { const functionExpression = assignmentBinaryExpression.right as FunctionExpression; - return createMethod(/*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined, + const method = createMethod(/*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined, /*typeParameters*/ undefined, functionExpression.parameters, /*type*/ undefined, functionExpression.body); + copyComments(assignmentBinaryExpression, method); + return method; + } - case SyntaxKind.ArrowFunction: + case SyntaxKind.ArrowFunction: { const arrowFunction = assignmentBinaryExpression.right as ArrowFunction; const arrowFunctionBody = arrowFunction.body; let bodyBlock: Block; @@ -183,20 +186,42 @@ namespace ts.refactor { const expression = arrowFunctionBody as Expression; bodyBlock = createBlock([createReturn(expression)]); } - return createMethod(/*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined, + const method = createMethod(/*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined, /*typeParameters*/ undefined, arrowFunction.parameters, /*type*/ undefined, bodyBlock); + copyComments(assignmentBinaryExpression, method); + return method; + } - default: + default: { // Don't try to declare members in JavaScript files if (isSourceFileJavaScript(sourceFile)) { return; } - return createProperty(/*decorators*/ undefined, modifiers, memberDeclaration.name, /*questionToken*/ undefined, + const prop = createProperty(/*decorators*/ undefined, modifiers, memberDeclaration.name, /*questionToken*/ undefined, /*type*/ undefined, assignmentBinaryExpression.right); + copyComments(assignmentBinaryExpression.parent, prop); + return prop; + } } } } + function copyComments(sourceNode: Node, targetNode: Node) { + forEachLeadingCommentRange(sourceFile.text, sourceNode.pos, (pos, end, kind, htnl) => { + if (kind === SyntaxKind.MultiLineCommentTrivia) { + // Remove leading /* + pos += 2; + // Remove trailing */ + end -= 2; + } + else { + // Remove leading // + pos += 2; + } + addSyntheticLeadingComment(targetNode, kind, sourceFile.text.slice(pos, end), htnl); + }); + } + function createClassFromVariableDeclaration(node: VariableDeclaration): ClassDeclaration { const initializer = node.initializer as FunctionExpression; if (!initializer || initializer.kind !== SyntaxKind.FunctionExpression) { @@ -212,8 +237,10 @@ namespace ts.refactor { memberElements.unshift(createConstructor(/*decorators*/ undefined, /*modifiers*/ undefined, initializer.parameters, initializer.body)); } - return createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, node.name, + const cls = createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, node.name, /*typeParameters*/ undefined, /*heritageClauses*/ undefined, memberElements); + // Don't call copyComments here because we'll already leave them in place + return cls; } function createClassFromFunctionDeclaration(node: FunctionDeclaration): ClassDeclaration { @@ -221,8 +248,11 @@ namespace ts.refactor { if (node.body) { memberElements.unshift(createConstructor(/*decorators*/ undefined, /*modifiers*/ undefined, node.parameters, node.body)); } - return createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, node.name, + + const cls = createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, node.name, /*typeParameters*/ undefined, /*heritageClauses*/ undefined, memberElements); + // Don't call copyComments here because we'll already leave them in place + return cls; } } } \ No newline at end of file diff --git a/tests/cases/fourslash/convertFunctionToEs6ClassJsDoc.ts b/tests/cases/fourslash/convertFunctionToEs6ClassJsDoc.ts new file mode 100644 index 00000000000..e9f6449c8fd --- /dev/null +++ b/tests/cases/fourslash/convertFunctionToEs6ClassJsDoc.ts @@ -0,0 +1,45 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: test123.js +//// function fn() { +//// /** neat! */ +//// this.x = 100; +//// } +//// +//// /** awesome +//// * stuff +//// */ +//// fn.prototype.arr = () => { return ""; } +//// /** great */ +//// fn.prototype.arr2 = () => []; +//// +//// /** +//// * This is a cool function! +//// */ +//// /*1*/fn.prototype.bar = function (x, y, z) { +//// this.x = y; +//// }; + +verify.fileAfterApplyingRefactorAtMarker('1', +`class fn { + constructor() { + /** neat! */ + this.x = 100; + } + /** awesome + * stuff + */ + arr() { return ""; } + /** great */ + arr2() { return []; } + /** + * This is a cool function! + */ + bar(x, y, z) { + this.x = y; + } +} + + +`, 'Convert to ES2015 class', 'convert');