Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 45 additions & 24 deletions src/services/jsDoc.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* @internal */
namespace ts.JsDoc {
const singleLineTemplate = { newText: "/** */", caretOffset: 3 };
const jsDocTagNames = [
"augments",
"author",
Expand Down Expand Up @@ -197,9 +196,16 @@ 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.
* Invalid positions are
* - within comments, strings (including template literals and regex), and JSXText
* - within a token
* Valid positions are
* - outside of comments, statements, and expressions, and
* - preceding a:
* - function/constructor/method declaration
* - class declarations
* - variable statements
* - namespace declarations
* - interface declarations
* - method signatures
* - type alias declarations
*
* Hosts should ideally check that:
* - The line is all whitespace up to 'position' before performing the insertion.
Expand All @@ -225,19 +231,17 @@ namespace ts.JsDoc {

const commentOwnerInfo = getCommentOwnerInfo(tokenAtPos);
if (!commentOwnerInfo) {
// if climbing the tree did not find a declaration with parameters, complete to a single line comment
return singleLineTemplate;
return undefined;
}
const { commentOwner, parameters } = commentOwnerInfo;

if (commentOwner.kind === SyntaxKind.JsxText) {
if (commentOwner.getStart() < position) {
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;
if (!parameters || parameters.length === 0) {
// if there are no parameters, just complete to a single line JSDoc comment
const singleLineResult = "/** */";
return { newText: singleLineResult, caretOffset: 3 };
}

const posLineAndChar = sourceFile.getLineAndCharacterOfPosition(position);
Expand All @@ -247,11 +251,19 @@ namespace ts.JsDoc {
const indentationStr = sourceFile.text.substr(lineStart, posLineAndChar.character).replace(/\S/i, () => " ");
const isJavaScriptFile = hasJavaScriptFileExtension(sourceFile.fileName);

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("");
let docParams = "";
for (let i = 0; i < parameters.length; i++) {
const currentName = parameters[i].name;
const paramName = currentName.kind === SyntaxKind.Identifier ?
(<Identifier>currentName).escapedText :
"param" + i;
if (isJavaScriptFile) {
docParams += `${indentationStr} * @param {any} ${paramName}${newLine}`;
}
else {
docParams += `${indentationStr} * @param ${paramName}${newLine}`;
}
}

// A doc comment consists of the following
// * The opening comment line
Expand All @@ -273,7 +285,7 @@ namespace ts.JsDoc {

interface CommentOwnerInfo {
readonly commentOwner: Node;
readonly parameters: ReadonlyArray<ParameterDeclaration>;
readonly parameters?: ReadonlyArray<ParameterDeclaration>;
}
function getCommentOwnerInfo(tokenAtPos: Node): CommentOwnerInfo | undefined {
for (let commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) {
Expand All @@ -285,18 +297,32 @@ namespace ts.JsDoc {
const { parameters } = commentOwner as FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature;
return { commentOwner, parameters };

case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.EnumMember:
case SyntaxKind.TypeAliasDeclaration:
return { commentOwner };

case SyntaxKind.VariableStatement: {
const varStatement = <VariableStatement>commentOwner;
const varDeclarations = varStatement.declarationList.declarations;
const parameters = varDeclarations.length === 1 && varDeclarations[0].initializer
? getParametersFromRightHandSideOfAssignment(varDeclarations[0].initializer)
: undefined;
return parameters ? { commentOwner, parameters } : undefined;
return { commentOwner, parameters };
}

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) {
Expand All @@ -305,11 +331,6 @@ namespace ts.JsDoc {
const parameters = isFunctionLike(be.right) ? be.right.parameters : emptyArray;
return { commentOwner, parameters };
}

case SyntaxKind.JsxText: {
const parameters: ReadonlyArray<ParameterDeclaration> = emptyArray;
return { commentOwner, parameters };
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/cases/fourslash/docCommentTemplateEmptyFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
// @Filename: emptyFile.ts
/////*0*/

verify.docCommentTemplateAt("0", 3, "/** */");
verify.noDocCommentTemplateAt("0");
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
// @Filename: functionDecl.ts
////f/*0*/unction /*1*/foo/*2*/(/*3*/) /*4*/{ /*5*/}

verify.noDocCommentTemplateAt("0");

verify.docCommentTemplateAt("1", 3, "/** */");
verify.docCommentTemplateAt("2", 3, "/** */");
verify.docCommentTemplateAt("3", 3, "/** */");
verify.docCommentTemplateAt("4", 3, "/** */");
verify.docCommentTemplateAt("5", 3, "/** */");
for (const marker of test.markers()) {
verify.noDocCommentTemplateAt(marker);
}
12 changes: 0 additions & 12 deletions tests/cases/fourslash/docCommentTemplateJSXText.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
verify.docCommentTemplateAt("top", /*indentation*/ 3,
"/** */");

verify.docCommentTemplateAt("n2", 3, "/** */");
verify.noDocCommentTemplateAt("n2");

verify.docCommentTemplateAt("n3", 3, "/** */");
verify.noDocCommentTemplateAt("n3");
8 changes: 3 additions & 5 deletions tests/cases/fourslash/docCommentTemplateRegex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
// @Filename: regex.ts
////var regex = /*0*///*1*/asdf/*2*/ /*3*///*4*/;

verify.docCommentTemplateAt("0", 3, "/** */");
verify.noDocCommentTemplateAt("1");
verify.noDocCommentTemplateAt("2");
verify.noDocCommentTemplateAt("3");
verify.docCommentTemplateAt("4", 3, "/** */");
for (const marker of test.markers()) {
verify.noDocCommentTemplateAt(marker);
}