File: Library\ClassView\AbstractSyncClassViewCommandHandler.cs
Web Access
Project: ..\..\..\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_ckcrqypr_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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.LanguageServices.Utilities;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
using Roslyn.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Library.ClassView
{
    internal abstract class AbstractSyncClassViewCommandHandler : ForegroundThreadAffinitizedObject,
        ICommandHandler<SyncClassViewCommandArgs>
    {
        private const string ClassView = "Class View";
 
        private readonly IServiceProvider _serviceProvider;
 
        public string DisplayName => ServicesVSResources.Sync_Class_View;
 
        protected AbstractSyncClassViewCommandHandler(
            IThreadingContext threadingContext,
            SVsServiceProvider serviceProvider)
            : base(threadingContext)
        {
            Contract.ThrowIfNull(serviceProvider);
 
            _serviceProvider = serviceProvider;
        }
 
        public bool ExecuteCommand(SyncClassViewCommandArgs args, CommandExecutionContext context)
        {
            this.AssertIsForeground();
 
            var caretPosition = args.TextView.GetCaretPoint(args.SubjectBuffer) ?? -1;
            if (caretPosition < 0)
            {
                return false;
            }
 
            var snapshot = args.SubjectBuffer.CurrentSnapshot;
 
            using var waitScope = context.OperationContext.AddScope(allowCancellation: true, string.Format(ServicesVSResources.Synchronizing_with_0, ClassView));
            var document = snapshot.GetFullyLoadedOpenDocumentInCurrentContextWithChangesAsync(
                context.OperationContext).WaitAndGetResult(context.OperationContext.UserCancellationToken);
            if (document == null)
            {
                return true;
            }
 
            var syntaxFactsService = document.GetLanguageService<ISyntaxFactsService>();
            if (syntaxFactsService == null)
            {
                return true;
            }
 
            var libraryService = document.GetLanguageService<ILibraryService>();
            if (libraryService == null)
            {
                return true;
            }
 
            var userCancellationToken = context.OperationContext.UserCancellationToken;
            var semanticModel = document
                .GetSemanticModelAsync(userCancellationToken)
                .WaitAndGetResult(userCancellationToken);
 
            var root = semanticModel.SyntaxTree
                .GetRootAsync(userCancellationToken)
                .WaitAndGetResult(userCancellationToken);
 
            var memberDeclaration = syntaxFactsService.GetContainingMemberDeclaration(root, caretPosition);
 
            var symbol = memberDeclaration != null
                ? semanticModel.GetDeclaredSymbol(memberDeclaration, userCancellationToken)
                : null;
 
            while (symbol != null && !IsValidSymbolToSynchronize(symbol))
            {
                symbol = symbol.ContainingSymbol;
            }
 
            IVsNavInfo navInfo = null;
            if (symbol != null)
            {
                navInfo = libraryService.NavInfoFactory.CreateForSymbol(symbol, document.Project, semanticModel.Compilation, useExpandedHierarchy: true);
            }
 
            navInfo ??= libraryService.NavInfoFactory.CreateForProject(document.Project);
 
            if (navInfo == null)
            {
                return true;
            }
 
            var navigationTool = _serviceProvider.GetServiceOnMainThread<SVsClassView, IVsNavigationTool>();
            navigationTool.NavigateToNavInfo(navInfo);
            return true;
        }
 
        private static bool IsValidSymbolToSynchronize(ISymbol symbol)
            => symbol.Kind is SymbolKind.Event or
            SymbolKind.Field or
            SymbolKind.Method or
            SymbolKind.NamedType or
            SymbolKind.Property;
 
        public CommandState GetCommandState(SyncClassViewCommandArgs args)
            => Commanding.CommandState.Available;
    }
}