/// <nodoc /> public Possible <IReadOnlyList <SymbolLocation> > GetDefinitionAtPosition(INode node) { // TODO: consider special hint/message if the current node is a template expression with interpolated expressions. if (node.Kind == SyntaxKind.NoSubstitutionTemplateLiteral && node.Parent.Kind == SyntaxKind.TaggedTemplateExpression && node.Parent.Cast <ITaggedTemplateExpression>().IsWellKnownTemplateExpression(out var name)) { return(DefinitionForTemplateExpression(node, name)); } var result = GetDefinitionFromVariableDeclarationList(node); if (result.Succeeded) { return(result); } if (Utilities.IsJumpStatementTarget(node)) { var labelName = node.Cast <IIdentifier>().Text; var label = Utilities.GetTargetLabel(node.Parent, labelName); // TODO: saqadri - Port // return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined; return(Success(new[] { GetLocationFromNode(label, symbol: null), })); } var symbol = TypeChecker.GetSymbolAtLocation(node) ?? node.Symbol ?? node.ResolvedSymbol; // Could not find a symbol e.g. node is string or number keyword, // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol if (symbol == null) { // return null; return(SilentError()); } // If this is an alias, and the request came at the declaration location // get the aliased symbol instead. This allows for goto def on an import e.g. // import {A, B} from "mod"; // to jump to the implementation directly. if ((symbol.Flags & SymbolFlags.Alias) != SymbolFlags.None) { var declaration = symbol.Declarations.FirstOrDefault(); // Go to the original declaration for cases: // // (1) when the aliased symbol was declared in the location(parent). // (2) when the aliased symbol is originating from a named import. if (node.Kind == SyntaxKind.Identifier && declaration != null && (node.Parent.ResolveUnionType() == declaration.ResolveUnionType() || (declaration.Kind == SyntaxKind.ImportSpecifier && declaration?.Parent.Kind == SyntaxKind.NamedImports))) { symbol = TypeChecker.GetAliasedSymbol(symbol); } else if (node.Kind == SyntaxKind.Identifier && declaration?.Kind == SyntaxKind.NamespaceImport && declaration.Name != null) { return(new[] { GetLocationFromNode(declaration.Name, symbol) }); } } // Because name in short-hand property assignment has two different meanings: property name and property value, // using go-to-definition at such position should go to the variable declaration of the property value rather than // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition // is performed at the location of property access, we would like to go to definition of the property in the short-hand // assignment. This case and others are handled by the following code. if (node.Parent.Kind == SyntaxKind.ShorthandPropertyAssignment) { var shorthandSymbol = TypeChecker.GetShorthandAssignmentValueSymbol(symbol.ValueDeclaration); if (shorthandSymbol == null) { return(Success(new SymbolLocation[] { })); } var shorthandDeclaratons = shorthandSymbol.GetDeclarations(); // var shorthandSymbolKind = GetSymbolKind(shorthandSymbol, node); // var shorthandSymbolName = TypeChecker.SymbolToString(shorthandSymbol); // var shorthandContainerName = TypeChecker.SymbolToString(shorthandSymbol.Parent, node); return(Success(shorthandDeclaratons.Select( declaration => GetLocationFromNode(declaration, shorthandSymbol)).ToArray())); } if (Utilities.IsNameOfPropertyAssignment(node)) { return(GetDefinitionForPropertyAssignment(node)); } return(GetDefinitionFromSymbol(symbol, node)); }
/// <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); }