Skip to content

Commit 8c714c3

Browse files
author
Andy
authored
Support special JS property assignments in doc comment templates (#18193)
1 parent 53b5abe commit 8c714c3

21 files changed

+157
-309
lines changed

src/harness/fourslash.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,7 +2414,7 @@ namespace FourSlash {
24142414
}
24152415
}
24162416

2417-
public verifyDocCommentTemplate(expected?: ts.TextInsertion) {
2417+
public verifyDocCommentTemplate(expected: ts.TextInsertion | undefined) {
24182418
const name = "verifyDocCommentTemplate";
24192419
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition);
24202420

@@ -3908,12 +3908,14 @@ namespace FourSlashInterface {
39083908
this.state.verifyNoMatchingBracePosition(bracePosition);
39093909
}
39103910

3911-
public DocCommentTemplate(expectedText: string, expectedOffset: number, empty?: boolean) {
3912-
this.state.verifyDocCommentTemplate(empty ? undefined : { newText: expectedText, caretOffset: expectedOffset });
3911+
public docCommentTemplateAt(marker: string | FourSlash.Marker, expectedOffset: number, expectedText: string) {
3912+
this.state.goToMarker(marker);
3913+
this.state.verifyDocCommentTemplate({ newText: expectedText.replace(/\r?\n/g, "\r\n"), caretOffset: expectedOffset });
39133914
}
39143915

3915-
public noDocCommentTemplate() {
3916-
this.DocCommentTemplate(/*expectedText*/ undefined, /*expectedOffset*/ undefined, /*empty*/ true);
3916+
public noDocCommentTemplateAt(marker: string | FourSlash.Marker) {
3917+
this.state.goToMarker(marker);
3918+
this.state.verifyDocCommentTemplate(/*expected*/ undefined);
39173919
}
39183920

39193921
public rangeAfterCodeFix(expectedText: string, includeWhiteSpace?: boolean, errorCode?: number, index?: number): void {

src/services/jsDoc.ts

Lines changed: 62 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -173,38 +173,15 @@ namespace ts.JsDoc {
173173
return undefined;
174174
}
175175

176-
// TODO: add support for:
177-
// - enums/enum members
178-
// - interfaces
179-
// - property declarations
180-
// - potentially property assignments
181-
let commentOwner: Node;
182-
findOwner: for (commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) {
183-
switch (commentOwner.kind) {
184-
case SyntaxKind.FunctionDeclaration:
185-
case SyntaxKind.MethodDeclaration:
186-
case SyntaxKind.Constructor:
187-
case SyntaxKind.ClassDeclaration:
188-
case SyntaxKind.VariableStatement:
189-
break findOwner;
190-
case SyntaxKind.SourceFile:
191-
return undefined;
192-
case SyntaxKind.ModuleDeclaration:
193-
// If in walking up the tree, we hit a a nested namespace declaration,
194-
// then we must be somewhere within a dotted namespace name; however we don't
195-
// want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'.
196-
if (commentOwner.parent.kind === SyntaxKind.ModuleDeclaration) {
197-
return undefined;
198-
}
199-
break findOwner;
200-
}
176+
const commentOwnerInfo = getCommentOwnerInfo(tokenAtPos);
177+
if (!commentOwnerInfo) {
178+
return undefined;
201179
}
202-
203-
if (!commentOwner || commentOwner.getStart() < position) {
180+
const { commentOwner, parameters } = commentOwnerInfo;
181+
if (commentOwner.getStart() < position) {
204182
return undefined;
205183
}
206184

207-
const parameters = getParametersForJsDocOwningNode(commentOwner);
208185
const posLineAndChar = sourceFile.getLineAndCharacterOfPosition(position);
209186
const lineStart = sourceFile.getLineStarts()[posLineAndChar.line];
210187

@@ -213,16 +190,18 @@ namespace ts.JsDoc {
213190
const isJavaScriptFile = hasJavaScriptFileExtension(sourceFile.fileName);
214191

215192
let docParams = "";
216-
for (let i = 0; i < parameters.length; i++) {
217-
const currentName = parameters[i].name;
218-
const paramName = currentName.kind === SyntaxKind.Identifier ?
219-
(<Identifier>currentName).escapedText :
220-
"param" + i;
221-
if (isJavaScriptFile) {
222-
docParams += `${indentationStr} * @param {any} ${paramName}${newLine}`;
223-
}
224-
else {
225-
docParams += `${indentationStr} * @param ${paramName}${newLine}`;
193+
if (parameters) {
194+
for (let i = 0; i < parameters.length; i++) {
195+
const currentName = parameters[i].name;
196+
const paramName = currentName.kind === SyntaxKind.Identifier ?
197+
(<Identifier>currentName).escapedText :
198+
"param" + i;
199+
if (isJavaScriptFile) {
200+
docParams += `${indentationStr} * @param {any} ${paramName}${newLine}`;
201+
}
202+
else {
203+
docParams += `${indentationStr} * @param ${paramName}${newLine}`;
204+
}
226205
}
227206
}
228207

@@ -244,21 +223,55 @@ namespace ts.JsDoc {
244223
return { newText: result, caretOffset: preamble.length };
245224
}
246225

247-
function getParametersForJsDocOwningNode(commentOwner: Node): ReadonlyArray<ParameterDeclaration> {
248-
if (isFunctionLike(commentOwner)) {
249-
return commentOwner.parameters;
250-
}
226+
interface CommentOwnerInfo {
227+
readonly commentOwner: Node;
228+
readonly parameters?: ReadonlyArray<ParameterDeclaration>;
229+
}
230+
function getCommentOwnerInfo(tokenAtPos: Node): CommentOwnerInfo | undefined {
231+
// TODO: add support for:
232+
// - enums/enum members
233+
// - interfaces
234+
// - property declarations
235+
// - potentially property assignments
236+
for (let commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) {
237+
switch (commentOwner.kind) {
238+
case SyntaxKind.FunctionDeclaration:
239+
case SyntaxKind.MethodDeclaration:
240+
case SyntaxKind.Constructor:
241+
const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration;
242+
return { commentOwner, parameters };
243+
244+
case SyntaxKind.ClassDeclaration:
245+
return { commentOwner };
251246

252-
if (commentOwner.kind === SyntaxKind.VariableStatement) {
253-
const varStatement = <VariableStatement>commentOwner;
254-
const varDeclarations = varStatement.declarationList.declarations;
247+
case SyntaxKind.VariableStatement: {
248+
const varStatement = <VariableStatement>commentOwner;
249+
const varDeclarations = varStatement.declarationList.declarations;
250+
const parameters = varDeclarations.length === 1 && varDeclarations[0].initializer
251+
? getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer)
252+
: undefined;
253+
return { commentOwner, parameters };
254+
}
255255

256-
if (varDeclarations.length === 1 && varDeclarations[0].initializer) {
257-
return getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer);
256+
case SyntaxKind.SourceFile:
257+
return undefined;
258+
259+
case SyntaxKind.ModuleDeclaration:
260+
// If in walking up the tree, we hit a a nested namespace declaration,
261+
// then we must be somewhere within a dotted namespace name; however we don't
262+
// want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'.
263+
return commentOwner.parent.kind === SyntaxKind.ModuleDeclaration ? undefined : { commentOwner };
264+
265+
case SyntaxKind.BinaryExpression: {
266+
const be = commentOwner as BinaryExpression;
267+
if (getSpecialPropertyAssignmentKind(be) === ts.SpecialPropertyAssignmentKind.None) {
268+
return undefined;
269+
}
270+
const parameters = isFunctionLike(be.right) ? be.right.parameters : emptyArray;
271+
return { commentOwner, parameters };
272+
}
258273
}
259274
}
260-
261-
return emptyArray;
262275
}
263276

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

3-
const CRLF = "\r\n";
4-
/**
5-
* @returns the given value with '\n' normalized to '\r\n' and with no leading newline
6-
*/
7-
function useCRLFAndStripLeadingNewline(str: string): string {
8-
str = str.replace(/\r?\n/g, CRLF);
9-
if (str.indexOf(CRLF) === 0) {
10-
str = str.slice(CRLF.length);
11-
}
12-
return str;
13-
}
14-
15-
function confirmNormalizedJsDoc(markerName: string, newTextOffset: number, template: string): void {
16-
goTo.marker(markerName);
17-
const normalized = useCRLFAndStripLeadingNewline(template);
18-
verify.DocCommentTemplate(normalized, newTextOffset);
19-
}
20-
213
/////*decl*/class C {
224
//// private p;
235
//// constructor(a, b, c, d);
@@ -29,8 +11,8 @@ function confirmNormalizedJsDoc(markerName: string, newTextOffset: number, templ
2911
//// }
3012
////}
3113

32-
confirmNormalizedJsDoc("decl", /*newTextOffset*/ 8, `
33-
/**
14+
verify.docCommentTemplateAt("decl", /*newTextOffset*/ 8,
15+
`/**
3416
*
3517
*/
3618
`);

tests/cases/fourslash/docCommentTemplateClassDeclMethods01.ts

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

3-
const CRLF = "\r\n";
4-
/**
5-
* @returns the given value with '\n' normalized to '\r\n' and with no leading newline
6-
*/
7-
function useCRLFAndStripLeadingNewline(str: string): string {
8-
str = str.replace(/\r?\n/g, CRLF);
9-
if (str.indexOf(CRLF) === 0) {
10-
str = str.slice(CRLF.length);
11-
}
12-
return str;
13-
}
14-
15-
function confirmNormalizedJsDoc(markerName: string, indentation: number, template: string): void {
16-
goTo.marker(markerName);
17-
const normalized = useCRLFAndStripLeadingNewline(template);
18-
verify.DocCommentTemplate(normalized, indentation);
19-
}
20-
213
const enum Indentation {
224
Standard = 8,
235
Indented = 12,
@@ -34,42 +16,42 @@ const enum Indentation {
3416
//// }
3517
////}
3618

37-
confirmNormalizedJsDoc("0", Indentation.Standard, `
38-
/**
19+
verify.docCommentTemplateAt("0", Indentation.Standard,
20+
`/**
3921
*
4022
*/`);
4123

4224

43-
confirmNormalizedJsDoc("1", Indentation.Indented,
25+
verify.docCommentTemplateAt("1", Indentation.Indented,
4426
`/**
4527
*
4628
*/`);
4729

4830

49-
confirmNormalizedJsDoc("2", Indentation.Indented,
31+
verify.docCommentTemplateAt("2", Indentation.Indented,
5032
`/**
5133
*
5234
* @param a
5335
*/
5436
`);
5537

56-
confirmNormalizedJsDoc("3", Indentation.Indented,
38+
verify.docCommentTemplateAt("3", Indentation.Indented,
5739
`/**
5840
*
5941
* @param a
6042
* @param b
6143
*/
6244
`);
6345

64-
confirmNormalizedJsDoc("4", Indentation.Indented,
46+
verify.docCommentTemplateAt("4", Indentation.Indented,
6547
`/**
6648
*
6749
* @param a
6850
* @param param1
6951
* @param param2
7052
*/`);
7153

72-
confirmNormalizedJsDoc("5", Indentation.Indented,
54+
verify.docCommentTemplateAt("5", Indentation.Indented,
7355
`/**
7456
*
7557
* @param a
Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,9 @@
11
/// <reference path='fourslash.ts' />
22

3-
const CRLF = "\r\n";
4-
/**
5-
* @returns the given value with '\n' normalized to '\r\n' and with no leading newline
6-
*/
7-
function useCRLFAndStripLeadingNewline(str: string): string {
8-
str = str.replace(/\r?\n/g, CRLF);
9-
if (str.indexOf(CRLF) === 0) {
10-
str = str.slice(CRLF.length);
11-
}
12-
return str;
13-
}
14-
15-
function confirmNormalizedJsDoc(markerName: string, indentation: number, template: string): void {
16-
goTo.marker(markerName);
17-
const normalized = useCRLFAndStripLeadingNewline(template);
18-
verify.DocCommentTemplate(normalized, indentation);
19-
}
20-
213
const enum Indentation {
224
Indented = 12,
235
}
246

25-
267
////class C {
278
//// /*0*/
289
//// [Symbol.iterator]() {
@@ -32,15 +13,15 @@ const enum Indentation {
3213
//// [1 + 2 + 3 + Math.rand()](x: number, y: string, z = true) { }
3314
////}
3415

35-
confirmNormalizedJsDoc("0", Indentation.Indented,
16+
verify.docCommentTemplateAt("0", Indentation.Indented,
3617
`/**
3718
*
3819
*/`);
3920

40-
confirmNormalizedJsDoc("1", Indentation.Indented,
21+
verify.docCommentTemplateAt("1", Indentation.Indented,
4122
`/**
4223
*
4324
* @param x
4425
* @param y
4526
* @param z
46-
*/`);
27+
*/`);

tests/cases/fourslash/docCommentTemplateConstructor01.ts

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

3-
const CRLF = "\r\n";
4-
/**
5-
* @returns the given value with '\n' normalized to '\r\n' and with no leading newline
6-
*/
7-
function useCRLFAndStripLeadingNewline(str: string): string {
8-
str = str.replace(/\r?\n/g, CRLF);
9-
if (str.indexOf(CRLF) === 0) {
10-
str = str.slice(CRLF.length);
11-
}
12-
return str;
13-
}
14-
15-
function confirmNormalizedJsDoc(markerName: string, newTextOffset: number, template: string): void {
16-
goTo.marker(markerName);
17-
const normalized = useCRLFAndStripLeadingNewline(template);
18-
verify.DocCommentTemplate(normalized, newTextOffset);
19-
}
20-
213
////class C {
224
//// private p;
235
//// /*0*/
@@ -32,7 +14,7 @@ function confirmNormalizedJsDoc(markerName: string, newTextOffset: number, templ
3214
////}
3315

3416
const newTextOffset = 12;
35-
confirmNormalizedJsDoc("0", /*newTextOffset*/ newTextOffset,
17+
verify.docCommentTemplateAt("0", /*newTextOffset*/ newTextOffset,
3618
`/**
3719
*
3820
* @param a
@@ -41,7 +23,7 @@ confirmNormalizedJsDoc("0", /*newTextOffset*/ newTextOffset,
4123
* @param d
4224
*/`);
4325

44-
confirmNormalizedJsDoc("1", /*newTextOffset*/ newTextOffset,
26+
verify.docCommentTemplateAt("1", /*newTextOffset*/ newTextOffset,
4527
`/**
4628
*
4729
* @param a

tests/cases/fourslash/docCommentTemplateEmptyFile.ts

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

6-
goTo.marker("0");
7-
verify.noDocCommentTemplate();
6+
verify.noDocCommentTemplateAt("0");

tests/cases/fourslash/docCommentTemplateFunctionWithParameters.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,5 @@ const noIndentOffset = 8;
1111
const oneIndentOffset = noIndentOffset + 4;
1212

1313
goTo.marker("0");
14-
verify.DocCommentTemplate(noIndentScaffolding, noIndentOffset);
15-
16-
goTo.marker("1");
17-
verify.DocCommentTemplate(oneIndentScaffolding, oneIndentOffset);
14+
verify.docCommentTemplateAt("0", noIndentOffset, noIndentScaffolding);
15+
verify.docCommentTemplateAt("1", oneIndentOffset, oneIndentScaffolding);

tests/cases/fourslash/docCommentTemplateInMultiLineComment.ts

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

6-
goTo.marker("0");
7-
verify.noDocCommentTemplate();
6+
verify.noDocCommentTemplateAt("0");

0 commit comments

Comments
 (0)