namespace ts . JsDoc { const jsDocTagNames = [ "abstract" , "access" , "alias" , "argument" , "async" , "augments" , "author" , "borrows" , "callback" , "class" , "classdesc" , "constant" , "constructor" , "constructs" , "copyright" , "default" , "deprecated" , "description" , "emits" , "enum" , "event" , "example" , "exports" , "extends" , "external" , "field" , "file" , "fileoverview" , "fires" , "function" , "generator" , "global" , "hideconstructor" , "host" , "ignore" , "implements" , "inheritdoc" , "inner" , "instance" , "interface" , "kind" , "lends" , "license" , "link" , "listens" , "member" , "memberof" , "method" , "mixes" , "module" , "name" , "namespace" , "override" , "package" , "param" , "private" , "property" , "protected" , "public" , "readonly" , "requires" , "returns" , "see" , "since" , "static" , "summary" , "template" , "this" , "throws" , "todo" , "tutorial" , "type" , "typedef" , "var" , "variation" , "version" , "virtual" , "yields" ] ; let jsDocTagNameCompletionEntries ; let jsDocTagCompletionEntries ; export function getJsDocCommentsFromDeclarations ( declarations , checker ? ) { const parts = [ ] ; forEachUnique ( declarations , declaration => { for ( const jsdoc of getCommentHavingNodes ( declaration ) ) { if ( jsdoc . comment === undefined || isJSDoc ( jsdoc ) && declaration . kind !== SyntaxKind . JSDocTypedefTag && declaration . kind !== SyntaxKind . JSDocCallbackTag && jsdoc . tags && jsdoc . tags . some ( t => t . kind === SyntaxKind . JSDocTypedefTag || t . kind === SyntaxKind . JSDocCallbackTag ) && ! jsdoc . tags . some ( t => t . kind === SyntaxKind . JSDocParameterTag || t . kind === SyntaxKind . JSDocReturnTag ) ) { continue ; } const newparts = getDisplayPartsFromComment ( jsdoc . comment , checker ) ; if ( ! contains ( parts , newparts , isIdenticalListOfDisplayParts ) ) { parts . push ( newparts ) ; } } } ) ; return flatten ( intersperse ( parts , [ lineBreakPart ( ) ] ) ) ; } function isIdenticalListOfDisplayParts ( parts1 , parts2 ) { return arraysEqual ( parts1 , parts2 , ( p1 , p2 ) => p1 . kind === p2 . kind && p1 . text === p2 . text ) ; } function getCommentHavingNodes ( declaration ) { switch ( declaration . kind ) { case SyntaxKind . JSDocParameterTag : case SyntaxKind . JSDocPropertyTag : return [ declaration as JSDocPropertyTag ] ; case SyntaxKind . JSDocCallbackTag : case SyntaxKind . JSDocTypedefTag : return [ ( declaration as JSDocTypedefTag ) , ( declaration as JSDocTypedefTag ) . parent ] ; default : return getJSDocCommentsAndTags ( declaration ) ; } } export function getJsDocTagsFromDeclarations ( declarations ? , checker ? ) { const infos = [ ] ; forEachUnique ( declarations , declaration => { const tags = getJSDocTags ( declaration ) ; if ( tags . some ( t => t . kind === SyntaxKind . JSDocTypedefTag || t . kind === SyntaxKind . JSDocCallbackTag ) && ! tags . some ( t => t . kind === SyntaxKind . JSDocParameterTag || t . kind === SyntaxKind . JSDocReturnTag ) ) { return ; } for ( const tag of tags ) { infos . push ( { name : tag . tagName . text , text : getCommentDisplayParts ( tag , checker ) } ) ; } } ) ; return infos ; } function getDisplayPartsFromComment ( comment , checker ) { if ( typeof comment === "string" ) { return [ textPart ( comment ) ] ; } return flatMap ( comment , node => node . kind === SyntaxKind . JSDocText ? [ textPart ( node . text ) ] : buildLinkParts ( node , checker ) ) as SymbolDisplayPart [ ] ; } function getCommentDisplayParts ( tag , checker ? ) { const { comment , kind } = tag ; const namePart = getTagNameDisplayPart ( kind ) ; switch ( kind ) { case SyntaxKind . JSDocImplementsTag : return withNode ( ( tag as JSDocImplementsTag ) . class ) ; case SyntaxKind . JSDocAugmentsTag : return withNode ( ( tag as JSDocAugmentsTag ) . class ) ; case SyntaxKind . JSDocTemplateTag : return addComment ( ( tag as JSDocTemplateTag ) . typeParameters . map ( tp => tp . getText ( ) ) . join ( ", " ) ) ; case SyntaxKind . JSDocTypeTag : return withNode ( ( tag as JSDocTypeTag ) . typeExpression ) ; case SyntaxKind . JSDocTypedefTag : case SyntaxKind . JSDocCallbackTag : case SyntaxKind . JSDocPropertyTag : case SyntaxKind . JSDocParameterTag : case SyntaxKind . JSDocSeeTag : const { name } = tag as JSDocTypedefTag | JSDocCallbackTag | JSDocPropertyTag | JSDocParameterTag | JSDocSeeTag ; return name ? withNode ( name ) : comment === undefined ? undefined : getDisplayPartsFromComment ( comment , checker ) ; default : return comment === undefined ? undefined : getDisplayPartsFromComment ( comment , checker ) ; } function withNode ( node ) { return addComment ( node . getText ( ) ) ; } function addComment ( s ) { if ( comment ) { if ( s . match ( /^https?$/ ) ) { return [ textPart ( s ) , ... getDisplayPartsFromComment ( comment , checker ) ] ; } else { return [ namePart ( s ) , spacePart ( ) , ... getDisplayPartsFromComment ( comment , checker ) ] ; } } else { return [ textPart ( s ) ] ; } } } function getTagNameDisplayPart ( kind ) { switch ( kind ) { case SyntaxKind . JSDocParameterTag : return parameterNamePart ; case SyntaxKind . JSDocPropertyTag : return propertyNamePart ; case SyntaxKind . JSDocTemplateTag : return typeParameterNamePart ; case SyntaxKind . JSDocTypedefTag : case SyntaxKind . JSDocCallbackTag : return typeAliasNamePart ; default : return textPart ; } } export function getJSDocTagNameCompletions ( ) { return jsDocTagNameCompletionEntries || ( jsDocTagNameCompletionEntries = map ( jsDocTagNames , tagName => { return { name : tagName , kind : ScriptElementKind . keyword , kindModifiers : "" , sortText : Completions . SortText . LocationPriority , } ; } ) ) ; } export const getJSDocTagNameCompletionDetails = getJSDocTagCompletionDetails ; export function getJSDocTagCompletions ( ) { return jsDocTagCompletionEntries || ( jsDocTagCompletionEntries = map ( jsDocTagNames , tagName => { return { name : `@${ tagName }` , kind : ScriptElementKind . keyword , kindModifiers : "" , sortText : Completions . SortText . LocationPriority } ; } ) ) ; } export function getJSDocTagCompletionDetails ( name ) { return { name , kind : ScriptElementKind . unknown , kindModifiers : "" , displayParts : [ textPart ( name ) ] , documentation : emptyArray , tags : undefined , codeActions : undefined , } ; } export function getJSDocParameterNameCompletions ( tag ) { if ( ! isIdentifier ( tag . name ) ) { return emptyArray ; } const nameThusFar = tag . name . text ; const jsdoc = tag . parent ; const fn = jsdoc . parent ; if ( ! isFunctionLike ( fn ) ) return [ ] ; return mapDefined ( fn . parameters , param => { if ( ! isIdentifier ( param . name ) ) return undefined ; const name = param . name . text ; if ( jsdoc . tags ! . some ( t => t !== tag && isJSDocParameterTag ( t ) && isIdentifier ( t . name ) && t . name . escapedText === name ) || nameThusFar !== undefined && ! startsWith ( name , nameThusFar ) ) { return undefined ; } return { name , kind : ScriptElementKind . parameterElement , kindModifiers : "" , sortText : Completions . SortText . LocationPriority } ; } ) ; } export function getJSDocParameterNameCompletionDetails ( name ) { return { name , kind : ScriptElementKind . parameterElement , kindModifiers : "" , displayParts : [ textPart ( name ) ] , documentation : emptyArray , tags : undefined , codeActions : undefined , } ; } param fileName param position export function getDocCommentTemplateAtPosition ( newLine , sourceFile , position , options ? ) { const tokenAtPos = getTokenAtPosition ( sourceFile , position ) ; const existingDocComment = findAncestor ( tokenAtPos , isJSDoc ) ; if ( existingDocComment && ( existingDocComment . comment !== undefined || length ( existingDocComment . tags ) ) ) { return undefined ; } const tokenStart = tokenAtPos . getStart ( sourceFile ) ; if ( ! existingDocComment && tokenStart < position ) { return undefined ; } const commentOwnerInfo = getCommentOwnerInfo ( tokenAtPos , options ) ; if ( ! commentOwnerInfo ) { return undefined ; } const { commentOwner , parameters , hasReturn } = commentOwnerInfo ; if ( commentOwner . getStart ( sourceFile ) < position ) { return undefined ; } const indentationStr = getIndentationStringAtPosition ( sourceFile , position ) ; const isJavaScriptFile = hasJSFileExtension ( sourceFile . fileName ) ; const tags = ( parameters ? parameterDocComments ( parameters || [ ] , isJavaScriptFile , indentationStr , newLine ) : "" ) + ( hasReturn ? returnsDocComment ( indentationStr , newLine ) : "" ) ; const openComment = "/**" ; const closeComment = " */" ; if ( tags ) { const preamble = openComment + newLine + indentationStr + " * " ; const endLine = tokenStart === position ? newLine + indentationStr : "" ; const result = preamble + newLine + tags + indentationStr + closeComment + endLine ; return { newText : result , caretOffset : preamble . length } ; } return { newText : openComment + closeComment , caretOffset : 3 } ; } function getIndentationStringAtPosition ( sourceFile , position ) { const { text } = sourceFile ; const lineStart = getLineStartPositionForPosition ( position , sourceFile ) ; let pos = lineStart ; for ( ; pos <= position && isWhiteSpaceSingleLine ( text . charCodeAt ( pos ) ) ; pos ++ ) ; return text . slice ( lineStart , pos ) ; } function parameterDocComments ( parameters , isJavaScriptFile , indentationStr , newLine ) { return parameters . map ( ( { name , dotDotDotToken } , i ) => { const paramName = name . kind === SyntaxKind . Identifier ? name . text : "param" + i ; const type = isJavaScriptFile ? ( dotDotDotToken ? "{...any} " : "{any} " ) : "" ; return `${ indentationStr } * @param ${ type }${ paramName }${ newLine }` ; } ) . join ( "" ) ; } function returnsDocComment ( indentationStr , newLine ) { return `${ indentationStr } * @returns${ newLine }` ; } interface CommentOwnerInfo { readonly commentOwner : Node ; readonly parameters ? : readonly ParameterDeclaration [ ] ; readonly hasReturn ? : boolean ; } function getCommentOwnerInfo ( tokenAtPos , options ) { return forEachAncestor ( tokenAtPos , n => getCommentOwnerInfoWorker ( n , options ) ) ; } function getCommentOwnerInfoWorker ( commentOwner , options ) { switch ( commentOwner . kind ) { case SyntaxKind . FunctionDeclaration : case SyntaxKind . FunctionExpression : case SyntaxKind . MethodDeclaration : case SyntaxKind . Constructor : case SyntaxKind . MethodSignature : case SyntaxKind . ArrowFunction : const host = commentOwner as ArrowFunction | FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature ; return { commentOwner , parameters : host . parameters , hasReturn : hasReturn ( host , options ) } ; case SyntaxKind . PropertyAssignment : return getCommentOwnerInfoWorker ( ( commentOwner as PropertyAssignment ) . initializer , options ) ; 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 = commentOwner as VariableStatement ; const varDeclarations = varStatement . declarationList . declarations ; const host = varDeclarations . length === 1 && varDeclarations [ 0 ] . initializer ? getRightHandSideOfAssignment ( varDeclarations [ 0 ] . initializer ) : undefined ; return host ? { commentOwner , parameters : host . parameters , hasReturn : hasReturn ( host , options ) } : { commentOwner } ; } case SyntaxKind . SourceFile : return "quit" ; case SyntaxKind . ModuleDeclaration : return commentOwner . parent . kind === SyntaxKind . ModuleDeclaration ? undefined : { commentOwner } ; case SyntaxKind . ExpressionStatement : return getCommentOwnerInfoWorker ( ( commentOwner as ExpressionStatement ) . expression , options ) ; case SyntaxKind . BinaryExpression : { const be = commentOwner as BinaryExpression ; if ( getAssignmentDeclarationKind ( be ) === AssignmentDeclarationKind . None ) { return "quit" ; } return isFunctionLike ( be . right ) ? { commentOwner , parameters : be . right . parameters , hasReturn : hasReturn ( be . right , options ) } : { commentOwner } ; } case SyntaxKind . PropertyDeclaration : const init = ( commentOwner as PropertyDeclaration ) . initializer ; if ( init && ( isFunctionExpression ( init ) || isArrowFunction ( init ) ) ) { return { commentOwner , parameters : init . parameters , hasReturn : hasReturn ( init , options ) } ; } } } function hasReturn ( node , options ) { return ! ! options ?. generateReturnInDocTemplate && ( isArrowFunction ( node ) && isExpression ( node . body ) || isFunctionLikeDeclaration ( node ) && node . body && isBlock ( node . body ) && ! ! forEachReturnStatement ( node . body , n => n ) ) ; } function getRightHandSideOfAssignment ( rightHandSide ) { while ( rightHandSide . kind === SyntaxKind . ParenthesizedExpression ) { rightHandSide = ( rightHandSide as ParenthesizedExpression ) . expression ; } switch ( rightHandSide . kind ) { case SyntaxKind . FunctionExpression : case SyntaxKind . ArrowFunction : return ( rightHandSide as FunctionExpression ) ; case SyntaxKind . ClassExpression : return find ( ( rightHandSide as ClassExpression ) . members , isConstructorDeclaration ) ; } } }