private void UpdateStateMachine() { AssertIsForeground(); var currentStatus = IsReSharperEnabled(); var options = _workspace.Options; ReSharperStatus lastStatus = options.GetOption(KeybindingResetOptions.ReSharperStatus); options = options.WithChangedOption(KeybindingResetOptions.ReSharperStatus, currentStatus); switch (lastStatus) { case ReSharperStatus.NotInstalledOrDisabled: case ReSharperStatus.Suspended: if (currentStatus == ReSharperStatus.Enabled) { // N->E or S->E. If ReSharper was just installed and is enabled, reset NeedsReset. options = options.WithChangedOption(KeybindingResetOptions.NeedsReset, false); } // Else is N->N, N->S, S->N, S->S. N->S can occur if the user suspends ReSharper, then disables // the extension, then reenables the extension. We will show the gold bar after the switch // if there is still a pending show. break; case ReSharperStatus.Enabled: if (currentStatus != ReSharperStatus.Enabled) { // E->N or E->S. Set NeedsReset. Pop the gold bar to the user. options = options.WithChangedOption(KeybindingResetOptions.NeedsReset, true); } // Else is E->E. No actions to take break; } _workspace.Options = options; if (options.GetOption(KeybindingResetOptions.NeedsReset)) { ShowGoldBar(); } }
/// <summary> /// Returns true if ReSharper is installed, enabled, and not suspended. /// </summary> private async ValueTask <ReSharperStatus> IsReSharperRunningAsync(ReSharperStatus lastStatus, CancellationToken cancellationToken) { // Quick exit if resharper is either uninstalled or not enabled if (!_resharperExtensionInstalledAndEnabled) { return(ReSharperStatus.NotInstalledOrDisabled); } await EnsureOleCommandTargetAsync().ConfigureAwait(false); // poll until either suspend or resume botton is available, or until operation is canceled while (true) { cancellationToken.ThrowIfCancellationRequested(); var suspendFlag = await QueryStatusAsync(SuspendId).ConfigureAwait(false); // 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. if (suspendFlag == 0) { return(ReSharperStatus.NotInstalledOrDisabled); } var resumeFlag = await QueryStatusAsync(ResumeId).ConfigureAwait(false); if (resumeFlag == 0) { return(ReSharperStatus.NotInstalledOrDisabled); } // When ReSharper is running, the ReSharper_Suspend command is Enabled and not Invisible if (suspendFlag.HasFlag(OLECMDF.OLECMDF_ENABLED) && !suspendFlag.HasFlag(OLECMDF.OLECMDF_INVISIBLE)) { return(ReSharperStatus.Enabled); } // When ReSharper is suspended, the ReSharper_Resume command is Enabled and not Invisible if (resumeFlag.HasFlag(OLECMDF.OLECMDF_ENABLED) && !resumeFlag.HasFlag(OLECMDF.OLECMDF_INVISIBLE)) { return(ReSharperStatus.Suspended); } // ReSharper has not finished initializing, so try again later await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken).ConfigureAwait(false); } async Task <OLECMDF> QueryStatusAsync(uint cmdId) { var cmds = new OLECMD[1]; cmds[0].cmdID = cmdId; cmds[0].cmdf = 0; await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); cancellationToken.ThrowIfCancellationRequested(); var hr = _oleCommandTarget.QueryStatus(ReSharperCommandGroup, (uint)cmds.Length, cmds, IntPtr.Zero); if (ErrorHandler.Failed(hr)) { FatalError.ReportWithoutCrash(Marshal.GetExceptionForHR(hr)); await ShutdownAsync().ConfigureAwait(false); return(0); } return((OLECMDF)cmds[0].cmdf); } async Task EnsureOleCommandTargetAsync() { if (_oleCommandTarget != null) { return; } await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); cancellationToken.ThrowIfCancellationRequested(); _oleCommandTarget = _serviceProvider.GetService <IOleCommandTarget, SUIHostCommandDispatcher>(); } }
/// <summary> /// Returns true if ReSharper is installed, enabled, and not suspended. /// </summary> private async Task <ReSharperStatus> IsReSharperRunningAsync(ReSharperStatus lastStatus, CancellationToken cancellationToken) { // Quick exit if resharper is either uninstalled or not enabled if (!_resharperExtensionInstalledAndEnabled) { return(ReSharperStatus.NotInstalledOrDisabled); } await EnsureOleCommandTargetAsync().ConfigureAwait(false); var cmds = new OLECMD[1]; cmds[0].cmdID = ResumeId; cmds[0].cmdf = 0; for (var count = 0; count < 10; count++) { cancellationToken.ThrowIfCancellationRequested(); 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 return(ReSharperStatus.Enabled); async Task <int> QueryStatusOnUIThreadAsync() { await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); return(_oleCommandTarget.QueryStatus(ReSharperCommandGroup, (uint)cmds.Length, cmds, IntPtr.Zero)); } async Task EnsureOleCommandTargetAsync() { if (_oleCommandTarget != null) { return; } await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); _oleCommandTarget = _serviceProvider.GetService <IOleCommandTarget, SUIHostCommandDispatcher>(); } }