From e81cfa10d6c670381ee0bb9823632df34bf57ab2 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 17 Nov 2016 15:32:16 -0800 Subject: [PATCH] Clean cache code and streamline other code Cache only uses one property now. getJSDocs isn't generic and doesn't take a function parameter any more. The code is overall easier to read. --- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 83 +++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 48 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index af46cb2173c..8b648000273 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -498,6 +498,7 @@ namespace ts { /* @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?: JSDoc[]; // JSDoc for the node, if it has any. + /* @internal */ jsDocCache?: (JSDoc | JSDocParameterTag)[]; // JSDoc for the node, plus JSDoc retrieved from its parents /* @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) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 243d4f4c519..e5fd8b067b0 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1416,49 +1416,42 @@ namespace ts { (node).parameters[0].type.kind === SyntaxKind.JSDocConstructorType; } - function getJSDocTag(node: Node, kind: SyntaxKind): JSDocTag { - if (!node) { - return undefined; - } - - const jsDocTags = getJSDocTags(node); - if (!jsDocTags) { - return undefined; - } - - for (const tag of jsDocTags) { - if (tag.kind === kind) { - return tag; - } - } - } - export function getJSDocComments(node: Node): string[] { - return getJSDocs(node, docs => map(docs, doc => doc.comment), tags => map(tags, tag => tag.comment)); + return map(getJSDocs(node), doc => doc.comment); } - function getJSDocTags(node: Node): JSDocTag[] { - return getJSDocs(node, docs => { + function getJSDocTags(node: Node, kind: SyntaxKind): JSDocTag[] { + const docs = getJSDocs(node); + if (docs) { const result: JSDocTag[] = []; for (const doc of docs) { - if (doc.tags) { - result.push(...doc.tags); + if (doc.kind === SyntaxKind.JSDocParameterTag) { + if (doc.kind === kind) { + result.push(doc as JSDocParameterTag); + } + } + else { + result.push(...filter((doc as JSDoc).tags, tag => tag.kind === kind)); } } return result; - }, tags => tags); + } } - function getJSDocs(node: Node, getContent: (docs: JSDoc[]) => T[], getContentFromParam: (tags: JSDocTag[]) => T[]): T[] { - return getJSDocsWorker(node); + function getFirstJSDocTag(node: Node, kind: SyntaxKind): JSDocTag { + return node && firstOrUndefined(getJSDocTags(node, kind)); + } + + function getJSDocs(node: Node): (JSDoc | JSDocParameterTag)[] { + let cache: (JSDoc | JSDocParameterTag)[] = node.jsDocCache; + if (!cache) { + getJSDocsWorker(node); + node.jsDocCache = cache; + } + return cache; function getJSDocsWorker(node: Node) { - // TODO: A lot of this work should be cached, maybe. I guess it's only used in services right now... - // This will be hard because it may need to cache parentvariable versions and nonparent versions - // maybe I should eliminate checkParent first ... const parent = node.parent; - let result: T[] = undefined; - // Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement. // /** // * @param {number} name @@ -1476,7 +1469,7 @@ namespace ts { isVariableOfVariableDeclarationStatement ? parent.parent : undefined; if (variableStatementNode) { - result = concatenate(result, getJSDocsWorker(variableStatementNode)); + getJSDocsWorker(variableStatementNode); } // Also recognize when the node is the RHS of an assignment expression @@ -1486,32 +1479,26 @@ namespace ts { (parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken && parent.parent.kind === SyntaxKind.ExpressionStatement; if (isSourceOfAssignmentExpressionStatement) { - result = concatenate(result, getJSDocsWorker(parent.parent)); + getJSDocsWorker(parent.parent); } const isModuleDeclaration = node.kind === SyntaxKind.ModuleDeclaration && parent && parent.kind === SyntaxKind.ModuleDeclaration; const isPropertyAssignmentExpression = parent && parent.kind === SyntaxKind.PropertyAssignment; if (isModuleDeclaration || isPropertyAssignmentExpression) { - result = concatenate(result, getJSDocsWorker(parent)); + getJSDocsWorker(parent); } // Pull parameter comments from declaring function as well if (node.kind === SyntaxKind.Parameter) { - result = concatenate(result, getContentFromParam(getJSDocParameterTag(node))); + cache = concatenate(cache, getJSDocParameterTag(node)); } if (isVariableLike(node) && node.initializer) { - result = concatenate(result, getOwnJSDocs(node.initializer)); + cache = concatenate(cache, node.initializer.jsDocComments); } - return concatenate(result, getOwnJSDocs(node)); - } - - function getOwnJSDocs(node: Node) { - if (node.jsDocComments) { - return getContent(node.jsDocComments); - } + cache = concatenate(cache, node.jsDocComments); } } @@ -1520,18 +1507,18 @@ namespace ts { return undefined; } const func = param.parent as FunctionLikeDeclaration; - const tags = getJSDocTags(func); + const tags = getJSDocTags(func, SyntaxKind.JSDocParameterTag) as JSDocParameterTag[]; 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) as JSDocParameterTag[]; + 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; - return filter(tags as JSDocParameterTag[], tag => tag.kind === SyntaxKind.JSDocParameterTag && tag.parameterName.text === name); + return filter(tags, tag => tag.kind === SyntaxKind.JSDocParameterTag && tag.parameterName.text === name); } else { // TODO: it's a destructured parameter, so it should look up an "object type" series of multiple lines @@ -1541,7 +1528,7 @@ namespace ts { } export function getJSDocType(node: Node): JSDocType { - let tag: JSDocTypeTag | JSDocParameterTag = getJSDocTag(node, SyntaxKind.JSDocTypeTag) as JSDocTypeTag; + let tag: JSDocTypeTag | JSDocParameterTag = getFirstJSDocTag(node, SyntaxKind.JSDocTypeTag) as JSDocTypeTag; if (!tag && node.kind === SyntaxKind.Parameter) { const paramTags = getJSDocParameterTag(node); if (paramTags) { @@ -1553,11 +1540,11 @@ namespace ts { } export function getJSDocReturnTag(node: Node): JSDocReturnTag { - return getJSDocTag(node, SyntaxKind.JSDocReturnTag) as JSDocReturnTag; + return getFirstJSDocTag(node, SyntaxKind.JSDocReturnTag) as JSDocReturnTag; } export function getJSDocTemplateTag(node: Node): JSDocTemplateTag { - return getJSDocTag(node, SyntaxKind.JSDocTemplateTag) as JSDocTemplateTag; + return getFirstJSDocTag(node, SyntaxKind.JSDocTemplateTag) as JSDocTemplateTag; } export function hasRestParameter(s: SignatureDeclaration): boolean {