-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Use the new JSDoc parser to handle @param tags #6867
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
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 |
---|---|---|
|
@@ -375,6 +375,68 @@ namespace ts { | |
|
||
return documentationComment; | ||
|
||
function getCommentFromJsDocTag(tag: JSDocTag, rangeEnd: number, sourceFile: SourceFile): SymbolDisplayPart { | ||
const text = sourceFile.text; | ||
|
||
const parts: string[] = []; | ||
let start = tag.end + 1; | ||
|
||
// Eat any leading ignored characters | ||
while (start < rangeEnd) { | ||
const ch = text.charCodeAt(start); | ||
if (ch === CharacterCodes.asterisk || isWhiteSpace(ch)) { | ||
start++; | ||
} | ||
else if (ch === CharacterCodes.at) { | ||
// Most likely a malformed tag | ||
return undefined; | ||
} | ||
else { | ||
break; | ||
} | ||
} | ||
|
||
let i: number; | ||
for (i = start; i < rangeEnd; i++) { | ||
const ch = text.charCodeAt(i); | ||
if (isLineBreak(ch)) { | ||
// Line break, push any non-empty content so far | ||
if (start !== i) { | ||
parts.push(text.substr(start, i - start)); | ||
} | ||
|
||
// Keep scanning until we hit either a non-ws/non-asterisk char | ||
while (i < rangeEnd) { | ||
i++; | ||
const nextCh = text.charCodeAt(i); | ||
if ((nextCh === CharacterCodes.asterisk) || (isWhiteSpace(nextCh) || isLineBreak(nextCh))) { | ||
// Eat *s and whitespace | ||
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. Just write |
||
continue; | ||
} | ||
else if (nextCh === CharacterCodes.at) { | ||
// Abort, we're running into another malformed jsdoc tag | ||
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. Malformed is not necessarily true - you could just be in the next JSDoc tag, right? 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. Ah, never mind. You figure out the start of the next tag later on. 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. But so does that mean that you'll always cut JSDoc comments off at an 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. Only if the @ occurs at the start of a line (which is basically indistinguishable from a malformed comment) |
||
i = rangeEnd; | ||
break; | ||
} | ||
else { | ||
// Scan this | ||
break; | ||
} | ||
} | ||
start = i; | ||
} | ||
} | ||
|
||
if (start < rangeEnd) { | ||
parts.push(text.substr(start, rangeEnd - start)); | ||
} | ||
|
||
return { | ||
kind: "documentation", | ||
text: parts.join("\n") | ||
}; | ||
} | ||
|
||
function getJsDocCommentsSeparatedByNewLines() { | ||
const paramTag = "@param"; | ||
const jsDocCommentParts: SymbolDisplayPart[] = []; | ||
|
@@ -391,9 +453,24 @@ namespace ts { | |
// If it is parameter - try and get the jsDoc comment with @param tag from function declaration's jsDoc comments | ||
if (canUseParsedParamTagComments && declaration.kind === SyntaxKind.Parameter) { | ||
ts.forEach(getJsDocCommentTextRange(declaration.parent, sourceFileOfDeclaration), jsDocCommentTextRange => { | ||
const cleanedParamJsDocComment = getCleanedParamJsDocComment(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration); | ||
if (cleanedParamJsDocComment) { | ||
addRange(jsDocCommentParts, cleanedParamJsDocComment); | ||
// getJsDocCommentTextRange removes leading /* and trailing */, so add those back (-2, +4 net) | ||
const content = parseIsolatedJSDocComment(sourceFileOfDeclaration.text, jsDocCommentTextRange.pos - 2, jsDocCommentTextRange.end - jsDocCommentTextRange.pos + 4); | ||
if (content && content.jsDocComment.tags) { | ||
const tags = content.jsDocComment.tags; | ||
|
||
for (let i = 0; i < tags.length; i++) { | ||
const tag = tags[i]; | ||
if (tag.kind === SyntaxKind.JSDocParameterTag) { | ||
const tagParamName = (tag as JSDocParameterTag).preParameterName || (tag as JSDocParameterTag).postParameterName; | ||
const nextTagStart = (i === tags.length - 1) ? jsDocCommentTextRange.end : tags[i + 1].pos; | ||
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. Why even bother with this if you immediately stop at an 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. The @ might not be the range end |
||
if (tagParamName && tagParamName.text === name) { | ||
const comment = getCommentFromJsDocTag(tag, nextTagStart, sourceFileOfDeclaration); | ||
if (comment) { | ||
jsDocCommentParts.push(comment); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
|
@@ -540,155 +617,6 @@ namespace ts { | |
|
||
return docComments; | ||
} | ||
|
||
function getCleanedParamJsDocComment(pos: number, end: number, sourceFile: SourceFile) { | ||
let paramHelpStringMargin: number; | ||
const paramDocComments: SymbolDisplayPart[] = []; | ||
while (pos < end) { | ||
if (isParamTag(pos, end, sourceFile)) { | ||
let blankLineCount = 0; | ||
let recordedParamTag = false; | ||
// Consume leading spaces | ||
pos = consumeWhiteSpaces(pos + paramTag.length); | ||
if (pos >= end) { | ||
break; | ||
} | ||
|
||
// Ignore type expression | ||
if (sourceFile.text.charCodeAt(pos) === CharacterCodes.openBrace) { | ||
pos++; | ||
for (let curlies = 1; pos < end; pos++) { | ||
const charCode = sourceFile.text.charCodeAt(pos); | ||
|
||
// { character means we need to find another } to match the found one | ||
if (charCode === CharacterCodes.openBrace) { | ||
curlies++; | ||
continue; | ||
} | ||
|
||
// } char | ||
if (charCode === CharacterCodes.closeBrace) { | ||
curlies--; | ||
if (curlies === 0) { | ||
// We do not have any more } to match the type expression is ignored completely | ||
pos++; | ||
break; | ||
} | ||
else { | ||
// there are more { to be matched with } | ||
continue; | ||
} | ||
} | ||
|
||
// Found start of another tag | ||
if (charCode === CharacterCodes.at) { | ||
break; | ||
} | ||
} | ||
|
||
// Consume white spaces | ||
pos = consumeWhiteSpaces(pos); | ||
if (pos >= end) { | ||
break; | ||
} | ||
} | ||
|
||
// Parameter name | ||
if (isName(pos, end, sourceFile, name)) { | ||
// Found the parameter we are looking for consume white spaces | ||
pos = consumeWhiteSpaces(pos + name.length); | ||
if (pos >= end) { | ||
break; | ||
} | ||
|
||
let paramHelpString = ""; | ||
const firstLineParamHelpStringPos = pos; | ||
while (pos < end) { | ||
const ch = sourceFile.text.charCodeAt(pos); | ||
|
||
// at line break, set this comment line text and go to next line | ||
if (isLineBreak(ch)) { | ||
if (paramHelpString) { | ||
pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount); | ||
paramHelpString = ""; | ||
blankLineCount = 0; | ||
recordedParamTag = true; | ||
} | ||
else if (recordedParamTag) { | ||
blankLineCount++; | ||
} | ||
|
||
// Get the pos after cleaning start of the line | ||
setPosForParamHelpStringOnNextLine(firstLineParamHelpStringPos); | ||
continue; | ||
} | ||
|
||
// Done scanning param help string - next tag found | ||
if (ch === CharacterCodes.at) { | ||
break; | ||
} | ||
|
||
paramHelpString += sourceFile.text.charAt(pos); | ||
|
||
// Go to next character | ||
pos++; | ||
} | ||
|
||
// If there is param help text, add it top the doc comments | ||
if (paramHelpString) { | ||
pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount); | ||
} | ||
paramHelpStringMargin = undefined; | ||
} | ||
|
||
// If this is the start of another tag, continue with the loop in seach of param tag with symbol name | ||
if (sourceFile.text.charCodeAt(pos) === CharacterCodes.at) { | ||
continue; | ||
} | ||
} | ||
|
||
// Next character | ||
pos++; | ||
} | ||
|
||
return paramDocComments; | ||
|
||
function consumeWhiteSpaces(pos: number) { | ||
while (pos < end && isWhiteSpace(sourceFile.text.charCodeAt(pos))) { | ||
pos++; | ||
} | ||
|
||
return pos; | ||
} | ||
|
||
function setPosForParamHelpStringOnNextLine(firstLineParamHelpStringPos: number) { | ||
// Get the pos after consuming line breaks | ||
pos = consumeLineBreaks(pos, end, sourceFile); | ||
if (pos >= end) { | ||
return; | ||
} | ||
|
||
if (paramHelpStringMargin === undefined) { | ||
paramHelpStringMargin = sourceFile.getLineAndCharacterOfPosition(firstLineParamHelpStringPos).character; | ||
} | ||
|
||
// Now consume white spaces max | ||
const startOfLinePos = pos; | ||
pos = consumeWhiteSpacesOnTheLine(pos, end, sourceFile, paramHelpStringMargin); | ||
if (pos >= end) { | ||
return; | ||
} | ||
|
||
const consumedSpaces = pos - startOfLinePos; | ||
if (consumedSpaces < paramHelpStringMargin) { | ||
const ch = sourceFile.text.charCodeAt(pos); | ||
if (ch === CharacterCodes.asterisk) { | ||
// Consume more spaces after asterisk | ||
pos = consumeWhiteSpacesOnTheLine(pos + 1, end, sourceFile, paramHelpStringMargin - consumedSpaces - 1); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
///<reference path="fourslash.ts" /> | ||
|
||
// @allowNonTsExtensions: true | ||
// @Filename: Foo.js | ||
|
||
//// /** | ||
//// * @param {string} p1 parameter 1 | ||
//// * @param {string?} p2 parameter 2 | ||
//// * @param {string} [p3] parameter 3 | ||
//// * @param {string} [p4="test"] parameter 4 | ||
//// */ | ||
//// function f1(p1, p2, p3, p4) { | ||
//// } | ||
//// f1(''/*1*/, ''/*2*/, ''/*3*/, ''/*4*/) | ||
|
||
|
||
for(const m of ['1', '2', '3', '4']) { | ||
goTo.marker(m); | ||
verify.currentParameterHelpArgumentDocCommentIs('parameter ' + m); | ||
} |
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.
Adding the below to the
if
expression gets rid of the leading-
in @param comments nicely for me 😄