Skip to content

Commit 042bf4e

Browse files
authored
feat(14248): add returns tag to JSDoc template (microsoft#42068)
1 parent 0d284e6 commit 042bf4e

8 files changed

+116
-46
lines changed

src/services/jsDoc.ts

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -269,34 +269,35 @@ namespace ts.JsDoc {
269269
if (!commentOwnerInfo) {
270270
return undefined;
271271
}
272-
const { commentOwner, parameters } = commentOwnerInfo;
272+
273+
const { commentOwner, parameters, hasReturn } = commentOwnerInfo;
273274
if (commentOwner.getStart(sourceFile) < position) {
274275
return undefined;
275276
}
276277

277-
if (!parameters || parameters.length === 0) {
278-
// if there are no parameters, just complete to a single line JSDoc comment
279-
const singleLineResult = "/** */";
280-
return { newText: singleLineResult, caretOffset: 3 };
281-
}
282-
283278
const indentationStr = getIndentationStringAtPosition(sourceFile, position);
279+
const isJavaScriptFile = hasJSFileExtension(sourceFile.fileName);
280+
const tags =
281+
(parameters ? parameterDocComments(parameters || [], isJavaScriptFile, indentationStr, newLine) : "") +
282+
(hasReturn ? returnsDocComment(indentationStr, newLine) : "");
284283

285284
// A doc comment consists of the following
286285
// * The opening comment line
287286
// * the first line (without a param) for the object's untagged info (this is also where the caret ends up)
288287
// * the '@param'-tagged lines
288+
// * the '@returns'-tag
289289
// * TODO: other tags.
290290
// * the closing comment line
291291
// * if the caret was directly in front of the object, then we add an extra line and indentation.
292-
const preamble = "/**" + newLine + indentationStr + " * ";
293-
const result =
294-
preamble + newLine +
295-
parameterDocComments(parameters, hasJSFileExtension(sourceFile.fileName), indentationStr, newLine) +
296-
indentationStr + " */" +
297-
(tokenStart === position ? newLine + indentationStr : "");
298-
299-
return { newText: result, caretOffset: preamble.length };
292+
const openComment = "/**";
293+
const closeComment = " */";
294+
if (tags) {
295+
const preamble = openComment + newLine + indentationStr + " * ";
296+
const endLine = tokenStart === position ? newLine + indentationStr : "";
297+
const result = preamble + newLine + tags + indentationStr + closeComment + endLine;
298+
return { newText: result, caretOffset: preamble.length };
299+
}
300+
return { newText: openComment + closeComment, caretOffset: 3 };
300301
}
301302

302303
function getIndentationStringAtPosition(sourceFile: SourceFile, position: number): string {
@@ -315,9 +316,14 @@ namespace ts.JsDoc {
315316
}).join("");
316317
}
317318

319+
function returnsDocComment(indentationStr: string, newLine: string) {
320+
return `${indentationStr} * @returns${newLine}`;
321+
}
322+
318323
interface CommentOwnerInfo {
319324
readonly commentOwner: Node;
320325
readonly parameters?: readonly ParameterDeclaration[];
326+
readonly hasReturn?: boolean;
321327
}
322328
function getCommentOwnerInfo(tokenAtPos: Node): CommentOwnerInfo | undefined {
323329
return forEachAncestor(tokenAtPos, getCommentOwnerInfoWorker);
@@ -330,8 +336,8 @@ namespace ts.JsDoc {
330336
case SyntaxKind.Constructor:
331337
case SyntaxKind.MethodSignature:
332338
case SyntaxKind.ArrowFunction:
333-
const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature;
334-
return { commentOwner, parameters };
339+
const host = commentOwner as ArrowFunction | FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature;
340+
return { commentOwner, parameters: host.parameters, hasReturn: hasReturn(host) };
335341

336342
case SyntaxKind.PropertyAssignment:
337343
return getCommentOwnerInfoWorker((commentOwner as PropertyAssignment).initializer);
@@ -347,10 +353,12 @@ namespace ts.JsDoc {
347353
case SyntaxKind.VariableStatement: {
348354
const varStatement = <VariableStatement>commentOwner;
349355
const varDeclarations = varStatement.declarationList.declarations;
350-
const parameters = varDeclarations.length === 1 && varDeclarations[0].initializer
351-
? getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer)
356+
const host = varDeclarations.length === 1 && varDeclarations[0].initializer
357+
? getRightHandSideOfAssignment(varDeclarations[0].initializer)
352358
: undefined;
353-
return { commentOwner, parameters };
359+
return host
360+
? { commentOwner, parameters: host.parameters, hasReturn: hasReturn(host) }
361+
: { commentOwner };
354362
}
355363

356364
case SyntaxKind.SourceFile:
@@ -369,40 +377,34 @@ namespace ts.JsDoc {
369377
if (getAssignmentDeclarationKind(be) === AssignmentDeclarationKind.None) {
370378
return "quit";
371379
}
372-
const parameters = isFunctionLike(be.right) ? be.right.parameters : emptyArray;
373-
return { commentOwner, parameters };
380+
return isFunctionLike(be.right)
381+
? { commentOwner, parameters: be.right.parameters, hasReturn: hasReturn(be.right) }
382+
: { commentOwner };
374383
}
375384
case SyntaxKind.PropertyDeclaration:
376385
const init = (commentOwner as PropertyDeclaration).initializer;
377386
if (init && (isFunctionExpression(init) || isArrowFunction(init))) {
378-
return { commentOwner, parameters: init.parameters };
387+
return { commentOwner, parameters: init.parameters, hasReturn: hasReturn(init) };
379388
}
380389
}
381390
}
382391

383-
/**
384-
* Digs into an an initializer or RHS operand of an assignment operation
385-
* to get the parameters of an apt signature corresponding to a
386-
* function expression or a class expression.
387-
*
388-
* @param rightHandSide the expression which may contain an appropriate set of parameters
389-
* @returns the parameters of a signature found on the RHS if one exists; otherwise 'emptyArray'.
390-
*/
391-
function getParametersFromRightHandSideOfAssignment(rightHandSide: Expression): readonly ParameterDeclaration[] {
392+
function hasReturn(node: Node) {
393+
return isArrowFunction(node) && isExpression(node.body)
394+
|| isFunctionLikeDeclaration(node) && node.body && isBlock(node.body) && !!forEachReturnStatement(node.body, n => n);
395+
}
396+
397+
function getRightHandSideOfAssignment(rightHandSide: Expression): FunctionExpression | ArrowFunction | ConstructorDeclaration | undefined {
392398
while (rightHandSide.kind === SyntaxKind.ParenthesizedExpression) {
393399
rightHandSide = (<ParenthesizedExpression>rightHandSide).expression;
394400
}
395401

396402
switch (rightHandSide.kind) {
397403
case SyntaxKind.FunctionExpression:
398404
case SyntaxKind.ArrowFunction:
399-
return (<FunctionExpression>rightHandSide).parameters;
400-
case SyntaxKind.ClassExpression: {
401-
const ctr = find((rightHandSide as ClassExpression).members, isConstructorDeclaration);
402-
return ctr ? ctr.parameters : emptyArray;
403-
}
405+
return (<FunctionExpression>rightHandSide);
406+
case SyntaxKind.ClassExpression:
407+
return find((rightHandSide as ClassExpression).members, isConstructorDeclaration);
404408
}
405-
406-
return emptyArray;
407409
}
408410
}

tests/cases/fourslash/docCommentTemplateClassDeclMethods02.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/// <reference path='fourslash.ts' />
22

3-
const singleLineOffset = 3;
43
const multiLineOffset = 12;
54

65
////class C {
@@ -12,8 +11,11 @@ const multiLineOffset = 12;
1211
//// [1 + 2 + 3 + Math.rand()](x: number, y: string, z = true) { }
1312
////}
1413

15-
verify.docCommentTemplateAt("0", singleLineOffset,
16-
"/** */");
14+
verify.docCommentTemplateAt("0", multiLineOffset,
15+
`/**
16+
*
17+
* @returns
18+
*/`);
1719

1820
verify.docCommentTemplateAt("1", multiLineOffset,
1921
`/**

tests/cases/fourslash/docCommentTemplateClassDeclProperty01.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,18 @@ verify.docCommentTemplateAt("0", multiLineOffset,
2323
`/**
2424
*
2525
* @param p0
26+
* @returns
2627
*/`);
2728
verify.docCommentTemplateAt("1", multiLineOffset,
2829
`/**
2930
*
3031
* @param p1
32+
* @returns
3133
*/`);
3234
verify.docCommentTemplateAt("2", multiLineOffset,
3335
`/**
3436
*
3537
* @param p2
3638
* @param p3
39+
* @returns
3740
*/`);
38-
39-

tests/cases/fourslash/docCommentTemplateObjectLiteralMethods01.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/// <reference path='fourslash.ts' />
22

3-
const singleLineOffset = 3;
43
const multiLineOffset = 12;
54

65
////var x = {
@@ -19,7 +18,11 @@ const multiLineOffset = 12;
1918
//// m2: (a: string, b: string) => {}
2019
////}
2120

22-
verify.docCommentTemplateAt("0", singleLineOffset, "/** */");
21+
verify.docCommentTemplateAt("0", multiLineOffset,
22+
`/**
23+
*
24+
* @returns
25+
*/`);
2326

2427
verify.docCommentTemplateAt("1", multiLineOffset,
2528
`/**
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
/////*0*/
4+
////function f1() {}
5+
6+
/////*1*/
7+
////function f2() {
8+
//// return 1;
9+
////}
10+
11+
/////*2*/
12+
////const f3 = () => 1;
13+
14+
/////*3*/
15+
////const f3 = () => {
16+
//// return 1;
17+
////}
18+
19+
////class Foo {
20+
//// /*4*/
21+
//// m1() {}
22+
////
23+
//// /*5*/
24+
//// m2() {
25+
//// return 1;
26+
//// }
27+
////}
28+
29+
verify.docCommentTemplateAt("0", 3, "/** */");
30+
31+
verify.docCommentTemplateAt("1", 8,
32+
`/**
33+
*
34+
* @returns
35+
*/`);
36+
37+
verify.docCommentTemplateAt("2", 8,
38+
`/**
39+
*
40+
* @returns
41+
*/`);
42+
43+
verify.docCommentTemplateAt("3", 8,
44+
`/**
45+
*
46+
* @returns
47+
*/`);
48+
49+
verify.docCommentTemplateAt("4", 3, "/** */");
50+
51+
52+
verify.docCommentTemplateAt("5", 12,
53+
`/**
54+
*
55+
* @returns
56+
*/`);

tests/cases/fourslash/docCommentTemplateVariableStatements01.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ verify.docCommentTemplateAt("e", /*newTextOffset*/ 8,
3939
* @param x
4040
* @param y
4141
* @param z
42+
* @returns
4243
*/`);
4344

4445
verify.docCommentTemplateAt("f", /*newTextOffset*/ 8,

tests/cases/fourslash/docCommentTemplateVariableStatements03.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ verify.docCommentTemplateAt("a", /*newTextOffset*/ 8,
3333
`/**
3434
*
3535
* @param x
36+
* @returns
3637
*/`);
3738

3839
verify.docCommentTemplateAt("b", /*newTextOffset*/ 8,
@@ -41,12 +42,14 @@ verify.docCommentTemplateAt("b", /*newTextOffset*/ 8,
4142
* @param x
4243
* @param y
4344
* @param z
45+
* @returns
4446
*/`);
4547

4648
verify.docCommentTemplateAt("c", /*newTextOffset*/ 8,
4749
`/**
4850
*
4951
* @param x
52+
* @returns
5053
*/`);
5154

5255
verify.docCommentTemplateAt("d", /*newTextOffset*/ 3,
@@ -56,6 +59,7 @@ verify.docCommentTemplateAt("e", /*newTextOffset*/ 8,
5659
`/**
5760
*
5861
* @param param0
62+
* @returns
5963
*/`);
6064

6165
verify.docCommentTemplateAt("f", /*newTextOffset*/ 3,

tests/cases/fourslash/docCommentTemplate_insideEmptyComment.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ verify.docCommentTemplateAt("", /*newTextOffset*/ 8,
1010
`/**
1111
*
1212
* @param p
13+
* @returns
1314
*/`);
1415

1516
verify.noDocCommentTemplateAt("1");

0 commit comments

Comments
 (0)