internal async Task <Hover> Hover(TextDocumentPositionParams @params, CancellationToken cancellationToken) { var uri = @params.textDocument.uri; ProjectFiles.GetAnalysis(@params.textDocument, @params.position, @params._version, out var entry, out var tree); TraceMessage($"Hover in {uri} at {@params.position}"); var analysis = entry != null ? await entry.GetAnalysisAsync(50, cancellationToken) : null; if (analysis == null) { TraceMessage($"No analysis found for {uri}"); return(EmptyHover); } tree = GetParseTree(entry, uri, cancellationToken, out var version) ?? tree; Expression expr; SourceSpan?exprSpan; var finder = new ExpressionFinder(tree, GetExpressionOptions.Hover); expr = finder.GetExpression(@params.position) as Expression; exprSpan = expr?.GetSpan(tree); if (expr == null) { TraceMessage($"No hover info found in {uri} at {@params.position}"); return(EmptyHover); } TraceMessage($"Getting hover for {expr.ToCodeString(tree, CodeFormattingOptions.Traditional)}"); // First try values from expression. This works for the import statement most of the time. var values = analysis.GetValues(expr, @params.position, null).ToList(); if (values.Count == 0) { // See if this is hover over import statement var index = tree.LocationToIndex(@params.position); var w = new ImportedModuleNameWalker(entry, index, tree); tree.Walk(w); if (w.ImportedType != null) { values = analysis.GetValues(w.ImportedType.Name, @params.position).ToList(); } else { var sb = new StringBuilder(); var span = SourceSpan.Invalid; foreach (var n in w.ImportedModules) { if (Analyzer.Modules.TryGetImportedModule(n.Name, out var modRef) && modRef.AnalysisModule != null) { if (sb.Length > 0) { sb.AppendLine(); sb.AppendLine(); } sb.Append(_displayTextBuilder.GetModuleDocumentation(modRef)); span = span.IsValid ? span.Union(n.SourceSpan) : n.SourceSpan; } } if (sb.Length > 0) { return(new Hover { contents = sb.ToString(), range = span }); } } } if (values.Count > 0) { string originalExpr; if (expr is ConstantExpression || expr is ErrorExpression) { originalExpr = null; } else { originalExpr = @params._expr?.Trim(); if (string.IsNullOrEmpty(originalExpr)) { originalExpr = expr.ToCodeString(tree, CodeFormattingOptions.Traditional); } } var names = values.Select(GetFullTypeName).Where(n => !string.IsNullOrEmpty(n)).Distinct().ToArray(); var res = new Hover { contents = GetMarkupContent( _displayTextBuilder.GetDocumentation(values, originalExpr), _clientCaps.textDocument?.hover?.contentFormat), range = exprSpan, _version = version?.Version, _typeNames = names }; return(res); } return(EmptyHover); }
public override Task <SignatureHelp> SignatureHelp(TextDocumentPositionParams @params) { var uri = @params.textDocument.uri; _projectFiles.GetAnalysis(@params.textDocument, @params.position, @params._version, out var entry, out var tree); TraceMessage($"Signatures in {uri} at {@params.position}"); var analysis = entry?.Analysis; if (analysis == null) { TraceMessage($"No analysis found for {uri}"); return(Task.FromResult(new SignatureHelp())); } IEnumerable <IOverloadResult> overloads; int activeSignature = -1, activeParameter = -1; if (!string.IsNullOrEmpty(@params._expr)) { TraceMessage($"Getting signatures for {@params._expr}"); overloads = analysis.GetSignatures(@params._expr, @params.position); } else { var finder = new ExpressionFinder(tree, new GetExpressionOptions { Calls = true }); var index = tree.LocationToIndex(@params.position); if (finder.GetExpression(@params.position) is CallExpression callExpr) { TraceMessage($"Getting signatures for {callExpr.ToCodeString(tree, CodeFormattingOptions.Traditional)}"); overloads = analysis.GetSignatures(callExpr.Target, @params.position); activeParameter = -1; if (callExpr.GetArgumentAtIndex(tree, index, out 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 = callExpr.Args.Count; } } else { TraceMessage($"No signatures found in {uri} at {@params.position}"); return(Task.FromResult(new SignatureHelp())); } } var sigs = overloads.Select(ToSignatureInformation).ToArray(); if (activeParameter >= 0 && activeSignature < 0) { // TODO: Better selection of active signature activeSignature = sigs .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 : (sigs.Length > 0 ? 0 : -1); var sh = new SignatureHelp { signatures = sigs, activeSignature = activeSignature, activeParameter = activeParameter }; return(Task.FromResult(sh)); }
public override Task <Hover> Hover(TextDocumentPositionParams @params) => Hover(@params, CancellationToken.None);
public override async Task <Hover> Hover(TextDocumentPositionParams @params) { await _analyzerCreationTask; await IfTestWaitForAnalysisCompleteAsync(); var uri = @params.textDocument.uri; _projectFiles.GetAnalysis(@params.textDocument, @params.position, @params._version, out var entry, out var tree); TraceMessage($"Hover in {uri} at {@params.position}"); var analysis = entry?.Analysis; if (analysis == null) { TraceMessage($"No analysis found for {uri}"); return(EmptyHover); } tree = GetParseTree(entry, uri, CancellationToken, out var version) ?? tree; var index = tree.LocationToIndex(@params.position); var w = new ImportedModuleNameWalker(entry.ModuleName, index); tree.Walk(w); if (!string.IsNullOrEmpty(w.ImportedName) && _analyzer.Modules.TryImport(w.ImportedName, out var modRef)) { var doc = _displayTextBuilder.GetModuleDocumentation(modRef); return(new Hover { contents = doc }); } Expression expr; SourceSpan?exprSpan; Analyzer.InterpreterScope scope = null; if (!string.IsNullOrEmpty(@params._expr)) { TraceMessage($"Getting hover for {@params._expr}"); expr = analysis.GetExpressionForText(@params._expr, @params.position, out scope, out var exprTree); // This span will not be valid within the document, but it will at least // have the correct length. If we have passed "_expr" then we are likely // planning to ignore the returned span anyway. exprSpan = expr?.GetSpan(exprTree); } else { var finder = new ExpressionFinder(tree, GetExpressionOptions.Hover); expr = finder.GetExpression(@params.position) as Expression; exprSpan = expr?.GetSpan(tree); } if (expr == null) { TraceMessage($"No hover info found in {uri} at {@params.position}"); return(EmptyHover); } TraceMessage($"Getting hover for {expr.ToCodeString(tree, CodeFormattingOptions.Traditional)}"); var values = analysis.GetValues(expr, @params.position, scope).ToList(); string originalExpr; if (expr is ConstantExpression || expr is ErrorExpression) { originalExpr = null; } else { originalExpr = @params._expr?.Trim(); if (string.IsNullOrEmpty(originalExpr)) { originalExpr = expr.ToCodeString(tree, CodeFormattingOptions.Traditional); } } var names = values.Select(GetFullTypeName).Where(n => !string.IsNullOrEmpty(n)).Distinct().ToArray(); var res = new Hover { contents = GetMarkupContent( _displayTextBuilder.GetDocumentation(values, originalExpr), _clientCaps.textDocument?.hover?.contentFormat), range = exprSpan, _version = version, _typeNames = names }; return(res); }