@@ -1426,39 +1426,75 @@ namespace ts {
1426
1426
return undefined ;
1427
1427
}
1428
1428
1429
- const jsDocComments = getJSDocComments ( node , checkParentVariableStatement ) ;
1430
- if ( ! jsDocComments ) {
1429
+ const jsDocTags = getJSDocTags ( node , checkParentVariableStatement ) ;
1430
+ if ( ! jsDocTags ) {
1431
1431
return undefined ;
1432
1432
}
1433
1433
1434
- for ( const jsDocComment of jsDocComments ) {
1435
- for ( const tag of jsDocComment . tags ) {
1436
- if ( tag . kind === kind ) {
1437
- return tag ;
1438
- }
1434
+ for ( const tag of jsDocTags ) {
1435
+ if ( tag . kind === kind ) {
1436
+ return tag ;
1439
1437
}
1440
1438
}
1441
1439
}
1442
1440
1443
- function getJSDocComments ( node : Node , checkParentVariableStatement : boolean ) : JSDocComment [ ] {
1444
- if ( node . jsDocComments ) {
1445
- return node . jsDocComments ;
1446
- }
1447
- // Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
1448
- // /**
1449
- // * @param {number } name
1450
- // * @returns {number }
1451
- // */
1452
- // var x = function(name) { return name.length; }
1441
+ function append < T > ( previous : T [ ] | undefined , additional : T [ ] | undefined ) : T [ ] | undefined {
1442
+ if ( additional ) {
1443
+ if ( ! previous ) {
1444
+ previous = [ ] ;
1445
+ }
1446
+ for ( const x of additional ) {
1447
+ previous . push ( x ) ;
1448
+ }
1449
+ }
1450
+ return previous ;
1451
+ }
1452
+
1453
+ export function getJSDocComments ( node : Node , checkParentVariableStatement : boolean ) : string [ ] {
1454
+ return getJSDocs ( node , checkParentVariableStatement , docs => map ( docs , doc => doc . comment ) , tags => map ( tags , tag => tag . comment ) ) ;
1455
+ }
1456
+
1457
+ function getJSDocTags ( node : Node , checkParentVariableStatement : boolean ) : JSDocTag [ ] {
1458
+ return getJSDocs ( node , checkParentVariableStatement , docs => {
1459
+ const result : JSDocTag [ ] = [ ] ;
1460
+ for ( const doc of docs ) {
1461
+ if ( doc . tags ) {
1462
+ result . push ( ...doc . tags ) ;
1463
+ }
1464
+ }
1465
+ return result ;
1466
+ } , tags => tags ) ;
1467
+ }
1468
+
1469
+ function getJSDocs < T > ( node : Node , checkParentVariableStatement : boolean , getDocs : ( docs : JSDoc [ ] ) => T [ ] , getTags : ( tags : JSDocTag [ ] ) => T [ ] ) : T [ ] {
1470
+ // TODO: Get rid of getJsDocComments and friends (note the lowercase 's' in Js)
1471
+ // TODO: A lot of this work should be cached, maybe. I guess it's only used in services right now...
1472
+ let result : T [ ] = undefined ;
1473
+ // prepend documentation from parent sources
1453
1474
if ( checkParentVariableStatement ) {
1475
+ // Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
1476
+ // /**
1477
+ // * @param {number } name
1478
+ // * @returns {number }
1479
+ // */
1480
+ // var x = function(name) { return name.length; }
1454
1481
const isInitializerOfVariableDeclarationInStatement =
1455
- node . parent . kind === SyntaxKind . VariableDeclaration &&
1456
- ( < VariableDeclaration > node . parent ) . initializer === node &&
1482
+ isVariableLike ( node . parent ) &&
1483
+ ( node . parent ) . initializer === node &&
1457
1484
node . parent . parent . parent . kind === SyntaxKind . VariableStatement ;
1485
+ const isVariableOfVariableDeclarationStatement = isVariableLike ( node ) &&
1486
+ node . parent . parent . kind === SyntaxKind . VariableStatement ;
1458
1487
1459
- const variableStatementNode = isInitializerOfVariableDeclarationInStatement ? node . parent . parent . parent : undefined ;
1488
+ const variableStatementNode =
1489
+ isInitializerOfVariableDeclarationInStatement ? node . parent . parent . parent :
1490
+ isVariableOfVariableDeclarationStatement ? node . parent . parent :
1491
+ undefined ;
1460
1492
if ( variableStatementNode ) {
1461
- return variableStatementNode . jsDocComments ;
1493
+ result = append ( result , getJSDocs ( variableStatementNode , checkParentVariableStatement , getDocs , getTags ) ) ;
1494
+ }
1495
+ if ( node . kind === SyntaxKind . ModuleDeclaration &&
1496
+ node . parent && node . parent . kind === SyntaxKind . ModuleDeclaration ) {
1497
+ result = append ( result , getJSDocs ( node . parent , checkParentVariableStatement , getDocs , getTags ) ) ;
1462
1498
}
1463
1499
1464
1500
// Also recognize when the node is the RHS of an assignment expression
@@ -1469,16 +1505,62 @@ namespace ts {
1469
1505
( parent as BinaryExpression ) . operatorToken . kind === SyntaxKind . EqualsToken &&
1470
1506
parent . parent . kind === SyntaxKind . ExpressionStatement ;
1471
1507
if ( isSourceOfAssignmentExpressionStatement ) {
1472
- return parent . parent . jsDocComments ;
1508
+ result = append ( result , getJSDocs ( parent . parent , checkParentVariableStatement , getDocs , getTags ) ) ;
1473
1509
}
1474
1510
1475
1511
const isPropertyAssignmentExpression = parent && parent . kind === SyntaxKind . PropertyAssignment ;
1476
1512
if ( isPropertyAssignmentExpression ) {
1477
- return parent . jsDocComments ;
1513
+ result = append ( result , getJSDocs ( parent , checkParentVariableStatement , getDocs , getTags ) ) ;
1514
+ }
1515
+
1516
+ // Pull parameter comments from declaring function as well
1517
+ if ( node . kind === SyntaxKind . Parameter ) {
1518
+ const paramTags = getJSDocParameterTag ( node as ParameterDeclaration , checkParentVariableStatement ) ;
1519
+ if ( paramTags ) {
1520
+ result = append ( result , getTags ( paramTags ) ) ;
1521
+ }
1478
1522
}
1479
1523
}
1480
1524
1481
- return undefined ;
1525
+ if ( isVariableLike ( node ) && node . initializer ) {
1526
+ result = append ( result , getJSDocs ( node . initializer , /*checkParentVariableStatement*/ false , getDocs , getTags ) ) ;
1527
+ }
1528
+
1529
+ if ( node . jsDocComments ) {
1530
+ if ( result ) {
1531
+ result = append ( result , getDocs ( node . jsDocComments ) ) ;
1532
+ }
1533
+ else {
1534
+ return getDocs ( node . jsDocComments ) ;
1535
+ }
1536
+ }
1537
+
1538
+ return result ;
1539
+ }
1540
+
1541
+ function getJSDocParameterTag ( param : ParameterDeclaration , checkParentVariableStatement : boolean ) : JSDocTag [ ] {
1542
+ const func = param . parent as FunctionLikeDeclaration ;
1543
+ const tags = getJSDocTags ( func , checkParentVariableStatement ) ;
1544
+ if ( ! param . name ) {
1545
+ // this is an anonymous jsdoc param from a `function(type1, type2): type3` specification
1546
+ const i = func . parameters . indexOf ( param ) ;
1547
+ const paramTags = filter ( tags , tag => tag . kind === SyntaxKind . JSDocParameterTag ) ;
1548
+ if ( paramTags && 0 <= i && i < paramTags . length ) {
1549
+ return [ paramTags [ i ] ] ;
1550
+ }
1551
+ }
1552
+ else if ( param . name . kind === SyntaxKind . Identifier ) {
1553
+ const name = ( param . name as Identifier ) . text ;
1554
+ const paramTags = filter ( tags , tag => tag . kind === SyntaxKind . JSDocParameterTag && ( tag as JSDocParameterTag ) . parameterName . text === name ) ;
1555
+ if ( paramTags ) {
1556
+ return paramTags ;
1557
+ }
1558
+ }
1559
+ else {
1560
+ // TODO: it's a destructured parameter, so it should look up an "object type" series of multiple lines
1561
+ // But multi-line object types aren't supported yet either
1562
+ return undefined ;
1563
+ }
1482
1564
}
1483
1565
1484
1566
export function getJSDocTypeTag ( node : Node ) : JSDocTypeTag {
@@ -1499,17 +1581,15 @@ namespace ts {
1499
1581
// annotation.
1500
1582
const parameterName = ( < Identifier > parameter . name ) . text ;
1501
1583
1502
- const jsDocComments = getJSDocComments ( parameter . parent , /*checkParentVariableStatement*/ true ) ;
1503
- if ( jsDocComments ) {
1504
- for ( const jsDocComment of jsDocComments ) {
1505
- for ( const tag of jsDocComment . tags ) {
1506
- if ( tag . kind === SyntaxKind . JSDocParameterTag ) {
1507
- const parameterTag = < JSDocParameterTag > tag ;
1508
- const name = parameterTag . preParameterName || parameterTag . postParameterName ;
1509
- if ( name . text === parameterName ) {
1510
- return parameterTag ;
1511
- }
1512
- }
1584
+ const jsDocTags = getJSDocTags ( parameter . parent , /*checkParentVariableStatement*/ true ) ;
1585
+ if ( ! jsDocTags ) {
1586
+ return undefined ;
1587
+ }
1588
+ for ( const tag of jsDocTags ) {
1589
+ if ( tag . kind === SyntaxKind . JSDocParameterTag ) {
1590
+ const parameterTag = < JSDocParameterTag > tag ;
1591
+ if ( parameterTag . parameterName . text === parameterName ) {
1592
+ return parameterTag ;
1513
1593
}
1514
1594
}
1515
1595
}
0 commit comments