diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index d246b55556c..ee7887c1e86 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -603,9 +603,11 @@ namespace ts { } export function getJsDocCommentsFromText(node: Node, text: string) { - const commentRanges = (node.kind === SyntaxKind.Parameter || node.kind === SyntaxKind.TypeParameter) ? - concatenate(getTrailingCommentRanges(text, node.pos), - getLeadingCommentRanges(text, node.pos)) : + const commentRanges = (node.kind === SyntaxKind.Parameter || + node.kind === SyntaxKind.TypeParameter || + node.kind === SyntaxKind.FunctionExpression || + node.kind === SyntaxKind.ArrowFunction) ? + concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) : getLeadingCommentRangesOfNodeFromText(node, text); return filter(commentRanges, isJsDocComment); diff --git a/src/services/services.ts b/src/services/services.ts index 34b521b3496..906ec2ae0d3 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -406,12 +406,11 @@ namespace ts { const sourceFileOfDeclaration = getSourceFileOfNode(declaration); // If it is parameter - try and get the jsDoc comment with @param tag from function declaration's jsDoc comments if (canUseParsedParamTagComments && declaration.kind === SyntaxKind.Parameter) { - ts.forEach(getJsDocCommentTextRange(declaration.parent, sourceFileOfDeclaration), jsDocCommentTextRange => { - const cleanedParamJsDocComment = getCleanedParamJsDocComment(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration); - if (cleanedParamJsDocComment) { - addRange(jsDocCommentParts, cleanedParamJsDocComment); - } - }); + if ((declaration.parent.kind === SyntaxKind.FunctionExpression || declaration.parent.kind === SyntaxKind.ArrowFunction) && + declaration.parent.parent.kind === SyntaxKind.VariableDeclaration) { + addCommentParts(declaration.parent.parent.parent, sourceFileOfDeclaration, getCleanedParamJsDocComment); + } + addCommentParts(declaration.parent, sourceFileOfDeclaration, getCleanedParamJsDocComment); } // If this is left side of dotted module declaration, there is no doc comments associated with this node @@ -419,24 +418,44 @@ namespace ts { return; } + if ((declaration.kind === SyntaxKind.FunctionExpression || declaration.kind === SyntaxKind.ArrowFunction) && + declaration.parent.kind === SyntaxKind.VariableDeclaration) { + addCommentParts(declaration.parent.parent, sourceFileOfDeclaration, getCleanedJsDocComment); + } + // If this is dotted module name, get the doc comments from the parent while (declaration.kind === SyntaxKind.ModuleDeclaration && declaration.parent.kind === SyntaxKind.ModuleDeclaration) { declaration = declaration.parent; } + addCommentParts(declaration.kind === SyntaxKind.VariableDeclaration ? declaration.parent.parent : declaration, + sourceFileOfDeclaration, + getCleanedJsDocComment); - // Get the cleaned js doc comment text from the declaration - ts.forEach(getJsDocCommentTextRange( - declaration.kind === SyntaxKind.VariableDeclaration ? declaration.parent.parent : declaration, sourceFileOfDeclaration), jsDocCommentTextRange => { - const cleanedJsDocComment = getCleanedJsDocComment(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration); - if (cleanedJsDocComment) { - addRange(jsDocCommentParts, cleanedJsDocComment); - } - }); + if (declaration.kind === SyntaxKind.VariableDeclaration) { + const init = (declaration as VariableDeclaration).initializer; + if (init && (init.kind === SyntaxKind.FunctionExpression || init.kind === SyntaxKind.ArrowFunction)) { + // Get the cleaned js doc comment text from the initializer + addCommentParts(init, sourceFileOfDeclaration, getCleanedJsDocComment); + } + } } }); return jsDocCommentParts; + function addCommentParts(commented: Node, + sourceFileOfDeclaration: SourceFile, + getCommentPart: (pos: number, end: number, file: SourceFile) => SymbolDisplayPart[]): void { + const ranges = getJsDocCommentTextRange(commented, sourceFileOfDeclaration); + // Get the cleaned js doc comment text from the declaration + ts.forEach(ranges, jsDocCommentTextRange => { + const cleanedComment = getCommentPart(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration); + if (cleanedComment) { + addRange(jsDocCommentParts, cleanedComment); + } + }); + } + function getJsDocCommentTextRange(node: Node, sourceFile: SourceFile): TextRange[] { return ts.map(getJsDocComments(node, sourceFile), jsDocComment => { diff --git a/tests/cases/fourslash/commentsFunction.ts b/tests/cases/fourslash/commentsFunction.ts index ac1e7f5effd..34d9e463eb6 100644 --- a/tests/cases/fourslash/commentsFunction.ts +++ b/tests/cases/fourslash/commentsFunction.ts @@ -33,6 +33,20 @@ //// } //// return lamb/*31*/daVar("World") + /*32*/a; ////} +/////** +//// * On variable +//// * @param s the first parameter! +//// * @returns the parameter's length +//// */ +////var assi/*33*/gned = /** +//// * Summary on expression +//// * @param s param on expression +//// * @returns return on expression +//// */function(/** On parameter */s: string) { +//// return s.length; +////} +////assig/*34*/ned/*35*/(/*36*/"hey"); + goTo.marker('1'); verify.currentSignatureHelpDocCommentIs("This comment should appear for foo"); @@ -68,14 +82,15 @@ verify.completionListContains('a', '(parameter) a: string', 'this is comment abo verify.completionListContains('b', '(parameter) b: number', 'this is comment for b'); goTo.marker('11'); -verify.quickInfoIs("var lambdaFoo: (a: number, b: number) => number", "lamdaFoo var comment"); +verify.quickInfoIs("var lambdaFoo: (a: number, b: number) => number", "lamdaFoo var comment\nthis is lambda comment"); goTo.marker('12'); -verify.quickInfoIs("var lambddaNoVarComment: (a: number, b: number) => number", ""); +// pick up doccomments from the lambda itself +verify.quickInfoIs("var lambddaNoVarComment: (a: number, b: number) => number", "this is lambda multiplication"); goTo.marker('13'); -verify.completionListContains('lambdaFoo', 'var lambdaFoo: (a: number, b: number) => number', ''); -verify.completionListContains('lambddaNoVarComment', 'var lambddaNoVarComment: (a: number, b: number) => number', ''); +verify.completionListContains('lambdaFoo', 'var lambdaFoo: (a: number, b: number) => number', 'lamdaFoo var comment\nthis is lambda comment'); +verify.completionListContains('lambddaNoVarComment', 'var lambddaNoVarComment: (a: number, b: number) => number', 'this is lambda multiplication'); goTo.marker('14'); verify.currentParameterHelpArgumentDocCommentIs("param a"); @@ -129,3 +144,14 @@ goTo.marker('31'); verify.quickInfoIs('(local var) lambdaVar: (b: string) => string', ''); goTo.marker('32'); verify.quickInfoIs('(parameter) a: number', ''); + +goTo.marker('33'); +verify.quickInfoIs("var assigned: (s: string) => number", "On variable\n@returns the parameter's length\nSummary on expression\n@returns return on expression"); +goTo.marker('34'); +verify.quickInfoIs("var assigned: (s: string) => number", "On variable\n@returns the parameter's length\nSummary on expression\n@returns return on expression"); +goTo.marker('35'); +verify.completionListContains("assigned", "var assigned: (s: string) => number", "On variable\n@returns the parameter's length\nSummary on expression\n@returns return on expression"); +goTo.marker('36'); +verify.currentSignatureHelpDocCommentIs("On variable\n@returns the parameter's length\nSummary on expression\n@returns return on expression"); +verify.currentParameterHelpArgumentDocCommentIs("param on expression\nthe first parameter!\nOn parameter "); + diff --git a/tests/cases/fourslash/getJavaScriptQuickInfo7.ts b/tests/cases/fourslash/getJavaScriptQuickInfo7.ts index 5aa8474757d..7b8d967d190 100644 --- a/tests/cases/fourslash/getJavaScriptQuickInfo7.ts +++ b/tests/cases/fourslash/getJavaScriptQuickInfo7.ts @@ -17,4 +17,6 @@ //// x - /**/a1() goTo.marker(); -verify.quickInfoExists(); \ No newline at end of file +verify.quickInfoExists(); +verify.quickInfoIs('function a1(p: any): number', + 'This is a very cool function that is very nice.\n@returns something');