public Hover GetHover()

in src/LanguageServer/Impl/Sources/HoverSource.cs [37:147]


        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) {
                    eval.LookupNameInScopes(nex.Name, out _, out variable, LookupOptions.All);
                    if (IsInvalidClassMember(variable, hoverScopeStatement, location.ToIndex(analysis.Ast))) {
                        return null;
                    }
                }

                value = variable?.Value ?? eval.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 = eval.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
            };
        }