diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 64ef1b552f5fa..c074b260a1d6b 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -492,7 +492,7 @@ namespace Harness.LanguageService { getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: ts.FormatCodeOptions): ts.TextChange[] { return unwrapJSONCallResult(this.shim.getFormattingEditsAfterKeystroke(fileName, position, key, JSON.stringify(options))); } - getDocCommentTemplateAtPosition(fileName: string, position: number): ts.TextInsertion { + getDocCommentTemplateAtPosition(fileName: string, position: number): ts.TextInsertion | undefined { return unwrapJSONCallResult(this.shim.getDocCommentTemplateAtPosition(fileName, position)); } isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean { diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index 3e9913fc6c726..bb2cfb03e4738 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -1,5 +1,6 @@ /* @internal */ namespace ts.JsDoc { + const singleLineTemplate = { newText: "/** */", caretOffset: 3 }; const jsDocTagNames = [ "augments", "author", @@ -170,13 +171,9 @@ namespace ts.JsDoc { /** * Checks if position points to a valid position to add JSDoc comments, and if so, * returns the appropriate template. Otherwise returns an empty string. - * Valid positions are - * - outside of comments, statements, and expressions, and - * - preceding a: - * - function/constructor/method declaration - * - class declarations - * - variable statements - * - namespace declarations + * Invalid positions are + * - within comments, strings (including template literals and regex), and JSXText + * - within a token * * Hosts should ideally check that: * - The line is all whitespace up to 'position' before performing the insertion. @@ -187,7 +184,8 @@ namespace ts.JsDoc { * @param position The (character-indexed) position in the file where the check should * be performed. */ - export function getDocCommentTemplateAtPosition(newLine: string, sourceFile: SourceFile, position: number): TextInsertion { + + export function getDocCommentTemplateAtPosition(newLine: string, sourceFile: SourceFile, position: number): TextInsertion | undefined { // Check if in a context where we don't want to perform any insertion if (isInString(sourceFile, position) || isInComment(sourceFile, position) || hasDocComment(sourceFile, position)) { return undefined; @@ -201,13 +199,21 @@ namespace ts.JsDoc { const commentOwnerInfo = getCommentOwnerInfo(tokenAtPos); if (!commentOwnerInfo) { - return undefined; + // if climbing the tree did not find a declaration with parameters, complete to a single line comment + return singleLineTemplate; } const { commentOwner, parameters } = commentOwnerInfo; - if (commentOwner.getStart() < position) { + + if (commentOwner.kind === SyntaxKind.JsxText) { return undefined; } + if (commentOwner.getStart() < position || parameters.length === 0) { + // if climbing the tree found a declaration with parameters but the request was made inside it + // or if there are no parameters, complete to a single line comment + return singleLineTemplate; + } + const posLineAndChar = sourceFile.getLineAndCharacterOfPosition(position); const lineStart = sourceFile.getLineStarts()[posLineAndChar.line]; @@ -215,21 +221,11 @@ namespace ts.JsDoc { const indentationStr = sourceFile.text.substr(lineStart, posLineAndChar.character).replace(/\S/i, () => " "); const isJavaScriptFile = hasJavaScriptFileExtension(sourceFile.fileName); - let docParams = ""; - if (parameters) { - for (let i = 0; i < parameters.length; i++) { - const currentName = parameters[i].name; - const paramName = currentName.kind === SyntaxKind.Identifier ? - (currentName).escapedText : - "param" + i; - if (isJavaScriptFile) { - docParams += `${indentationStr} * @param {any} ${paramName}${newLine}`; - } - else { - docParams += `${indentationStr} * @param ${paramName}${newLine}`; - } - } - } + const docParams = parameters.map(({name}, i) => { + const nameText = isIdentifier(name) ? name.text : `param${i}`; + const type = isJavaScriptFile ? "{any} " : ""; + return `${indentationStr} * @param ${type}${nameText}${newLine}`; + }).join(""); // A doc comment consists of the following // * The opening comment line @@ -251,43 +247,30 @@ namespace ts.JsDoc { interface CommentOwnerInfo { readonly commentOwner: Node; - readonly parameters?: ReadonlyArray; + readonly parameters: ReadonlyArray; } function getCommentOwnerInfo(tokenAtPos: Node): CommentOwnerInfo | undefined { - // TODO: add support for: - // - enums/enum members - // - interfaces - // - property declarations - // - potentially property assignments for (let commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) { switch (commentOwner.kind) { case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodDeclaration: case SyntaxKind.Constructor: - const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration; + case SyntaxKind.MethodSignature: + const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature; return { commentOwner, parameters }; - case SyntaxKind.ClassDeclaration: - return { commentOwner }; - case SyntaxKind.VariableStatement: { const varStatement = commentOwner; const varDeclarations = varStatement.declarationList.declarations; const parameters = varDeclarations.length === 1 && varDeclarations[0].initializer ? getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer) : undefined; - return { commentOwner, parameters }; + return parameters ? { commentOwner, parameters } : undefined; } case SyntaxKind.SourceFile: return undefined; - case SyntaxKind.ModuleDeclaration: - // If in walking up the tree, we hit a a nested namespace declaration, - // then we must be somewhere within a dotted namespace name; however we don't - // want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'. - return commentOwner.parent.kind === SyntaxKind.ModuleDeclaration ? undefined : { commentOwner }; - case SyntaxKind.BinaryExpression: { const be = commentOwner as BinaryExpression; if (getSpecialPropertyAssignmentKind(be) === ts.SpecialPropertyAssignmentKind.None) { @@ -296,6 +279,11 @@ namespace ts.JsDoc { const parameters = isFunctionLike(be.right) ? be.right.parameters : emptyArray; return { commentOwner, parameters }; } + + case SyntaxKind.JsxText: { + const parameters: ReadonlyArray = emptyArray; + return { commentOwner, parameters }; + } } } } diff --git a/src/services/services.ts b/src/services/services.ts index d6e75868852ae..0df273e8fcecb 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1791,7 +1791,7 @@ namespace ts { } } - function getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion { + function getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion | undefined { return JsDoc.getDocCommentTemplateAtPosition(getNewLineOrDefaultFromHost(host), syntaxTreeCache.getCurrentSourceFile(fileName), position); } diff --git a/tests/cases/fourslash/docCommentTemplateClassDecl01.ts b/tests/cases/fourslash/docCommentTemplateClassDecl01.ts index 5a96f20d2e25d..342d35a3b4a9e 100644 --- a/tests/cases/fourslash/docCommentTemplateClassDecl01.ts +++ b/tests/cases/fourslash/docCommentTemplateClassDecl01.ts @@ -11,8 +11,5 @@ //// } ////} -verify.docCommentTemplateAt("decl", /*newTextOffset*/ 8, -`/** - * - */ -`); +verify.docCommentTemplateAt("decl", /*newTextOffset*/ 3, +"/** */"); diff --git a/tests/cases/fourslash/docCommentTemplateClassDeclMethods01.ts b/tests/cases/fourslash/docCommentTemplateClassDeclMethods01.ts index ef4c82e7df74f..2e72924349730 100644 --- a/tests/cases/fourslash/docCommentTemplateClassDeclMethods01.ts +++ b/tests/cases/fourslash/docCommentTemplateClassDeclMethods01.ts @@ -1,9 +1,7 @@ /// -const enum Indentation { - Standard = 8, - Indented = 12, -} +const singleLineOffset = 3; +const multiLineOffset = 12; ////class C { @@ -16,26 +14,22 @@ const enum Indentation { //// } ////} -verify.docCommentTemplateAt("0", Indentation.Standard, -`/** - * - */`); +verify.docCommentTemplateAt("0", singleLineOffset, +"/** */"); -verify.docCommentTemplateAt("1", Indentation.Indented, - `/** - * - */`); +verify.docCommentTemplateAt("1", singleLineOffset, +"/** */"); -verify.docCommentTemplateAt("2", Indentation.Indented, +verify.docCommentTemplateAt("2", multiLineOffset, `/** * * @param a */ `); -verify.docCommentTemplateAt("3", Indentation.Indented, +verify.docCommentTemplateAt("3", multiLineOffset, `/** * * @param a @@ -43,7 +37,7 @@ verify.docCommentTemplateAt("3", Indentation.Indented, */ `); -verify.docCommentTemplateAt("4", Indentation.Indented, +verify.docCommentTemplateAt("4", multiLineOffset, `/** * * @param a @@ -51,7 +45,7 @@ verify.docCommentTemplateAt("4", Indentation.Indented, * @param param2 */`); -verify.docCommentTemplateAt("5", Indentation.Indented, +verify.docCommentTemplateAt("5", multiLineOffset, `/** * * @param a diff --git a/tests/cases/fourslash/docCommentTemplateClassDeclMethods02.ts b/tests/cases/fourslash/docCommentTemplateClassDeclMethods02.ts index 28da24d381a7f..7a35e60ae9ec7 100644 --- a/tests/cases/fourslash/docCommentTemplateClassDeclMethods02.ts +++ b/tests/cases/fourslash/docCommentTemplateClassDeclMethods02.ts @@ -1,8 +1,7 @@ /// -const enum Indentation { - Indented = 12, -} +const singleLineOffset = 3; +const multiLineOffset = 12; ////class C { //// /*0*/ @@ -13,12 +12,10 @@ const enum Indentation { //// [1 + 2 + 3 + Math.rand()](x: number, y: string, z = true) { } ////} -verify.docCommentTemplateAt("0", Indentation.Indented, - `/** - * - */`); +verify.docCommentTemplateAt("0", singleLineOffset, +"/** */"); -verify.docCommentTemplateAt("1", Indentation.Indented, +verify.docCommentTemplateAt("1", multiLineOffset, `/** * * @param x diff --git a/tests/cases/fourslash/docCommentTemplateEmptyFile.ts b/tests/cases/fourslash/docCommentTemplateEmptyFile.ts index f04653dc32838..064306e3fbd92 100644 --- a/tests/cases/fourslash/docCommentTemplateEmptyFile.ts +++ b/tests/cases/fourslash/docCommentTemplateEmptyFile.ts @@ -3,4 +3,4 @@ // @Filename: emptyFile.ts /////*0*/ -verify.noDocCommentTemplateAt("0"); +verify.docCommentTemplateAt("0", 3, "/** */"); diff --git a/tests/cases/fourslash/docCommentTemplateIndentation.ts b/tests/cases/fourslash/docCommentTemplateIndentation.ts index c3015a6d9dd0b..bc909aa0265cb 100644 --- a/tests/cases/fourslash/docCommentTemplateIndentation.ts +++ b/tests/cases/fourslash/docCommentTemplateIndentation.ts @@ -5,13 +5,8 @@ //// /*1*/ /////*0*/ function foo() { } -const noIndentEmptyScaffolding = "/**\r\n * \r\n */"; -const oneIndentEmptyScaffolding = "/**\r\n * \r\n */"; -const twoIndentEmptyScaffolding = "/**\r\n * \r\n */"; -const noIndentOffset = 8; -const oneIndentOffset = noIndentOffset + 4; -const twoIndentOffset = oneIndentOffset + 4; +const singleLineComment = "/** */"; -verify.docCommentTemplateAt("0", noIndentOffset, noIndentEmptyScaffolding); -verify.docCommentTemplateAt("1", oneIndentOffset, oneIndentEmptyScaffolding); -verify.docCommentTemplateAt("2", twoIndentOffset, twoIndentEmptyScaffolding); +verify.docCommentTemplateAt("0", 3, singleLineComment); +verify.docCommentTemplateAt("1", 3, singleLineComment); +verify.docCommentTemplateAt("2", 3, singleLineComment); diff --git a/tests/cases/fourslash/docCommentTemplateInsideFunctionDeclaration.ts b/tests/cases/fourslash/docCommentTemplateInsideFunctionDeclaration.ts index e0ebc00dc392a..67a11a27133e9 100644 --- a/tests/cases/fourslash/docCommentTemplateInsideFunctionDeclaration.ts +++ b/tests/cases/fourslash/docCommentTemplateInsideFunctionDeclaration.ts @@ -3,6 +3,10 @@ // @Filename: functionDecl.ts ////f/*0*/unction /*1*/foo/*2*/(/*3*/) /*4*/{ /*5*/} -for (const marker of test.markers()) { - verify.noDocCommentTemplateAt(marker); -} +verify.noDocCommentTemplateAt("0"); + +verify.docCommentTemplateAt("1", 3, "/** */"); +verify.docCommentTemplateAt("2", 3, "/** */"); +verify.docCommentTemplateAt("3", 3, "/** */"); +verify.docCommentTemplateAt("4", 3, "/** */"); +verify.docCommentTemplateAt("5", 3, "/** */"); diff --git a/tests/cases/fourslash/docCommentTemplateInterfacesEnumsAndTypeAliases.ts b/tests/cases/fourslash/docCommentTemplateInterfacesEnumsAndTypeAliases.ts new file mode 100644 index 0000000000000..d0805d5325570 --- /dev/null +++ b/tests/cases/fourslash/docCommentTemplateInterfacesEnumsAndTypeAliases.ts @@ -0,0 +1,49 @@ +/// + +/////*interfaceFoo*/ +////interface Foo { +//// /*propertybar*/ +//// bar: any; +//// +//// /*methodbaz*/ +//// baz(message: any): void; +//// +//// /*methodUnit*/ +//// unit(): void; +////} +//// +/////*enumStatus*/ +////const enum Status { +//// /*memberOpen*/ +//// Open, +//// +//// /*memberClosed*/ +//// Closed +////} +//// +/////*aliasBar*/ +////type Bar = Foo & any; + +verify.docCommentTemplateAt("interfaceFoo", /*expectedOffset*/ 3, + "/** */"); + +verify.docCommentTemplateAt("propertybar", /*expectedOffset*/ 3, + "/** */"); + +verify.docCommentTemplateAt("methodbaz", /*expectedOffset*/ 12, + `/** + * + * @param message + */`); + +verify.docCommentTemplateAt("methodUnit", /*expectedOffset*/ 3, + "/** */"); + +verify.docCommentTemplateAt("enumStatus", /*expectedOffset*/ 3, + "/** */"); + +verify.docCommentTemplateAt("memberOpen", /*expectedOffset*/ 3, + "/** */"); + +verify.docCommentTemplateAt("memberClosed", /*expectedOffset*/ 3, + "/** */"); \ No newline at end of file diff --git a/tests/cases/fourslash/docCommentTemplateJSXText.ts b/tests/cases/fourslash/docCommentTemplateJSXText.ts new file mode 100644 index 0000000000000..845f969f4e3bf --- /dev/null +++ b/tests/cases/fourslash/docCommentTemplateJSXText.ts @@ -0,0 +1,12 @@ +/// + +//@Filename: file.tsx +//// +//// var x =
+//// /*0*/hello/*1*/ +//// /*2*/goodbye/*3*/ +////
; + +for (const marker in test.markers()) { + verify.noDocCommentTemplateAt(marker); +} \ No newline at end of file diff --git a/tests/cases/fourslash/docCommentTemplateNamespacesAndModules01.ts b/tests/cases/fourslash/docCommentTemplateNamespacesAndModules01.ts index e7e52fd5e9485..f3ba46605c41f 100644 --- a/tests/cases/fourslash/docCommentTemplateNamespacesAndModules01.ts +++ b/tests/cases/fourslash/docCommentTemplateNamespacesAndModules01.ts @@ -12,17 +12,11 @@ ////module "ambientModule" { ////} -verify.docCommentTemplateAt("namespaceN", /*indentation*/ 8, -`/** - * - */`); +verify.docCommentTemplateAt("namespaceN", /*indentation*/ 3, + "/** */"); -verify.docCommentTemplateAt("namespaceM", /*indentation*/ 8, -`/** - * - */`); +verify.docCommentTemplateAt("namespaceM", /*indentation*/ 3, + "/** */"); -verify.docCommentTemplateAt("namespaceM", /*indentation*/ 8, -`/** - * - */`); +verify.docCommentTemplateAt("namespaceM", /*indentation*/ 3, + "/** */"); \ No newline at end of file diff --git a/tests/cases/fourslash/docCommentTemplateNamespacesAndModules02.ts b/tests/cases/fourslash/docCommentTemplateNamespacesAndModules02.ts index dad2e9745a9be..3beb9368661aa 100644 --- a/tests/cases/fourslash/docCommentTemplateNamespacesAndModules02.ts +++ b/tests/cases/fourslash/docCommentTemplateNamespacesAndModules02.ts @@ -6,11 +6,9 @@ //// /*n3*/ n3 { ////} -verify.docCommentTemplateAt("top", /*indentation*/ 8, -`/** - * - */`); +verify.docCommentTemplateAt("top", /*indentation*/ 3, +"/** */"); -verify.noDocCommentTemplateAt("n2"); +verify.docCommentTemplateAt("n2", 3, "/** */"); -verify.noDocCommentTemplateAt("n3"); +verify.docCommentTemplateAt("n3", 3, "/** */"); diff --git a/tests/cases/fourslash/docCommentTemplateObjectLiteralMethods01.ts b/tests/cases/fourslash/docCommentTemplateObjectLiteralMethods01.ts index 2ae77d4afac38..5121957a71889 100644 --- a/tests/cases/fourslash/docCommentTemplateObjectLiteralMethods01.ts +++ b/tests/cases/fourslash/docCommentTemplateObjectLiteralMethods01.ts @@ -1,8 +1,7 @@ /// -const enum Indentation { - Indented = 12, -} +const singleLineOffset = 3; +const multiLineOffset = 12; ////var x = { //// /*0*/ @@ -13,12 +12,10 @@ const enum Indentation { //// [1 + 2 + 3 + Math.rand()](x: number, y: string, z = true) { } ////} -verify.docCommentTemplateAt("0", Indentation.Indented, - `/** - * - */`); +verify.docCommentTemplateAt("0", singleLineOffset, + "/** */"); -verify.docCommentTemplateAt("1", Indentation.Indented, +verify.docCommentTemplateAt("1", multiLineOffset, `/** * * @param x diff --git a/tests/cases/fourslash/docCommentTemplateRegex.ts b/tests/cases/fourslash/docCommentTemplateRegex.ts index 685c1ca5aef79..c1368190ca248 100644 --- a/tests/cases/fourslash/docCommentTemplateRegex.ts +++ b/tests/cases/fourslash/docCommentTemplateRegex.ts @@ -3,6 +3,8 @@ // @Filename: regex.ts ////var regex = /*0*///*1*/asdf/*2*/ /*3*///*4*/; -for (const marker of test.markers()) { - verify.noDocCommentTemplateAt(marker); -} +verify.docCommentTemplateAt("0", 3, "/** */"); +verify.noDocCommentTemplateAt("1"); +verify.noDocCommentTemplateAt("2"); +verify.noDocCommentTemplateAt("3"); +verify.docCommentTemplateAt("4", 3, "/** */"); \ No newline at end of file diff --git a/tests/cases/fourslash/docCommentTemplateVariableStatements01.ts b/tests/cases/fourslash/docCommentTemplateVariableStatements01.ts index b624365216711..9112c9a8cab6f 100644 --- a/tests/cases/fourslash/docCommentTemplateVariableStatements01.ts +++ b/tests/cases/fourslash/docCommentTemplateVariableStatements01.ts @@ -29,10 +29,8 @@ ////} for (const varName of ["a", "b", "c", "d"]) { - verify.docCommentTemplateAt(varName, /*newTextOffset*/ 8, -`/** - * - */`); + verify.docCommentTemplateAt(varName, /*newTextOffset*/ 3, + "/** */"); } verify.docCommentTemplateAt("e", /*newTextOffset*/ 8, diff --git a/tests/cases/fourslash/docCommentTemplateVariableStatements02.ts b/tests/cases/fourslash/docCommentTemplateVariableStatements02.ts index f22e361f63f87..8e513780aadaf 100644 --- a/tests/cases/fourslash/docCommentTemplateVariableStatements02.ts +++ b/tests/cases/fourslash/docCommentTemplateVariableStatements02.ts @@ -29,8 +29,6 @@ ////}, f2 = null; for (const varName of ["a", "b", "c", "d", "e", "f"]) { - verify.docCommentTemplateAt(varName, /*newTextOffset*/ 8, -`/** - * - */`); + verify.docCommentTemplateAt(varName, /*newTextOffset*/ 3, + "/** */"); } diff --git a/tests/cases/fourslash/docCommentTemplateVariableStatements03.ts b/tests/cases/fourslash/docCommentTemplateVariableStatements03.ts index 195553098f023..6971b86a312cd 100644 --- a/tests/cases/fourslash/docCommentTemplateVariableStatements03.ts +++ b/tests/cases/fourslash/docCommentTemplateVariableStatements03.ts @@ -49,10 +49,8 @@ verify.docCommentTemplateAt("c", /*newTextOffset*/ 8, * @param x */`); -verify.docCommentTemplateAt("d", /*newTextOffset*/ 8, -`/** - * - */`); +verify.docCommentTemplateAt("d", /*newTextOffset*/ 3, +"/** */"); verify.docCommentTemplateAt("e", /*newTextOffset*/ 8, `/** @@ -60,10 +58,8 @@ verify.docCommentTemplateAt("e", /*newTextOffset*/ 8, * @param param0 */`); -verify.docCommentTemplateAt("f", /*newTextOffset*/ 8, -`/** - * - */`); +verify.docCommentTemplateAt("f", /*newTextOffset*/ 3, +"/** */"); verify.docCommentTemplateAt("g", /*newTextOffset*/ 8, `/**