Skip to content

Commit 612616a

Browse files
Loosen restrictions on jsdoc completion locations
1 parent b1b611f commit 612616a

6 files changed

Lines changed: 50 additions & 40 deletions

src/services/jsDoc.ts

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* @internal */
22
namespace ts.JsDoc {
3+
const singleLineTemplate = { newText: "/** */", caretOffset: 3 };
34
const jsDocTagNames = [
45
"augments",
56
"author",
@@ -170,15 +171,9 @@ namespace ts.JsDoc {
170171
/**
171172
* Checks if position points to a valid position to add JSDoc comments, and if so,
172173
* returns the appropriate template. Otherwise returns an empty string.
173-
* Valid positions are
174-
* - outside of comments, statements, and expressions, and
175-
* - preceding a:
176-
* - function/constructor/method declaration
177-
* - class declarations
178-
* - variable statements
179-
* - namespace declarations
180-
* - interface declarations
181-
* - method signatures
174+
* Invalid positions are
175+
* - within comments, strings (including template literals and regex), and JSXText
176+
* - within a token
182177
*
183178
* Hosts should ideally check that:
184179
* - The line is all whitespace up to 'position' before performing the insertion.
@@ -204,17 +199,23 @@ namespace ts.JsDoc {
204199

205200
const commentOwnerInfo = getCommentOwnerInfo(tokenAtPos);
206201
if (!commentOwnerInfo) {
207-
return undefined;
202+
// if climbing the tree did not find a declaration with parameters, complete to a single line comment
203+
return singleLineTemplate;
208204
}
209205
const { commentOwner, parameters } = commentOwnerInfo;
210-
if (commentOwner.getStart() < position) {
206+
207+
if (commentOwner.kind === SyntaxKind.JsxText) {
211208
return undefined;
212209
}
213210

214-
if (!parameters || parameters.length === 0) {
215-
// if there are no parameters, just complete to a single line JSDoc comment
216-
const singleLineResult = "/** */";
217-
return { newText: singleLineResult, caretOffset: 3 };
211+
if (commentOwner.getStart() < position) {
212+
// if climbing the tree found a declaration with parameters but the request was made inside it, complete to a single line comment
213+
return singleLineTemplate;
214+
}
215+
216+
if (parameters.length === 0) {
217+
// if there are no parameters, complete to a single line comment
218+
return singleLineTemplate;
218219
}
219220

220221
const posLineAndChar = sourceFile.getLineAndCharacterOfPosition(position);
@@ -258,7 +259,7 @@ namespace ts.JsDoc {
258259

259260
interface CommentOwnerInfo {
260261
readonly commentOwner: Node;
261-
readonly parameters?: ReadonlyArray<ParameterDeclaration>;
262+
readonly parameters: ReadonlyArray<ParameterDeclaration>;
262263
}
263264
function getCommentOwnerInfo(tokenAtPos: Node): CommentOwnerInfo | undefined {
264265
for (let commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) {
@@ -270,32 +271,18 @@ namespace ts.JsDoc {
270271
const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature;
271272
return { commentOwner, parameters };
272273

273-
case SyntaxKind.ClassDeclaration:
274-
case SyntaxKind.InterfaceDeclaration:
275-
case SyntaxKind.PropertySignature:
276-
case SyntaxKind.EnumDeclaration:
277-
case SyntaxKind.EnumMember:
278-
case SyntaxKind.TypeAliasDeclaration:
279-
return { commentOwner };
280-
281274
case SyntaxKind.VariableStatement: {
282275
const varStatement = <VariableStatement>commentOwner;
283276
const varDeclarations = varStatement.declarationList.declarations;
284277
const parameters = varDeclarations.length === 1 && varDeclarations[0].initializer
285278
? getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer)
286279
: undefined;
287-
return { commentOwner, parameters };
280+
return parameters ? { commentOwner, parameters } : undefined;
288281
}
289282

290283
case SyntaxKind.SourceFile:
291284
return undefined;
292285

293-
case SyntaxKind.ModuleDeclaration:
294-
// If in walking up the tree, we hit a a nested namespace declaration,
295-
// then we must be somewhere within a dotted namespace name; however we don't
296-
// want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'.
297-
return commentOwner.parent.kind === SyntaxKind.ModuleDeclaration ? undefined : { commentOwner };
298-
299286
case SyntaxKind.BinaryExpression: {
300287
const be = commentOwner as BinaryExpression;
301288
if (getSpecialPropertyAssignmentKind(be) === ts.SpecialPropertyAssignmentKind.None) {
@@ -304,6 +291,11 @@ namespace ts.JsDoc {
304291
const parameters = isFunctionLike(be.right) ? be.right.parameters : emptyArray;
305292
return { commentOwner, parameters };
306293
}
294+
295+
case SyntaxKind.JsxText: {
296+
const parameters: ReadonlyArray<ParameterDeclaration> = emptyArray;
297+
return { commentOwner, parameters };
298+
}
307299
}
308300
}
309301
}

tests/cases/fourslash/docCommentTemplateEmptyFile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
// @Filename: emptyFile.ts
44
/////*0*/
55

6-
verify.noDocCommentTemplateAt("0");
6+
verify.docCommentTemplateAt("0", 3, "/** */");

tests/cases/fourslash/docCommentTemplateInsideFunctionDeclaration.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
// @Filename: functionDecl.ts
44
////f/*0*/unction /*1*/foo/*2*/(/*3*/) /*4*/{ /*5*/}
55

6-
for (const marker of test.markers()) {
7-
verify.noDocCommentTemplateAt(marker);
8-
}
6+
verify.noDocCommentTemplateAt("0");
7+
8+
verify.docCommentTemplateAt("1", 3, "/** */");
9+
verify.docCommentTemplateAt("2", 3, "/** */");
10+
verify.docCommentTemplateAt("3", 3, "/** */");
11+
verify.docCommentTemplateAt("4", 3, "/** */");
12+
verify.docCommentTemplateAt("5", 3, "/** */");
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
//@Filename: file.tsx
4+
////
5+
//// var x = <div>
6+
//// /*0*/hello/*1*/
7+
//// /*2*/goodbye/*3*/
8+
//// </div>;
9+
10+
for (const marker in test.markers()) {
11+
verify.noDocCommentTemplateAt(marker);
12+
}

tests/cases/fourslash/docCommentTemplateNamespacesAndModules02.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
verify.docCommentTemplateAt("top", /*indentation*/ 3,
1010
"/** */");
1111

12-
verify.noDocCommentTemplateAt("n2");
12+
verify.docCommentTemplateAt("n2", 3, "/** */");
1313

14-
verify.noDocCommentTemplateAt("n3");
14+
verify.docCommentTemplateAt("n3", 3, "/** */");

tests/cases/fourslash/docCommentTemplateRegex.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// @Filename: regex.ts
44
////var regex = /*0*///*1*/asdf/*2*/ /*3*///*4*/;
55

6-
for (const marker of test.markers()) {
7-
verify.noDocCommentTemplateAt(marker);
8-
}
6+
verify.docCommentTemplateAt("0", 3, "/** */");
7+
verify.noDocCommentTemplateAt("1");
8+
verify.noDocCommentTemplateAt("2");
9+
verify.noDocCommentTemplateAt("3");
10+
verify.docCommentTemplateAt("4", 3, "/** */");

0 commit comments

Comments
 (0)