|
// 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;
}
|