Skip to content

Commit fe2a33f

Browse files
authored
Merge existing JSDoc comments (#27978)
* Correct indentation, using correct (I hope) indentation code Note that part of the code, in formatting.ts, is cloned but should be extracted to a function instead. * Remove some possibly-superfluous code But I see 4 failures with whitespace, so perhaps not. * Restrict indentation change to avoid breaking baselines The indentation code is very complex so I'm just going to avoid breaking our single-line tests for now, plus add a simple jsdoc test to show that multiline jsdoc indentation isn't destroyed in the common case. * Switched over to construction for @return/@type Still doesn't merge correctly though * Add @return tags to emitter * Merge multiple jsdocs (not for @param yet) * Merge multiple jsdoc for parameters too * Emit more jsdoc tags Not all of them; I got cold feet since I'll have to write tests for them. I'll do that tomorrow. * Many fixes to JSDoc emit And single tests (at least) for all tags * Cleanup in textChanges.ts * Cleanup in formatting.ts (Plus a little more in textChanges.ts) * Cleanup in inferFromUsage.ts * Fix minor omissions * Separate merged top-level JSDoc comments with \n instead of space. * Don't delete intrusive non-jsdoc comments * Cleanup from PR comments 1. Refactor emit code into smaller functions. 2. Preceding-whitespace utility is slightly easier to use. 3. Better casts and types in inferFromUsage make it easier to read. * Fix bogus newline * Use @Andy-MS' cleanup annotateJSDocParameters
1 parent e46c846 commit fe2a33f

20 files changed

+654
-249
lines changed

src/compiler/emitter.ts

Lines changed: 187 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,34 @@ namespace ts {
862862
case SyntaxKind.EnumMember:
863863
return emitEnumMember(<EnumMember>node);
864864

865-
// JSDoc nodes (ignored)
865+
// JSDoc nodes (only used in codefixes currently)
866+
case SyntaxKind.JSDocParameterTag:
867+
case SyntaxKind.JSDocPropertyTag:
868+
return emitJSDocPropertyLikeTag(node as JSDocPropertyLikeTag);
869+
case SyntaxKind.JSDocReturnTag:
870+
case SyntaxKind.JSDocTypeTag:
871+
case SyntaxKind.JSDocThisTag:
872+
case SyntaxKind.JSDocEnumTag:
873+
return emitJSDocSimpleTypedTag(node as JSDocTypeTag);
874+
case SyntaxKind.JSDocAugmentsTag:
875+
return emitJSDocAugmentsTag(node as JSDocAugmentsTag);
876+
case SyntaxKind.JSDocTemplateTag:
877+
return emitJSDocTemplateTag(node as JSDocTemplateTag);
878+
case SyntaxKind.JSDocTypedefTag:
879+
return emitJSDocTypedefTag(node as JSDocTypedefTag);
880+
case SyntaxKind.JSDocCallbackTag:
881+
return emitJSDocCallbackTag(node as JSDocCallbackTag);
882+
case SyntaxKind.JSDocSignature:
883+
return emitJSDocSignature(node as JSDocSignature);
884+
case SyntaxKind.JSDocTypeLiteral:
885+
return emitJSDocTypeLiteral(node as JSDocTypeLiteral);
886+
case SyntaxKind.JSDocClassTag:
887+
case SyntaxKind.JSDocTag:
888+
return emitJSDocSimpleTag(node as JSDocTag);
889+
890+
case SyntaxKind.JSDocComment:
891+
return emitJSDoc(node as JSDoc);
892+
866893
// Transformation nodes (ignored)
867894
}
868895

@@ -2584,6 +2611,154 @@ namespace ts {
25842611
emitInitializer(node.initializer, node.name.end, node);
25852612
}
25862613

2614+
//
2615+
// JSDoc
2616+
//
2617+
function emitJSDoc(node: JSDoc) {
2618+
write("/**");
2619+
if (node.comment) {
2620+
const lines = node.comment.split(/\r\n?|\n/g);
2621+
for (const line of lines) {
2622+
writeLine();
2623+
writeSpace();
2624+
writePunctuation("*");
2625+
writeSpace();
2626+
write(line);
2627+
}
2628+
}
2629+
if (node.tags) {
2630+
if (node.tags.length === 1 && node.tags[0].kind === SyntaxKind.JSDocTypeTag && !node.comment) {
2631+
writeSpace();
2632+
emit(node.tags[0]);
2633+
}
2634+
else {
2635+
emitList(node, node.tags, ListFormat.JSDocComment);
2636+
}
2637+
}
2638+
writeSpace();
2639+
write("*/");
2640+
}
2641+
2642+
function emitJSDocSimpleTypedTag(tag: JSDocTypeTag | JSDocThisTag | JSDocEnumTag | JSDocReturnTag) {
2643+
emitJSDocTagName(tag.tagName);
2644+
emitJSDocTypeExpression(tag.typeExpression);
2645+
emitJSDocComment(tag.comment);
2646+
}
2647+
2648+
function emitJSDocAugmentsTag(tag: JSDocAugmentsTag) {
2649+
emitJSDocTagName(tag.tagName);
2650+
writeSpace();
2651+
writePunctuation("{");
2652+
emit(tag.class);
2653+
writePunctuation("}");
2654+
emitJSDocComment(tag.comment);
2655+
}
2656+
2657+
function emitJSDocTemplateTag(tag: JSDocTemplateTag) {
2658+
emitJSDocTagName(tag.tagName);
2659+
emitJSDocTypeExpression(tag.constraint);
2660+
writeSpace();
2661+
emitList(tag, tag.typeParameters, ListFormat.CommaListElements);
2662+
emitJSDocComment(tag.comment);
2663+
}
2664+
2665+
function emitJSDocTypedefTag(tag: JSDocTypedefTag) {
2666+
emitJSDocTagName(tag.tagName);
2667+
if (tag.typeExpression) {
2668+
if (tag.typeExpression.kind === SyntaxKind.JSDocTypeExpression) {
2669+
emitJSDocTypeExpression(tag.typeExpression);
2670+
}
2671+
else {
2672+
writeSpace();
2673+
writePunctuation("{");
2674+
write("Object");
2675+
if (tag.typeExpression.isArrayType) {
2676+
writePunctuation("[");
2677+
writePunctuation("]");
2678+
}
2679+
writePunctuation("}");
2680+
}
2681+
}
2682+
if (tag.fullName) {
2683+
writeSpace();
2684+
emit(tag.fullName);
2685+
}
2686+
emitJSDocComment(tag.comment);
2687+
if (tag.typeExpression && tag.typeExpression.kind === SyntaxKind.JSDocTypeLiteral) {
2688+
emitJSDocTypeLiteral(tag.typeExpression);
2689+
}
2690+
}
2691+
2692+
function emitJSDocCallbackTag(tag: JSDocCallbackTag) {
2693+
emitJSDocTagName(tag.tagName);
2694+
if (tag.name) {
2695+
writeSpace();
2696+
emit(tag.name);
2697+
}
2698+
emitJSDocComment(tag.comment);
2699+
emitJSDocSignature(tag.typeExpression);
2700+
}
2701+
2702+
function emitJSDocSimpleTag(tag: JSDocTag) {
2703+
emitJSDocTagName(tag.tagName);
2704+
emitJSDocComment(tag.comment);
2705+
}
2706+
2707+
function emitJSDocTypeLiteral(lit: JSDocTypeLiteral) {
2708+
emitList(lit, createNodeArray(lit.jsDocPropertyTags), ListFormat.JSDocComment);
2709+
}
2710+
2711+
function emitJSDocSignature(sig: JSDocSignature) {
2712+
if (sig.typeParameters) {
2713+
emitList(sig, createNodeArray(sig.typeParameters), ListFormat.JSDocComment);
2714+
}
2715+
if (sig.parameters) {
2716+
emitList(sig, createNodeArray(sig.parameters), ListFormat.JSDocComment);
2717+
}
2718+
if (sig.type) {
2719+
writeLine();
2720+
writeSpace();
2721+
writePunctuation("*");
2722+
writeSpace();
2723+
emit(sig.type);
2724+
}
2725+
}
2726+
2727+
function emitJSDocPropertyLikeTag(param: JSDocPropertyLikeTag) {
2728+
emitJSDocTagName(param.tagName);
2729+
emitJSDocTypeExpression(param.typeExpression);
2730+
writeSpace();
2731+
if (param.isBracketed) {
2732+
writePunctuation("[");
2733+
}
2734+
emit(param.name);
2735+
if (param.isBracketed) {
2736+
writePunctuation("]");
2737+
}
2738+
emitJSDocComment(param.comment);
2739+
}
2740+
2741+
function emitJSDocTagName(tagName: Identifier) {
2742+
writePunctuation("@");
2743+
emit(tagName);
2744+
}
2745+
2746+
function emitJSDocComment(comment: string | undefined) {
2747+
if (comment) {
2748+
writeSpace();
2749+
write(comment);
2750+
}
2751+
}
2752+
2753+
function emitJSDocTypeExpression(typeExpression: JSDocTypeExpression | undefined) {
2754+
if (typeExpression) {
2755+
writeSpace();
2756+
writePunctuation("{");
2757+
emit(typeExpression.type);
2758+
writePunctuation("}");
2759+
}
2760+
}
2761+
25872762
//
25882763
// Top-level nodes
25892764
//
@@ -2875,6 +3050,11 @@ namespace ts {
28753050
writeSpace();
28763051
writePunctuation("|");
28773052
break;
3053+
case ListFormat.AsteriskDelimited:
3054+
writeSpace();
3055+
writePunctuation("*");
3056+
writeSpace();
3057+
break;
28783058
case ListFormat.AmpersandDelimited:
28793059
writeSpace();
28803060
writePunctuation("&");
@@ -2944,7 +3124,12 @@ namespace ts {
29443124
const child = children![start + i];
29453125

29463126
// Write the delimiter if this is not the first node.
2947-
if (previousSibling) {
3127+
if (format & ListFormat.AsteriskDelimited) {
3128+
// always write JSDoc in the format "\n *"
3129+
writeLine();
3130+
writeDelimiter(format);
3131+
}
3132+
else if (previousSibling) {
29483133
// i.e
29493134
// function commentedParameters(
29503135
// /* Parameter a */

src/compiler/factory.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2170,6 +2170,57 @@ namespace ts {
21702170
: node;
21712171
}
21722172

2173+
// JSDoc
2174+
2175+
/* @internal */
2176+
export function createJSDocTypeExpression(type: TypeNode): JSDocTypeExpression {
2177+
const node = createSynthesizedNode(SyntaxKind.JSDocTypeExpression) as JSDocTypeExpression;
2178+
node.type = type;
2179+
return node;
2180+
}
2181+
2182+
/* @internal */
2183+
export function createJSDocTypeTag(typeExpression?: JSDocTypeExpression, comment?: string): JSDocTypeTag {
2184+
const tag = createJSDocTag<JSDocTypeTag>(SyntaxKind.JSDocTypeTag, "type");
2185+
tag.typeExpression = typeExpression;
2186+
tag.comment = comment;
2187+
return tag;
2188+
}
2189+
2190+
/* @internal */
2191+
export function createJSDocReturnTag(typeExpression?: JSDocTypeExpression, comment?: string): JSDocReturnTag {
2192+
const tag = createJSDocTag<JSDocReturnTag>(SyntaxKind.JSDocReturnTag, "returns");
2193+
tag.typeExpression = typeExpression;
2194+
tag.comment = comment;
2195+
return tag;
2196+
}
2197+
2198+
/* @internal */
2199+
export function createJSDocParamTag(name: EntityName, isBracketed: boolean, typeExpression?: JSDocTypeExpression, comment?: string): JSDocParameterTag {
2200+
const tag = createJSDocTag<JSDocParameterTag>(SyntaxKind.JSDocParameterTag, "param");
2201+
tag.typeExpression = typeExpression;
2202+
tag.name = name;
2203+
tag.isBracketed = isBracketed;
2204+
tag.comment = comment;
2205+
return tag;
2206+
}
2207+
2208+
/* @internal */
2209+
export function createJSDocComment(comment?: string | undefined, tags?: NodeArray<JSDocTag> | undefined) {
2210+
const node = createSynthesizedNode(SyntaxKind.JSDocComment) as JSDoc;
2211+
node.comment = comment;
2212+
node.tags = tags;
2213+
return node;
2214+
}
2215+
2216+
/* @internal */
2217+
function createJSDocTag<T extends JSDocTag>(kind: T["kind"], tagName: string): T {
2218+
const node = createSynthesizedNode(kind) as T;
2219+
node.atToken = createToken(SyntaxKind.AtToken);
2220+
node.tagName = createIdentifier(tagName);
2221+
return node;
2222+
}
2223+
21732224
// JSX
21742225

21752226
export function createJsxElement(openingElement: JsxOpeningElement, children: ReadonlyArray<JsxChild>, closingElement: JsxClosingElement) {

src/compiler/types.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,7 +2447,7 @@ namespace ts {
24472447

24482448
export interface JSDocTemplateTag extends JSDocTag {
24492449
kind: SyntaxKind.JSDocTemplateTag;
2450-
constraint: TypeNode | undefined;
2450+
constraint: JSDocTypeExpression | undefined;
24512451
typeParameters: NodeArray<TypeParameterDeclaration>;
24522452
}
24532453

@@ -5532,33 +5532,34 @@ namespace ts {
55325532
BarDelimited = 1 << 2, // Each list item is space-and-bar (" |") delimited.
55335533
AmpersandDelimited = 1 << 3, // Each list item is space-and-ampersand (" &") delimited.
55345534
CommaDelimited = 1 << 4, // Each list item is comma (",") delimited.
5535-
DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited,
5535+
AsteriskDelimited = 1 << 5, // Each list item is asterisk ("\n *") delimited, used with JSDoc.
5536+
DelimitersMask = BarDelimited | AmpersandDelimited | CommaDelimited | AsteriskDelimited,
55365537

5537-
AllowTrailingComma = 1 << 5, // Write a trailing comma (",") if present.
5538+
AllowTrailingComma = 1 << 6, // Write a trailing comma (",") if present.
55385539

55395540
// Whitespace
5540-
Indented = 1 << 6, // The list should be indented.
5541-
SpaceBetweenBraces = 1 << 7, // Inserts a space after the opening brace and before the closing brace.
5542-
SpaceBetweenSiblings = 1 << 8, // Inserts a space between each sibling node.
5541+
Indented = 1 << 7, // The list should be indented.
5542+
SpaceBetweenBraces = 1 << 8, // Inserts a space after the opening brace and before the closing brace.
5543+
SpaceBetweenSiblings = 1 << 9, // Inserts a space between each sibling node.
55435544

55445545
// Brackets/Braces
5545-
Braces = 1 << 9, // The list is surrounded by "{" and "}".
5546-
Parenthesis = 1 << 10, // The list is surrounded by "(" and ")".
5547-
AngleBrackets = 1 << 11, // The list is surrounded by "<" and ">".
5548-
SquareBrackets = 1 << 12, // The list is surrounded by "[" and "]".
5546+
Braces = 1 << 10, // The list is surrounded by "{" and "}".
5547+
Parenthesis = 1 << 11, // The list is surrounded by "(" and ")".
5548+
AngleBrackets = 1 << 12, // The list is surrounded by "<" and ">".
5549+
SquareBrackets = 1 << 13, // The list is surrounded by "[" and "]".
55495550
BracketsMask = Braces | Parenthesis | AngleBrackets | SquareBrackets,
55505551

5551-
OptionalIfUndefined = 1 << 13, // Do not emit brackets if the list is undefined.
5552-
OptionalIfEmpty = 1 << 14, // Do not emit brackets if the list is empty.
5552+
OptionalIfUndefined = 1 << 14, // Do not emit brackets if the list is undefined.
5553+
OptionalIfEmpty = 1 << 15, // Do not emit brackets if the list is empty.
55535554
Optional = OptionalIfUndefined | OptionalIfEmpty,
55545555

55555556
// Other
5556-
PreferNewLine = 1 << 15, // Prefer adding a LineTerminator between synthesized nodes.
5557-
NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list.
5558-
NoInterveningComments = 1 << 17, // Do not emit comments between each node
5557+
PreferNewLine = 1 << 16, // Prefer adding a LineTerminator between synthesized nodes.
5558+
NoTrailingNewLine = 1 << 17, // Do not emit a trailing NewLine for a MultiLine list.
5559+
NoInterveningComments = 1 << 18, // Do not emit comments between each node
55595560

5560-
NoSpaceIfEmpty = 1 << 18, // If the literal is empty, do not add spaces between braces.
5561-
SingleElement = 1 << 19,
5561+
NoSpaceIfEmpty = 1 << 19, // If the literal is empty, do not add spaces between braces.
5562+
SingleElement = 1 << 20,
55625563

55635564
// Precomputed Formats
55645565
Modifiers = SingleLine | SpaceBetweenSiblings | NoInterveningComments,
@@ -5598,6 +5599,7 @@ namespace ts {
55985599
TypeParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | AngleBrackets | Optional,
55995600
Parameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis,
56005601
IndexSignatureParameters = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented | SquareBrackets,
5602+
JSDocComment = MultiLine | AsteriskDelimited,
56015603
}
56025604

56035605
/* @internal */

0 commit comments

Comments
 (0)