| 
									
										
										
										
											2020-10-12 12:51:43 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import {ReferenceSymbol, ShimLocation, Symbol, SymbolKind, VariableSymbol} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; | 
					
						
							|  |  |  | import * as ts from 'typescript'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Reverse mappings of enum would generate strings
 | 
					
						
							|  |  |  | export const ALIAS_NAME = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.aliasName]; | 
					
						
							|  |  |  | export const SYMBOL_INTERFACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.interfaceName]; | 
					
						
							|  |  |  | export const SYMBOL_PUNC = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.punctuation]; | 
					
						
							|  |  |  | export const SYMBOL_SPACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.space]; | 
					
						
							|  |  |  | export const SYMBOL_TEXT = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.text]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Label for various kinds of Angular entities for TS display info. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export enum DisplayInfoKind { | 
					
						
							|  |  |  |   COMPONENT = 'component', | 
					
						
							|  |  |  |   DIRECTIVE = 'directive', | 
					
						
							|  |  |  |   EVENT = 'event', | 
					
						
							|  |  |  |   REFERENCE = 'reference', | 
					
						
							|  |  |  |   ELEMENT = 'element', | 
					
						
							|  |  |  |   VARIABLE = 'variable', | 
					
						
							|  |  |  |   PIPE = 'pipe', | 
					
						
							|  |  |  |   PROPERTY = 'property', | 
					
						
							|  |  |  |   METHOD = 'method', | 
					
						
							|  |  |  |   TEMPLATE = 'template', | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export interface DisplayInfo { | 
					
						
							|  |  |  |   kind: DisplayInfoKind; | 
					
						
							|  |  |  |   displayParts: ts.SymbolDisplayPart[]; | 
					
						
							|  |  |  |   documentation: ts.SymbolDisplayPart[]|undefined; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function getDisplayInfo( | 
					
						
							|  |  |  |     tsLS: ts.LanguageService, typeChecker: ts.TypeChecker, | 
					
						
							|  |  |  |     symbol: ReferenceSymbol|VariableSymbol): DisplayInfo { | 
					
						
							|  |  |  |   let kind: DisplayInfoKind; | 
					
						
							|  |  |  |   if (symbol.kind === SymbolKind.Reference) { | 
					
						
							|  |  |  |     kind = DisplayInfoKind.REFERENCE; | 
					
						
							|  |  |  |   } else if (symbol.kind === SymbolKind.Variable) { | 
					
						
							|  |  |  |     kind = DisplayInfoKind.VARIABLE; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |         `AssertionError: unexpected symbol kind ${SymbolKind[(symbol as Symbol).kind]}`); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const displayParts = createDisplayParts( | 
					
						
							|  |  |  |       symbol.declaration.name, kind, /* containerName */ undefined, | 
					
						
							|  |  |  |       typeChecker.typeToString(symbol.tsType)); | 
					
						
							| 
									
										
										
										
											2020-11-16 11:22:11 -08:00
										 |  |  |   const documentation = symbol.kind === SymbolKind.Reference ? | 
					
						
							|  |  |  |       getDocumentationFromTypeDefAtLocation(tsLS, symbol.targetLocation) : | 
					
						
							|  |  |  |       getDocumentationFromTypeDefAtLocation(tsLS, symbol.initializerLocation); | 
					
						
							| 
									
										
										
										
											2020-10-12 12:51:43 -07:00
										 |  |  |   return { | 
					
						
							|  |  |  |     kind, | 
					
						
							|  |  |  |     displayParts, | 
					
						
							|  |  |  |     documentation, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Construct a compound `ts.SymbolDisplayPart[]` which incorporates the container and type of a | 
					
						
							|  |  |  |  * target declaration. | 
					
						
							|  |  |  |  * @param name Name of the target | 
					
						
							|  |  |  |  * @param kind component, directive, pipe, etc. | 
					
						
							|  |  |  |  * @param containerName either the Symbol's container or the NgModule that contains the directive | 
					
						
							|  |  |  |  * @param type user-friendly name of the type | 
					
						
							|  |  |  |  * @param documentation docstring or comment | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function createDisplayParts( | 
					
						
							|  |  |  |     name: string, kind: DisplayInfoKind, containerName: string|undefined, | 
					
						
							|  |  |  |     type: string|undefined): ts.SymbolDisplayPart[] { | 
					
						
							|  |  |  |   const containerDisplayParts = containerName !== undefined ? | 
					
						
							|  |  |  |       [ | 
					
						
							|  |  |  |         {text: containerName, kind: SYMBOL_INTERFACE}, | 
					
						
							|  |  |  |         {text: '.', kind: SYMBOL_PUNC}, | 
					
						
							|  |  |  |       ] : | 
					
						
							|  |  |  |       []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const typeDisplayParts = type !== undefined ? | 
					
						
							|  |  |  |       [ | 
					
						
							|  |  |  |         {text: ':', kind: SYMBOL_PUNC}, | 
					
						
							|  |  |  |         {text: ' ', kind: SYMBOL_SPACE}, | 
					
						
							|  |  |  |         {text: type, kind: SYMBOL_INTERFACE}, | 
					
						
							|  |  |  |       ] : | 
					
						
							|  |  |  |       []; | 
					
						
							|  |  |  |   return [ | 
					
						
							|  |  |  |     {text: '(', kind: SYMBOL_PUNC}, | 
					
						
							|  |  |  |     {text: kind, kind: SYMBOL_TEXT}, | 
					
						
							|  |  |  |     {text: ')', kind: SYMBOL_PUNC}, | 
					
						
							|  |  |  |     {text: ' ', kind: SYMBOL_SPACE}, | 
					
						
							|  |  |  |     ...containerDisplayParts, | 
					
						
							|  |  |  |     {text: name, kind: SYMBOL_INTERFACE}, | 
					
						
							|  |  |  |     ...typeDisplayParts, | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Convert a `SymbolDisplayInfoKind` to a `ts.ScriptElementKind` type, allowing it to pass through | 
					
						
							|  |  |  |  * TypeScript APIs. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * In practice, this is an "illegal" type cast. Since `ts.ScriptElementKind` is a string, this is | 
					
						
							|  |  |  |  * safe to do if TypeScript only uses the value in a string context. Consumers of this conversion | 
					
						
							|  |  |  |  * function are responsible for ensuring this is the case. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export function unsafeCastDisplayInfoKindToScriptElementKind(kind: DisplayInfoKind): | 
					
						
							|  |  |  |     ts.ScriptElementKind { | 
					
						
							|  |  |  |   return kind as string as ts.ScriptElementKind; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getDocumentationFromTypeDefAtLocation( | 
					
						
							|  |  |  |     tsLS: ts.LanguageService, shimLocation: ShimLocation): ts.SymbolDisplayPart[]|undefined { | 
					
						
							|  |  |  |   const typeDefs = | 
					
						
							|  |  |  |       tsLS.getTypeDefinitionAtPosition(shimLocation.shimPath, shimLocation.positionInShimFile); | 
					
						
							|  |  |  |   if (typeDefs === undefined || typeDefs.length === 0) { | 
					
						
							|  |  |  |     return undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return tsLS.getQuickInfoAtPosition(typeDefs[0].fileName, typeDefs[0].textSpan.start) | 
					
						
							|  |  |  |       ?.documentation; | 
					
						
							|  |  |  | } |