-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Expose JSDoc tags through the language service #12856
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bd0893a
cf2bfc0
5130e95
d7c4001
2875d5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,6 +68,28 @@ namespace ts.JsDoc { | |
return documentationComment; | ||
} | ||
|
||
export function getJsDocTagsFromDeclarations(declarations: Declaration[]) { | ||
// Only collect doc comments from duplicate declarations once. | ||
const tags: JSDocTagInfo[] = []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here too the array may be lazy-allocated when first tag is encountered -- meaning no allocation for the default case. |
||
forEachUnique(declarations, declaration => { | ||
const jsDocs = getJSDocs(declaration); | ||
if (!jsDocs) { | ||
return; | ||
} | ||
for (const doc of jsDocs) { | ||
const tagsForDoc = (doc as JSDoc).tags; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is type assertion necessary? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess not. I got confused by the 2.0 language service giving me an implicit any warning... |
||
if (tagsForDoc) { | ||
tags.push(...tagsForDoc.filter(tag => tag.kind === SyntaxKind.JSDocTag).map(jsDocTag => { | ||
return { | ||
name: jsDocTag.tagName.text, | ||
text: jsDocTag.comment | ||
} })); | ||
} | ||
} | ||
}); | ||
return tags; | ||
} | ||
|
||
/** | ||
* Iterates through 'array' by index and performs the callback on each element of array until the callback | ||
* returns a truthy value, then returns that value. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -292,6 +292,10 @@ namespace ts { | |
// symbol has no doc comment, then the empty string will be returned. | ||
documentationComment: SymbolDisplayPart[]; | ||
|
||
// Undefined is used to indicate the value has not been computed. If, after computing, the | ||
// symbol has no JSDoc tags, then the empty array will be returned. | ||
tags: JSDocTagInfo[]; | ||
|
||
constructor(flags: SymbolFlags, name: string) { | ||
this.flags = flags; | ||
this.name = name; | ||
|
@@ -316,6 +320,14 @@ namespace ts { | |
|
||
return this.documentationComment; | ||
} | ||
|
||
getJsDocTags(): JSDocTagInfo[] { | ||
if (this.tags === undefined) { | ||
this.tags = JsDoc.getJsDocTagsFromDeclarations(this.declarations); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here too the check would need to change to avoid empty array allocation for the default no-JSDoc case. |
||
|
||
return this.tags; | ||
} | ||
} | ||
|
||
class TokenObject<TKind extends SyntaxKind> extends TokenOrIdentifierObject implements Token<TKind> { | ||
|
@@ -404,6 +416,10 @@ namespace ts { | |
// symbol has no doc comment, then the empty string will be returned. | ||
documentationComment: SymbolDisplayPart[]; | ||
|
||
// Undefined is used to indicate the value has not been computed. If, after computing, the | ||
// symbol has no doc comment, then the empty array will be returned. | ||
jsDocTags: JSDocTagInfo[]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Empty array is expensive here. Most of |
||
|
||
constructor(checker: TypeChecker) { | ||
this.checker = checker; | ||
} | ||
|
@@ -427,6 +443,14 @@ namespace ts { | |
|
||
return this.documentationComment; | ||
} | ||
|
||
getJsDocTags(): JSDocTagInfo[] { | ||
if (this.jsDocTags === undefined) { | ||
this.jsDocTags = this.declaration ? JsDoc.getJsDocTagsFromDeclarations([this.declaration]) : []; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about using |
||
|
||
return this.jsDocTags; | ||
} | ||
} | ||
|
||
class SourceFileObject extends NodeObject implements SourceFile { | ||
|
@@ -1315,7 +1339,8 @@ namespace ts { | |
kindModifiers: ScriptElementKindModifier.none, | ||
textSpan: createTextSpan(node.getStart(), node.getWidth()), | ||
displayParts: typeToDisplayParts(typeChecker, type, getContainerNode(node)), | ||
documentation: type.symbol ? type.symbol.getDocumentationComment() : undefined | ||
documentation: type.symbol ? type.symbol.getDocumentationComment() : undefined, | ||
tags: type.symbol ? type.symbol.getJsDocTags() : undefined | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that the API may return |
||
}; | ||
} | ||
} | ||
|
@@ -1329,7 +1354,8 @@ namespace ts { | |
kindModifiers: SymbolDisplay.getSymbolModifiers(symbol), | ||
textSpan: createTextSpan(node.getStart(), node.getWidth()), | ||
displayParts: displayPartsDocumentationsAndKind.displayParts, | ||
documentation: displayPartsDocumentationsAndKind.documentation | ||
documentation: displayPartsDocumentationsAndKind.documentation, | ||
tags: displayPartsDocumentationsAndKind.tags | ||
}; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this just a drive-by bug fix? I didn't see any real difference in parsing functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes I should clarify. This isn't irrelevant. It fixes a bug I discovered while running the new quickinfo test I added. Consider this comment:
When the comment parser starts out at "SawAsterisk" state I get this tag comment for mytag1:
"* here all the comments are on a new line"
With my change it's fixed:
"here all the comments are on a new line"
This is because we skip all the whitespace (including newlines) before we begin parsing so we start right at the *. This would have been handled in the AsteriskToken case below, but only if the last state had been BeginningOfLine.
Going over the whole logic I believe it just makes more sense this way. Starting at BeginningOfLine is more logical since we didn't actually see an asterisk so why start at SawAsterisk...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. There was a similar bug in the start state for the main jsdoc parsing, so it makes sense that you ran into it here too.