@@ -1416,49 +1416,42 @@ namespace ts {
14161416 ( < JSDocFunctionType > node ) . parameters [ 0 ] . type . kind === SyntaxKind . JSDocConstructorType ;
14171417 }
14181418
1419- function getJSDocTag ( node : Node , kind : SyntaxKind ) : JSDocTag {
1420- if ( ! node ) {
1421- return undefined ;
1422- }
1423-
1424- const jsDocTags = getJSDocTags ( node ) ;
1425- if ( ! jsDocTags ) {
1426- return undefined ;
1427- }
1428-
1429- for ( const tag of jsDocTags ) {
1430- if ( tag . kind === kind ) {
1431- return tag ;
1432- }
1433- }
1434- }
1435-
14361419 export function getJSDocComments ( node : Node ) : string [ ] {
1437- return getJSDocs ( node , docs => map ( docs , doc => doc . comment ) , tags => map ( tags , tag => tag . comment ) ) ;
1420+ return map ( getJSDocs ( node ) , doc => doc . comment ) ;
14381421 }
14391422
1440- function getJSDocTags ( node : Node ) : JSDocTag [ ] {
1441- return getJSDocs ( node , docs => {
1423+ function getJSDocTags ( node : Node , kind : SyntaxKind ) : JSDocTag [ ] {
1424+ const docs = getJSDocs ( node ) ;
1425+ if ( docs ) {
14421426 const result : JSDocTag [ ] = [ ] ;
14431427 for ( const doc of docs ) {
1444- if ( doc . tags ) {
1445- result . push ( ...doc . tags ) ;
1428+ if ( doc . kind === SyntaxKind . JSDocParameterTag ) {
1429+ if ( doc . kind === kind ) {
1430+ result . push ( doc as JSDocParameterTag ) ;
1431+ }
1432+ }
1433+ else {
1434+ result . push ( ...filter ( ( doc as JSDoc ) . tags , tag => tag . kind === kind ) ) ;
14461435 }
14471436 }
14481437 return result ;
1449- } , tags => tags ) ;
1438+ }
1439+ }
1440+
1441+ function getFirstJSDocTag ( node : Node , kind : SyntaxKind ) : JSDocTag {
1442+ return node && firstOrUndefined ( getJSDocTags ( node , kind ) ) ;
14501443 }
14511444
1452- function getJSDocs < T > ( node : Node , getContent : ( docs : JSDoc [ ] ) => T [ ] , getContentFromParam : ( tags : JSDocTag [ ] ) => T [ ] ) : T [ ] {
1453- return getJSDocsWorker ( node ) ;
1445+ function getJSDocs ( node : Node ) : ( JSDoc | JSDocParameterTag ) [ ] {
1446+ let cache : ( JSDoc | JSDocParameterTag ) [ ] = node . jsDocCache ;
1447+ if ( ! cache ) {
1448+ getJSDocsWorker ( node ) ;
1449+ node . jsDocCache = cache ;
1450+ }
1451+ return cache ;
14541452
14551453 function getJSDocsWorker ( node : Node ) {
1456- // TODO: A lot of this work should be cached, maybe. I guess it's only used in services right now...
1457- // This will be hard because it may need to cache parentvariable versions and nonparent versions
1458- // maybe I should eliminate checkParent first ...
14591454 const parent = node . parent ;
1460- let result : T [ ] = undefined ;
1461-
14621455 // Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
14631456 // /**
14641457 // * @param {number } name
@@ -1476,7 +1469,7 @@ namespace ts {
14761469 isVariableOfVariableDeclarationStatement ? parent . parent :
14771470 undefined ;
14781471 if ( variableStatementNode ) {
1479- result = concatenate ( result , getJSDocsWorker ( variableStatementNode ) ) ;
1472+ getJSDocsWorker ( variableStatementNode ) ;
14801473 }
14811474
14821475 // Also recognize when the node is the RHS of an assignment expression
@@ -1486,32 +1479,26 @@ namespace ts {
14861479 ( parent as BinaryExpression ) . operatorToken . kind === SyntaxKind . EqualsToken &&
14871480 parent . parent . kind === SyntaxKind . ExpressionStatement ;
14881481 if ( isSourceOfAssignmentExpressionStatement ) {
1489- result = concatenate ( result , getJSDocsWorker ( parent . parent ) ) ;
1482+ getJSDocsWorker ( parent . parent ) ;
14901483 }
14911484
14921485 const isModuleDeclaration = node . kind === SyntaxKind . ModuleDeclaration &&
14931486 parent && parent . kind === SyntaxKind . ModuleDeclaration ;
14941487 const isPropertyAssignmentExpression = parent && parent . kind === SyntaxKind . PropertyAssignment ;
14951488 if ( isModuleDeclaration || isPropertyAssignmentExpression ) {
1496- result = concatenate ( result , getJSDocsWorker ( parent ) ) ;
1489+ getJSDocsWorker ( parent ) ;
14971490 }
14981491
14991492 // Pull parameter comments from declaring function as well
15001493 if ( node . kind === SyntaxKind . Parameter ) {
1501- result = concatenate ( result , getContentFromParam ( getJSDocParameterTag ( node ) ) ) ;
1494+ cache = concatenate ( cache , getJSDocParameterTag ( node ) ) ;
15021495 }
15031496
15041497 if ( isVariableLike ( node ) && node . initializer ) {
1505- result = concatenate ( result , getOwnJSDocs ( node . initializer ) ) ;
1498+ cache = concatenate ( cache , node . initializer . jsDocComments ) ;
15061499 }
15071500
1508- return concatenate ( result , getOwnJSDocs ( node ) ) ;
1509- }
1510-
1511- function getOwnJSDocs ( node : Node ) {
1512- if ( node . jsDocComments ) {
1513- return getContent ( node . jsDocComments ) ;
1514- }
1501+ cache = concatenate ( cache , node . jsDocComments ) ;
15151502 }
15161503 }
15171504
@@ -1520,18 +1507,18 @@ namespace ts {
15201507 return undefined ;
15211508 }
15221509 const func = param . parent as FunctionLikeDeclaration ;
1523- const tags = getJSDocTags ( func ) ;
1510+ const tags = getJSDocTags ( func , SyntaxKind . JSDocParameterTag ) as JSDocParameterTag [ ] ;
15241511 if ( ! param . name ) {
15251512 // this is an anonymous jsdoc param from a `function(type1, type2): type3` specification
15261513 const i = func . parameters . indexOf ( param ) ;
1527- const paramTags = filter ( tags , tag => tag . kind === SyntaxKind . JSDocParameterTag ) as JSDocParameterTag [ ] ;
1514+ const paramTags = filter ( tags , tag => tag . kind === SyntaxKind . JSDocParameterTag ) ;
15281515 if ( paramTags && 0 <= i && i < paramTags . length ) {
15291516 return [ paramTags [ i ] ] ;
15301517 }
15311518 }
15321519 else if ( param . name . kind === SyntaxKind . Identifier ) {
15331520 const name = ( param . name as Identifier ) . text ;
1534- return filter ( tags as JSDocParameterTag [ ] , tag => tag . kind === SyntaxKind . JSDocParameterTag && tag . parameterName . text === name ) ;
1521+ return filter ( tags , tag => tag . kind === SyntaxKind . JSDocParameterTag && tag . parameterName . text === name ) ;
15351522 }
15361523 else {
15371524 // TODO: it's a destructured parameter, so it should look up an "object type" series of multiple lines
@@ -1541,7 +1528,7 @@ namespace ts {
15411528 }
15421529
15431530 export function getJSDocType ( node : Node ) : JSDocType {
1544- let tag : JSDocTypeTag | JSDocParameterTag = getJSDocTag ( node , SyntaxKind . JSDocTypeTag ) as JSDocTypeTag ;
1531+ let tag : JSDocTypeTag | JSDocParameterTag = getFirstJSDocTag ( node , SyntaxKind . JSDocTypeTag ) as JSDocTypeTag ;
15451532 if ( ! tag && node . kind === SyntaxKind . Parameter ) {
15461533 const paramTags = getJSDocParameterTag ( node ) ;
15471534 if ( paramTags ) {
@@ -1553,11 +1540,11 @@ namespace ts {
15531540 }
15541541
15551542 export function getJSDocReturnTag ( node : Node ) : JSDocReturnTag {
1556- return getJSDocTag ( node , SyntaxKind . JSDocReturnTag ) as JSDocReturnTag ;
1543+ return getFirstJSDocTag ( node , SyntaxKind . JSDocReturnTag ) as JSDocReturnTag ;
15571544 }
15581545
15591546 export function getJSDocTemplateTag ( node : Node ) : JSDocTemplateTag {
1560- return getJSDocTag ( node , SyntaxKind . JSDocTemplateTag ) as JSDocTemplateTag ;
1547+ return getFirstJSDocTag ( node , SyntaxKind . JSDocTemplateTag ) as JSDocTemplateTag ;
15611548 }
15621549
15631550 export function hasRestParameter ( s : SignatureDeclaration ) : boolean {
0 commit comments