File: Implementation\VisualStudioSupportsFeatureService.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_jtj4zmta_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.Implementation.Venus;
using Microsoft.VisualStudio.Text;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.SuggestionService;
 
internal sealed class VisualStudioSupportsFeatureService
{
    private const string ContainedLanguageMarker = nameof(ContainedLanguageMarker);
 
    [ExportWorkspaceService(typeof(ITextBufferSupportsFeatureService), ServiceLayer.Host), Shared]
    private class VisualStudioTextBufferSupportsFeatureService : ITextBufferSupportsFeatureService
    {
        [ImportingConstructor]
        [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
        public VisualStudioTextBufferSupportsFeatureService()
        {
        }
 
        public bool SupportsCodeFixes(ITextBuffer textBuffer)
        {
            return SupportsCodeFixesWorker(GetContainedDocumentId(textBuffer));
        }
 
        public bool SupportsRefactorings(ITextBuffer textBuffer)
        {
            return SupportsRefactoringsWorker(GetContainedDocumentId(textBuffer));
        }
 
        public bool SupportsRename(ITextBuffer textBuffer)
        {
            // TS creates generated documents to back script blocks in razor generated files.
            // These files are opened in the roslyn workspace but are not valid to rename
            // as they are not proper buffers.  So we exclude any buffer that is marked as a contained language.
            if (textBuffer.Properties.TryGetProperty<bool>(ContainedLanguageMarker, out var markerValue) && markerValue)
            {
                return false;
            }
 
            var sourceTextContainer = textBuffer.AsTextContainer();
            if (Workspace.TryGetWorkspace(sourceTextContainer, out var workspace))
            {
                var documentId = workspace.GetDocumentIdInCurrentContext(sourceTextContainer);
                return SupportsRenameWorker(workspace.CurrentSolution.GetRelatedDocumentIds(documentId));
            }
 
            return false;
        }
 
        public bool SupportsNavigationToAnyPosition(ITextBuffer textBuffer)
            => SupportsNavigationToAnyPositionWorker(GetContainedDocumentId(textBuffer));
 
        private static DocumentId GetContainedDocumentId(ITextBuffer textBuffer)
        {
            var sourceTextContainer = textBuffer.AsTextContainer();
            if (Workspace.TryGetWorkspace(sourceTextContainer, out var workspace)
                && workspace is VisualStudioWorkspaceImpl vsWorkspace)
            {
                return vsWorkspace.GetDocumentIdInCurrentContext(sourceTextContainer);
            }
 
            return null;
        }
    }
 
    [ExportWorkspaceService(typeof(IDocumentSupportsFeatureService), ServiceLayer.Host), Shared]
    private class VisualStudioDocumentSupportsFeatureService : IDocumentSupportsFeatureService
    {
        [ImportingConstructor]
        [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
        public VisualStudioDocumentSupportsFeatureService()
        {
        }
 
        public bool SupportsCodeFixes(Document document)
            => SupportsCodeFixesWorker(document.Id);
 
        public bool SupportsRefactorings(Document document)
            => SupportsRefactoringsWorker(document.Id);
 
        public bool SupportsRename(Document document)
            => SupportsRenameWorker(document.Project.Solution.GetRelatedDocumentIds(document.Id));
 
        public bool SupportsSemanticSnippets(Document document)
            => SupportsSemanticSnippetsWorker(document.Id);
 
        public bool SupportsNavigationToAnyPosition(Document document)
            => SupportsNavigationToAnyPositionWorker(document.Id);
    }
 
    private static bool SupportsCodeFixesWorker(DocumentId id)
        => ContainedDocument.TryGetContainedDocument(id) == null;
 
    private static bool SupportsRefactoringsWorker(DocumentId id)
        => ContainedDocument.TryGetContainedDocument(id) == null;
 
    private static bool SupportsRenameWorker(ImmutableArray<DocumentId> ids)
    {
        return ids.Select(id => ContainedDocument.TryGetContainedDocument(id))
                .All(cd => cd == null || cd.SupportsRename);
    }
 
    private static bool SupportsSemanticSnippetsWorker(DocumentId id)
    {
        var containedDocument = ContainedDocument.TryGetContainedDocument(id);
        return containedDocument == null || containedDocument.SupportsSemanticSnippets;
    }
 
    private static bool SupportsNavigationToAnyPositionWorker(DocumentId id)
        => ContainedDocument.TryGetContainedDocument(id) == null;
}