/// <summary> /// Using the find usages service is more expensive than using the definitions service because a lot of unnecessary information is computed. However, some languages /// don't provide an <see cref="IGoToDefinitionService"/> implementation that will return definitions so we must use <see cref="IFindUsagesService"/>. /// </summary> private async Task <List <LSP.Location> > GetDefinitionsWithFindUsagesService(Document document, int pos, CancellationToken cancellationToken) { var findUsagesService = document.Project.LanguageServices.GetService <IFindUsagesService>(); var context = new SimpleFindUsagesContext(cancellationToken); // Roslyn calls into third party extensions to compute reference results and needs to be on the UI thread to compute results. // This is not great for us and ideally we should ask for a Roslyn API where we can make this call without blocking the UI. await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); await findUsagesService.FindReferencesAsync(document, pos, context).ConfigureAwait(false); var locations = new List <LSP.Location>(); var definitions = context.GetDefinitions(); if (definitions != null) { foreach (var definition in definitions) { foreach (var docSpan in definition.SourceSpans) { locations.Add(await ProtocolConversions.DocumentSpanToLocationAsync(docSpan, cancellationToken).ConfigureAwait(false)); } } } return(locations); }
public override async Task <LSP.Location[]> HandleRequestAsync(LSP.TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; Contract.ThrowIfNull(document); var locations = ArrayBuilder <LSP.Location> .GetInstance(); var findUsagesService = document.GetRequiredLanguageService <IFindUsagesService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var findUsagesContext = new SimpleFindUsagesContext(); await FindImplementationsAsync(findUsagesService, document, position, findUsagesContext, cancellationToken).ConfigureAwait(false); foreach (var definition in findUsagesContext.GetDefinitions()) { var text = definition.GetClassifiedText(); foreach (var sourceSpan in definition.SourceSpans) { if (context.ClientCapabilities?.HasVisualStudioLspCapability() == true) { locations.AddIfNotNull(await ProtocolConversions.DocumentSpanToLocationWithTextAsync(sourceSpan, text, cancellationToken).ConfigureAwait(false)); } else { locations.AddIfNotNull(await ProtocolConversions.DocumentSpanToLocationAsync(sourceSpan, cancellationToken).ConfigureAwait(false)); } } } return(locations.ToArrayAndFree()); }
private async Task <LSP.ReferenceGroup[]> GetReferenceGroupsAsync(LSP.ReferenceParams request, SimpleFindUsagesContext context, CancellationToken cancellationToken) { var definitionMap = new Dictionary <DefinitionItem, List <SourceReferenceItem> >(); foreach (var reference in context.GetReferences()) { if (!definitionMap.ContainsKey(reference.Definition)) { definitionMap.Add(reference.Definition, new List <SourceReferenceItem>()); } definitionMap[reference.Definition].Add(reference); } var referenceGroups = ArrayBuilder <LSP.ReferenceGroup> .GetInstance(); foreach (var keyValuePair in definitionMap) { var definition = keyValuePair.Key; var references = keyValuePair.Value; var referenceGroup = new LSP.ReferenceGroup(); var text = definition.GetClassifiedText(); referenceGroup.Definition = await ProtocolConversions.DocumentSpanToLocationWithTextAsync(definition.SourceSpans.First(), text, cancellationToken).ConfigureAwait(false); referenceGroup.DefinitionIcon = new ImageElement(definition.Tags.GetFirstGlyph().GetImageId()); var locationWithTexts = new ArrayBuilder <LSP.LocationWithText>(); foreach (var reference in references) { var classifiedSpansAndHighlightSpan = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(reference.SourceSpan, context.CancellationToken).ConfigureAwait(false); var classifiedSpans = classifiedSpansAndHighlightSpan.ClassifiedSpans; var referenceLocation = await ProtocolConversions.DocumentSpanToLocationAsync(reference.SourceSpan, cancellationToken).ConfigureAwait(false); var docText = await reference.SourceSpan.Document.GetTextAsync(context.CancellationToken).ConfigureAwait(false); var classifiedText = new ClassifiedTextElement(classifiedSpans.Select(cspan => new ClassifiedTextRun(cspan.ClassificationType, docText.ToString(cspan.TextSpan)))); var locationWithText = new LSP.LocationWithText { Range = referenceLocation.Range, Uri = referenceLocation.Uri, Text = classifiedText }; locationWithTexts.Add(locationWithText); } referenceGroup.References = locationWithTexts.ToArrayAndFree(); referenceGroups.Add(referenceGroup); } return(referenceGroups.ToArrayAndFree()); }
// Local functions static async Task <LSP.Location?> ComputeLocationAsync( Document document, int position, DocumentSpan documentSpan, IMetadataAsSourceFileService metadataAsSourceFileService, CancellationToken cancellationToken) { if (documentSpan != default) { // We do have a document span, so compute location normally. return(await ProtocolConversions.DocumentSpanToLocationAsync(documentSpan, cancellationToken).ConfigureAwait(false)); } // If we have no document span, our location may be in metadata or may be a namespace. var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false); if (symbol == null || symbol.Locations.IsEmpty || symbol.Kind == SymbolKind.Namespace) { // Either: // (1) We couldn't find the location in metadata and it's not in any of our known documents. // (2) The symbol is a namespace (and therefore has no location). return(null); } var declarationFile = await metadataAsSourceFileService.GetGeneratedFileAsync( document.Project, symbol, allowDecompilation : false, cancellationToken).ConfigureAwait(false); var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span; if (string.IsNullOrEmpty(declarationFile.FilePath)) { return(null); } try { return(new LSP.Location { Uri = ProtocolConversions.GetUriFromFilePath(declarationFile.FilePath), Range = ProtocolConversions.LinePositionToRange(linePosSpan), }); } catch (UriFormatException e) when(FatalError.ReportAndCatch(e)) { // We might reach this point if the file path is formatted incorrectly. return(null); } }
private static async Task <LSP.Location[]> GetLocationsAsync(LSP.ReferenceParams request, SimpleFindUsagesContext context, CancellationToken cancellationToken) { var locations = ArrayBuilder <LSP.Location> .GetInstance(); if (request.Context.IncludeDeclaration) { foreach (var definition in context.GetDefinitions()) { foreach (var docSpan in definition.SourceSpans) { locations.Add(await ProtocolConversions.DocumentSpanToLocationAsync(docSpan, cancellationToken).ConfigureAwait(false)); } } } foreach (var reference in context.GetReferences()) { locations.Add(await ProtocolConversions.DocumentSpanToLocationAsync(reference.SourceSpan, cancellationToken).ConfigureAwait(false)); } return(locations.ToArrayAndFree()); }
public async Task <object> HandleRequestAsync(Solution solution, LSP.TextDocumentPositionParams request, LSP.ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { var locations = ArrayBuilder <LSP.Location> .GetInstance(); var document = solution.GetDocumentFromURI(request.TextDocument.Uri); if (document == null) { return(locations.ToArrayAndFree()); } var findUsagesService = document.Project.LanguageServices.GetService <IFindUsagesService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var context = new SimpleFindUsagesContext(cancellationToken); await FindImplementationsAsync(findUsagesService, document, position, context).ConfigureAwait(false); foreach (var definition in context.GetDefinitions()) { var text = definition.GetClassifiedText(); foreach (var sourceSpan in context.GetDefinitions().SelectMany(definition => definition.SourceSpans)) { if (clientCapabilities?.HasVisualStudioLspCapability() == true) { locations.Add(await ProtocolConversions.DocumentSpanToLocationWithTextAsync(sourceSpan, text, cancellationToken).ConfigureAwait(false)); } else { locations.Add(await ProtocolConversions.DocumentSpanToLocationAsync(sourceSpan, cancellationToken).ConfigureAwait(false)); } } } return(locations.ToArrayAndFree()); }