/// <summary> /// Given a <paramref name="type"/>, returns the symbols that can be accessed relative to <paramref name="node"/> /// </summary> /// <param name="type">The type whose symbols will be tested for valid property access.</param> /// <param name="node">The node in which the type's symbols will be accessed.</param> /// <param name="typeChecker">Reference to the type checker.</param> /// <remarks> /// Ported from TypeScript version. /// </remarks> private static IEnumerable <ISymbol> AddTypeProperties(IType type, INode node, ITypeChecker typeChecker) { var typeSymbols = typeChecker.GetPropertiesOfType(type); if (typeSymbols != null) { return(typeSymbols.Where(typeSymbol => typeSymbol.Name != null && typeChecker.IsValidPropertyAccess(node.Parent /*HINT: IPropertyAccessExpression*/, typeSymbol.Name))); } return(Enumerable.Empty <ISymbol>()); }
/// <summary> /// Creates an array of symbols for a node whose parent is either a qualified name or a property access expression. /// </summary> /// <remarks> /// This was ported from the TypeScript version of the language server. /// </remarks> public static IEnumerable <ISymbol> GetTypeScriptMemberSymbols(INode node, ITypeChecker typeChecker) { var symbols = new HashSet <ISymbol>(); // If we are part of a type (say creating an interface and and assigning a type to a property) // interface Test { // myField: ImportedModuleQualifiedName.<Type> // }; // when we want to filter the symbols ot the types bool isTypeLocation = node.Parent != null && IsPartOfTypeNode(node.Parent); // This case handles when you are accessing a property out of // an import statement. // const foo = importFrom("").<Type or Value>. // NOTE: We don't really hit this case in our completion implementation // as it is already handled as part of the property access expression completion code. bool isRhsOfImportDeclaration = IsInRightSideOfImport(node); if (IsEntityNode(node)) { var symbol = typeChecker.GetSymbolAtLocation(node); if (symbol != null) { symbol = SkipAlias(symbol, typeChecker); if ((symbol.Flags & (SymbolFlags.Enum | SymbolFlags.Module)) != SymbolFlags.None) { var exportedSymbols = typeChecker.GetExportsOfModule(symbol); foreach (var exportedSymbol in exportedSymbols) { if (isRhsOfImportDeclaration) { if (typeChecker.IsValidPropertyAccess(node.Parent, exportedSymbol.Value.Name) || SymbolCanBeReferencedAtTypeLocation(exportedSymbol.Value, typeChecker)) { symbols.Add(exportedSymbol.Value); } } else if (isTypeLocation) { if (SymbolCanBeReferencedAtTypeLocation(exportedSymbol.Value, typeChecker)) { symbols.Add(exportedSymbol.Value); } } else { if (typeChecker.IsValidPropertyAccess(node.Parent, exportedSymbol.Value.Name)) { symbols.Add(exportedSymbol.Value); } } } // If the module is merged with a value, we must get the type of the class and add its propertes (for inherited static methods). if (!isTypeLocation && symbol.Declarations != null && symbol.Declarations.Any(d => d.Kind != SyntaxKind.SourceFile && d.Kind != SyntaxKind.ModuleDeclaration && d.Kind != SyntaxKind.EnumDeclaration)) { symbols.AddRange(AddTypeProperties(typeChecker.GetTypeOfSymbolAtLocation(symbol, node), node, typeChecker)); } } } } // If we are not in a type location, and not handling module exports, then add the symbols for the // type of the "left side" (i.e. the "QualifiedName" in "QualifiedName."Access") that can be accessed // through QualifiedName (i.e. if the user types QualifiedName.<Completion happens here> what symbols // have valid property access from there). if (!isTypeLocation) { symbols.AddRange(AddTypeProperties(typeChecker.GetTypeAtLocation(node), node, typeChecker)); } return(symbols); }