File: Library\ClassView\AbstractSyncClassViewCommandHandler.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_oab33ils_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.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 : ICommandHandler<SyncClassViewCommandArgs>
{
    private const string ClassView = "Class View";
    private readonly IThreadingContext _threadingContext;
    private readonly IServiceProvider _serviceProvider;
 
    public string DisplayName => ServicesVSResources.Sync_Class_View;
 
    protected AbstractSyncClassViewCommandHandler(
        IThreadingContext threadingContext,
        SVsServiceProvider serviceProvider)
    {
        Contract.ThrowIfNull(serviceProvider);
        _threadingContext = threadingContext;
        _serviceProvider = serviceProvider;
    }
 
    public bool ExecuteCommand(SyncClassViewCommandArgs args, CommandExecutionContext context)
    {
        _threadingContext.ThrowIfNotOnUIThread();
 
        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;
}