1/* @internal */ 2namespace ts.SymbolDisplay { 3 const symbolDisplayNodeBuilderFlags = NodeBuilderFlags.OmitParameterModifiers | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope; 4 5 // TODO(drosen): use contextual SemanticMeaning. 6 export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind { 7 const result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location); 8 if (result !== ScriptElementKind.unknown) { 9 return result; 10 } 11 12 const flags = getCombinedLocalAndExportSymbolFlags(symbol); 13 if (flags & SymbolFlags.Class) { 14 return getDeclarationOfKind(symbol, SyntaxKind.ClassExpression) ? 15 ScriptElementKind.localClassElement : ScriptElementKind.classElement; 16 } 17 if (flags & SymbolFlags.Enum) return ScriptElementKind.enumElement; 18 if (flags & SymbolFlags.TypeAlias) return ScriptElementKind.typeElement; 19 if (flags & SymbolFlags.Interface) return ScriptElementKind.interfaceElement; 20 if (flags & SymbolFlags.TypeParameter) return ScriptElementKind.typeParameterElement; 21 if (flags & SymbolFlags.EnumMember) return ScriptElementKind.enumMemberElement; 22 if (flags & SymbolFlags.Alias) return ScriptElementKind.alias; 23 if (flags & SymbolFlags.Module) return ScriptElementKind.moduleElement; 24 25 return result; 26 } 27 28 function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind { 29 const roots = typeChecker.getRootSymbols(symbol); 30 // If this is a method from a mapped type, leave as a method so long as it still has a call signature. 31 if (roots.length === 1 32 && first(roots).flags & SymbolFlags.Method 33 // Ensure the mapped version is still a method, as opposed to `{ [K in keyof I]: number }`. 34 && typeChecker.getTypeOfSymbolAtLocation(symbol, location).getNonNullableType().getCallSignatures().length !== 0) { 35 return ScriptElementKind.memberFunctionElement; 36 } 37 38 if (typeChecker.isUndefinedSymbol(symbol)) { 39 return ScriptElementKind.variableElement; 40 } 41 if (typeChecker.isArgumentsSymbol(symbol)) { 42 return ScriptElementKind.localVariableElement; 43 } 44 if (location.kind === SyntaxKind.ThisKeyword && isExpression(location)) { 45 return ScriptElementKind.parameterElement; 46 } 47 const flags = getCombinedLocalAndExportSymbolFlags(symbol); 48 if (flags & SymbolFlags.Variable) { 49 if (isFirstDeclarationOfSymbolParameter(symbol)) { 50 return ScriptElementKind.parameterElement; 51 } 52 else if (symbol.valueDeclaration && isVarConst(symbol.valueDeclaration as VariableDeclaration)) { 53 return ScriptElementKind.constElement; 54 } 55 else if (forEach(symbol.declarations, isLet)) { 56 return ScriptElementKind.letElement; 57 } 58 return isLocalVariableOrFunction(symbol) ? ScriptElementKind.localVariableElement : ScriptElementKind.variableElement; 59 } 60 if (flags & SymbolFlags.Function) return isLocalVariableOrFunction(symbol) ? ScriptElementKind.localFunctionElement : ScriptElementKind.functionElement; 61 if (flags & SymbolFlags.GetAccessor) return ScriptElementKind.memberGetAccessorElement; 62 if (flags & SymbolFlags.SetAccessor) return ScriptElementKind.memberSetAccessorElement; 63 if (flags & SymbolFlags.Method) return ScriptElementKind.memberFunctionElement; 64 if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement; 65 66 if (flags & SymbolFlags.Property) { 67 if (flags & SymbolFlags.Transient && (<TransientSymbol>symbol).checkFlags & CheckFlags.Synthetic) { 68 // If union property is result of union of non method (property/accessors/variables), it is labeled as property 69 const unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => { 70 const rootSymbolFlags = rootSymbol.getFlags(); 71 if (rootSymbolFlags & (SymbolFlags.PropertyOrAccessor | SymbolFlags.Variable)) { 72 return ScriptElementKind.memberVariableElement; 73 } 74 }); 75 if (!unionPropertyKind) { 76 // If this was union of all methods, 77 // make sure it has call signatures before we can label it as method 78 const typeOfUnionProperty = typeChecker.getTypeOfSymbolAtLocation(symbol, location); 79 if (typeOfUnionProperty.getCallSignatures().length) { 80 return ScriptElementKind.memberFunctionElement; 81 } 82 return ScriptElementKind.memberVariableElement; 83 } 84 return unionPropertyKind; 85 } 86 // If we requested completions after `x.` at the top-level, we may be at a source file location. 87 switch (location.parent && location.parent.kind) { 88 // If we've typed a character of the attribute name, will be 'JsxAttribute', else will be 'JsxOpeningElement'. 89 case SyntaxKind.JsxOpeningElement: 90 case SyntaxKind.JsxElement: 91 case SyntaxKind.JsxSelfClosingElement: 92 return location.kind === SyntaxKind.Identifier ? ScriptElementKind.memberVariableElement : ScriptElementKind.jsxAttribute; 93 case SyntaxKind.JsxAttribute: 94 return ScriptElementKind.jsxAttribute; 95 default: 96 return ScriptElementKind.memberVariableElement; 97 } 98 } 99 100 return ScriptElementKind.unknown; 101 } 102 103 function isDeprecatedDeclaration(decl: Declaration) { 104 return !!(getCombinedNodeFlagsAlwaysIncludeJSDoc(decl) & ModifierFlags.Deprecated); 105 } 106 107 function getNormalizedSymbolModifiers(symbol: Symbol) { 108 if (symbol.declarations && symbol.declarations.length) { 109 const [declaration, ...declarations] = symbol.declarations; 110 // omit deprecated flag if some declarations are not deprecated 111 const excludeFlags = length(declarations) && isDeprecatedDeclaration(declaration) && some(declarations, d => !isDeprecatedDeclaration(d)) 112 ? ModifierFlags.Deprecated 113 : ModifierFlags.None; 114 const modifiers = getNodeModifiers(declaration, excludeFlags); 115 if (modifiers) { 116 return modifiers.split(","); 117 } 118 } 119 return []; 120 } 121 122 export function getSymbolModifiers(typeChecker: TypeChecker, symbol: Symbol): string { 123 if (!symbol) { 124 return ScriptElementKindModifier.none; 125 } 126 127 const modifiers = new Set(getNormalizedSymbolModifiers(symbol)); 128 if (symbol.flags & SymbolFlags.Alias) { 129 const resolvedSymbol = typeChecker.getAliasedSymbol(symbol); 130 if (resolvedSymbol !== symbol) { 131 forEach(getNormalizedSymbolModifiers(resolvedSymbol), modifier => { 132 modifiers.add(modifier); 133 }); 134 } 135 } 136 if (symbol.flags & SymbolFlags.Optional) { 137 modifiers.add(ScriptElementKindModifier.optionalModifier); 138 } 139 return modifiers.size > 0 ? arrayFrom(modifiers.values()).join(",") : ScriptElementKindModifier.none; 140 } 141 142 interface SymbolDisplayPartsDocumentationAndSymbolKind { 143 displayParts: SymbolDisplayPart[]; 144 documentation: SymbolDisplayPart[]; 145 symbolKind: ScriptElementKind; 146 tags: JSDocTagInfo[] | undefined; 147 } 148 149 // TODO(drosen): Currently completion entry details passes the SemanticMeaning.All instead of using semanticMeaning of location 150 export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: TypeChecker, symbol: Symbol, sourceFile: SourceFile, enclosingDeclaration: Node | undefined, 151 location: Node, semanticMeaning = getMeaningFromLocation(location), alias?: Symbol): SymbolDisplayPartsDocumentationAndSymbolKind { 152 const displayParts: SymbolDisplayPart[] = []; 153 let documentation: SymbolDisplayPart[] = []; 154 let tags: JSDocTagInfo[] = []; 155 const symbolFlags = getCombinedLocalAndExportSymbolFlags(symbol); 156 let symbolKind = semanticMeaning & SemanticMeaning.Value ? getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location) : ScriptElementKind.unknown; 157 let hasAddedSymbolInfo = false; 158 const isThisExpression = location.kind === SyntaxKind.ThisKeyword && isInExpressionContext(location); 159 let type: Type | undefined; 160 let printer: Printer; 161 let documentationFromAlias: SymbolDisplayPart[] | undefined; 162 let tagsFromAlias: JSDocTagInfo[] | undefined; 163 let hasMultipleSignatures = false; 164 165 if (location.kind === SyntaxKind.ThisKeyword && !isThisExpression) { 166 return { displayParts: [keywordPart(SyntaxKind.ThisKeyword)], documentation: [], symbolKind: ScriptElementKind.primitiveType, tags: undefined }; 167 } 168 169 // Class at constructor site need to be shown as constructor apart from property,method, vars 170 if (symbolKind !== ScriptElementKind.unknown || symbolFlags & SymbolFlags.Class || symbolFlags & SymbolFlags.Alias) { 171 // If it is accessor they are allowed only if location is at name of the accessor 172 if (symbolKind === ScriptElementKind.memberGetAccessorElement || symbolKind === ScriptElementKind.memberSetAccessorElement) { 173 symbolKind = ScriptElementKind.memberVariableElement; 174 } 175 176 let signature: Signature | undefined; 177 type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol.exportSymbol || symbol, location); 178 179 if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) { 180 const right = (<PropertyAccessExpression>location.parent).name; 181 // Either the location is on the right of a property access, or on the left and the right is missing 182 if (right === location || (right && right.getFullWidth() === 0)) { 183 location = location.parent; 184 } 185 } 186 187 // try get the call/construct signature from the type if it matches 188 let callExpressionLike: CallExpression | NewExpression | JsxOpeningLikeElement | TaggedTemplateExpression | undefined; 189 if (isCallOrNewExpression(location)) { 190 callExpressionLike = location; 191 } 192 else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) { 193 callExpressionLike = <CallExpression | NewExpression>location.parent; 194 } 195 else if (location.parent && (isJsxOpeningLikeElement(location.parent) || isTaggedTemplateExpression(location.parent)) && isFunctionLike(symbol.valueDeclaration)) { 196 callExpressionLike = location.parent; 197 } 198 199 if (callExpressionLike) { 200 signature = typeChecker.tryGetResolvedSignatureWithoutCheck(callExpressionLike)!; // TODO: GH#18217 201 202 const useConstructSignatures = callExpressionLike.kind === SyntaxKind.NewExpression || (isCallExpression(callExpressionLike) && callExpressionLike.expression.kind === SyntaxKind.SuperKeyword); 203 204 const allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures(); 205 206 if (!contains(allSignatures, signature.target) && !contains(allSignatures, signature)) { 207 // Get the first signature if there is one -- allSignatures may contain 208 // either the original signature or its target, so check for either 209 signature = allSignatures.length ? allSignatures[0] : undefined; 210 } 211 212 if (signature) { 213 if (useConstructSignatures && (symbolFlags & SymbolFlags.Class)) { 214 // Constructor 215 symbolKind = ScriptElementKind.constructorImplementationElement; 216 addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); 217 } 218 else if (symbolFlags & SymbolFlags.Alias) { 219 symbolKind = ScriptElementKind.alias; 220 pushSymbolKind(symbolKind); 221 displayParts.push(spacePart()); 222 if (useConstructSignatures) { 223 if (signature.flags & SignatureFlags.Abstract) { 224 displayParts.push(keywordPart(SyntaxKind.AbstractKeyword)); 225 displayParts.push(spacePart()); 226 } 227 displayParts.push(keywordPart(SyntaxKind.NewKeyword)); 228 displayParts.push(spacePart()); 229 } 230 addFullSymbolName(symbol); 231 } 232 else { 233 addPrefixForAnyFunctionOrVar(symbol, symbolKind); 234 } 235 236 switch (symbolKind) { 237 case ScriptElementKind.jsxAttribute: 238 case ScriptElementKind.memberVariableElement: 239 case ScriptElementKind.variableElement: 240 case ScriptElementKind.constElement: 241 case ScriptElementKind.letElement: 242 case ScriptElementKind.parameterElement: 243 case ScriptElementKind.localVariableElement: 244 // If it is call or construct signature of lambda's write type name 245 displayParts.push(punctuationPart(SyntaxKind.ColonToken)); 246 displayParts.push(spacePart()); 247 if (!(getObjectFlags(type) & ObjectFlags.Anonymous) && type.symbol) { 248 addRange(displayParts, symbolToDisplayParts(typeChecker, type.symbol, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.AllowAnyNodeKind | SymbolFormatFlags.WriteTypeParametersOrArguments)); 249 displayParts.push(lineBreakPart()); 250 } 251 if (useConstructSignatures) { 252 if (signature.flags & SignatureFlags.Abstract) { 253 displayParts.push(keywordPart(SyntaxKind.AbstractKeyword)); 254 displayParts.push(spacePart()); 255 } 256 displayParts.push(keywordPart(SyntaxKind.NewKeyword)); 257 displayParts.push(spacePart()); 258 } 259 addSignatureDisplayParts(signature, allSignatures, TypeFormatFlags.WriteArrowStyleSignature); 260 break; 261 262 default: 263 // Just signature 264 addSignatureDisplayParts(signature, allSignatures); 265 } 266 hasAddedSymbolInfo = true; 267 hasMultipleSignatures = allSignatures.length > 1; 268 } 269 } 270 else if ((isNameOfFunctionDeclaration(location) && !(symbolFlags & SymbolFlags.Accessor)) || // name of function declaration 271 (location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration 272 // get the signature from the declaration and write it 273 const functionDeclaration = <SignatureDeclaration>location.parent; 274 // Use function declaration to write the signatures only if the symbol corresponding to this declaration 275 const locationIsSymbolDeclaration = symbol.declarations && find(symbol.declarations, declaration => 276 declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration)); 277 278 if (locationIsSymbolDeclaration) { 279 const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures(); 280 if (!typeChecker.isImplementationOfOverload(functionDeclaration)) { 281 signature = typeChecker.getSignatureFromDeclaration(functionDeclaration)!; // TODO: GH#18217 282 } 283 else { 284 signature = allSignatures[0]; 285 } 286 287 if (functionDeclaration.kind === SyntaxKind.Constructor) { 288 // show (constructor) Type(...) signature 289 symbolKind = ScriptElementKind.constructorImplementationElement; 290 addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); 291 } 292 else { 293 // (function/method) symbol(..signature) 294 addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature && 295 !(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind); 296 } 297 298 addSignatureDisplayParts(signature, allSignatures); 299 hasAddedSymbolInfo = true; 300 hasMultipleSignatures = allSignatures.length > 1; 301 } 302 } 303 } 304 if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo && !isThisExpression) { 305 addAliasPrefixIfNecessary(); 306 if (getDeclarationOfKind(symbol, SyntaxKind.ClassExpression)) { 307 // Special case for class expressions because we would like to indicate that 308 // the class name is local to the class body (similar to function expression) 309 // (local class) class <className> 310 pushSymbolKind(ScriptElementKind.localClassElement); 311 } 312 else if (getDeclarationOfKind(symbol, SyntaxKind.StructDeclaration)) { 313 // struct declaration 314 displayParts.push(keywordPart(SyntaxKind.StructKeyword)); 315 } 316 else { 317 // Class declaration has name which is not local. 318 displayParts.push(keywordPart(SyntaxKind.ClassKeyword)); 319 } 320 displayParts.push(spacePart()); 321 addFullSymbolName(symbol); 322 writeTypeParametersOfSymbol(symbol, sourceFile); 323 } 324 if ((symbolFlags & SymbolFlags.Interface) && (semanticMeaning & SemanticMeaning.Type)) { 325 prefixNextMeaning(); 326 displayParts.push(keywordPart(SyntaxKind.InterfaceKeyword)); 327 displayParts.push(spacePart()); 328 addFullSymbolName(symbol); 329 writeTypeParametersOfSymbol(symbol, sourceFile); 330 } 331 if ((symbolFlags & SymbolFlags.TypeAlias) && (semanticMeaning & SemanticMeaning.Type)) { 332 prefixNextMeaning(); 333 displayParts.push(keywordPart(SyntaxKind.TypeKeyword)); 334 displayParts.push(spacePart()); 335 addFullSymbolName(symbol); 336 writeTypeParametersOfSymbol(symbol, sourceFile); 337 displayParts.push(spacePart()); 338 displayParts.push(operatorPart(SyntaxKind.EqualsToken)); 339 displayParts.push(spacePart()); 340 addRange(displayParts, typeToDisplayParts(typeChecker, typeChecker.getDeclaredTypeOfSymbol(symbol), enclosingDeclaration, TypeFormatFlags.InTypeAlias)); 341 } 342 if (symbolFlags & SymbolFlags.Enum) { 343 prefixNextMeaning(); 344 if (some(symbol.declarations, d => isEnumDeclaration(d) && isEnumConst(d))) { 345 displayParts.push(keywordPart(SyntaxKind.ConstKeyword)); 346 displayParts.push(spacePart()); 347 } 348 displayParts.push(keywordPart(SyntaxKind.EnumKeyword)); 349 displayParts.push(spacePart()); 350 addFullSymbolName(symbol); 351 } 352 if (symbolFlags & SymbolFlags.Module && !isThisExpression) { 353 prefixNextMeaning(); 354 const declaration = getDeclarationOfKind<ModuleDeclaration>(symbol, SyntaxKind.ModuleDeclaration); 355 const isNamespace = declaration && declaration.name && declaration.name.kind === SyntaxKind.Identifier; 356 displayParts.push(keywordPart(isNamespace ? SyntaxKind.NamespaceKeyword : SyntaxKind.ModuleKeyword)); 357 displayParts.push(spacePart()); 358 addFullSymbolName(symbol); 359 } 360 if ((symbolFlags & SymbolFlags.TypeParameter) && (semanticMeaning & SemanticMeaning.Type)) { 361 prefixNextMeaning(); 362 displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); 363 displayParts.push(textPart("type parameter")); 364 displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); 365 displayParts.push(spacePart()); 366 addFullSymbolName(symbol); 367 if (symbol.parent) { 368 // Class/Interface type parameter 369 addInPrefix(); 370 addFullSymbolName(symbol.parent, enclosingDeclaration); 371 writeTypeParametersOfSymbol(symbol.parent, enclosingDeclaration); 372 } 373 else { 374 // Method/function type parameter 375 const decl = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter); 376 if (decl === undefined) return Debug.fail(); 377 const declaration = decl.parent; 378 379 if (declaration) { 380 if (isFunctionLikeKind(declaration.kind)) { 381 addInPrefix(); 382 const signature = typeChecker.getSignatureFromDeclaration(<SignatureDeclaration>declaration)!; // TODO: GH#18217 383 if (declaration.kind === SyntaxKind.ConstructSignature) { 384 displayParts.push(keywordPart(SyntaxKind.NewKeyword)); 385 displayParts.push(spacePart()); 386 } 387 else if (declaration.kind !== SyntaxKind.CallSignature && (<SignatureDeclaration>declaration).name) { 388 addFullSymbolName(declaration.symbol); 389 } 390 addRange(displayParts, signatureToDisplayParts(typeChecker, signature, sourceFile, TypeFormatFlags.WriteTypeArgumentsOfSignature)); 391 } 392 else if (declaration.kind === SyntaxKind.TypeAliasDeclaration) { 393 // Type alias type parameter 394 // For example 395 // type list<T> = T[]; // Both T will go through same code path 396 addInPrefix(); 397 displayParts.push(keywordPart(SyntaxKind.TypeKeyword)); 398 displayParts.push(spacePart()); 399 addFullSymbolName(declaration.symbol); 400 writeTypeParametersOfSymbol(declaration.symbol, sourceFile); 401 } 402 } 403 } 404 } 405 if (symbolFlags & SymbolFlags.EnumMember) { 406 symbolKind = ScriptElementKind.enumMemberElement; 407 addPrefixForAnyFunctionOrVar(symbol, "enum member"); 408 const declaration = symbol.declarations[0]; 409 if (declaration.kind === SyntaxKind.EnumMember) { 410 const constantValue = typeChecker.getConstantValue(<EnumMember>declaration); 411 if (constantValue !== undefined) { 412 displayParts.push(spacePart()); 413 displayParts.push(operatorPart(SyntaxKind.EqualsToken)); 414 displayParts.push(spacePart()); 415 displayParts.push(displayPart(getTextOfConstantValue(constantValue), 416 typeof constantValue === "number" ? SymbolDisplayPartKind.numericLiteral : SymbolDisplayPartKind.stringLiteral)); 417 } 418 } 419 } 420 // don't use symbolFlags since getAliasedSymbol requires the flag on the symbol itself 421 if (symbol.flags & SymbolFlags.Alias) { 422 prefixNextMeaning(); 423 if (!hasAddedSymbolInfo) { 424 const resolvedSymbol = typeChecker.getAliasedSymbol(symbol); 425 if (resolvedSymbol !== symbol && resolvedSymbol.declarations && resolvedSymbol.declarations.length > 0) { 426 const resolvedNode = resolvedSymbol.declarations[0]; 427 const declarationName = getNameOfDeclaration(resolvedNode); 428 if (declarationName) { 429 const isExternalModuleDeclaration = 430 isModuleWithStringLiteralName(resolvedNode) && 431 hasSyntacticModifier(resolvedNode, ModifierFlags.Ambient); 432 const shouldUseAliasName = symbol.name !== "default" && !isExternalModuleDeclaration; 433 const resolvedInfo = getSymbolDisplayPartsDocumentationAndSymbolKind( 434 typeChecker, 435 resolvedSymbol, 436 getSourceFileOfNode(resolvedNode), 437 resolvedNode, 438 declarationName, 439 semanticMeaning, 440 shouldUseAliasName ? symbol : resolvedSymbol); 441 displayParts.push(...resolvedInfo.displayParts); 442 displayParts.push(lineBreakPart()); 443 documentationFromAlias = resolvedInfo.documentation; 444 tagsFromAlias = resolvedInfo.tags; 445 } 446 else { 447 documentationFromAlias = resolvedSymbol.getContextualDocumentationComment(resolvedNode, typeChecker); 448 tagsFromAlias = resolvedSymbol.getJsDocTags(); 449 } 450 } 451 } 452 453 switch (symbol.declarations[0].kind) { 454 case SyntaxKind.NamespaceExportDeclaration: 455 displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); 456 displayParts.push(spacePart()); 457 displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword)); 458 break; 459 case SyntaxKind.ExportAssignment: 460 displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); 461 displayParts.push(spacePart()); 462 displayParts.push(keywordPart((symbol.declarations[0] as ExportAssignment).isExportEquals ? SyntaxKind.EqualsToken : SyntaxKind.DefaultKeyword)); 463 break; 464 case SyntaxKind.ExportSpecifier: 465 displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); 466 break; 467 default: 468 displayParts.push(keywordPart(SyntaxKind.ImportKeyword)); 469 } 470 displayParts.push(spacePart()); 471 addFullSymbolName(symbol); 472 forEach(symbol.declarations, declaration => { 473 if (declaration.kind === SyntaxKind.ImportEqualsDeclaration) { 474 const importEqualsDeclaration = <ImportEqualsDeclaration>declaration; 475 if (isExternalModuleImportEqualsDeclaration(importEqualsDeclaration)) { 476 displayParts.push(spacePart()); 477 displayParts.push(operatorPart(SyntaxKind.EqualsToken)); 478 displayParts.push(spacePart()); 479 displayParts.push(keywordPart(SyntaxKind.RequireKeyword)); 480 displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); 481 displayParts.push(displayPart(getTextOfNode(getExternalModuleImportEqualsDeclarationExpression(importEqualsDeclaration)), SymbolDisplayPartKind.stringLiteral)); 482 displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); 483 } 484 else { 485 const internalAliasSymbol = typeChecker.getSymbolAtLocation(importEqualsDeclaration.moduleReference); 486 if (internalAliasSymbol) { 487 displayParts.push(spacePart()); 488 displayParts.push(operatorPart(SyntaxKind.EqualsToken)); 489 displayParts.push(spacePart()); 490 addFullSymbolName(internalAliasSymbol, enclosingDeclaration); 491 } 492 } 493 return true; 494 } 495 }); 496 } 497 if (!hasAddedSymbolInfo) { 498 if (symbolKind !== ScriptElementKind.unknown) { 499 if (type) { 500 if (isThisExpression) { 501 prefixNextMeaning(); 502 displayParts.push(keywordPart(SyntaxKind.ThisKeyword)); 503 } 504 else { 505 addPrefixForAnyFunctionOrVar(symbol, symbolKind); 506 } 507 508 // For properties, variables and local vars: show the type 509 if (symbolKind === ScriptElementKind.memberVariableElement || 510 symbolKind === ScriptElementKind.jsxAttribute || 511 symbolFlags & SymbolFlags.Variable || 512 symbolKind === ScriptElementKind.localVariableElement || 513 isThisExpression) { 514 displayParts.push(punctuationPart(SyntaxKind.ColonToken)); 515 displayParts.push(spacePart()); 516 // If the type is type parameter, format it specially 517 if (type.symbol && type.symbol.flags & SymbolFlags.TypeParameter) { 518 const typeParameterParts = mapToDisplayParts(writer => { 519 const param = typeChecker.typeParameterToDeclaration(type as TypeParameter, enclosingDeclaration, symbolDisplayNodeBuilderFlags)!; 520 getPrinter().writeNode(EmitHint.Unspecified, param, getSourceFileOfNode(getParseTreeNode(enclosingDeclaration)), writer); 521 }); 522 addRange(displayParts, typeParameterParts); 523 } 524 else { 525 addRange(displayParts, typeToDisplayParts(typeChecker, type, enclosingDeclaration)); 526 } 527 if ((symbol as TransientSymbol).target && ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) { 528 const labelDecl = ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!; 529 Debug.assertNode(labelDecl.name, isIdentifier); 530 displayParts.push(spacePart()); 531 displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); 532 displayParts.push(textPart(idText(labelDecl.name))); 533 displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); 534 } 535 } 536 else if (symbolFlags & SymbolFlags.Function || 537 symbolFlags & SymbolFlags.Method || 538 symbolFlags & SymbolFlags.Constructor || 539 symbolFlags & SymbolFlags.Signature || 540 symbolFlags & SymbolFlags.Accessor || 541 symbolKind === ScriptElementKind.memberFunctionElement) { 542 const allSignatures = type.getNonNullableType().getCallSignatures(); 543 if (allSignatures.length) { 544 addSignatureDisplayParts(allSignatures[0], allSignatures); 545 hasMultipleSignatures = allSignatures.length > 1; 546 } 547 } 548 } 549 } 550 else { 551 symbolKind = getSymbolKind(typeChecker, symbol, location); 552 } 553 } 554 555 if (documentation.length === 0 && !hasMultipleSignatures) { 556 documentation = symbol.getContextualDocumentationComment(enclosingDeclaration, typeChecker); 557 } 558 559 if (documentation.length === 0 && symbolFlags & SymbolFlags.Property) { 560 // For some special property access expressions like `exports.foo = foo` or `module.exports.foo = foo` 561 // there documentation comments might be attached to the right hand side symbol of their declarations. 562 // The pattern of such special property access is that the parent symbol is the symbol of the file. 563 if (symbol.parent && forEach(symbol.parent.declarations, declaration => declaration.kind === SyntaxKind.SourceFile)) { 564 for (const declaration of symbol.declarations) { 565 if (!declaration.parent || declaration.parent.kind !== SyntaxKind.BinaryExpression) { 566 continue; 567 } 568 569 const rhsSymbol = typeChecker.getSymbolAtLocation((<BinaryExpression>declaration.parent).right); 570 if (!rhsSymbol) { 571 continue; 572 } 573 574 documentation = rhsSymbol.getDocumentationComment(typeChecker); 575 tags = rhsSymbol.getJsDocTags(); 576 if (documentation.length > 0) { 577 break; 578 } 579 } 580 } 581 } 582 583 if (tags.length === 0 && !hasMultipleSignatures) { 584 tags = symbol.getJsDocTags(); 585 } 586 587 if (documentation.length === 0 && documentationFromAlias) { 588 documentation = documentationFromAlias; 589 } 590 591 if (tags.length === 0 && tagsFromAlias) { 592 tags = tagsFromAlias; 593 } 594 595 return { displayParts, documentation, symbolKind, tags: tags.length === 0 ? undefined : tags }; 596 597 function getPrinter() { 598 if (!printer) { 599 printer = createPrinter({ removeComments: true }); 600 } 601 return printer; 602 } 603 604 function prefixNextMeaning() { 605 if (displayParts.length) { 606 displayParts.push(lineBreakPart()); 607 } 608 addAliasPrefixIfNecessary(); 609 } 610 611 function addAliasPrefixIfNecessary() { 612 if (alias) { 613 pushSymbolKind(ScriptElementKind.alias); 614 displayParts.push(spacePart()); 615 } 616 } 617 618 function addInPrefix() { 619 displayParts.push(spacePart()); 620 displayParts.push(keywordPart(SyntaxKind.InKeyword)); 621 displayParts.push(spacePart()); 622 } 623 624 function addFullSymbolName(symbolToDisplay: Symbol, enclosingDeclaration?: Node) { 625 if (alias && symbolToDisplay === symbol) { 626 symbolToDisplay = alias; 627 } 628 const fullSymbolDisplayParts = symbolToDisplayParts(typeChecker, symbolToDisplay, enclosingDeclaration || sourceFile, /*meaning*/ undefined, 629 SymbolFormatFlags.WriteTypeParametersOrArguments | SymbolFormatFlags.UseOnlyExternalAliasing | SymbolFormatFlags.AllowAnyNodeKind); 630 addRange(displayParts, fullSymbolDisplayParts); 631 632 if (symbol.flags & SymbolFlags.Optional) { 633 displayParts.push(punctuationPart(SyntaxKind.QuestionToken)); 634 } 635 } 636 637 function addPrefixForAnyFunctionOrVar(symbol: Symbol, symbolKind: string) { 638 prefixNextMeaning(); 639 if (symbolKind) { 640 pushSymbolKind(symbolKind); 641 if (symbol && !some(symbol.declarations, d => isArrowFunction(d) || (isFunctionExpression(d) || isClassExpression(d)) && !d.name)) { 642 displayParts.push(spacePart()); 643 addFullSymbolName(symbol); 644 } 645 } 646 } 647 648 function pushSymbolKind(symbolKind: string) { 649 switch (symbolKind) { 650 case ScriptElementKind.variableElement: 651 case ScriptElementKind.functionElement: 652 case ScriptElementKind.letElement: 653 case ScriptElementKind.constElement: 654 case ScriptElementKind.constructorImplementationElement: 655 displayParts.push(textOrKeywordPart(symbolKind)); 656 return; 657 default: 658 displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); 659 displayParts.push(textOrKeywordPart(symbolKind)); 660 displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); 661 return; 662 } 663 } 664 665 function addSignatureDisplayParts(signature: Signature, allSignatures: readonly Signature[], flags = TypeFormatFlags.None) { 666 addRange(displayParts, signatureToDisplayParts(typeChecker, signature, enclosingDeclaration, flags | TypeFormatFlags.WriteTypeArgumentsOfSignature)); 667 if (allSignatures.length > 1) { 668 displayParts.push(spacePart()); 669 displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); 670 displayParts.push(operatorPart(SyntaxKind.PlusToken)); 671 displayParts.push(displayPart((allSignatures.length - 1).toString(), SymbolDisplayPartKind.numericLiteral)); 672 displayParts.push(spacePart()); 673 displayParts.push(textPart(allSignatures.length === 2 ? "overload" : "overloads")); 674 displayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); 675 } 676 documentation = signature.getDocumentationComment(typeChecker); 677 tags = signature.getJsDocTags(); 678 679 if (allSignatures.length > 1 && documentation.length === 0 && tags.length === 0) { 680 documentation = allSignatures[0].getDocumentationComment(typeChecker); 681 tags = allSignatures[0].getJsDocTags(); 682 } 683 } 684 685 function writeTypeParametersOfSymbol(symbol: Symbol, enclosingDeclaration: Node | undefined) { 686 const typeParameterParts = mapToDisplayParts(writer => { 687 const params = typeChecker.symbolToTypeParameterDeclarations(symbol, enclosingDeclaration, symbolDisplayNodeBuilderFlags); 688 getPrinter().writeList(ListFormat.TypeParameters, params, getSourceFileOfNode(getParseTreeNode(enclosingDeclaration)), writer); 689 }); 690 addRange(displayParts, typeParameterParts); 691 } 692 } 693 694 function isLocalVariableOrFunction(symbol: Symbol) { 695 if (symbol.parent) { 696 return false; // This is exported symbol 697 } 698 699 return forEach(symbol.declarations, declaration => { 700 // Function expressions are local 701 if (declaration.kind === SyntaxKind.FunctionExpression) { 702 return true; 703 } 704 705 if (declaration.kind !== SyntaxKind.VariableDeclaration && declaration.kind !== SyntaxKind.FunctionDeclaration) { 706 return false; 707 } 708 709 // If the parent is not sourceFile or module block it is local variable 710 for (let parent = declaration.parent; !isFunctionBlock(parent); parent = parent.parent) { 711 // Reached source file or module block 712 if (parent.kind === SyntaxKind.SourceFile || parent.kind === SyntaxKind.ModuleBlock) { 713 return false; 714 } 715 } 716 717 // parent is in function block 718 return true; 719 }); 720 } 721} 722