Пример #1
0
        /// <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));
        }
Пример #2
0
        /// <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);
        }