protected async Task <LSP.Location[]?> GetDefinitionAsync(LSP.TextDocumentPositionParams request, bool typeOnly, RequestContext context, CancellationToken cancellationToken)
        {
            var document = context.Document;

            if (document == null)
            {
                return(null);
            }

            var locations = ArrayBuilder <LSP.Location> .GetInstance();

            var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false);

            var findDefinitionService = document.GetRequiredLanguageService <IFindDefinitionService>();

            var definitions = await findDefinitionService.FindDefinitionsAsync(document, position, cancellationToken).ConfigureAwait(false);

            if (definitions.Any())
            {
                foreach (var definition in definitions)
                {
                    if (!ShouldInclude(definition, typeOnly))
                    {
                        continue;
                    }

                    var location = await ProtocolConversions.TextSpanToLocationAsync(
                        definition.Document, definition.SourceSpan, definition.IsStale, cancellationToken).ConfigureAwait(false);

                    locations.AddIfNotNull(location);
                }
            }
            else if (document.SupportsSemanticModel && _metadataAsSourceFileService != null)
            {
                // No definition found - see if we can get metadata as source but that's only applicable for C#\VB.
                var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false);

                if (symbol != null && _metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
                {
                    if (!typeOnly || symbol is ITypeSymbol)
                    {
                        var options         = _globalOptions.GetMetadataAsSourceOptions(document.Project.LanguageServices);
                        var declarationFile = await _metadataAsSourceFileService.GetGeneratedFileAsync(document.Project, symbol, signaturesOnly : false, options, cancellationToken).ConfigureAwait(false);

                        var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span;
                        locations.Add(new LSP.Location
                        {
                            Uri   = new Uri(declarationFile.FilePath),
                            Range = ProtocolConversions.LinePositionToRange(linePosSpan),
                        });
                    }
                }
            }

            return(locations.ToArrayAndFree());
        private async Task <INavigableLocation?> GetNavigableLocationForMetadataAsync(
            Project project, ISymbol symbol, CancellationToken cancellationToken)
        {
            var masOptions = _globalOptions.GetMetadataAsSourceOptions();

            var result = await _metadataAsSourceFileService.GetGeneratedFileAsync(project, symbol, signaturesOnly : false, masOptions, cancellationToken).ConfigureAwait(false);

            return(new NavigableLocation(async(options, cancellationToken) =>
            {
                await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

                var vsRunningDocumentTable4 = _serviceProvider.GetServiceOnMainThread <SVsRunningDocumentTable, IVsRunningDocumentTable4>();
                var fileAlreadyOpen = vsRunningDocumentTable4.IsMonikerValid(result.FilePath);

                var openDocumentService = _serviceProvider.GetServiceOnMainThread <SVsUIShellOpenDocument, IVsUIShellOpenDocument>();
                openDocumentService.OpenDocumentViaProject(result.FilePath, VSConstants.LOGVIEWID.TextView_guid, out _, out _, out _, out var windowFrame);

                var documentCookie = vsRunningDocumentTable4.GetDocumentCookie(result.FilePath);

                var vsTextBuffer = (IVsTextBuffer)vsRunningDocumentTable4.GetDocumentData(documentCookie);

                // Set the buffer to read only, just in case the file isn't
                ErrorHandler.ThrowOnFailure(vsTextBuffer.GetStateFlags(out var flags));
                flags |= (int)BUFFERSTATEFLAGS.BSF_USER_READONLY;
                ErrorHandler.ThrowOnFailure(vsTextBuffer.SetStateFlags(flags));

                var textBuffer = _editorAdaptersFactory.GetDataBuffer(vsTextBuffer);

                if (!fileAlreadyOpen)
                {
                    ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_IsProvisional, true));
                    ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_OverrideCaption, result.DocumentTitle));
                    ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_OverrideToolTip, result.DocumentTooltip));
                }

                windowFrame.Show();

                var openedDocument = textBuffer?.AsTextContainer().GetRelatedDocuments().FirstOrDefault();
                if (openedDocument != null)
                {
                    var editorWorkspace = openedDocument.Project.Solution.Workspace;
                    var navigationService = editorWorkspace.Services.GetRequiredService <IDocumentNavigationService>();

                    await navigationService.TryNavigateToSpanAsync(
                        this.ThreadingContext,
                        editorWorkspace,
                        openedDocument.Id,
                        result.IdentifierLocation.SourceSpan,
                        options with {
                        PreferProvisionalTab = true
                    },
                        cancellationToken).ConfigureAwait(false);
                }
예제 #3
0
        private static async Task <LSP.Location[]> GetSymbolDefinitionLocationsAsync(XamlSymbolDefinition symbolDefinition, RequestContext context, IMetadataAsSourceFileService metadataAsSourceFileService, IGlobalOptionService globalOptions, CancellationToken cancellationToken)
        {
            Contract.ThrowIfNull(symbolDefinition.Symbol);

            using var _ = ArrayBuilder <LSP.Location> .GetInstance(out var locations);

            var symbol = symbolDefinition.Symbol;

            var items = NavigableItemFactory.GetItemsFromPreferredSourceLocations(context.Solution, symbol, displayTaggedParts: null, cancellationToken);

            if (items.Any())
            {
                foreach (var item in items)
                {
                    var location = await ProtocolConversions.TextSpanToLocationAsync(
                        item.Document, item.SourceSpan, item.IsStale, cancellationToken).ConfigureAwait(false);

                    locations.AddIfNotNull(location);
                }
            }
            else
            {
                if (metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
                {
                    var project = context.Document?.GetCodeProject();
                    if (project != null)
                    {
                        var options         = globalOptions.GetMetadataAsSourceOptions(project.LanguageServices);
                        var declarationFile = await metadataAsSourceFileService.GetGeneratedFileAsync(project, symbol, signaturesOnly : true, options, cancellationToken).ConfigureAwait(false);

                        var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span;
                        locations.Add(new LSP.Location
                        {
                            Uri   = new Uri(declarationFile.FilePath),
                            Range = ProtocolConversions.LinePositionToRange(linePosSpan),
                        });
                    }
                }
            }

            return(locations.ToArray());
        }
        /// <summary>
        /// Internal for testing purposes.
        /// </summary>
        internal async Task <ImmutableArray <CodeDefinitionWindowLocation> > GetContextFromPointAsync(
            Document document, int position, CancellationToken cancellationToken)
        {
            var workspace      = document.Project.Solution.Workspace;
            var navigableItems = await GoToDefinitionHelpers.GetDefinitionsAsync(document, position, cancellationToken).ConfigureAwait(false);

            if (navigableItems?.Any() == true)
            {
                var navigationService = workspace.Services.GetRequiredService <IDocumentNavigationService>();

                using var _ = PooledObjects.ArrayBuilder <CodeDefinitionWindowLocation> .GetInstance(out var builder);

                foreach (var item in navigableItems)
                {
                    if (await navigationService.CanNavigateToSpanAsync(workspace, item.Document.Id, item.SourceSpan, cancellationToken).ConfigureAwait(false))
                    {
                        var text = await item.Document.GetTextAsync(cancellationToken).ConfigureAwait(false);

                        var linePositionSpan = text.Lines.GetLinePositionSpan(item.SourceSpan);

                        if (item.Document.FilePath != null)
                        {
                            builder.Add(new CodeDefinitionWindowLocation(item.DisplayTaggedParts.JoinText(), item.Document.FilePath, linePositionSpan.Start));
                        }
                    }
                }

                return(builder.ToImmutable());
            }

            // We didn't have regular source references, but possibly:
            // 1. Another language (like XAML) will take over via ISymbolNavigationService
            // 2. There are no locations from source, so we'll try to generate a metadata as source file and use that
            var symbol = await SymbolFinder.FindSymbolAtPositionAsync(
                document,
                position,
                cancellationToken : cancellationToken).ConfigureAwait(false);

            if (symbol == null)
            {
                return(ImmutableArray <CodeDefinitionWindowLocation> .Empty);
            }

            var symbolNavigationService = workspace.Services.GetRequiredService <ISymbolNavigationService>();
            var definitionItem          = symbol.ToNonClassifiedDefinitionItem(document.Project.Solution, includeHiddenLocations: false);
            var result = await symbolNavigationService.GetExternalNavigationSymbolLocationAsync(definitionItem, cancellationToken).ConfigureAwait(false);

            if (result != null)
            {
                return(ImmutableArray.Create(new CodeDefinitionWindowLocation(symbol.ToDisplayString(), result.Value.filePath, result.Value.linePosition)));
            }
            else if (_metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol))
            {
                var options         = _globalOptions.GetMetadataAsSourceOptions(document.Project.LanguageServices);
                var declarationFile = await _metadataAsSourceFileService.GetGeneratedFileAsync(document.Project, symbol, signaturesOnly : false, options, cancellationToken).ConfigureAwait(false);

                var identifierSpan = declarationFile.IdentifierLocation.GetLineSpan().Span;
                return(ImmutableArray.Create(new CodeDefinitionWindowLocation(symbol.ToDisplayString(), declarationFile.FilePath, identifierSpan.Start)));
            }

            return(ImmutableArray <CodeDefinitionWindowLocation> .Empty);
        }