提交 c517a927 编写于 作者: G Gen Lu

Revert #31132

上级 553cba01
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Experimentation; using Microsoft.CodeAnalysis.Experimentation;
...@@ -68,19 +66,13 @@ internal sealed class KeybindingResetDetector : ForegroundThreadAffinitizedObjec ...@@ -68,19 +66,13 @@ internal sealed class KeybindingResetDetector : ForegroundThreadAffinitizedObjec
private OleComponent _oleComponent; private OleComponent _oleComponent;
private uint _priorityCommandTargetCookie = VSConstants.VSCOOKIE_NIL; private uint _priorityCommandTargetCookie = VSConstants.VSCOOKIE_NIL;
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
/// <summary> /// <summary>
/// If false, ReSharper is either not installed, or has been disabled in the extension manager. /// If false, ReSharper is either not installed, or has been disabled in the extension manager.
/// If true, the ReSharper extension is enabled. ReSharper's internal status could be either suspended or enabled. /// If true, the ReSharper extension is enabled. ReSharper's internal status could be either suspended or enabled.
/// </summary> /// </summary>
private bool _resharperExtensionInstalledAndEnabled = false; private bool _resharperExtensionEnabled = false;
private bool _infoBarOpen = false;
private bool _isFirstRun = true;
/// <summary> private bool _infoBarOpen = false;
/// Chain all update tasks so that task runs serially
/// </summary>
private Task _lastTask = Task.CompletedTask;
[ImportingConstructor] [ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
...@@ -121,9 +113,9 @@ private void InitializeCore() ...@@ -121,9 +113,9 @@ private void InitializeCore()
return; return;
} }
_resharperExtensionInstalledAndEnabled = extensionEnabled != 0; _resharperExtensionEnabled = extensionEnabled != 0;
if (_resharperExtensionInstalledAndEnabled) if (_resharperExtensionEnabled)
{ {
// We need to monitor for suspend/resume commands, so create and install the command target and the modal callback. // We need to monitor for suspend/resume commands, so create and install the command target and the modal callback.
var priorityCommandTargetRegistrar = _serviceProvider.GetService<IVsRegisterPriorityCommandTarget, SVsRegisterPriorityCommandTarget>(); var priorityCommandTargetRegistrar = _serviceProvider.GetService<IVsRegisterPriorityCommandTarget, SVsRegisterPriorityCommandTarget>();
...@@ -143,46 +135,16 @@ private void InitializeCore() ...@@ -143,46 +135,16 @@ private void InitializeCore()
_oleComponent.ModalStateChanged += OnModalStateChanged; _oleComponent.ModalStateChanged += OnModalStateChanged;
} }
// run it from background and fire and forget UpdateStateMachine();
StartUpdateStateMachine();
} }
private void StartUpdateStateMachine() private void UpdateStateMachine()
{ {
// cancel previous state machine update request AssertIsForeground();
_cancellationTokenSource.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = _cancellationTokenSource.Token;
// make sure all state machine change work is serialized so that cancellation
// doesn't mess the state up.
_lastTask = _lastTask.ContinueWith(async _ =>
{
await UpdateStateMachineWorkerAsync(cancellationToken).ConfigureAwait(false);
}, cancellationToken, TaskContinuationOptions.LazyCancellation, TaskScheduler.Default).Unwrap();
}
private async Task UpdateStateMachineWorkerAsync(CancellationToken cancellationToken) var currentStatus = IsReSharperEnabled();
{
var options = _workspace.Options; var options = _workspace.Options;
var lastStatus = options.GetOption(KeybindingResetOptions.ReSharperStatus); ReSharperStatus lastStatus = options.GetOption(KeybindingResetOptions.ReSharperStatus);
ReSharperStatus currentStatus;
try
{
currentStatus = await IsReSharperRunningAsync(lastStatus, cancellationToken)
.ConfigureAwait(false);
}
catch (OperationCanceledException)
{
return;
}
if (!_isFirstRun && currentStatus == lastStatus)
{
return;
}
options = options.WithChangedOption(KeybindingResetOptions.ReSharperStatus, currentStatus); options = options.WithChangedOption(KeybindingResetOptions.ReSharperStatus, currentStatus);
...@@ -199,12 +161,6 @@ private async Task UpdateStateMachineWorkerAsync(CancellationToken cancellationT ...@@ -199,12 +161,6 @@ private async Task UpdateStateMachineWorkerAsync(CancellationToken cancellationT
// the extension, then reenables the extension. We will show the gold bar after the switch // the extension, then reenables the extension. We will show the gold bar after the switch
// if there is still a pending show. // if there is still a pending show.
// If ReSharper was suspended and the user closed and reopened VS, we want to reset the gold bar
else if (_isFirstRun)
{
options = options.WithChangedOption(KeybindingResetOptions.NeedsReset, true);
}
break; break;
case ReSharperStatus.Enabled: case ReSharperStatus.Enabled:
if (currentStatus != ReSharperStatus.Enabled) if (currentStatus != ReSharperStatus.Enabled)
...@@ -218,16 +174,17 @@ private async Task UpdateStateMachineWorkerAsync(CancellationToken cancellationT ...@@ -218,16 +174,17 @@ private async Task UpdateStateMachineWorkerAsync(CancellationToken cancellationT
} }
_workspace.Options = options; _workspace.Options = options;
if (options.GetOption(KeybindingResetOptions.NeedsReset)) if (options.GetOption(KeybindingResetOptions.NeedsReset))
{ {
ShowGoldBar(); ShowGoldBar();
} }
_isFirstRun = false;
} }
private void ShowGoldBar() private void ShowGoldBar()
{ {
AssertIsForeground();
// If the gold bar is already open, do not show // If the gold bar is already open, do not show
if (_infoBarOpen) if (_infoBarOpen)
{ {
...@@ -239,7 +196,7 @@ private void ShowGoldBar() ...@@ -239,7 +196,7 @@ private void ShowGoldBar()
Debug.Assert(_experimentationService.IsExperimentEnabled(InternalFlightName) || Debug.Assert(_experimentationService.IsExperimentEnabled(InternalFlightName) ||
_experimentationService.IsExperimentEnabled(ExternalFlightName)); _experimentationService.IsExperimentEnabled(ExternalFlightName));
var message = ServicesVSResources.We_notice_you_suspended_0_Reset_keymappings_to_continue_to_navigate_and_refactor; string message = ServicesVSResources.We_notice_you_suspended_0_Reset_keymappings_to_continue_to_navigate_and_refactor;
KeybindingsResetLogger.Log("InfoBarShown"); KeybindingsResetLogger.Log("InfoBarShown");
var infoBarService = _workspace.Services.GetRequiredService<IInfoBarService>(); var infoBarService = _workspace.Services.GetRequiredService<IInfoBarService>();
infoBarService.ShowInfoBarInGlobalView( infoBarService.ShowInfoBarInGlobalView(
...@@ -260,69 +217,37 @@ private void ShowGoldBar() ...@@ -260,69 +217,37 @@ private void ShowGoldBar()
action: InfoBarClose)); action: InfoBarClose));
} }
/// <summary> private ReSharperStatus IsReSharperEnabled()
/// Returns true if ReSharper is installed, enabled, and not suspended.
/// </summary>
private async Task<ReSharperStatus> IsReSharperRunningAsync(ReSharperStatus lastStatus, CancellationToken cancellationToken)
{ {
AssertIsForeground();
// Quick exit if resharper is either uninstalled or not enabled // Quick exit if resharper is either uninstalled or not enabled
if (!_resharperExtensionInstalledAndEnabled) if (!_resharperExtensionEnabled)
{ {
return ReSharperStatus.NotInstalledOrDisabled; return ReSharperStatus.NotInstalledOrDisabled;
} }
await EnsureOleCommandTargetAsync().ConfigureAwait(false); if (_oleCommandTarget == null)
var cmds = new OLECMD[1];
cmds[0].cmdID = ResumeId;
cmds[0].cmdf = 0;
for (var count = 0; count < 10; count++)
{ {
cancellationToken.ThrowIfCancellationRequested(); _oleCommandTarget = _serviceProvider.GetService<IOleCommandTarget, SUIHostCommandDispatcher>();
var hr = await QueryStatusOnUIThreadAsync().ConfigureAwait(false);
if (ErrorHandler.Failed(hr))
{
// In the case of an error when attempting to get the status, pretend that ReSharper isn't enabled. We also
// shut down monitoring so we don't keep hitting this.
FatalError.ReportWithoutCrash(Marshal.GetExceptionForHR(hr));
await ShutdownAsync().ConfigureAwait(false);
return ReSharperStatus.NotInstalledOrDisabled;
}
// When ReSharper is suspended, the ReSharper_Resume command has the Enabled | Supported flags.
if (((OLECMDF)cmds[0].cmdf).HasFlag(OLECMDF.OLECMDF_ENABLED))
{
return ReSharperStatus.Suspended;
}
//otherwise sleep for a bit and check again
await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken).ConfigureAwait(false);
} }
// If resume button doesn't become active within a reasonable amount of time, assume ReSharper is Enabled var cmds = new OLECMD[1];
return ReSharperStatus.Enabled; cmds[0].cmdID = SuspendId;
cmds[0].cmdf = 0;
async Task<int> QueryStatusOnUIThreadAsync() var hr = _oleCommandTarget.QueryStatus(ReSharperCommandGroup, (uint)cmds.Length, cmds, IntPtr.Zero);
if (ErrorHandler.Failed(hr))
{ {
await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); // In the case of an error when attempting to get the status, pretend that ReSharper isn't enabled. We also
// shut down monitoring so we don't keep hitting this.
return _oleCommandTarget.QueryStatus(ReSharperCommandGroup, (uint)cmds.Length, cmds, IntPtr.Zero); FatalError.ReportWithoutCrash(Marshal.GetExceptionForHR(hr));
Shutdown();
return ReSharperStatus.NotInstalledOrDisabled;
} }
async Task EnsureOleCommandTargetAsync() // When ReSharper is enabled, the ReSharper_Suspend command has the Enabled | Supported flags. When disabled, it has Invisible | Supported.
{ return ((OLECMDF)cmds[0].cmdf).HasFlag(OLECMDF.OLECMDF_ENABLED) ? ReSharperStatus.Enabled : ReSharperStatus.Suspended;
if (_oleCommandTarget != null)
{
return;
}
await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
_oleCommandTarget = _serviceProvider.GetService<IOleCommandTarget, SUIHostCommandDispatcher>();
}
} }
private void RestoreVsKeybindings() private void RestoreVsKeybindings()
...@@ -370,7 +295,7 @@ private void NeverShowAgain() ...@@ -370,7 +295,7 @@ private void NeverShowAgain()
KeybindingsResetLogger.Log("NeverShowAgain"); KeybindingsResetLogger.Log("NeverShowAgain");
// The only external references to this object are as callbacks, which are removed by the Shutdown method. // The only external references to this object are as callbacks, which are removed by the Shutdown method.
ThreadingContext.JoinableTaskFactory.Run(ShutdownAsync); Shutdown();
} }
private void InfoBarClose() private void InfoBarClose()
...@@ -394,7 +319,7 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv ...@@ -394,7 +319,7 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv
if (pguidCmdGroup == ReSharperCommandGroup && nCmdID >= ResumeId && nCmdID <= ToggleSuspendId) if (pguidCmdGroup == ReSharperCommandGroup && nCmdID >= ResumeId && nCmdID <= ToggleSuspendId)
{ {
// Don't delay command processing to update resharper status // Don't delay command processing to update resharper status
StartUpdateStateMachine(); Task.Run(() => InvokeBelowInputPriorityAsync(UpdateStateMachine));
} }
// No matter the command, we never actually want to respond to it, so always return not supported. We're just monitoring. // No matter the command, we never actually want to respond to it, so always return not supported. We're just monitoring.
...@@ -410,24 +335,19 @@ private void OnModalStateChanged(object sender, StateChangedEventArgs args) ...@@ -410,24 +335,19 @@ private void OnModalStateChanged(object sender, StateChangedEventArgs args)
// extra QueryStatus. // extra QueryStatus.
if (args.TransitionType == StateTransitionType.Exit) if (args.TransitionType == StateTransitionType.Exit)
{ {
StartUpdateStateMachine(); InvokeBelowInputPriorityAsync(UpdateStateMachine);
} }
} }
private async Task ShutdownAsync() public void Shutdown()
{ {
// we are shutting down, cancel any pending work. AssertIsForeground();
_cancellationTokenSource.Cancel();
await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync();
if (_priorityCommandTargetCookie != VSConstants.VSCOOKIE_NIL) if (_priorityCommandTargetCookie != VSConstants.VSCOOKIE_NIL)
{ {
var priorityCommandTargetRegistrar = _serviceProvider.GetService<IVsRegisterPriorityCommandTarget, SVsRegisterPriorityCommandTarget>(); var priorityCommandTargetRegistrar = _serviceProvider.GetService<IVsRegisterPriorityCommandTarget, SVsRegisterPriorityCommandTarget>();
var cookie = _priorityCommandTargetCookie; var cookie = _priorityCommandTargetCookie;
_priorityCommandTargetCookie = VSConstants.VSCOOKIE_NIL; _priorityCommandTargetCookie = VSConstants.VSCOOKIE_NIL;
var hr = priorityCommandTargetRegistrar.UnregisterPriorityCommandTarget(cookie); var hr = priorityCommandTargetRegistrar.UnregisterPriorityCommandTarget(cookie);
if (ErrorHandler.Failed(hr)) if (ErrorHandler.Failed(hr))
{ {
FatalError.ReportWithoutCrash(Marshal.GetExceptionForHR(hr)); FatalError.ReportWithoutCrash(Marshal.GetExceptionForHR(hr));
......
...@@ -13,7 +13,7 @@ internal enum ReSharperStatus ...@@ -13,7 +13,7 @@ internal enum ReSharperStatus
/// </summary> /// </summary>
Suspended, Suspended,
/// <summary> /// <summary>
/// ReSharper is installed and enabled. /// ReSharper is running.
/// </summary> /// </summary>
Enabled Enabled
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册