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