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
};
}