Merge pull request #10671 from Microsoft/new-jsdoc-parser

Remove service's jsdoc parser and enhance parser's jsdoc parser
This commit is contained in:
Nathan Shively-Sanders
2016-09-15 12:27:43 -07:00
committed by GitHub
68 changed files with 1294 additions and 918 deletions

View File

@@ -268,7 +268,7 @@ namespace ts {
Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType);
let functionType = <JSDocFunctionType>node.parent;
let index = indexOf(functionType.parameters, node);
return "p" + index;
return "arg" + index;
case SyntaxKind.JSDocTypedefTag:
const parentNode = node.parent && node.parent.parent;
let nameFromParentNode: string;
@@ -540,9 +540,7 @@ namespace ts {
// because the scope of JsDocComment should not be affected by whether the current node is a
// container or not.
if (isInJavaScriptFile(node) && node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
bind(jsDocComment);
}
forEach(node.jsDocComments, bind);
}
if (checkUnreachable(node)) {
forEachChild(node, bind);

View File

@@ -5653,12 +5653,13 @@ namespace ts {
case SyntaxKind.JSDocThisType:
case SyntaxKind.JSDocOptionalType:
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode>node).type);
case SyntaxKind.JSDocRecordType:
return getTypeFromTypeNode((node as JSDocRecordType).literal);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.TypeLiteral:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.JSDocRecordType:
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node, aliasSymbol, aliasTypeArguments);
// This function assumes that an identifier or qualified name is a type expression
// Callers should first ensure this by calling isTypeNode

View File

@@ -379,7 +379,7 @@ namespace ts {
case SyntaxKind.JSDocNullableType:
return visitNode(cbNode, (<JSDocNullableType>node).type);
case SyntaxKind.JSDocRecordType:
return visitNodes(cbNodes, (<JSDocRecordType>node).members);
return visitNode(cbNode, (<JSDocRecordType>node).literal);
case SyntaxKind.JSDocTypeReference:
return visitNode(cbNode, (<JSDocTypeReference>node).name) ||
visitNodes(cbNodes, (<JSDocTypeReference>node).typeArguments);
@@ -398,7 +398,7 @@ namespace ts {
return visitNode(cbNode, (<JSDocRecordMember>node).name) ||
visitNode(cbNode, (<JSDocRecordMember>node).type);
case SyntaxKind.JSDocComment:
return visitNodes(cbNodes, (<JSDocComment>node).tags);
return visitNodes(cbNodes, (<JSDoc>node).tags);
case SyntaxKind.JSDocParameterTag:
return visitNode(cbNode, (<JSDocParameterTag>node).preParameterName) ||
visitNode(cbNode, (<JSDocParameterTag>node).typeExpression) ||
@@ -453,10 +453,10 @@ namespace ts {
/* @internal */
export function parseIsolatedJSDocComment(content: string, start?: number, length?: number) {
const result = Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length);
if (result && result.jsDocComment) {
if (result && result.jsDoc) {
// because the jsDocComment was parsed out of the source file, it might
// not be covered by the fixupParentReferences.
Parser.fixupParentReferences(result.jsDocComment);
Parser.fixupParentReferences(result.jsDoc);
}
return result;
@@ -655,20 +655,18 @@ namespace ts {
function addJSDocComment<T extends Node>(node: T): T {
if (contextFlags & NodeFlags.JavaScriptFile) {
const comments = getLeadingCommentRangesOfNode(node, sourceFile);
if (comments) {
for (const comment of comments) {
const jsDocComment = JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos);
if (!jsDocComment) {
continue;
}
if (!node.jsDocComments) {
node.jsDocComments = [];
}
node.jsDocComments.push(jsDocComment);
const comments = getJsDocCommentsFromText(node, sourceFile.text);
if (comments) {
for (const comment of comments) {
const jsDoc = JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos);
if (!jsDoc) {
continue;
}
if (!node.jsDocComments) {
node.jsDocComments = [];
}
node.jsDocComments.push(jsDoc);
}
}
@@ -2204,7 +2202,7 @@ namespace ts {
}
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node);
parseTypeMemberSemicolon();
return finishNode(node);
return addJSDocComment(finishNode(node));
}
function isIndexSignature(): boolean {
@@ -2294,7 +2292,7 @@ namespace ts {
// [Yield] nor [Await]
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, method);
parseTypeMemberSemicolon();
return finishNode(method);
return addJSDocComment(finishNode(method));
}
else {
const property = <PropertySignature>createNode(SyntaxKind.PropertySignature, fullStart);
@@ -2311,7 +2309,7 @@ namespace ts {
}
parseTypeMemberSemicolon();
return finishNode(property);
return addJSDocComment(finishNode(property));
}
}
@@ -2898,7 +2896,7 @@ namespace ts {
node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, "=>");
node.body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier);
return finishNode(node);
return addJSDocComment(finishNode(node));
}
function tryParseParenthesizedArrowFunctionExpression(): Expression {
@@ -2931,7 +2929,7 @@ namespace ts {
? parseArrowFunctionExpressionBody(isAsync)
: parseIdentifier();
return finishNode(arrowFunction);
return addJSDocComment(finishNode(arrowFunction));
}
// True -> We definitely expect a parenthesized arrow function here.
@@ -4114,7 +4112,7 @@ namespace ts {
function tryParseAccessorDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: NodeArray<Modifier>): AccessorDeclaration {
if (parseContextualModifier(SyntaxKind.GetKeyword)) {
return addJSDocComment(parseAccessorDeclaration(SyntaxKind.GetAccessor, fullStart, decorators, modifiers));
return parseAccessorDeclaration(SyntaxKind.GetAccessor, fullStart, decorators, modifiers);
}
else if (parseContextualModifier(SyntaxKind.SetKeyword)) {
return parseAccessorDeclaration(SyntaxKind.SetAccessor, fullStart, decorators, modifiers);
@@ -4997,7 +4995,7 @@ namespace ts {
: doOutsideOfContext(NodeFlags.YieldContext | NodeFlags.DisallowInContext, parseNonParameterInitializer);
parseSemicolon();
return finishNode(property);
return addJSDocComment(finishNode(property));
}
function parsePropertyOrMethodDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: NodeArray<Modifier>): ClassElement {
@@ -5026,7 +5024,7 @@ namespace ts {
node.name = parsePropertyName();
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false);
return finishNode(node);
return addJSDocComment(finishNode(node));
}
function isClassMemberModifier(idToken: SyntaxKind) {
@@ -5264,7 +5262,7 @@ namespace ts {
node.members = createMissingList<ClassElement>();
}
return finishNode(node);
return addJSDocComment(finishNode(node));
}
function parseNameOfClassDeclarationOrExpression(): Identifier {
@@ -5332,7 +5330,7 @@ namespace ts {
node.typeParameters = parseTypeParameters();
node.heritageClauses = parseHeritageClauses(/*isClassHeritageClause*/ false);
node.members = parseObjectTypeMembers();
return finishNode(node);
return addJSDocComment(finishNode(node));
}
function parseTypeAliasDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: NodeArray<Modifier>): TypeAliasDeclaration {
@@ -5356,7 +5354,7 @@ namespace ts {
const node = <EnumMember>createNode(SyntaxKind.EnumMember, scanner.getStartPos());
node.name = parsePropertyName();
node.initializer = allowInAnd(parseNonParameterInitializer);
return finishNode(node);
return addJSDocComment(finishNode(node));
}
function parseEnumDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: NodeArray<Modifier>): EnumDeclaration {
@@ -5372,7 +5370,7 @@ namespace ts {
else {
node.members = createMissingList<EnumMember>();
}
return finishNode(node);
return addJSDocComment(finishNode(node));
}
function parseModuleBlock(): ModuleBlock {
@@ -5399,7 +5397,7 @@ namespace ts {
node.body = parseOptional(SyntaxKind.DotToken)
? parseModuleOrNamespaceDeclaration(getNodePos(), /*decorators*/ undefined, /*modifiers*/ undefined, NodeFlags.NestedNamespace | namespaceFlag)
: parseModuleBlock();
return finishNode(node);
return addJSDocComment(finishNode(node));
}
function parseAmbientExternalModuleDeclaration(fullStart: number, decorators: NodeArray<Decorator>, modifiers: NodeArray<Modifier>): ModuleDeclaration {
@@ -5488,7 +5486,7 @@ namespace ts {
parseExpected(SyntaxKind.EqualsToken);
importEqualsDeclaration.moduleReference = parseModuleReference();
parseSemicolon();
return finishNode(importEqualsDeclaration);
return addJSDocComment(finishNode(importEqualsDeclaration));
}
}
@@ -5809,6 +5807,7 @@ namespace ts {
export function parseJSDocTypeExpressionForTests(content: string, start: number, length: number) {
initializeState("file.js", content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS);
sourceFile = createSourceFile("file.js", ScriptTarget.Latest, ScriptKind.JS);
scanner.setText(content, start, length);
currentToken = scanner.scan();
const jsDocTypeExpression = parseJSDocTypeExpression();
@@ -6027,22 +6026,7 @@ namespace ts {
function parseJSDocRecordType(): JSDocRecordType {
const result = <JSDocRecordType>createNode(SyntaxKind.JSDocRecordType);
nextToken();
result.members = parseDelimitedList(ParsingContext.JSDocRecordMembers, parseJSDocRecordMember);
checkForTrailingComma(result.members);
parseExpected(SyntaxKind.CloseBraceToken);
return finishNode(result);
}
function parseJSDocRecordMember(): JSDocRecordMember {
const result = <JSDocRecordMember>createNode(SyntaxKind.JSDocRecordMember);
result.name = parseSimplePropertyName();
if (token() === SyntaxKind.ColonToken) {
nextToken();
result.type = parseJSDocType();
}
result.literal = parseTypeLiteral();
return finishNode(result);
}
@@ -6140,14 +6124,14 @@ namespace ts {
export function parseIsolatedJSDocComment(content: string, start: number, length: number) {
initializeState("file.js", content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS);
sourceFile = <SourceFile>{ languageVariant: LanguageVariant.Standard, text: content };
const jsDocComment = parseJSDocCommentWorker(start, length);
const jsDoc = parseJSDocCommentWorker(start, length);
const diagnostics = parseDiagnostics;
clearState();
return jsDocComment ? { jsDocComment, diagnostics } : undefined;
return jsDoc ? { jsDoc, diagnostics } : undefined;
}
export function parseJSDocComment(parent: Node, start: number, length: number): JSDocComment {
export function parseJSDocComment(parent: Node, start: number, length: number): JSDoc {
const saveToken = currentToken;
const saveParseDiagnosticsLength = parseDiagnostics.length;
const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode;
@@ -6164,7 +6148,13 @@ namespace ts {
return comment;
}
export function parseJSDocCommentWorker(start: number, length: number): JSDocComment {
const enum JSDocState {
BeginningOfLine,
SawAsterisk,
SavingComments,
}
export function parseJSDocCommentWorker(start: number, length: number): JSDoc {
const content = sourceText;
start = start || 0;
const end = length === undefined ? content.length : start + length;
@@ -6175,76 +6165,134 @@ namespace ts {
Debug.assert(end <= content.length);
let tags: NodeArray<JSDocTag>;
let result: JSDocComment;
const comments: string[] = [];
let result: JSDoc;
// Check for /** (JSDoc opening part)
if (content.charCodeAt(start) === CharacterCodes.slash &&
content.charCodeAt(start + 1) === CharacterCodes.asterisk &&
content.charCodeAt(start + 2) === CharacterCodes.asterisk &&
content.charCodeAt(start + 3) !== CharacterCodes.asterisk) {
if (!isJsDocStart(content, start)) {
return result;
}
// + 3 for leading /**, - 5 in total for /** */
scanner.scanRange(start + 3, length - 5, () => {
// Initially we can parse out a tag. We also have seen a starting asterisk.
// This is so that /** * @type */ doesn't parse.
let advanceToken = true;
let state = JSDocState.SawAsterisk;
let margin: number | undefined = undefined;
// + 4 for leading '/** '
let indent = start - Math.max(content.lastIndexOf("\n", start), 0) + 4;
function pushComment(text: string) {
if (!margin) {
margin = indent;
}
comments.push(text);
indent += text.length;
}
// + 3 for leading /**, - 5 in total for /** */
scanner.scanRange(start + 3, length - 5, () => {
// Initially we can parse out a tag. We also have seen a starting asterisk.
// This is so that /** * @type */ doesn't parse.
let canParseTag = true;
let seenAsterisk = true;
nextJSDocToken();
while (token() === SyntaxKind.WhitespaceTrivia) {
nextJSDocToken();
while (token() !== SyntaxKind.EndOfFileToken) {
switch (token()) {
case SyntaxKind.AtToken:
if (canParseTag) {
parseTag();
}
// This will take us to the end of the line, so it's OK to parse a tag on the next pass through the loop
seenAsterisk = false;
break;
case SyntaxKind.NewLineTrivia:
// After a line break, we can parse a tag, and we haven't seen an asterisk on the next line yet
canParseTag = true;
seenAsterisk = false;
break;
case SyntaxKind.AsteriskToken:
if (seenAsterisk) {
// If we've already seen an asterisk, then we can no longer parse a tag on this line
canParseTag = false;
}
}
if (token() === SyntaxKind.NewLineTrivia) {
state = JSDocState.BeginningOfLine;
nextJSDocToken();
}
while (token() !== SyntaxKind.EndOfFileToken) {
switch (token()) {
case SyntaxKind.AtToken:
if (state === JSDocState.BeginningOfLine || state === JSDocState.SawAsterisk) {
removeTrailingNewlines(comments);
parseTag(indent);
// NOTE: According to usejsdoc.org, a tag goes to end of line, except the last tag.
// Real-world comments may break this rule, so "BeginningOfLine" will not be a real line beginning
// for malformed examples like `/** @param {string} x @returns {number} the length */`
state = JSDocState.BeginningOfLine;
advanceToken = false;
margin = undefined;
indent++;
}
else {
pushComment(scanner.getTokenText());
}
break;
case SyntaxKind.NewLineTrivia:
comments.push(scanner.getTokenText());
state = JSDocState.BeginningOfLine;
indent = 0;
break;
case SyntaxKind.AsteriskToken:
const asterisk = scanner.getTokenText();
if (state === JSDocState.SawAsterisk) {
// If we've already seen an asterisk, then we can no longer parse a tag on this line
state = JSDocState.SavingComments;
pushComment(asterisk);
}
else {
// Ignore the first asterisk on a line
seenAsterisk = true;
break;
case SyntaxKind.Identifier:
// Anything else is doc comment text. We can't do anything with it. Because it
// wasn't a tag, we can no longer parse a tag on this line until we hit the next
// line break.
canParseTag = false;
break;
case SyntaxKind.EndOfFileToken:
break;
}
state = JSDocState.SawAsterisk;
indent += asterisk.length;
}
break;
case SyntaxKind.Identifier:
// Anything else is doc comment text. We just save it. Because it
// wasn't a tag, we can no longer parse a tag on this line until we hit the next
// line break.
pushComment(scanner.getTokenText());
state = JSDocState.SavingComments;
break;
case SyntaxKind.WhitespaceTrivia:
// only collect whitespace if we're already saving comments or have just crossed the comment indent margin
const whitespace = scanner.getTokenText();
if (state === JSDocState.SavingComments || margin !== undefined && indent + whitespace.length > margin) {
comments.push(whitespace.slice(margin - indent - 1));
}
indent += whitespace.length;
break;
case SyntaxKind.EndOfFileToken:
break;
default:
pushComment(scanner.getTokenText());
break;
}
if (advanceToken) {
nextJSDocToken();
}
else {
advanceToken = true;
}
}
removeLeadingNewlines(comments);
removeTrailingNewlines(comments);
result = createJSDocComment();
result = createJSDocComment();
});
}
});
return result;
function createJSDocComment(): JSDocComment {
if (!tags) {
return undefined;
function removeLeadingNewlines(comments: string[]) {
while (comments.length && (comments[0] === "\n" || comments[0] === "\r")) {
comments.shift();
}
}
const result = <JSDocComment>createNode(SyntaxKind.JSDocComment, start);
function removeTrailingNewlines(comments: string[]) {
while (comments.length && (comments[comments.length - 1] === "\n" || comments[comments.length - 1] === "\r")) {
comments.pop();
}
}
function isJsDocStart(content: string, start: number) {
return content.charCodeAt(start) === CharacterCodes.slash &&
content.charCodeAt(start + 1) === CharacterCodes.asterisk &&
content.charCodeAt(start + 2) === CharacterCodes.asterisk &&
content.charCodeAt(start + 3) !== CharacterCodes.asterisk;
}
function createJSDocComment(): JSDoc {
const result = <JSDoc>createNode(SyntaxKind.JSDocComment, start);
result.tags = tags;
result.comment = comments.length ? comments.join("") : undefined;
return finishNode(result, end);
}
@@ -6254,78 +6302,154 @@ namespace ts {
}
}
function parseTag(): void {
function parseTag(indent: number) {
Debug.assert(token() === SyntaxKind.AtToken);
const atToken = createNode(SyntaxKind.AtToken, scanner.getTokenPos());
atToken.end = scanner.getTextPos();
nextJSDocToken();
const tagName = parseJSDocIdentifierName();
skipWhitespace();
if (!tagName) {
return;
}
const tag = handleTag(atToken, tagName) || handleUnknownTag(atToken, tagName);
addTag(tag);
}
function handleTag(atToken: Node, tagName: Identifier): JSDocTag {
let tag: JSDocTag;
if (tagName) {
switch (tagName.text) {
case "param":
return handleParamTag(atToken, tagName);
tag = parseParamTag(atToken, tagName);
break;
case "return":
case "returns":
return handleReturnTag(atToken, tagName);
tag = parseReturnTag(atToken, tagName);
break;
case "template":
return handleTemplateTag(atToken, tagName);
tag = parseTemplateTag(atToken, tagName);
break;
case "type":
return handleTypeTag(atToken, tagName);
tag = parseTypeTag(atToken, tagName);
break;
case "typedef":
return handleTypedefTag(atToken, tagName);
tag = parseTypedefTag(atToken, tagName);
break;
default:
tag = parseUnknownTag(atToken, tagName);
break;
}
}
else {
tag = parseUnknownTag(atToken, tagName);
}
return undefined;
if (!tag) {
// a badly malformed tag should not be added to the list of tags
return;
}
addTag(tag, parseTagComments(indent + tag.end - tag.pos));
}
function handleUnknownTag(atToken: Node, tagName: Identifier) {
function parseTagComments(indent: number) {
const comments: string[] = [];
let state = JSDocState.SawAsterisk;
let margin: number | undefined;
function pushComment(text: string) {
if (!margin) {
margin = indent;
}
comments.push(text);
indent += text.length;
}
while (token() !== SyntaxKind.AtToken && token() !== SyntaxKind.EndOfFileToken) {
switch (token()) {
case SyntaxKind.NewLineTrivia:
if (state >= JSDocState.SawAsterisk) {
state = JSDocState.BeginningOfLine;
comments.push(scanner.getTokenText());
}
indent = 0;
break;
case SyntaxKind.AtToken:
// Done
break;
case SyntaxKind.WhitespaceTrivia:
if (state === JSDocState.SavingComments) {
pushComment(scanner.getTokenText());
}
else {
const whitespace = scanner.getTokenText();
// if the whitespace crosses the margin, take only the whitespace that passes the margin
if (margin !== undefined && indent + whitespace.length > margin) {
comments.push(whitespace.slice(margin - indent - 1));
}
indent += whitespace.length;
}
break;
case SyntaxKind.AsteriskToken:
if (state === JSDocState.BeginningOfLine) {
// leading asterisks start recording on the *next* (non-whitespace) token
state = JSDocState.SawAsterisk;
indent += scanner.getTokenText().length;
break;
}
// FALLTHROUGH otherwise to record the * as a comment
default:
state = JSDocState.SavingComments; // leading identifiers start recording as well
pushComment(scanner.getTokenText());
break;
}
if (token() === SyntaxKind.AtToken) {
// Done
break;
}
nextJSDocToken();
}
removeLeadingNewlines(comments);
removeTrailingNewlines(comments);
return comments;
}
function parseUnknownTag(atToken: Node, tagName: Identifier) {
const result = <JSDocTag>createNode(SyntaxKind.JSDocTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
return finishNode(result);
}
function addTag(tag: JSDocTag): void {
if (tag) {
if (!tags) {
tags = createNodeArray([tag], tag.pos);
}
else {
tags.push(tag);
}
tags.end = tag.end;
function addTag(tag: JSDocTag, comments: string[]): void {
tag.comment = comments.join("");
if (!tags) {
tags = createNodeArray([tag], tag.pos);
}
else {
tags.push(tag);
}
tags.end = tag.end;
}
function tryParseTypeExpression(): JSDocTypeExpression {
if (token() !== SyntaxKind.OpenBraceToken) {
return undefined;
}
return tryParse(() => {
skipWhitespace();
if (token() !== SyntaxKind.OpenBraceToken) {
return undefined;
}
const typeExpression = parseJSDocTypeExpression();
return typeExpression;
return parseJSDocTypeExpression();
});
}
function handleParamTag(atToken: Node, tagName: Identifier) {
function parseParamTag(atToken: Node, tagName: Identifier) {
let typeExpression = tryParseTypeExpression();
skipWhitespace();
let name: Identifier;
let isBracketed: boolean;
// Looking for something like '[foo]' or 'foo'
if (parseOptionalToken(SyntaxKind.OpenBracketToken)) {
name = parseJSDocIdentifierName();
skipWhitespace();
isBracketed = true;
// May have an optional default, e.g. '[foo = 42]'
@@ -6362,11 +6486,12 @@ namespace ts {
result.preParameterName = preName;
result.typeExpression = typeExpression;
result.postParameterName = postName;
result.parameterName = postName || preName;
result.isBracketed = isBracketed;
return finishNode(result);
}
function handleReturnTag(atToken: Node, tagName: Identifier): JSDocReturnTag {
function parseReturnTag(atToken: Node, tagName: Identifier): JSDocReturnTag {
if (forEach(tags, t => t.kind === SyntaxKind.JSDocReturnTag)) {
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text);
}
@@ -6378,7 +6503,7 @@ namespace ts {
return finishNode(result);
}
function handleTypeTag(atToken: Node, tagName: Identifier): JSDocTypeTag {
function parseTypeTag(atToken: Node, tagName: Identifier): JSDocTypeTag {
if (forEach(tags, t => t.kind === SyntaxKind.JSDocTypeTag)) {
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text);
}
@@ -6390,10 +6515,11 @@ namespace ts {
return finishNode(result);
}
function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag {
function parsePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag {
const typeExpression = tryParseTypeExpression();
skipWhitespace();
const name = parseJSDocIdentifierName();
skipWhitespace();
if (!name) {
parseErrorAtPosition(scanner.getStartPos(), /*length*/ 0, Diagnostics.Identifier_expected);
return undefined;
@@ -6407,7 +6533,7 @@ namespace ts {
return finishNode(result);
}
function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag {
function parseTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag {
const typeExpression = tryParseTypeExpression();
skipWhitespace();
@@ -6416,6 +6542,7 @@ namespace ts {
typedefTag.tagName = tagName;
typedefTag.name = parseJSDocIdentifierName();
typedefTag.typeExpression = typeExpression;
skipWhitespace();
if (typeExpression) {
if (typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
@@ -6485,6 +6612,7 @@ namespace ts {
nextJSDocToken();
const tagName = parseJSDocIdentifierName();
skipWhitespace();
if (!tagName) {
return false;
}
@@ -6495,21 +6623,21 @@ namespace ts {
// already has a @type tag, terminate the parent tag now.
return false;
}
parentTag.jsDocTypeTag = handleTypeTag(atToken, tagName);
parentTag.jsDocTypeTag = parseTypeTag(atToken, tagName);
return true;
case "prop":
case "property":
if (!parentTag.jsDocPropertyTags) {
parentTag.jsDocPropertyTags = <NodeArray<JSDocPropertyTag>>[];
}
const propertyTag = handlePropertyTag(atToken, tagName);
const propertyTag = parsePropertyTag(atToken, tagName);
parentTag.jsDocPropertyTags.push(propertyTag);
return true;
}
return false;
}
function handleTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag {
function parseTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag {
if (forEach(tags, t => t.kind === SyntaxKind.JSDocTemplateTag)) {
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text);
}
@@ -6519,6 +6647,7 @@ namespace ts {
while (true) {
const name = parseJSDocIdentifierName();
skipWhitespace();
if (!name) {
parseErrorAtPosition(scanner.getStartPos(), 0, Diagnostics.Identifier_expected);
return undefined;
@@ -6532,6 +6661,7 @@ namespace ts {
if (token() === SyntaxKind.CommaToken) {
nextJSDocToken();
skipWhitespace();
}
else {
break;

View File

@@ -1759,40 +1759,46 @@ namespace ts {
}
startPos = pos;
// Eat leading whitespace
let ch = text.charCodeAt(pos);
while (pos < end) {
ch = text.charCodeAt(pos);
if (isWhiteSpaceSingleLine(ch)) {
pos++;
}
else {
break;
}
}
tokenPos = pos;
const ch = text.charCodeAt(pos);
switch (ch) {
case CharacterCodes.tab:
case CharacterCodes.verticalTab:
case CharacterCodes.formFeed:
case CharacterCodes.space:
while (pos < end && isWhiteSpaceSingleLine(text.charCodeAt(pos))) {
pos++;
}
return token = SyntaxKind.WhitespaceTrivia;
case CharacterCodes.at:
return pos += 1, token = SyntaxKind.AtToken;
pos++;
return token = SyntaxKind.AtToken;
case CharacterCodes.lineFeed:
case CharacterCodes.carriageReturn:
return pos += 1, token = SyntaxKind.NewLineTrivia;
pos++;
return token = SyntaxKind.NewLineTrivia;
case CharacterCodes.asterisk:
return pos += 1, token = SyntaxKind.AsteriskToken;
pos++;
return token = SyntaxKind.AsteriskToken;
case CharacterCodes.openBrace:
return pos += 1, token = SyntaxKind.OpenBraceToken;
pos++;
return token = SyntaxKind.OpenBraceToken;
case CharacterCodes.closeBrace:
return pos += 1, token = SyntaxKind.CloseBraceToken;
pos++;
return token = SyntaxKind.CloseBraceToken;
case CharacterCodes.openBracket:
return pos += 1, token = SyntaxKind.OpenBracketToken;
pos++;
return token = SyntaxKind.OpenBracketToken;
case CharacterCodes.closeBracket:
return pos += 1, token = SyntaxKind.CloseBracketToken;
pos++;
return token = SyntaxKind.CloseBracketToken;
case CharacterCodes.equals:
return pos += 1, token = SyntaxKind.EqualsToken;
pos++;
return token = SyntaxKind.EqualsToken;
case CharacterCodes.comma:
return pos += 1, token = SyntaxKind.CommaToken;
pos++;
return token = SyntaxKind.CommaToken;
}
if (isIdentifierStart(ch, ScriptTarget.Latest)) {

View File

@@ -487,7 +487,7 @@ namespace ts {
parent?: Node; // Parent node (initialized by binding)
/* @internal */ original?: Node; // The original node if this is an updated node.
/* @internal */ startsOnNewLine?: boolean; // Whether a synthesized node should start on a new line (used by transforms).
/* @internal */ jsDocComments?: JSDocComment[]; // JSDoc for the node, if it has any. Only for .js files.
/* @internal */ jsDocComments?: JSDoc[]; // JSDoc for the node, if it has any.
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
@@ -1555,7 +1555,7 @@ namespace ts {
// @kind(SyntaxKind.JSDocRecordType)
export interface JSDocRecordType extends JSDocType, TypeLiteralNode {
members: NodeArray<JSDocRecordMember>;
literal: TypeLiteralNode;
}
// @kind(SyntaxKind.JSDocTypeReference)
@@ -1603,14 +1603,16 @@ namespace ts {
}
// @kind(SyntaxKind.JSDocComment)
export interface JSDocComment extends Node {
export interface JSDoc extends Node {
tags: NodeArray<JSDocTag>;
comment: string | undefined;
}
// @kind(SyntaxKind.JSDocTag)
export interface JSDocTag extends Node {
atToken: Node;
tagName: Identifier;
comment: string | undefined;
}
// @kind(SyntaxKind.JSDocTemplateTag)
@@ -1649,9 +1651,13 @@ namespace ts {
// @kind(SyntaxKind.JSDocParameterTag)
export interface JSDocParameterTag extends JSDocTag {
/** the parameter name, if provided *before* the type (TypeScript-style) */
preParameterName?: Identifier;
typeExpression?: JSDocTypeExpression;
/** the parameter name, if provided *after* the type (JSDoc-standard) */
postParameterName?: Identifier;
/** the parameter name, regardless of the location it was provided */
parameterName: Identifier;
isBracketed: boolean;
}

View File

@@ -1426,39 +1426,75 @@ namespace ts {
return undefined;
}
const jsDocComments = getJSDocComments(node, checkParentVariableStatement);
if (!jsDocComments) {
const jsDocTags = getJSDocTags(node, checkParentVariableStatement);
if (!jsDocTags) {
return undefined;
}
for (const jsDocComment of jsDocComments) {
for (const tag of jsDocComment.tags) {
if (tag.kind === kind) {
return tag;
}
for (const tag of jsDocTags) {
if (tag.kind === kind) {
return tag;
}
}
}
function getJSDocComments(node: Node, checkParentVariableStatement: boolean): JSDocComment[] {
if (node.jsDocComments) {
return node.jsDocComments;
function append<T>(previous: T[] | undefined, additional: T[] | undefined): T[] | undefined {
if (additional) {
if (!previous) {
previous = [];
}
for (const x of additional) {
previous.push(x);
}
}
// Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
// /**
// * @param {number} name
// * @returns {number}
// */
// var x = function(name) { return name.length; }
if (checkParentVariableStatement) {
const isInitializerOfVariableDeclarationInStatement =
node.parent.kind === SyntaxKind.VariableDeclaration &&
(<VariableDeclaration>node.parent).initializer === node &&
node.parent.parent.parent.kind === SyntaxKind.VariableStatement;
return previous;
}
const variableStatementNode = isInitializerOfVariableDeclarationInStatement ? node.parent.parent.parent : undefined;
export function getJSDocComments(node: Node, checkParentVariableStatement: boolean): string[] {
return getJSDocs(node, checkParentVariableStatement, docs => map(docs, doc => doc.comment), tags => map(tags, tag => tag.comment));
}
function getJSDocTags(node: Node, checkParentVariableStatement: boolean): JSDocTag[] {
return getJSDocs(node, checkParentVariableStatement, docs => {
const result: JSDocTag[] = [];
for (const doc of docs) {
if (doc.tags) {
result.push(...doc.tags);
}
}
return result;
}, tags => tags);
}
function getJSDocs<T>(node: Node, checkParentVariableStatement: boolean, getDocs: (docs: JSDoc[]) => T[], getTags: (tags: JSDocTag[]) => T[]): T[] {
// TODO: Get rid of getJsDocComments and friends (note the lowercase 's' in Js)
// TODO: A lot of this work should be cached, maybe. I guess it's only used in services right now...
let result: T[] = undefined;
// prepend documentation from parent sources
if (checkParentVariableStatement) {
// Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
// /**
// * @param {number} name
// * @returns {number}
// */
// var x = function(name) { return name.length; }
const isInitializerOfVariableDeclarationInStatement =
isVariableLike(node.parent) &&
(node.parent).initializer === node &&
node.parent.parent.parent.kind === SyntaxKind.VariableStatement;
const isVariableOfVariableDeclarationStatement = isVariableLike(node) &&
node.parent.parent.kind === SyntaxKind.VariableStatement;
const variableStatementNode =
isInitializerOfVariableDeclarationInStatement ? node.parent.parent.parent :
isVariableOfVariableDeclarationStatement ? node.parent.parent :
undefined;
if (variableStatementNode) {
return variableStatementNode.jsDocComments;
result = append(result, getJSDocs(variableStatementNode, checkParentVariableStatement, getDocs, getTags));
}
if (node.kind === SyntaxKind.ModuleDeclaration &&
node.parent && node.parent.kind === SyntaxKind.ModuleDeclaration) {
result = append(result, getJSDocs(node.parent, checkParentVariableStatement, getDocs, getTags));
}
// Also recognize when the node is the RHS of an assignment expression
@@ -1469,16 +1505,62 @@ namespace ts {
(parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken &&
parent.parent.kind === SyntaxKind.ExpressionStatement;
if (isSourceOfAssignmentExpressionStatement) {
return parent.parent.jsDocComments;
result = append(result, getJSDocs(parent.parent, checkParentVariableStatement, getDocs, getTags));
}
const isPropertyAssignmentExpression = parent && parent.kind === SyntaxKind.PropertyAssignment;
if (isPropertyAssignmentExpression) {
return parent.jsDocComments;
result = append(result, getJSDocs(parent, checkParentVariableStatement, getDocs, getTags));
}
// Pull parameter comments from declaring function as well
if (node.kind === SyntaxKind.Parameter) {
const paramTags = getJSDocParameterTag(node as ParameterDeclaration, checkParentVariableStatement);
if (paramTags) {
result = append(result, getTags(paramTags));
}
}
}
return undefined;
if (isVariableLike(node) && node.initializer) {
result = append(result, getJSDocs(node.initializer, /*checkParentVariableStatement*/ false, getDocs, getTags));
}
if (node.jsDocComments) {
if (result) {
result = append(result, getDocs(node.jsDocComments));
}
else {
return getDocs(node.jsDocComments);
}
}
return result;
}
function getJSDocParameterTag(param: ParameterDeclaration, checkParentVariableStatement: boolean): JSDocTag[] {
const func = param.parent as FunctionLikeDeclaration;
const tags = getJSDocTags(func, checkParentVariableStatement);
if (!param.name) {
// this is an anonymous jsdoc param from a `function(type1, type2): type3` specification
const i = func.parameters.indexOf(param);
const paramTags = filter(tags, tag => tag.kind === SyntaxKind.JSDocParameterTag);
if (paramTags && 0 <= i && i < paramTags.length) {
return [paramTags[i]];
}
}
else if (param.name.kind === SyntaxKind.Identifier) {
const name = (param.name as Identifier).text;
const paramTags = filter(tags, tag => tag.kind === SyntaxKind.JSDocParameterTag && (tag as JSDocParameterTag).parameterName.text === name);
if (paramTags) {
return paramTags;
}
}
else {
// TODO: it's a destructured parameter, so it should look up an "object type" series of multiple lines
// But multi-line object types aren't supported yet either
return undefined;
}
}
export function getJSDocTypeTag(node: Node): JSDocTypeTag {
@@ -1499,17 +1581,15 @@ namespace ts {
// annotation.
const parameterName = (<Identifier>parameter.name).text;
const jsDocComments = getJSDocComments(parameter.parent, /*checkParentVariableStatement*/ true);
if (jsDocComments) {
for (const jsDocComment of jsDocComments) {
for (const tag of jsDocComment.tags) {
if (tag.kind === SyntaxKind.JSDocParameterTag) {
const parameterTag = <JSDocParameterTag>tag;
const name = parameterTag.preParameterName || parameterTag.postParameterName;
if (name.text === parameterName) {
return parameterTag;
}
}
const jsDocTags = getJSDocTags(parameter.parent, /*checkParentVariableStatement*/ true);
if (!jsDocTags) {
return undefined;
}
for (const tag of jsDocTags) {
if (tag.kind === SyntaxKind.JSDocParameterTag) {
const parameterTag = <JSDocParameterTag>tag;
if (parameterTag.parameterName.text === parameterName) {
return parameterTag;
}
}
}

View File

@@ -7,7 +7,7 @@ namespace ts {
function parsesCorrectly(name: string, content: string) {
it(name, () => {
const typeAndDiagnostics = ts.parseJSDocTypeExpressionForTests(content);
assert.isTrue(typeAndDiagnostics && typeAndDiagnostics.diagnostics.length === 0);
assert.isTrue(typeAndDiagnostics && typeAndDiagnostics.diagnostics.length === 0, "no errors issued");
Harness.Baseline.runBaseline("JSDocParsing/TypeExpressions.parsesCorrectly." + name + ".json",
() => Utils.sourceFileToJSON(typeAndDiagnostics.jsDocTypeExpression.type));
@@ -36,6 +36,9 @@ namespace ts {
parsesCorrectly("recordType6", "{{foo, bar: number}}");
parsesCorrectly("recordType7", "{{foo: number, bar: number}}");
parsesCorrectly("recordType8", "{{function}}");
parsesCorrectly("trailingCommaInRecordType", "{{a,}}");
parsesCorrectly("callSignatureInRecordType", "{{(): number}}");
parsesCorrectly("methodInRecordType", "{{foo(): number}}");
parsesCorrectly("unionType", "{(number|string)}");
parsesCorrectly("topLevelNoParenUnionType", "{number|string}");
parsesCorrectly("functionType1", "{function()}");
@@ -63,7 +66,6 @@ namespace ts {
describe("parsesIncorrectly", () => {
parsesIncorrectly("emptyType", "{}");
parsesIncorrectly("trailingCommaInRecordType", "{{a,}}");
parsesIncorrectly("unionTypeWithTrailingBar", "{(a|)}");
parsesIncorrectly("unionTypeWithoutTypes", "{()}");
parsesIncorrectly("nullableTypeWithoutType", "{!}");
@@ -80,8 +82,6 @@ namespace ts {
parsesIncorrectly("tsConstructoType", "{new () => string}");
parsesIncorrectly("typeOfType", "{typeof M}");
parsesIncorrectly("namedParameter", "{function(a: number)}");
parsesIncorrectly("callSignatureInRecordType", "{{(): number}}");
parsesIncorrectly("methodInRecordType", "{{foo(): number}}");
parsesIncorrectly("tupleTypeWithComma", "{[,]}");
parsesIncorrectly("tupleTypeWithTrailingComma", "{[number,]}");
parsesIncorrectly("tupleTypeWithLeadingComma", "{[,number]}");
@@ -100,7 +100,7 @@ namespace ts {
}
Harness.Baseline.runBaseline("JSDocParsing/DocComments.parsesCorrectly." + name + ".json",
() => JSON.stringify(comment.jsDocComment,
() => JSON.stringify(comment.jsDoc,
(k, v) => v && v.pos !== undefined ? JSON.parse(Utils.sourceFileToJSON(v)) : v, 4));
});
}
@@ -115,7 +115,6 @@ namespace ts {
describe("parsesIncorrectly", () => {
parsesIncorrectly("emptyComment", "/***/");
parsesIncorrectly("threeAsterisks", "/*** */");
parsesIncorrectly("asteriskAfterPreamble", "/** * @type {number} */");
parsesIncorrectly("multipleTypes",
`/**
* @type {number}
@@ -167,6 +166,7 @@ namespace ts {
* @type {number}
*/`);
parsesCorrectly("asteriskAfterPreamble", "/** * @type {number} */");
parsesCorrectly("typeTag",
`/**

View File

@@ -698,9 +698,9 @@ namespace ts {
// See if this is a doc comment. If so, we'll classify certain portions of it
// specially.
const docCommentAndDiagnostics = parseIsolatedJSDocComment(sourceFile.text, start, width);
if (docCommentAndDiagnostics && docCommentAndDiagnostics.jsDocComment) {
docCommentAndDiagnostics.jsDocComment.parent = token;
classifyJSDocComment(docCommentAndDiagnostics.jsDocComment);
if (docCommentAndDiagnostics && docCommentAndDiagnostics.jsDoc) {
docCommentAndDiagnostics.jsDoc.parent = token;
classifyJSDocComment(docCommentAndDiagnostics.jsDoc);
return;
}
}
@@ -713,37 +713,39 @@ namespace ts {
pushClassification(start, width, ClassificationType.comment);
}
function classifyJSDocComment(docComment: JSDocComment) {
function classifyJSDocComment(docComment: JSDoc) {
let pos = docComment.pos;
for (const tag of docComment.tags) {
// As we walk through each tag, classify the portion of text from the end of
// the last tag (or the start of the entire doc comment) as 'comment'.
if (tag.pos !== pos) {
pushCommentRange(pos, tag.pos - pos);
if (docComment.tags) {
for (const tag of docComment.tags) {
// As we walk through each tag, classify the portion of text from the end of
// the last tag (or the start of the entire doc comment) as 'comment'.
if (tag.pos !== pos) {
pushCommentRange(pos, tag.pos - pos);
}
pushClassification(tag.atToken.pos, tag.atToken.end - tag.atToken.pos, ClassificationType.punctuation);
pushClassification(tag.tagName.pos, tag.tagName.end - tag.tagName.pos, ClassificationType.docCommentTagName);
pos = tag.tagName.end;
switch (tag.kind) {
case SyntaxKind.JSDocParameterTag:
processJSDocParameterTag(<JSDocParameterTag>tag);
break;
case SyntaxKind.JSDocTemplateTag:
processJSDocTemplateTag(<JSDocTemplateTag>tag);
break;
case SyntaxKind.JSDocTypeTag:
processElement((<JSDocTypeTag>tag).typeExpression);
break;
case SyntaxKind.JSDocReturnTag:
processElement((<JSDocReturnTag>tag).typeExpression);
break;
}
pos = tag.end;
}
pushClassification(tag.atToken.pos, tag.atToken.end - tag.atToken.pos, ClassificationType.punctuation);
pushClassification(tag.tagName.pos, tag.tagName.end - tag.tagName.pos, ClassificationType.docCommentTagName);
pos = tag.tagName.end;
switch (tag.kind) {
case SyntaxKind.JSDocParameterTag:
processJSDocParameterTag(<JSDocParameterTag>tag);
break;
case SyntaxKind.JSDocTemplateTag:
processJSDocTemplateTag(<JSDocTemplateTag>tag);
break;
case SyntaxKind.JSDocTypeTag:
processElement((<JSDocTypeTag>tag).typeExpression);
break;
case SyntaxKind.JSDocReturnTag:
processElement((<JSDocReturnTag>tag).typeExpression);
break;
}
pos = tag.end;
}
if (pos !== docComment.end) {
@@ -982,4 +984,4 @@ namespace ts {
}
}
}
}
}

View File

@@ -44,349 +44,47 @@ namespace ts.JsDoc {
let jsDocCompletionEntries: CompletionEntry[];
export function getJsDocCommentsFromDeclarations(declarations: Declaration[], name: string, canUseParsedParamTagComments: boolean) {
// Only collect doc comments from duplicate declarations once:
// In case of a union property there might be same declaration multiple times
// which only varies in type parameter
// Eg. const a: Array<string> | Array<number>; a.length
// The property length will have two declarations of property length coming
// from Array<T> - Array<string> and Array<number>
const documentationComment = <SymbolDisplayPart[]>[];
const docComments = getJsDocCommentsSeparatedByNewLines();
ts.forEach(docComments, docComment => {
if (documentationComment.length) {
documentationComment.push(lineBreakPart());
forEachUnique(declarations, declaration => {
const comments = getJSDocComments(declaration, /*checkParentVariableStatement*/ true);
if (!comments) {
return;
}
for (const comment of comments) {
if (comment) {
if (documentationComment.length) {
documentationComment.push(lineBreakPart());
}
documentationComment.push(textPart(comment));
}
}
documentationComment.push(docComment);
});
return documentationComment;
}
function getJsDocCommentsSeparatedByNewLines() {
const paramTag = "@param";
const jsDocCommentParts: SymbolDisplayPart[] = [];
ts.forEach(declarations, (declaration, indexOfDeclaration) => {
// Make sure we are collecting doc comment from declaration once,
// In case of union property there might be same declaration multiple times
// which only varies in type parameter
// Eg. const a: Array<string> | Array<number>; a.length
// The property length will have two declarations of property length coming
// from Array<T> - Array<string> and Array<number>
if (indexOf(declarations, declaration) === indexOfDeclaration) {
const sourceFileOfDeclaration = getSourceFileOfNode(declaration);
// 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) {
if ((declaration.parent.kind === SyntaxKind.FunctionExpression || declaration.parent.kind === SyntaxKind.ArrowFunction) &&
declaration.parent.parent.kind === SyntaxKind.VariableDeclaration) {
addCommentParts(declaration.parent.parent.parent, sourceFileOfDeclaration, getCleanedParamJsDocComment);
}
addCommentParts(declaration.parent, sourceFileOfDeclaration, getCleanedParamJsDocComment);
}
// If this is left side of dotted module declaration, there is no doc comments associated with this node
if (declaration.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>declaration).body && (<ModuleDeclaration>declaration).body.kind === SyntaxKind.ModuleDeclaration) {
return;
}
if ((declaration.kind === SyntaxKind.FunctionExpression || declaration.kind === SyntaxKind.ArrowFunction) &&
declaration.parent.kind === SyntaxKind.VariableDeclaration) {
addCommentParts(declaration.parent.parent, sourceFileOfDeclaration, getCleanedJsDocComment);
}
// If this is dotted module name, get the doc comments from the parent
while (declaration.kind === SyntaxKind.ModuleDeclaration && declaration.parent.kind === SyntaxKind.ModuleDeclaration) {
declaration = <ModuleDeclaration>declaration.parent;
}
addCommentParts(declaration.kind === SyntaxKind.VariableDeclaration ? declaration.parent.parent : declaration,
sourceFileOfDeclaration,
getCleanedJsDocComment);
if (declaration.kind === SyntaxKind.VariableDeclaration) {
const init = (declaration as VariableDeclaration).initializer;
if (init && (init.kind === SyntaxKind.FunctionExpression || init.kind === SyntaxKind.ArrowFunction)) {
// Get the cleaned js doc comment text from the initializer
addCommentParts(init, sourceFileOfDeclaration, getCleanedJsDocComment);
}
}
}
});
return jsDocCommentParts;
function addCommentParts(commented: Node,
sourceFileOfDeclaration: SourceFile,
getCommentPart: (pos: number, end: number, file: SourceFile) => SymbolDisplayPart[]): void {
const ranges = getJsDocCommentTextRange(commented, sourceFileOfDeclaration);
// Get the cleaned js doc comment text from the declaration
ts.forEach(ranges, jsDocCommentTextRange => {
const cleanedComment = getCommentPart(jsDocCommentTextRange.pos, jsDocCommentTextRange.end, sourceFileOfDeclaration);
if (cleanedComment) {
addRange(jsDocCommentParts, cleanedComment);
}
});
}
function getJsDocCommentTextRange(node: Node, sourceFile: SourceFile): TextRange[] {
return ts.map(getJsDocComments(node, sourceFile),
jsDocComment => {
return {
pos: jsDocComment.pos + "/*".length, // Consume /* from the comment
end: jsDocComment.end - "*/".length // Trim off comment end indicator
};
});
}
function consumeWhiteSpacesOnTheLine(pos: number, end: number, sourceFile: SourceFile, maxSpacesToRemove?: number) {
if (maxSpacesToRemove !== undefined) {
end = Math.min(end, pos + maxSpacesToRemove);
}
for (; pos < end; pos++) {
const ch = sourceFile.text.charCodeAt(pos);
if (!isWhiteSpaceSingleLine(ch)) {
return pos;
}
}
return end;
}
function consumeLineBreaks(pos: number, end: number, sourceFile: SourceFile) {
while (pos < end && isLineBreak(sourceFile.text.charCodeAt(pos))) {
pos++;
}
return pos;
}
function isName(pos: number, end: number, sourceFile: SourceFile, name: string) {
return pos + name.length < end &&
sourceFile.text.substr(pos, name.length) === name &&
isWhiteSpace(sourceFile.text.charCodeAt(pos + name.length));
}
function isParamTag(pos: number, end: number, sourceFile: SourceFile) {
// If it is @param tag
return isName(pos, end, sourceFile, paramTag);
}
function pushDocCommentLineText(docComments: SymbolDisplayPart[], text: string, blankLineCount: number) {
// Add the empty lines in between texts
while (blankLineCount) {
blankLineCount--;
docComments.push(textPart(""));
}
docComments.push(textPart(text));
}
function getCleanedJsDocComment(pos: number, end: number, sourceFile: SourceFile) {
let spacesToRemoveAfterAsterisk: number;
const docComments: SymbolDisplayPart[] = [];
let blankLineCount = 0;
let isInParamTag = false;
while (pos < end) {
let docCommentTextOfLine = "";
// First consume leading white space
pos = consumeWhiteSpacesOnTheLine(pos, end, sourceFile);
// If the comment starts with '*' consume the spaces on this line
if (pos < end && sourceFile.text.charCodeAt(pos) === CharacterCodes.asterisk) {
const lineStartPos = pos + 1;
pos = consumeWhiteSpacesOnTheLine(pos + 1, end, sourceFile, spacesToRemoveAfterAsterisk);
// Set the spaces to remove after asterisk as margin if not already set
if (spacesToRemoveAfterAsterisk === undefined && pos < end && !isLineBreak(sourceFile.text.charCodeAt(pos))) {
spacesToRemoveAfterAsterisk = pos - lineStartPos;
}
}
else if (spacesToRemoveAfterAsterisk === undefined) {
spacesToRemoveAfterAsterisk = 0;
}
// Analyze text on this line
while (pos < end && !isLineBreak(sourceFile.text.charCodeAt(pos))) {
const ch = sourceFile.text.charAt(pos);
if (ch === "@") {
// If it is @param tag
if (isParamTag(pos, end, sourceFile)) {
isInParamTag = true;
pos += paramTag.length;
continue;
}
else {
isInParamTag = false;
}
}
// Add the ch to doc text if we arent in param tag
if (!isInParamTag) {
docCommentTextOfLine += ch;
}
// Scan next character
pos++;
}
// Continue with next line
pos = consumeLineBreaks(pos, end, sourceFile);
if (docCommentTextOfLine) {
pushDocCommentLineText(docComments, docCommentTextOfLine, blankLineCount);
blankLineCount = 0;
}
else if (!isInParamTag && docComments.length) {
// This is blank line when there is text already parsed
blankLineCount++;
}
}
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 search 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 && isWhiteSpaceSingleLine(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);
}
/**
* 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.
* If no such value is found, the callback is applied to each element of array and undefined is returned.
*/
function forEachUnique<T, U>(array: T[], callback: (element: T, index: number) => U): U {
if (array) {
for (let i = 0, len = array.length; i < len; i++) {
if (indexOf(array, array[i]) === i) {
const result = callback(array[i], i);
if (result) {
return result;
}
}
}
}
return undefined;
}
export function getAllJsDocCompletionEntries(): CompletionEntry[] {

View File

@@ -42,7 +42,7 @@ namespace ts {
public end: number;
public flags: NodeFlags;
public parent: Node;
public jsDocComments: JSDocComment[];
public jsDocComments: JSDoc[];
public original: Node;
public transformFlags: TransformFlags;
public excludeTransformFlags: TransformFlags;
@@ -215,7 +215,7 @@ namespace ts {
public end: number;
public flags: NodeFlags;
public parent: Node;
public jsDocComments: JSDocComment[];
public jsDocComments: JSDoc[];
public __tokenTag: any;
constructor(pos: number, end: number) {

View File

@@ -953,9 +953,11 @@ namespace ts {
if (node) {
if (node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
for (const tag of jsDocComment.tags) {
if (tag.pos <= position && position <= tag.end) {
return tag;
if (jsDocComment.tags) {
for (const tag of jsDocComment.tags) {
if (tag.pos <= position && position <= tag.end) {
return tag;
}
}
}
}
@@ -1353,4 +1355,4 @@ namespace ts {
}
return { configJsonObject, diagnostics };
}
}
}

View File

@@ -0,0 +1,6 @@
{
"kind": "JSDocComment",
"pos": 0,
"end": 23,
"comment": "* @type {number} "
}

View File

@@ -27,7 +27,8 @@
"pos": 15,
"end": 21
}
}
},
"comment": ""
},
"length": 1,
"pos": 8,

View File

@@ -27,7 +27,8 @@
"pos": 15,
"end": 21
}
}
},
"comment": ""
},
"length": 1,
"pos": 8,

View File

@@ -6,7 +6,7 @@
"0": {
"kind": "JSDocReturnTag",
"pos": 8,
"end": 15,
"end": 16,
"atToken": {
"kind": "AtToken",
"pos": 8,
@@ -17,10 +17,11 @@
"pos": 9,
"end": 15,
"text": "return"
}
},
"comment": ""
},
"length": 1,
"pos": 8,
"end": 15
"end": 16
}
}

View File

@@ -6,7 +6,7 @@
"0": {
"kind": "JSDocTypeTag",
"pos": 8,
"end": 13,
"end": 14,
"atToken": {
"kind": "AtToken",
"pos": 8,
@@ -17,10 +17,11 @@
"pos": 9,
"end": 13,
"text": "type"
}
},
"comment": ""
},
"length": 1,
"pos": 8,
"end": 13
"end": 14
}
}

View File

@@ -33,7 +33,14 @@
"pos": 24,
"end": 29,
"text": "name1"
}
},
"parameterName": {
"kind": "Identifier",
"pos": 24,
"end": 29,
"text": "name1"
},
"comment": ""
},
"length": 1,
"pos": 8,

View File

@@ -33,7 +33,14 @@
"pos": 24,
"end": 29,
"text": "name1"
}
},
"parameterName": {
"kind": "Identifier",
"pos": 24,
"end": 29,
"text": "name1"
},
"comment": "Description text follows"
},
"length": 1,
"pos": 8,

View File

@@ -34,7 +34,14 @@
"end": 30,
"text": "name1"
},
"isBracketed": true
"parameterName": {
"kind": "Identifier",
"pos": 25,
"end": 30,
"text": "name1"
},
"isBracketed": true,
"comment": "Description text follows"
},
"length": 1,
"pos": 8,

View File

@@ -34,7 +34,14 @@
"end": 31,
"text": "name1"
},
"isBracketed": true
"parameterName": {
"kind": "Identifier",
"pos": 26,
"end": 31,
"text": "name1"
},
"isBracketed": true,
"comment": "Description text follows"
},
"length": 1,
"pos": 8,

View File

@@ -33,7 +33,14 @@
"pos": 22,
"end": 28
}
}
},
"parameterName": {
"kind": "Identifier",
"pos": 15,
"end": 20,
"text": "name1"
},
"comment": ""
},
"length": 1,
"pos": 8,

View File

@@ -33,7 +33,14 @@
"pos": 22,
"end": 28
}
}
},
"parameterName": {
"kind": "Identifier",
"pos": 15,
"end": 20,
"text": "name1"
},
"comment": "Description"
},
"length": 1,
"pos": 8,

View File

@@ -23,7 +23,14 @@
"pos": 15,
"end": 18,
"text": "foo"
}
},
"parameterName": {
"kind": "Identifier",
"pos": 15,
"end": 18,
"text": "foo"
},
"comment": ""
},
"length": 1,
"pos": 8,

View File

@@ -27,7 +27,8 @@
"pos": 17,
"end": 23
}
}
},
"comment": ""
},
"length": 1,
"pos": 8,

View File

@@ -27,7 +27,8 @@
"pos": 17,
"end": 23
}
}
},
"comment": "Description text follows"
},
"length": 1,
"pos": 8,

View File

@@ -27,7 +27,8 @@
"pos": 18,
"end": 24
}
}
},
"comment": ""
},
"length": 1,
"pos": 8,

View File

@@ -6,7 +6,7 @@
"0": {
"kind": "JSDocTemplateTag",
"pos": 8,
"end": 19,
"end": 20,
"atToken": {
"kind": "AtToken",
"pos": 8,
@@ -22,7 +22,7 @@
"0": {
"kind": "TypeParameter",
"pos": 18,
"end": 19,
"end": 20,
"name": {
"kind": "Identifier",
"pos": 18,
@@ -31,12 +31,13 @@
}
},
"length": 1,
"pos": 17,
"end": 19
}
"pos": 18,
"end": 20
},
"comment": ""
},
"length": 1,
"pos": 8,
"end": 19
"end": 20
}
}

View File

@@ -6,7 +6,7 @@
"0": {
"kind": "JSDocTemplateTag",
"pos": 8,
"end": 21,
"end": 22,
"atToken": {
"kind": "AtToken",
"pos": 8,
@@ -33,7 +33,7 @@
"1": {
"kind": "TypeParameter",
"pos": 20,
"end": 21,
"end": 22,
"name": {
"kind": "Identifier",
"pos": 20,
@@ -42,12 +42,13 @@
}
},
"length": 2,
"pos": 17,
"end": 21
}
"pos": 18,
"end": 22
},
"comment": ""
},
"length": 1,
"pos": 8,
"end": 21
"end": 22
}
}

View File

@@ -6,7 +6,7 @@
"0": {
"kind": "JSDocTemplateTag",
"pos": 8,
"end": 22,
"end": 23,
"atToken": {
"kind": "AtToken",
"pos": 8,
@@ -22,7 +22,7 @@
"0": {
"kind": "TypeParameter",
"pos": 18,
"end": 19,
"end": 20,
"name": {
"kind": "Identifier",
"pos": 18,
@@ -33,7 +33,7 @@
"1": {
"kind": "TypeParameter",
"pos": 21,
"end": 22,
"end": 23,
"name": {
"kind": "Identifier",
"pos": 21,
@@ -42,12 +42,13 @@
}
},
"length": 2,
"pos": 17,
"end": 22
}
"pos": 18,
"end": 23
},
"comment": ""
},
"length": 1,
"pos": 8,
"end": 22
"end": 23
}
}

View File

@@ -6,7 +6,7 @@
"0": {
"kind": "JSDocTemplateTag",
"pos": 8,
"end": 22,
"end": 23,
"atToken": {
"kind": "AtToken",
"pos": 8,
@@ -33,7 +33,7 @@
"1": {
"kind": "TypeParameter",
"pos": 21,
"end": 22,
"end": 23,
"name": {
"kind": "Identifier",
"pos": 21,
@@ -42,12 +42,13 @@
}
},
"length": 2,
"pos": 17,
"end": 22
}
"pos": 18,
"end": 23
},
"comment": ""
},
"length": 1,
"pos": 8,
"end": 22
"end": 23
}
}

View File

@@ -6,7 +6,7 @@
"0": {
"kind": "JSDocTemplateTag",
"pos": 8,
"end": 23,
"end": 24,
"atToken": {
"kind": "AtToken",
"pos": 8,
@@ -22,7 +22,7 @@
"0": {
"kind": "TypeParameter",
"pos": 18,
"end": 19,
"end": 20,
"name": {
"kind": "Identifier",
"pos": 18,
@@ -33,7 +33,7 @@
"1": {
"kind": "TypeParameter",
"pos": 22,
"end": 23,
"end": 24,
"name": {
"kind": "Identifier",
"pos": 22,
@@ -42,12 +42,13 @@
}
},
"length": 2,
"pos": 17,
"end": 23
}
"pos": 18,
"end": 24
},
"comment": ""
},
"length": 1,
"pos": 8,
"end": 23
"end": 24
}
}

View File

@@ -6,7 +6,7 @@
"0": {
"kind": "JSDocTemplateTag",
"pos": 8,
"end": 23,
"end": 24,
"atToken": {
"kind": "AtToken",
"pos": 8,
@@ -22,7 +22,7 @@
"0": {
"kind": "TypeParameter",
"pos": 18,
"end": 19,
"end": 20,
"name": {
"kind": "Identifier",
"pos": 18,
@@ -33,7 +33,7 @@
"1": {
"kind": "TypeParameter",
"pos": 22,
"end": 23,
"end": 24,
"name": {
"kind": "Identifier",
"pos": 22,
@@ -42,12 +42,13 @@
}
},
"length": 2,
"pos": 17,
"end": 23
}
"pos": 18,
"end": 24
},
"comment": "Description of type parameters."
},
"length": 1,
"pos": 8,
"end": 23
"end": 24
}
}

View File

@@ -33,7 +33,14 @@
"pos": 24,
"end": 29,
"text": "name1"
}
},
"parameterName": {
"kind": "Identifier",
"pos": 24,
"end": 29,
"text": "name1"
},
"comment": ""
},
"1": {
"kind": "JSDocParameterTag",
@@ -65,7 +72,14 @@
"pos": 50,
"end": 55,
"text": "name2"
}
},
"parameterName": {
"kind": "Identifier",
"pos": 50,
"end": 55,
"text": "name2"
},
"comment": ""
},
"length": 2,
"pos": 8,

View File

@@ -33,10 +33,56 @@
"pos": 24,
"end": 29,
"text": "name1"
}
},
"parameterName": {
"kind": "Identifier",
"pos": 24,
"end": 29,
"text": "name1"
},
"comment": ""
},
"length": 1,
"1": {
"kind": "JSDocParameterTag",
"pos": 30,
"end": 51,
"atToken": {
"kind": "AtToken",
"pos": 30,
"end": 31
},
"tagName": {
"kind": "Identifier",
"pos": 31,
"end": 36,
"text": "param"
},
"typeExpression": {
"kind": "JSDocTypeExpression",
"pos": 37,
"end": 45,
"type": {
"kind": "NumberKeyword",
"pos": 38,
"end": 44
}
},
"postParameterName": {
"kind": "Identifier",
"pos": 46,
"end": 51,
"text": "name2"
},
"parameterName": {
"kind": "Identifier",
"pos": 46,
"end": 51,
"text": "name2"
},
"comment": ""
},
"length": 2,
"pos": 8,
"end": 29
"end": 51
}
}

View File

@@ -27,7 +27,8 @@
"pos": 15,
"end": 21
}
}
},
"comment": ""
},
"length": 1,
"pos": 8,

View File

@@ -6,7 +6,7 @@
"0": {
"kind": "JSDocTypedefTag",
"pos": 8,
"end": 97,
"end": 98,
"atToken": {
"kind": "AtToken",
"pos": 8,
@@ -26,15 +26,15 @@
},
"jsDocTypeLiteral": {
"kind": "JSDocTypeLiteral",
"pos": 23,
"end": 97,
"pos": 26,
"end": 98,
"jsDocTypeTag": {
"kind": "JSDocTypeTag",
"pos": 27,
"pos": 28,
"end": 42,
"atToken": {
"kind": "AtToken",
"pos": 27,
"pos": 28,
"end": 29
},
"tagName": {
@@ -63,11 +63,11 @@
"jsDocPropertyTags": [
{
"kind": "JSDocPropertyTag",
"pos": 46,
"end": 69,
"pos": 47,
"end": 72,
"atToken": {
"kind": "AtToken",
"pos": 46,
"pos": 47,
"end": 48
},
"tagName": {
@@ -95,11 +95,11 @@
},
{
"kind": "JSDocPropertyTag",
"pos": 73,
"end": 97,
"pos": 74,
"end": 98,
"atToken": {
"kind": "AtToken",
"pos": 73,
"pos": 74,
"end": 75
},
"tagName": {
@@ -126,10 +126,11 @@
}
}
]
}
},
"comment": ""
},
"length": 1,
"pos": 8,
"end": 97
"end": 98
}
}

View File

@@ -0,0 +1,30 @@
{
"kind": "JSDocRecordType",
"pos": 1,
"end": 13,
"literal": {
"kind": "TypeLiteral",
"pos": 1,
"end": 13,
"members": {
"0": {
"kind": "CallSignature",
"pos": 2,
"end": 12,
"parameters": {
"length": 0,
"pos": 3,
"end": 3
},
"type": {
"kind": "NumberKeyword",
"pos": 5,
"end": 12
}
},
"length": 1,
"pos": 2,
"end": 12
}
}
}

View File

@@ -0,0 +1,36 @@
{
"kind": "JSDocRecordType",
"pos": 1,
"end": 16,
"literal": {
"kind": "TypeLiteral",
"pos": 1,
"end": 16,
"members": {
"0": {
"kind": "MethodSignature",
"pos": 2,
"end": 15,
"name": {
"kind": "Identifier",
"pos": 2,
"end": 5,
"text": "foo"
},
"parameters": {
"length": 0,
"pos": 6,
"end": 6
},
"type": {
"kind": "NumberKeyword",
"pos": 8,
"end": 15
}
},
"length": 1,
"pos": 2,
"end": 15
}
}
}

View File

@@ -2,9 +2,14 @@
"kind": "JSDocRecordType",
"pos": 1,
"end": 3,
"members": {
"length": 0,
"pos": 2,
"end": 2
"literal": {
"kind": "TypeLiteral",
"pos": 1,
"end": 3,
"members": {
"length": 0,
"pos": 2,
"end": 2
}
}
}

View File

@@ -2,20 +2,25 @@
"kind": "JSDocRecordType",
"pos": 1,
"end": 6,
"members": {
"0": {
"kind": "JSDocRecordMember",
"pos": 2,
"end": 5,
"name": {
"kind": "Identifier",
"literal": {
"kind": "TypeLiteral",
"pos": 1,
"end": 6,
"members": {
"0": {
"kind": "PropertySignature",
"pos": 2,
"end": 5,
"text": "foo"
}
},
"length": 1,
"pos": 2,
"end": 5
"name": {
"kind": "Identifier",
"pos": 2,
"end": 5,
"text": "foo"
}
},
"length": 1,
"pos": 2,
"end": 5
}
}
}

View File

@@ -2,25 +2,30 @@
"kind": "JSDocRecordType",
"pos": 1,
"end": 14,
"members": {
"0": {
"kind": "JSDocRecordMember",
"pos": 2,
"end": 13,
"name": {
"kind": "Identifier",
"literal": {
"kind": "TypeLiteral",
"pos": 1,
"end": 14,
"members": {
"0": {
"kind": "PropertySignature",
"pos": 2,
"end": 5,
"text": "foo"
"end": 13,
"name": {
"kind": "Identifier",
"pos": 2,
"end": 5,
"text": "foo"
},
"type": {
"kind": "NumberKeyword",
"pos": 6,
"end": 13
}
},
"type": {
"kind": "NumberKeyword",
"pos": 6,
"end": 13
}
},
"length": 1,
"pos": 2,
"end": 13
"length": 1,
"pos": 2,
"end": 13
}
}
}

View File

@@ -2,31 +2,36 @@
"kind": "JSDocRecordType",
"pos": 1,
"end": 11,
"members": {
"0": {
"kind": "JSDocRecordMember",
"pos": 2,
"end": 5,
"name": {
"kind": "Identifier",
"literal": {
"kind": "TypeLiteral",
"pos": 1,
"end": 11,
"members": {
"0": {
"kind": "PropertySignature",
"pos": 2,
"end": 5,
"text": "foo"
}
},
"1": {
"kind": "JSDocRecordMember",
"pos": 6,
"end": 10,
"name": {
"kind": "Identifier",
"end": 6,
"name": {
"kind": "Identifier",
"pos": 2,
"end": 5,
"text": "foo"
}
},
"1": {
"kind": "PropertySignature",
"pos": 6,
"end": 10,
"text": "bar"
}
},
"length": 2,
"pos": 2,
"end": 10
"name": {
"kind": "Identifier",
"pos": 6,
"end": 10,
"text": "bar"
}
},
"length": 2,
"pos": 2,
"end": 10
}
}
}

View File

@@ -2,36 +2,41 @@
"kind": "JSDocRecordType",
"pos": 1,
"end": 19,
"members": {
"0": {
"kind": "JSDocRecordMember",
"pos": 2,
"end": 13,
"name": {
"kind": "Identifier",
"literal": {
"kind": "TypeLiteral",
"pos": 1,
"end": 19,
"members": {
"0": {
"kind": "PropertySignature",
"pos": 2,
"end": 5,
"text": "foo"
"end": 14,
"name": {
"kind": "Identifier",
"pos": 2,
"end": 5,
"text": "foo"
},
"type": {
"kind": "NumberKeyword",
"pos": 6,
"end": 13
}
},
"type": {
"kind": "NumberKeyword",
"pos": 6,
"end": 13
}
},
"1": {
"kind": "JSDocRecordMember",
"pos": 14,
"end": 18,
"name": {
"kind": "Identifier",
"1": {
"kind": "PropertySignature",
"pos": 14,
"end": 18,
"text": "bar"
}
},
"length": 2,
"pos": 2,
"end": 18
"name": {
"kind": "Identifier",
"pos": 14,
"end": 18,
"text": "bar"
}
},
"length": 2,
"pos": 2,
"end": 18
}
}
}

View File

@@ -2,36 +2,41 @@
"kind": "JSDocRecordType",
"pos": 1,
"end": 19,
"members": {
"0": {
"kind": "JSDocRecordMember",
"pos": 2,
"end": 5,
"name": {
"kind": "Identifier",
"literal": {
"kind": "TypeLiteral",
"pos": 1,
"end": 19,
"members": {
"0": {
"kind": "PropertySignature",
"pos": 2,
"end": 5,
"text": "foo"
}
},
"1": {
"kind": "JSDocRecordMember",
"pos": 6,
"end": 18,
"name": {
"kind": "Identifier",
"pos": 6,
"end": 10,
"text": "bar"
"end": 6,
"name": {
"kind": "Identifier",
"pos": 2,
"end": 5,
"text": "foo"
}
},
"type": {
"kind": "NumberKeyword",
"pos": 11,
"end": 18
}
},
"length": 2,
"pos": 2,
"end": 18
"1": {
"kind": "PropertySignature",
"pos": 6,
"end": 18,
"name": {
"kind": "Identifier",
"pos": 6,
"end": 10,
"text": "bar"
},
"type": {
"kind": "NumberKeyword",
"pos": 11,
"end": 18
}
},
"length": 2,
"pos": 2,
"end": 18
}
}
}

View File

@@ -2,41 +2,46 @@
"kind": "JSDocRecordType",
"pos": 1,
"end": 27,
"members": {
"0": {
"kind": "JSDocRecordMember",
"pos": 2,
"end": 13,
"name": {
"kind": "Identifier",
"literal": {
"kind": "TypeLiteral",
"pos": 1,
"end": 27,
"members": {
"0": {
"kind": "PropertySignature",
"pos": 2,
"end": 5,
"text": "foo"
"end": 14,
"name": {
"kind": "Identifier",
"pos": 2,
"end": 5,
"text": "foo"
},
"type": {
"kind": "NumberKeyword",
"pos": 6,
"end": 13
}
},
"type": {
"kind": "NumberKeyword",
"pos": 6,
"end": 13
}
},
"1": {
"kind": "JSDocRecordMember",
"pos": 14,
"end": 26,
"name": {
"kind": "Identifier",
"1": {
"kind": "PropertySignature",
"pos": 14,
"end": 18,
"text": "bar"
"end": 26,
"name": {
"kind": "Identifier",
"pos": 14,
"end": 18,
"text": "bar"
},
"type": {
"kind": "NumberKeyword",
"pos": 19,
"end": 26
}
},
"type": {
"kind": "NumberKeyword",
"pos": 19,
"end": 26
}
},
"length": 2,
"pos": 2,
"end": 26
"length": 2,
"pos": 2,
"end": 26
}
}
}

View File

@@ -2,21 +2,26 @@
"kind": "JSDocRecordType",
"pos": 1,
"end": 11,
"members": {
"0": {
"kind": "JSDocRecordMember",
"pos": 2,
"end": 10,
"name": {
"kind": "Identifier",
"literal": {
"kind": "TypeLiteral",
"pos": 1,
"end": 11,
"members": {
"0": {
"kind": "PropertySignature",
"pos": 2,
"end": 10,
"originalKeywordKind": "FunctionKeyword",
"text": "function"
}
},
"length": 1,
"pos": 2,
"end": 10
"name": {
"kind": "Identifier",
"pos": 2,
"end": 10,
"originalKeywordKind": "FunctionKeyword",
"text": "function"
}
},
"length": 1,
"pos": 2,
"end": 10
}
}
}

View File

@@ -0,0 +1,26 @@
{
"kind": "JSDocRecordType",
"pos": 1,
"end": 5,
"literal": {
"kind": "TypeLiteral",
"pos": 1,
"end": 5,
"members": {
"0": {
"kind": "PropertySignature",
"pos": 2,
"end": 4,
"name": {
"kind": "Identifier",
"pos": 2,
"end": 3,
"text": "a"
}
},
"length": 1,
"pos": 2,
"end": 4
}
}
}

View File

@@ -0,0 +1,16 @@
//// [returns.js]
/**
* @returns {string} This comment is not currently exposed
*/
function f() {
return "";
}
//// [dummy.js]
/**
* @returns {string} This comment is not currently exposed
*/
function f() {
return "";
}

View File

@@ -0,0 +1,10 @@
=== tests/cases/conformance/jsdoc/returns.js ===
/**
* @returns {string} This comment is not currently exposed
*/
function f() {
>f : Symbol(f, Decl(returns.js, 0, 0))
return "";
}

View File

@@ -0,0 +1,11 @@
=== tests/cases/conformance/jsdoc/returns.js ===
/**
* @returns {string} This comment is not currently exposed
*/
function f() {
>f : () => string
return "";
>"" : ""
}

View File

@@ -0,0 +1,9 @@
// @allowJs: true
// @filename: returns.js
// @out: dummy.js
/**
* @returns {string} This comment is not currently exposed
*/
function f() {
return "";
}

View File

@@ -8,11 +8,11 @@
//// public p/*3*/2(/** number to add*/b: number) {
//// return this./*4*/p1 + /*5*/b;
//// }
//// /** getter property*/
//// /** getter property 1*/
//// public get p/*6*/3() {
//// return this./*7*/p/*8q*/2(/*8*/this./*9*/p1);
//// }
//// /** setter property*/
//// /** setter property 1*/
//// public set p/*10*/3(/** this is value*/value: number) {
//// this./*11*/p1 = this./*12*/p/*13q*/2(/*13*/value);
//// }
@@ -22,11 +22,11 @@
//// private p/*15*/p2(/** number to add*/b: number) {
//// return this./*16*/p1 + /*17*/b;
//// }
//// /** getter property*/
//// /** getter property 2*/
//// private get p/*18*/p3() {
//// return this./*19*/p/*20q*/p2(/*20*/this./*21*/pp1);
//// }
//// /** setter property*/
//// /** setter property 2*/
//// private set p/*22*/p3( /** this is value*/value: number) {
//// this./*23*/pp1 = this./*24*/p/*25q*/p2(/*25*/value);
//// }
@@ -43,7 +43,7 @@
//// static get s/*32*/3() {
//// return /*33*/c1./*34*/s/*35q*/2(/*35*/c1./*36*/s1);
//// }
//// /** setter property*/
//// /** setter property 3*/
//// static set s/*37*/3( /** this is value*/value: number) {
//// /*38*/c1./*39*/s1 = /*40*/c1./*41*/s/*42q*/2(/*42*/value);
//// }
@@ -141,10 +141,10 @@ verify.quickInfos({
goTo.marker('4');
verify.memberListContains("p1", "(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p2", "(method) c1.p2(b: number): number", "sum with property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property\nsetter property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property 1\nsetter property 1");
verify.memberListContains("pp1", "(property) c1.pp1: number", "pp1 is property of c1");
verify.memberListContains("pp2", "(method) c1.pp2(b: number): number", "sum with property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property\nsetter property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "");
verify.memberListContains("nc_p2", "(method) c1.nc_p2(b: number): number", "");
verify.memberListContains("nc_p3", "(property) c1.nc_p3: number", "");
@@ -155,15 +155,15 @@ verify.memberListContains("nc_pp3", "(property) c1.nc_pp3: number", "");
goTo.marker('5');
verify.completionListContains("b", "(parameter) b: number", "number to add");
verify.quickInfoAt("6", "(property) c1.p3: number", "getter property\nsetter property");
verify.quickInfoAt("6", "(property) c1.p3: number", "getter property 1\nsetter property 1");
goTo.marker('7');
verify.memberListContains("p1", "(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p2", "(method) c1.p2(b: number): number", "sum with property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property\nsetter property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property 1\nsetter property 1");
verify.memberListContains("pp1", "(property) c1.pp1: number", "pp1 is property of c1");
verify.memberListContains("pp2", "(method) c1.pp2(b: number): number", "sum with property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property\nsetter property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "");
verify.memberListContains("nc_p2", "(method) c1.nc_p2(b: number): number", "");
verify.memberListContains("nc_p3", "(property) c1.nc_p3: number", "");
@@ -179,10 +179,10 @@ verify.quickInfoAt("8q", "(method) c1.p2(b: number): number", "sum with property
goTo.marker('9');
verify.memberListContains("p1", "(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p2", "(method) c1.p2(b: number): number", "sum with property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property\nsetter property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property 1\nsetter property 1");
verify.memberListContains("pp1", "(property) c1.pp1: number", "pp1 is property of c1");
verify.memberListContains("pp2", "(method) c1.pp2(b: number): number", "sum with property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property\nsetter property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "");
verify.memberListContains("nc_p2", "(method) c1.nc_p2(b: number): number", "");
verify.memberListContains("nc_p3", "(property) c1.nc_p3: number", "");
@@ -190,15 +190,15 @@ verify.memberListContains("nc_pp1", "(property) c1.nc_pp1: number", "");
verify.memberListContains("nc_pp2", "(method) c1.nc_pp2(b: number): number", "");
verify.memberListContains("nc_pp3", "(property) c1.nc_pp3: number", "");
verify.quickInfoAt("10", "(property) c1.p3: number", "getter property\nsetter property");
verify.quickInfoAt("10", "(property) c1.p3: number", "getter property 1\nsetter property 1");
goTo.marker('11');
verify.memberListContains("p1", "(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p2", "(method) c1.p2(b: number): number", "sum with property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property\nsetter property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property 1\nsetter property 1");
verify.memberListContains("pp1", "(property) c1.pp1: number", "pp1 is property of c1");
verify.memberListContains("pp2", "(method) c1.pp2(b: number): number", "sum with property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property\nsetter property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "");
verify.memberListContains("nc_p2", "(method) c1.nc_p2(b: number): number", "");
verify.memberListContains("nc_p3", "(property) c1.nc_p3: number", "");
@@ -209,10 +209,10 @@ verify.memberListContains("nc_pp3", "(property) c1.nc_pp3: number", "");
goTo.marker('12');
verify.memberListContains("p1", "(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p2", "(method) c1.p2(b: number): number", "sum with property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property\nsetter property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property 1\nsetter property 1");
verify.memberListContains("pp1", "(property) c1.pp1: number", "pp1 is property of c1");
verify.memberListContains("pp2", "(method) c1.pp2(b: number): number", "sum with property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property\nsetter property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "");
verify.memberListContains("nc_p2", "(method) c1.nc_p2(b: number): number", "");
verify.memberListContains("nc_p3", "(property) c1.nc_p3: number", "");
@@ -234,10 +234,10 @@ verify.quickInfos({
goTo.marker('16');
verify.memberListContains("p1", "(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p2", "(method) c1.p2(b: number): number", "sum with property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property\nsetter property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property 1\nsetter property 1");
verify.memberListContains("pp1", "(property) c1.pp1: number", "pp1 is property of c1");
verify.memberListContains("pp2", "(method) c1.pp2(b: number): number", "sum with property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property\nsetter property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "");
verify.memberListContains("nc_p2", "(method) c1.nc_p2(b: number): number", "");
verify.memberListContains("nc_p3", "(property) c1.nc_p3: number", "");
@@ -248,15 +248,15 @@ verify.memberListContains("nc_pp3", "(property) c1.nc_pp3: number", "");
goTo.marker('17');
verify.completionListContains("b", "(parameter) b: number", "number to add");
verify.quickInfoAt("18", "(property) c1.pp3: number", "getter property\nsetter property");
verify.quickInfoAt("18", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
goTo.marker('19');
verify.memberListContains("p1", "(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p2", "(method) c1.p2(b: number): number", "sum with property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property\nsetter property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property 1\nsetter property 1");
verify.memberListContains("pp1", "(property) c1.pp1: number", "pp1 is property of c1");
verify.memberListContains("pp2", "(method) c1.pp2(b: number): number", "sum with property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property\nsetter property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "");
verify.memberListContains("nc_p2", "(method) c1.nc_p2(b: number): number", "");
verify.memberListContains("nc_p3", "(property) c1.nc_p3: number", "");
@@ -272,10 +272,10 @@ verify.quickInfoAt("20q", "(method) c1.pp2(b: number): number", "sum with proper
goTo.marker('21');
verify.memberListContains("p1", "(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p2", "(method) c1.p2(b: number): number", "sum with property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property\nsetter property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property 1\nsetter property 1");
verify.memberListContains("pp1", "(property) c1.pp1: number", "pp1 is property of c1");
verify.memberListContains("pp2", "(method) c1.pp2(b: number): number", "sum with property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property\nsetter property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "");
verify.memberListContains("nc_p2", "(method) c1.nc_p2(b: number): number", "");
verify.memberListContains("nc_p3", "(property) c1.nc_p3: number", "");
@@ -283,15 +283,15 @@ verify.memberListContains("nc_pp1", "(property) c1.nc_pp1: number", "");
verify.memberListContains("nc_pp2", "(method) c1.nc_pp2(b: number): number", "");
verify.memberListContains("nc_pp3", "(property) c1.nc_pp3: number", "");
verify.quickInfoAt("22", "(property) c1.pp3: number", "getter property\nsetter property");
verify.quickInfoAt("22", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
goTo.marker('23');
verify.memberListContains("p1", "(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p2", "(method) c1.p2(b: number): number", "sum with property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property\nsetter property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property 1\nsetter property 1");
verify.memberListContains("pp1", "(property) c1.pp1: number", "pp1 is property of c1");
verify.memberListContains("pp2", "(method) c1.pp2(b: number): number", "sum with property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property\nsetter property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "");
verify.memberListContains("nc_p2", "(method) c1.nc_p2(b: number): number", "");
verify.memberListContains("nc_p3", "(property) c1.nc_p3: number", "");
@@ -302,10 +302,10 @@ verify.memberListContains("nc_pp3", "(property) c1.nc_pp3: number", "");
goTo.marker('24');
verify.memberListContains("p1", "(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p2", "(method) c1.p2(b: number): number", "sum with property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property\nsetter property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property 1\nsetter property 1");
verify.memberListContains("pp1", "(property) c1.pp1: number", "pp1 is property of c1");
verify.memberListContains("pp2", "(method) c1.pp2(b: number): number", "sum with property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property\nsetter property");
verify.memberListContains("pp3", "(property) c1.pp3: number", "getter property 2\nsetter property 2");
verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "");
verify.memberListContains("nc_p2", "(method) c1.nc_p2(b: number): number", "");
verify.memberListContains("nc_p3", "(property) c1.nc_p3: number", "");
@@ -331,7 +331,7 @@ verify.completionListContains("c1", "class c1", "This is comment for c1");
goTo.marker('30');
verify.memberListContains("s1", "(property) c1.s1: number", "s1 is static property of c1");
verify.memberListContains("s2", "(method) c1.s2(b: number): number", "static sum with property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property 3");
verify.memberListContains("nc_s1", "(property) c1.nc_s1: number", "");
verify.memberListContains("nc_s2", "(method) c1.nc_s2(b: number): number", "");
verify.memberListContains("nc_s3", "(property) c1.nc_s3: number", "");
@@ -339,7 +339,7 @@ verify.memberListContains("nc_s3", "(property) c1.nc_s3: number", "");
goTo.marker('31');
verify.completionListContains("b", "(parameter) b: number", "number to add");
verify.quickInfoAt("32", "(property) c1.s3: number", "static getter property\nsetter property");
verify.quickInfoAt("32", "(property) c1.s3: number", "static getter property\nsetter property 3");
goTo.marker('33');
verify.completionListContains("c1", "class c1", "This is comment for c1");
@@ -347,7 +347,7 @@ verify.completionListContains("c1", "class c1", "This is comment for c1");
goTo.marker('34');
verify.memberListContains("s1", "(property) c1.s1: number", "s1 is static property of c1");
verify.memberListContains("s2", "(method) c1.s2(b: number): number", "static sum with property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property 3");
verify.memberListContains("nc_s1", "(property) c1.nc_s1: number", "");
verify.memberListContains("nc_s2", "(method) c1.nc_s2(b: number): number", "");
verify.memberListContains("nc_s3", "(property) c1.nc_s3: number", "");
@@ -361,12 +361,12 @@ verify.quickInfoAt("35q", "(method) c1.s2(b: number): number", "static sum with
goTo.marker('36');
verify.memberListContains("s1", "(property) c1.s1: number", "s1 is static property of c1");
verify.memberListContains("s2", "(method) c1.s2(b: number): number", "static sum with property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property 3");
verify.memberListContains("nc_s1", "(property) c1.nc_s1: number", "");
verify.memberListContains("nc_s2", "(method) c1.nc_s2(b: number): number", "");
verify.memberListContains("nc_s3", "(property) c1.nc_s3: number", "");
verify.quickInfoAt("37", "(property) c1.s3: number", "static getter property\nsetter property");
verify.quickInfoAt("37", "(property) c1.s3: number", "static getter property\nsetter property 3");
goTo.marker('38');
verify.completionListContains("c1", "class c1", "This is comment for c1");
@@ -374,7 +374,7 @@ verify.completionListContains("c1", "class c1", "This is comment for c1");
goTo.marker('39');
verify.memberListContains("s1", "(property) c1.s1: number", "s1 is static property of c1");
verify.memberListContains("s2", "(method) c1.s2(b: number): number", "static sum with property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property 3");
verify.memberListContains("nc_s1", "(property) c1.nc_s1: number", "");
verify.memberListContains("nc_s2", "(method) c1.nc_s2(b: number): number", "");
verify.memberListContains("nc_s3", "(property) c1.nc_s3: number", "");
@@ -385,7 +385,7 @@ verify.completionListContains("c1", "class c1", "This is comment for c1");
goTo.marker('41');
verify.memberListContains("s1", "(property) c1.s1: number", "s1 is static property of c1");
verify.memberListContains("s2", "(method) c1.s2(b: number): number", "static sum with property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property 3");
verify.memberListContains("nc_s1", "(property) c1.nc_s1: number", "");
verify.memberListContains("nc_s2", "(method) c1.nc_s2(b: number): number", "");
verify.memberListContains("nc_s3", "(property) c1.nc_s3: number", "");
@@ -479,7 +479,7 @@ goTo.marker("67");
verify.quickInfoIs("(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p1", "(property) c1.p1: number", "p1 is property of c1");
verify.memberListContains("p2", "(method) c1.p2(b: number): number", "sum with property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property\nsetter property");
verify.memberListContains("p3", "(property) c1.p3: number", "getter property 1\nsetter property 1");
verify.memberListContains("nc_p1", "(property) c1.nc_p1: number", "");
verify.memberListContains("nc_p2", "(method) c1.nc_p2(b: number): number", "");
verify.memberListContains("nc_p3", "(property) c1.nc_p3: number", "");
@@ -497,8 +497,8 @@ verify.currentParameterHelpArgumentDocCommentIs("number to add");
verify.quickInfos({
"71q": ["(method) c1.p2(b: number): number", "sum with property"],
72: "var i1_prop: number",
73: ["(property) c1.p3: number", "getter property\nsetter property"],
74: ["(property) c1.p3: number", "getter property\nsetter property"],
73: ["(property) c1.p3: number", "getter property 1\nsetter property 1"],
74: ["(property) c1.p3: number", "getter property 1\nsetter property 1"],
75: "var i1_prop: number",
76: "var i1_nc_p: number",
77: "(property) c1.nc_p1: number",
@@ -528,7 +528,7 @@ goTo.marker('88');
verify.quickInfoIs("(property) c1.s1: number", "s1 is static property of c1");
verify.memberListContains("s1", "(property) c1.s1: number", "s1 is static property of c1");
verify.memberListContains("s2", "(method) c1.s2(b: number): number", "static sum with property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property");
verify.memberListContains("s3", "(property) c1.s3: number", "static getter property\nsetter property 3");
verify.memberListContains("nc_s1", "(property) c1.nc_s1: number", "");
verify.memberListContains("nc_s2", "(method) c1.nc_s2(b: number): number", "");
verify.memberListContains("nc_s3", "(property) c1.nc_s3: number", "");
@@ -546,8 +546,8 @@ verify.currentParameterHelpArgumentDocCommentIs("number to add");
verify.quickInfos({
"92q": ["(method) c1.s2(b: number): number", "static sum with property"],
93: "var i1_s_prop: number",
94: ["(property) c1.s3: number", "static getter property\nsetter property"],
95: ["(property) c1.s3: number", "static getter property\nsetter property"],
94: ["(property) c1.s3: number", "static getter property\nsetter property 3"],
95: ["(property) c1.s3: number", "static getter property\nsetter property 3"],
96: "var i1_s_prop: number",
97: "var i1_s_nc_p: number",
98: "(property) c1.nc_s1: number",
@@ -605,8 +605,8 @@ verify.quickInfos({
});
goTo.marker('114');
verify.memberListContains("a", "(property) cWithConstructorProperty.a: number", "more info about a");
verify.quickInfoIs("(property) cWithConstructorProperty.a: number", "more info about a");
verify.memberListContains("a", "(property) cWithConstructorProperty.a: number", "this is first parameter a\nmore info about a");
verify.quickInfoIs("(property) cWithConstructorProperty.a: number", "this is first parameter a\nmore info about a");
goTo.marker('115');
verify.completionListContains("a", "(parameter) a: number", "this is first parameter a\nmore info about a");

View File

@@ -48,7 +48,7 @@
////}
////jsDocMi/*7q*/xedComments2(/*7*/);
////
/////** jsdoc comment */ /*** another jsDocComment*/
/////** jsdoc comment */ /*** malformed jsDocComment*/
/////// Triple slash comment
////function jsDocMixedComments3() {
////}
@@ -231,8 +231,8 @@ verify.currentSignatureHelpDocCommentIs("jsdoc comment \nanother jsDocComment");
verify.quickInfoAt("7q", "function jsDocMixedComments2(): void", "jsdoc comment \nanother jsDocComment");
goTo.marker('8');
verify.currentSignatureHelpDocCommentIs("jsdoc comment \n* another jsDocComment");
verify.quickInfoAt("8q", "function jsDocMixedComments3(): void", "jsdoc comment \n* another jsDocComment");
verify.currentSignatureHelpDocCommentIs("jsdoc comment ");
verify.quickInfoAt("8q", "function jsDocMixedComments3(): void", "jsdoc comment ");
goTo.marker('9');
verify.currentSignatureHelpDocCommentIs("jsdoc comment \nanother jsDocComment");
@@ -280,33 +280,33 @@ verify.completionListContains("a", "(parameter) a: number", "first number");
verify.completionListContains("b", "(parameter) b: number", "second number");
goTo.marker('19');
verify.currentSignatureHelpDocCommentIs("This is multiplication function\n@anotherTag\n@anotherTag");
verify.currentSignatureHelpDocCommentIs("This is multiplication function");
verify.currentParameterHelpArgumentDocCommentIs("first number");
verify.quickInfos({
"19q": [
"function multiply(a: number, b: number, c?: number, d?: any, e?: any): void",
"This is multiplication function\n@anotherTag\n@anotherTag"
"This is multiplication function"
],
"19aq": ["(parameter) a: number", "first number"]
});
goTo.marker('20');
verify.currentSignatureHelpDocCommentIs("This is multiplication function\n@anotherTag\n@anotherTag");
verify.currentSignatureHelpDocCommentIs("This is multiplication function");
verify.currentParameterHelpArgumentDocCommentIs("");
verify.quickInfoAt("20aq", "(parameter) b: number");
goTo.marker('21');
verify.currentSignatureHelpDocCommentIs("This is multiplication function\n@anotherTag\n@anotherTag");
verify.currentParameterHelpArgumentDocCommentIs("{");
verify.quickInfoAt("21aq", "(parameter) c: number", "{");
verify.currentSignatureHelpDocCommentIs("This is multiplication function");
verify.currentParameterHelpArgumentDocCommentIs("");
verify.quickInfoAt("21aq", "(parameter) c: number");
goTo.marker('22');
verify.currentSignatureHelpDocCommentIs("This is multiplication function\n@anotherTag\n@anotherTag");
verify.currentSignatureHelpDocCommentIs("This is multiplication function");
verify.currentParameterHelpArgumentDocCommentIs("");
verify.quickInfoAt("22aq", "(parameter) d: any");
goTo.marker('23');
verify.currentSignatureHelpDocCommentIs("This is multiplication function\n@anotherTag\n@anotherTag");
verify.currentSignatureHelpDocCommentIs("This is multiplication function");
verify.currentParameterHelpArgumentDocCommentIs("LastParam ");
verify.quickInfoAt("23aq", "(parameter) e: any", "LastParam ");
@@ -331,52 +331,52 @@ verify.quickInfos({
});
goTo.marker('27');
verify.completionListContains("multiply", "function multiply(a: number, b: number, c?: number, d?: any, e?: any): void", "This is multiplication function\n@anotherTag\n@anotherTag");
verify.completionListContains("multiply", "function multiply(a: number, b: number, c?: number, d?: any, e?: any): void", "This is multiplication function");
verify.completionListContains("f1", "function f1(a: number): any (+1 overload)", "fn f1 with number");
goTo.marker('28');
verify.currentSignatureHelpDocCommentIs("This is subtract function");
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
verify.currentParameterHelpArgumentDocCommentIs("");
verify.quickInfos({
"28q": [
"function subtract(a: number, b: number, c?: () => string, d?: () => string, e?: () => string, f?: () => string): void",
"This is subtract function"
"This is subtract function{ () => string; } } f this is optional param f"
],
"28aq": "(parameter) a: number"
});
goTo.marker('29');
verify.currentSignatureHelpDocCommentIs("This is subtract function");
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
verify.currentParameterHelpArgumentDocCommentIs("this is about b");
verify.quickInfoAt("29aq", "(parameter) b: number", "this is about b");
goTo.marker('30');
verify.currentSignatureHelpDocCommentIs("This is subtract function");
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
verify.currentParameterHelpArgumentDocCommentIs("this is optional param c");
verify.quickInfoAt("30aq", "(parameter) c: () => string", "this is optional param c");
goTo.marker('31');
verify.currentSignatureHelpDocCommentIs("This is subtract function");
verify.currentParameterHelpArgumentDocCommentIs("");
verify.quickInfoAt("31aq", "(parameter) d: () => string");
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
verify.currentParameterHelpArgumentDocCommentIs("this is optional param d");
verify.quickInfoAt("31aq", "(parameter) d: () => string", "this is optional param d");
goTo.marker('32');
verify.currentSignatureHelpDocCommentIs("This is subtract function");
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
verify.currentParameterHelpArgumentDocCommentIs("this is optional param e");
verify.quickInfoAt("32aq", "(parameter) e: () => string", "this is optional param e");
goTo.marker('33');
verify.currentSignatureHelpDocCommentIs("This is subtract function");
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
verify.currentParameterHelpArgumentDocCommentIs("");
verify.quickInfoAt("33aq", "(parameter) f: () => string");
goTo.marker('34');
verify.currentSignatureHelpDocCommentIs("this is square function\n@paramTag { number } a this is input number of paramTag\n@returnType { number } it is return type");
verify.currentSignatureHelpDocCommentIs("this is square function");
verify.currentParameterHelpArgumentDocCommentIs("this is input number");
verify.quickInfos({
"34q": [
"function square(a: number): number",
"this is square function\n@paramTag { number } a this is input number of paramTag\n@returnType { number } it is return type"
"this is square function"
],
"34aq": [
"(parameter) a: number",
@@ -385,12 +385,12 @@ verify.quickInfos({
});
goTo.marker('35');
verify.currentSignatureHelpDocCommentIs("this is divide function\n@paramTag { number } g this is optional param g");
verify.currentSignatureHelpDocCommentIs("this is divide function");
verify.currentParameterHelpArgumentDocCommentIs("this is a");
verify.quickInfos({
"35q": [
"function divide(a: number, b: number): void",
"this is divide function\n@paramTag { number } g this is optional param g"
"this is divide function"
],
"35aq": [
"(parameter) a: number",
@@ -399,7 +399,7 @@ verify.quickInfos({
});
goTo.marker('36');
verify.currentSignatureHelpDocCommentIs("this is divide function\n@paramTag { number } g this is optional param g");
verify.currentSignatureHelpDocCommentIs("this is divide function");
verify.currentParameterHelpArgumentDocCommentIs("this is b");
verify.quickInfoAt("36aq", "(parameter) b: number", "this is b");

View File

@@ -60,23 +60,23 @@ verify.currentParameterHelpArgumentDocCommentIs("param b");
verify.quickInfos({
7: "function anotherFunc(a: number): string",
8: ["(local var) lambdaVar: (b: string) => string", "documentation\ninner docs "],
9: ["(parameter) b: string", "{string} inner parameter "],
9: ["(parameter) b: string", "inner parameter "],
10: "(local var) localVar: string",
11: "(local var) localVar: string",
12: ["(parameter) b: string", "{string} inner parameter "],
12: ["(parameter) b: string", "inner parameter "],
13: ["(local var) lambdaVar: (b: string) => string", "documentation\ninner docs "],
14: [
"var assigned: (s: string) => number",
"On variable\n@returns the parameter's length\nSummary on expression\n@returns return on expression"
"On variable\nSummary on expression"
]
});
goTo.marker('15');
verify.completionListContains('s', '(parameter) s: string', "the first parameter!\nparam on expression\nOn parameter ");
verify.quickInfoAt("16", "var assigned: (s: string) => number", "On variable\n@returns the parameter's length\nSummary on expression\n@returns return on expression");
verify.quickInfoAt("16", "var assigned: (s: string) => number", "On variable\nSummary on expression");
goTo.marker('17');
verify.completionListContains("assigned", "var assigned: (s: string) => number", "On variable\n@returns the parameter's length\nSummary on expression\n@returns return on expression");
verify.completionListContains("assigned", "var assigned: (s: string) => number", "On variable\nSummary on expression");
goTo.marker('18');
verify.currentSignatureHelpDocCommentIs("On variable\n@returns the parameter's length\nSummary on expression\n@returns return on expression");
verify.currentSignatureHelpDocCommentIs("On variable\nSummary on expression");
verify.currentParameterHelpArgumentDocCommentIs("the first parameter!\nparam on expression\nOn parameter ");

View File

@@ -111,19 +111,19 @@ verify.quickInfos({
b: ["var b: string", "This is firstLine\nThis is second Line\n\nThis is fourth Line"],
c: ["var c: string", "This is firstLine\nThis is second Line\n\nThis is fourth Line"],
d: ["function d(param: string): void", "This is firstLine\nThis is second Line\n@random tag This should be third line"],
d: ["function d(param: string): void", "This is firstLine\nThis is second Line"],
1: "(parameter) param: string",
e: ["function e(param: string): void", "This is firstLine\nThis is second Line"],
2: "(parameter) param: string",
f: ["function f(param1: string): void", "This is firstLine\nThis is second Line\n@random tag This should be third line"],
f: ["function f(param1: string): void", "This is firstLine\nThis is second Line"],
3: ["(parameter) param1: string", "first line of param\n\nparam information third line"],
g: ["function g(param1: string): void", "This is firstLine\nThis is second Line\n@random tag This should be third line"],
g: ["function g(param1: string): void", "This is firstLine\nThis is second Line"],
4: ["(parameter) param1: string", "param information first line"],
h: ["function h(param1: string): void", "This is firstLine\nThis is second Line\n@random tag This should be third line"],
h: ["function h(param1: string): void", "This is firstLine\nThis is second Line"],
5: ["(parameter) param1: string", "param information first line\n\nparam information third line"],
i: ["function i(param1: string): void", "This is firstLine\nThis is second Line"],
@@ -132,7 +132,7 @@ verify.quickInfos({
j: ["function j(param1: string): void", "This is firstLine\nThis is second Line"],
7: ["(parameter) param1: string", "param information first line\n\nparam information third line"],
k: ["function k(param1: string): void", "This is firstLine\nThis is second Line\n@randomtag \n\n random information first line\n\n random information third line"],
k: ["function k(param1: string): void", "This is firstLine\nThis is second Line"],
8: ["(parameter) param1: string", "hello "],
l: ["function l(param1: string): void", "This is firstLine\nThis is second Line"],

View File

@@ -126,8 +126,8 @@ verify.memberListContains("c", "constructor m1.m2.c(): m1.m2.c", "");
verify.memberListContains("i", "var m1.m2.i: m1.m2.c", "i");
goTo.marker('9');
verify.completionListContains("m2", "namespace m2", "");
verify.quickInfoIs("namespace m2");
verify.completionListContains("m2", "namespace m2", "namespace comment of m2.m3");
verify.quickInfoIs("namespace m2", "namespace comment of m2.m3");
goTo.marker('10');
verify.memberListContains("m3", "namespace m2.m3");
@@ -138,12 +138,12 @@ verify.quickInfoIs("constructor m2.m3.c(): m2.m3.c");
verify.memberListContains("c", "constructor m2.m3.c(): m2.m3.c", "");
goTo.marker('12');
verify.completionListContains("m3", "namespace m3", "");
verify.quickInfoIs("namespace m3");
verify.completionListContains("m3", "namespace m3", "namespace comment of m3.m4.m5");
verify.quickInfoIs("namespace m3", "namespace comment of m3.m4.m5");
goTo.marker('13');
verify.memberListContains("m4", "namespace m3.m4", "");
verify.quickInfoIs("namespace m3.m4");
verify.memberListContains("m4", "namespace m3.m4", "namespace comment of m3.m4.m5");
verify.quickInfoIs("namespace m3.m4", "namespace comment of m3.m4.m5");
goTo.marker('14');
verify.memberListContains("m5", "namespace m3.m4.m5");
@@ -154,12 +154,12 @@ verify.quickInfoIs("constructor m3.m4.m5.c(): m3.m4.m5.c");
verify.memberListContains("c", "constructor m3.m4.m5.c(): m3.m4.m5.c", "");
goTo.marker('16');
verify.completionListContains("m4", "namespace m4", "");
verify.quickInfoIs("namespace m4");
verify.completionListContains("m4", "namespace m4", "namespace comment of m4.m5.m6");
verify.quickInfoIs("namespace m4", "namespace comment of m4.m5.m6");
goTo.marker('17');
verify.memberListContains("m5", "namespace m4.m5", "");
verify.quickInfoIs("namespace m4.m5");
verify.memberListContains("m5", "namespace m4.m5", "namespace comment of m4.m5.m6");
verify.quickInfoIs("namespace m4.m5", "namespace comment of m4.m5.m6");
goTo.marker('18');
verify.memberListContains("m6", "namespace m4.m5.m6");
@@ -175,11 +175,11 @@ verify.quickInfoIs("constructor m4.m5.m6.m7.c(): m4.m5.m6.m7.c");
goTo.marker('21');
verify.completionListContains("m5", "namespace m5");
verify.quickInfoIs("namespace m5");
verify.quickInfoIs("namespace m5", "namespace comment of m5.m6.m7");
goTo.marker('22');
verify.memberListContains("m6", "namespace m5.m6");
verify.quickInfoIs("namespace m5.m6");
verify.quickInfoIs("namespace m5.m6", "namespace comment of m5.m6.m7");
goTo.marker('23');
verify.memberListContains("m7", "namespace m5.m6.m7");

View File

@@ -5,4 +5,4 @@
/////** @type {function(new:string,number)} */
////var /**/v;
verify.quickInfoAt("", "var v: new (p1: number) => string", "@type {function(new:string,number)} ");
verify.quickInfoAt("", "var v: new (arg1: number) => string", undefined);

View File

@@ -17,4 +17,4 @@
//// x - /**/a1()
verify.quickInfoAt("", "function a1(p: any): number",
"This is a very cool function that is very nice.\n@returns something");
"This is a very cool function that is very nice.");

View File

@@ -0,0 +1,15 @@
///<reference path="fourslash.ts" />
// @allowJs: true
// @Filename: Foo.js
/////**
//// * Do some foo things
//// * @template T A Foolish template
//// * @param {T} x a parameter
//// */
////function foo(x) {
////}
////
////fo/**/o()
goTo.marker();
verify.quickInfoIs("function foo<T>(x: T): void", "Do some foo things");

View File

@@ -0,0 +1,9 @@
///<reference path="fourslash.ts" />
// @allowJs: true
// @Filename: Foo.js
/////**
//// * @type {{ [name: string]: string; }} variables
//// */
////const vari/**/ables = {};
goTo.marker();
verify.quickInfoIs("const variables: {\n [name: string]: string;\n}");

View File

@@ -0,0 +1,13 @@
///<reference path="fourslash.ts" />
// @allowJs: true
// @Filename: Foo.js
/////**
//// * @param {{ stringProp: string,
//// * numProp: number }} o
//// */
////function f1(o) {
//// o/**/;
////}
goTo.marker();
verify.quickInfoIs("(parameter) o: any");

View File

@@ -9,4 +9,4 @@
//// f6('', /**/false)
goTo.marker();
verify.currentSignatureHelpIs('f6(p0: string, p1?: boolean): number')
verify.currentSignatureHelpIs('f6(arg0: string, arg1?: boolean): number')

View File

@@ -0,0 +1,18 @@
///<reference path="fourslash.ts" />
// @allowJs: true
// @Filename: Foo.js
/////**
//// * Filters a path based on a regexp or glob pattern.
//// * @param {String} basePath The base path where the search will be performed.
//// * @param {String} pattern A string defining a regexp of a glob pattern.
//// * @param {String} type The search pattern type, can be a regexp or a glob.
//// * @param {Object} options A object containing options to the search.
//// * @return {Array} A list containing the filtered paths.
//// */
////function pathFilter(basePath, pattern, type, options){
//////...
////}
////pathFilter(/**/'foo', 'bar', 'baz', {});
goTo.marker();
verify.currentSignatureHelpDocCommentIs("Filters a path based on a regexp or glob pattern.");

View File

@@ -0,0 +1,19 @@
///<reference path="fourslash.ts" />
// @allowJs: true
// @Filename: Foo.js
/////**
//// * @param {string} p1 - A string param
//// * @param {string?} p2 - An optional param
//// * @param {string} [p3] - Another optional param
//// * @param {string} [p4="test"] - An optional param with a default value
//// */
////function f1(p1, p2, p3, p4){}
////f1(/*1*/'foo', /*2*/'bar', /*3*/'baz', /*4*/'qux');
goTo.marker('1');
verify.currentParameterHelpArgumentDocCommentIs("- A string param");
goTo.marker('2');
verify.currentParameterHelpArgumentDocCommentIs("- An optional param ");
goTo.marker('3');
verify.currentParameterHelpArgumentDocCommentIs("- Another optional param");
goTo.marker('4');
verify.currentParameterHelpArgumentDocCommentIs("- An optional param with a default value");

View File

@@ -0,0 +1,16 @@
///<reference path="fourslash.ts" />
// @allowJs: true
// @Filename: Foo.js
/////**
//// * @param {string} p0
//// * @param {string} [p1]
//// */
////function Test(p0, p1) {
//// this.P0 = p0;
//// this.P1 = p1;
////}
////
////
////var /**/test = new Test("");
goTo.marker();
verify.quickInfoIs('var test: {\n P0: string;\n P1: string;\n}');

View File

@@ -0,0 +1,16 @@
///<reference path="fourslash.ts" />
// @allowJs: true
// @Filename: Foo.js
/////**
//// * Represents a person
//// * @constructor
//// * @param {string} name The name of the person
//// * @param {number} age The age of the person
//// */
////function Person(name, age) {
//// this.name = name;
//// this.age = age;
////}
////var p = new Pers/**/on();
goTo.marker();
verify.quickInfoIs("function Person(name: string, age: number): void", "Represents a person");

View File

@@ -0,0 +1,22 @@
///<reference path="fourslash.ts" />
// @allowJs: true
// @Filename: Foo.js
/////** first line of the comment
////
////third line */
////function foo() {}
////foo/**/();
goTo.marker();
verify.verifyQuickInfoDisplayParts('function',
'',
{ start: 63, length: 3 },
[{"text": "function", "kind": "keyword"},
{"text": " ", "kind": "space"},
{"text": "foo", "kind": "functionName"},
{"text": "(", "kind": "punctuation"},
{"text": ")", "kind": "punctuation"},
{"text": ":", "kind": "punctuation"},
{"text": " ", "kind": "space"},
{"text": "void", "kind": "keyword"}
],
[{"text": "first line of the comment\n\nthird line ", "kind": "text"}]);

View File

@@ -0,0 +1,17 @@
///<reference path="fourslash.ts" />
// @allowJs: true
// @Filename: dummy.js
/////**
//// * Find an item
//// * @template T
//// * @param {T[]} l
//// * @param {T} x
//// * @returns {?T} The names of the found item(s).
//// */
////function find(l, x) {
////}
////find(''/**/);
goTo.marker();
verify.currentSignatureHelpIs("find<T>(l: T[], x: T): T")
// There currently isn't a way to display the return tag comment