public Reference FindDefinition(IDocumentAnalysis analysis, SourceLocation location, out ILocatedMember member)
        {
            member = null;
            if (analysis?.Ast == null)
            {
                return(null);
            }

            ExpressionLocator.FindExpression(analysis.Ast, location,
                                             FindExpressionOptions.Hover, out var exprNode, out var statement, out var exprScope);

            if (exprNode is ConstantExpression || !(exprNode is Expression expr))
            {
                return(null); // No hover for literals.
            }

            var eval = analysis.ExpressionEvaluator;

            using (eval.OpenScope(analysis.Document, exprScope)) {
                if (expr is MemberExpression mex)
                {
                    return(FromMemberExpression(mex, analysis, out member));
                }

                // Try variables
                var     name  = (expr as NameExpression)?.Name;
                IMember value = null;
                if (!string.IsNullOrEmpty(name))
                {
                    var reference = TryFromVariable(name, analysis, location, statement, out member);
                    if (reference != null)
                    {
                        return(reference);
                    }

                    if (statement is ImportStatement || statement is FromImportStatement)
                    {
                        reference = TryFromImport(statement, name, analysis, out value);
                        if (reference != null)
                        {
                            member = value as ILocatedMember;
                            return(reference);
                        }
                    }
                }

                value = value ?? eval.GetValueFromExpression(expr);
                if (value.IsUnknown())
                {
                    return(null);
                }
                member = value as ILocatedMember;
                return(FromMember(value));
            }
        }
        /// <summary>
        /// Locates definition or declaration of a symbol at the provided location.
        /// </summary>
        /// <param name="analysis">Document analysis.</param>
        /// <param name="location">Location in the document.</param>
        /// <param name="definingMember">Member location or null of not found.</param>
        /// <returns>Definition location (module URI and the text range).</returns>
        public Reference FindDefinition(IDocumentAnalysis analysis, SourceLocation location, out ILocatedMember definingMember)
        {
            definingMember = null;
            if (analysis?.Ast == null)
            {
                return(null);
            }

            ExpressionLocator.FindExpression(analysis.Ast, location,
                                             FindExpressionOptions.Hover, out var exprNode, out var statement, out var exprScope);

            if (exprNode is ConstantExpression || !(exprNode is Expression expr))
            {
                return(null); // No goto definition for literals.
            }

            Reference reference = null;

            switch (statement)
            {
            // Check if this is a relative import
            case FromImportStatement fromImport:
                reference = HandleFromImport(analysis, location, fromImport, exprNode, out definingMember);
                break;

            case ImportStatement import:
                reference = HandleImport(analysis, import, exprNode, out definingMember);
                break;
            }

            if (reference != null)
            {
                return(reference.uri == null ? null : reference);
            }

            var eval = analysis.ExpressionEvaluator;

            using (eval.OpenScope(analysis.Document, exprScope)) {
                if (expr is MemberExpression mex)
                {
                    return(FromMemberExpression(mex, analysis, out definingMember));
                }

                // Try variables
                var name = (expr as NameExpression)?.Name;
                if (!string.IsNullOrEmpty(name))
                {
                    reference = TryFromVariable(name, analysis, location, statement, out definingMember);
                }
            }

            return(reference);
        }
        public Reference FindDefinition(IDocumentAnalysis analysis, SourceLocation location)
        {
            ExpressionLocator.FindExpression(analysis.Ast, location,
                                             FindExpressionOptions.Hover, out var exprNode, out var statement, out var exprScope);

            if (exprNode is ConstantExpression)
            {
                return(null); // No hover for literals.
            }
            if (!(exprNode is Expression expr))
            {
                return(null);
            }

            var eval = analysis.ExpressionEvaluator;
            var name = (expr as NameExpression)?.Name;

            using (eval.OpenScope(analysis.Document, exprScope)) {
                // First try variables, except in imports
                if (!string.IsNullOrEmpty(name) && !(statement is ImportStatement) && !(statement is FromImportStatement))
                {
                    var m = eval.LookupNameInScopes(name, out var scope);
                    if (m != null && scope.Variables[name] is IVariable v)
                    {
                        var type   = v.Value.GetPythonType();
                        var module = type as IPythonModule ?? type?.DeclaringModule;
                        if (CanNavigateToModule(module, analysis))
                        {
                            return(new Reference {
                                range = v.Location.Span, uri = v.Location.DocumentUri
                            });
                        }
                    }
                }

                var value = eval.GetValueFromExpression(expr);
                if (value.IsUnknown() && !string.IsNullOrEmpty(name))
                {
                    var reference = FromImport(statement, name, analysis, out value);
                    if (reference != null)
                    {
                        return(reference);
                    }
                }

                if (value.IsUnknown())
                {
                    return(null);
                }
                return(FromMember(value, expr, statement, analysis));
            }
        }
Beispiel #4
0
        public Hover GetHover(IDocumentAnalysis analysis, SourceLocation location)
        {
            if (analysis is EmptyAnalysis)
            {
                return(new Hover {
                    contents = Resources.AnalysisIsInProgressHover
                });
            }

            ExpressionLocator.FindExpression(analysis.Ast, location,
                                             FindExpressionOptions.Hover, out var node, out var statement, out var scope);

            if (node is ConstantExpression || node is FString || !(node is Expression expr))
            {
                // node is FString only if it didn't save an f-string subexpression
                return(null); // No hover for literals.
            }

            var range = new Range {
                start = expr.GetStart(analysis.Ast),
                end   = expr.GetEnd(analysis.Ast)
            };

            var eval = analysis.ExpressionEvaluator;

            switch (statement)
            {
            case FromImportStatement fi when node is NameExpression nex: {
                var contents = HandleFromImport(fi, location, scope, analysis);
                if (contents != null)
                {
                    return(new Hover {
                            contents = contents,
                            range = range
                        });
                }

                break;
            }

            case ImportStatement imp: {
                var contents = HandleImport(imp, location, scope, analysis);
                if (contents != null)
                {
                    return(new Hover {
                            contents = contents,
                            range = range
                        });
                }

                break;
            }
            }

            IMember     value;
            IPythonType type;

            using (eval.OpenScope(analysis.Document, scope)) {
                value = analysis.ExpressionEvaluator.GetValueFromExpression(expr);
                type  = value?.GetPythonType();
                if (type == null)
                {
                    return(null);
                }
            }

            IPythonType self = null;
            string      name = null;

            // If expression is A.B, trim applicable span to 'B'.
            if (expr is MemberExpression mex)
            {
                name  = mex.Name;
                range = new Range {
                    start = mex.Target.GetEnd(analysis.Ast),
                    end   = range.end
                };

                // In case of a member expression get the target since if we end up with method
                // of a generic class, the function will need specific type to determine its return
                // value correctly. I.e. in x.func() we need to determine type of x (self for func).
                var v = analysis.ExpressionEvaluator.GetValueFromExpression(mex.Target);
                self = v?.GetPythonType();
            }

            // Figure out name, if any
            name = name ?? (node as NameExpression)?.Name;

            // Special case hovering over self or cls
            if ((name.EqualsOrdinal("self") || name.EqualsOrdinal("cls")) && type is IPythonClassType)
            {
                return(new Hover {
                    contents = _docSource.GetHover(null, type),
                    range = range
                });
            }

            name = name == null && statement is ClassDefinition cd ? cd.Name : name;
            name = name == null && statement is FunctionDefinition fd ? fd.Name : name;

            return(new Hover {
                contents = _docSource.GetHover(name, value, self),
                range = range
            });
        }
Beispiel #5
0
        public Hover GetHover(IDocumentAnalysis analysis, SourceLocation location)
        {
            if (analysis is EmptyAnalysis)
            {
                return(new Hover {
                    contents = Resources.AnalysisIsInProgressHover
                });
            }

            ExpressionLocator.FindExpression(analysis.Ast, location,
                                             FindExpressionOptions.Hover, out var node, out var statement, out var hoverScopeStatement);

            if (!HasHover(node) || !(node is Expression expr))
            {
                return(null);
            }

            var range = new Range {
                start = expr.GetStart(analysis.Ast),
                end   = expr.GetEnd(analysis.Ast)
            };

            var eval = analysis.ExpressionEvaluator;

            switch (statement)
            {
            case FromImportStatement fi when node is NameExpression nex: {
                var contents = HandleFromImport(fi, location, hoverScopeStatement, analysis);
                if (contents != null)
                {
                    return(new Hover {
                            contents = contents,
                            range = range
                        });
                }

                break;
            }

            case ImportStatement imp: {
                var contents = HandleImport(imp, location, hoverScopeStatement, analysis);
                if (contents != null)
                {
                    return(new Hover {
                            contents = contents,
                            range = range
                        });
                }

                break;
            }
            }

            IMember     value;
            IPythonType type;

            using (eval.OpenScope(analysis.Document, hoverScopeStatement)) {
                // Here we can be hovering over a class member. Class members are declared
                // as members as well as special variables in the class scope. If this is
                // a name expression (rather than a member expression) and it is a class
                // variable NOT in the immediate class scope, filter it out. Consider:
                //   class A:
                //     x = 1
                //     y = x
                // hover over 'x' in 'y = x' should produce proper tooltip. However, in
                //   class A:
                //     x = 1
                //     def func(self):
                //       y = x
                // hover over 'x' in 'y = x' should not produce tooltip.

                IVariable variable = null;
                if (expr is NameExpression nex)
                {
                    analysis.ExpressionEvaluator.LookupNameInScopes(nex.Name, out _, out variable, LookupOptions.All);
                    if (IsInvalidClassMember(variable, hoverScopeStatement, location.ToIndex(analysis.Ast)))
                    {
                        return(null);
                    }
                }

                value = variable?.Value ?? analysis.ExpressionEvaluator.GetValueFromExpression(expr, LookupOptions.All);
                type  = value?.GetPythonType();
                if (type == null)
                {
                    return(null);
                }
            }

            IPythonType self = null;
            string      name = null;

            // If expression is A.B, trim applicable span to 'B'.
            if (expr is MemberExpression mex)
            {
                name  = mex.Name;
                range = new Range {
                    start = mex.Target.GetEnd(analysis.Ast),
                    end   = range.end
                };

                // In case of a member expression get the target since if we end up with method
                // of a generic class, the function will need specific type to determine its return
                // value correctly. I.e. in x.func() we need to determine type of x (self for func).
                var v = analysis.ExpressionEvaluator.GetValueFromExpression(mex.Target);
                self = v?.GetPythonType();
            }

            // Figure out name, if any
            name = name ?? (node as NameExpression)?.Name;

            // Special case hovering over self or cls
            if ((name.EqualsOrdinal("self") || name.EqualsOrdinal("cls")) && type is IPythonClassType)
            {
                return(new Hover {
                    contents = _docSource.GetHover(null, type),
                    range = range
                });
            }

            name = name == null && statement is ClassDefinition cd ? cd.Name : name;
            name = name == null && statement is FunctionDefinition fd ? fd.Name : name;

            return(new Hover {
                contents = _docSource.GetHover(name, value, self),
                range = range
            });
        }
        public SignatureHelp GetSignature(IDocumentAnalysis analysis, SourceLocation location)
        {
            if (analysis is EmptyAnalysis)
            {
                return(null);
            }

            ExpressionLocator.FindExpression(analysis.Ast, location,
                                             FindExpressionOptions.Hover, out var node, out var statement, out var scope);

            IMember     value    = null;
            IPythonType selfType = null;
            string      name     = null;
            var         call     = node as CallExpression;

            if (call != null)
            {
                using (analysis.ExpressionEvaluator.OpenScope(analysis.Document, scope)) {
                    switch (call.Target)
                    {
                    case MemberExpression mex:
                        var v = analysis.ExpressionEvaluator.GetValueFromExpression(mex.Target);
                        selfType = v?.GetPythonType();
                        name     = mex.Name;
                        break;

                    case NameExpression ne:
                        name = ne.Name;
                        break;
                    }

                    value = analysis.ExpressionEvaluator.GetValueFromExpression(call.Target);
                }
            }

            var ft = value.TryGetFunctionType();

            if (ft == null)
            {
                return(null);
            }

            var signatures = new SignatureInformation[ft.Overloads.Count];

            for (var i = 0; i < ft.Overloads.Count; i++)
            {
                var o = ft.Overloads[i];

                var signatureLabel = _docSource.GetSignatureString(ft, selfType, out var parameterSpans, i, name);

                var parameterInfo = new ParameterInformation[parameterSpans.Length];
                for (var j = 0; j < parameterSpans.Length; j++)
                {
                    var(ps, p) = parameterSpans[j];

                    parameterInfo[j] = new ParameterInformation {
                        label         = _labelOffsetSupport ? new[] { ps.Start, ps.End } : (object)p.Name,
                        documentation = _docSource.FormatParameterDocumentation(p)
                    };
                }

                signatures[i] = new SignatureInformation {
                    label         = signatureLabel,
                    documentation = _docSource.FormatDocumentation(ft.Documentation),
                    parameters    = parameterInfo
                };
            }

            var index = location.ToIndex(analysis.Ast);

            if (call.GetArgumentAtIndex(analysis.Ast, index, out var activeParameter) && activeParameter < 0)
            {
                // Returned 'true' and activeParameter == -1 means that we are after
                // the trailing comma, so assume partially typed expression such as 'pow(x, y, |)
                activeParameter = call.Args.Count;
            }

            var activeSignature = -1;

            if (activeParameter >= 0)
            {
                // TODO: Better selection of active signature by argument set
                activeSignature = signatures
                                  .Select((s, i) => Tuple.Create(s, i))
                                  .OrderBy(t => t.Item1.parameters.Length)
                                  .FirstOrDefault(t => t.Item1.parameters.Length > activeParameter)
                                  ?.Item2 ?? -1;
            }

            activeSignature = activeSignature >= 0
                ? activeSignature
                : (signatures.Length > 0 ? 0 : -1);

            return(new SignatureHelp {
                signatures = signatures.ToArray(),
                activeSignature = activeSignature,
                activeParameter = activeParameter
            });
        }
        public SignatureHelp GetSignature(IDocumentAnalysis analysis, SourceLocation location)
        {
            if (analysis is EmptyAnalysis)
            {
                return(null);
            }

            ExpressionLocator.FindExpression(analysis.Ast, location,
                                             FindExpressionOptions.Hover, out var node, out var statement, out var scope);

            IMember     value    = null;
            IPythonType selfType = null;
            var         call     = node as CallExpression;

            if (call != null)
            {
                using (analysis.ExpressionEvaluator.OpenScope(analysis.Document, scope)) {
                    if (call.Target is MemberExpression mex)
                    {
                        var v = analysis.ExpressionEvaluator.GetValueFromExpression(mex.Target);
                        selfType = v?.GetPythonType();
                    }
                    value = analysis.ExpressionEvaluator.GetValueFromExpression(call.Target);
                }
            }

            var ft = value?.GetPythonType <IPythonFunctionType>();

            if (ft == null)
            {
                return(null);
            }

            var skip = ft.IsStatic || ft.IsUnbound ? 0 : 1;

            var signatures = new SignatureInformation[ft.Overloads.Count];

            for (var i = 0; i < ft.Overloads.Count; i++)
            {
                var o = ft.Overloads[i];

                var parameters = o.Parameters.Skip(skip).Select(p => new ParameterInformation {
                    label         = p.Name,
                    documentation = _docSource.FormatParameterDocumentation(p)
                }).ToArray();

                signatures[i] = new SignatureInformation {
                    label         = _docSource.GetSignatureString(ft, selfType, i),
                    documentation = _docSource.FormatDocumentation(ft.Documentation),
                    parameters    = parameters
                };
            }

            var index = location.ToIndex(analysis.Ast);

            if (call.GetArgumentAtIndex(analysis.Ast, index, out var activeParameter) && activeParameter < 0)
            {
                // Returned 'true' and activeParameter == -1 means that we are after
                // the trailing comma, so assume partially typed expression such as 'pow(x, y, |)
                activeParameter = call.Args.Count;
            }

            var activeSignature = -1;

            if (activeParameter >= 0)
            {
                // TODO: Better selection of active signature by argument set
                activeSignature = signatures
                                  .Select((s, i) => Tuple.Create(s, i))
                                  .OrderBy(t => t.Item1.parameters.Length)
                                  .FirstOrDefault(t => t.Item1.parameters.Length > activeParameter)
                                  ?.Item2 ?? -1;
            }

            activeSignature = activeSignature >= 0
                ? activeSignature
                : (signatures.Length > 0 ? 0 : -1);

            return(new SignatureHelp {
                signatures = signatures.ToArray(),
                activeSignature = activeSignature,
                activeParameter = activeParameter
            });
        }