public CompletionItemEx CreateCompletionItem(string text, CompletionItemKind kind, IMember member, IPythonType self = null, string label = null)
        {
            var t         = member?.GetPythonType();
            var docFormat = _docSource.DocumentationFormat;

            if (Options.addBrackets && (kind == CompletionItemKind.Constructor || kind == CompletionItemKind.Function || kind == CompletionItemKind.Method))
            {
                label     = text;
                text     += "($0)";
                docFormat = InsertTextFormat.Snippet;
            }

            return(new CompletionItemEx {
                label = label ?? text,
                insertText = text,
                insertTextFormat = docFormat,
                // Place regular items first, advanced entries last
                sortText = char.IsLetter(text, 0) ? "1" : "2",
                kind = kind,
                documentation = !t.IsUnknown() ? _docSource.GetHover(label ?? text, member, self) : null,
                // Custom fields used by the LS extensions that may modify
                // the completion list. Not passed to the client.
                Member = member,
                PythonType = self
            });
        }
예제 #2
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
            });
        }
예제 #3
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
            });
        }