55#nullable disable
66
77using System ;
8+ using System . Collections . Immutable ;
89using System . ComponentModel . Composition ;
910using System . Diagnostics ;
1011using System . Runtime . InteropServices ;
1112using System . Threading ;
1213using System . Threading . Tasks ;
1314using Microsoft . CodeAnalysis . Editor . Shared . Utilities ;
1415using Microsoft . CodeAnalysis . ErrorReporting ;
15- using Microsoft . CodeAnalysis . Experimentation ;
1616using Microsoft . CodeAnalysis . Extensions ;
1717using Microsoft . CodeAnalysis . Host . Mef ;
18- using Microsoft . VisualStudio . LanguageServices . Experimentation ;
18+ using Microsoft . CodeAnalysis . Options ;
19+ using Microsoft . VisualStudio . LanguageServices . Implementation ;
1920using Microsoft . VisualStudio . LanguageServices . Implementation . Utilities ;
2021using Microsoft . VisualStudio . LanguageServices . Utilities ;
2122using Microsoft . VisualStudio . OLE . Interop ;
2526using Roslyn . Utilities ;
2627using Task = System . Threading . Tasks . Task ;
2728
28- namespace Microsoft . VisualStudio . LanguageServices . Implementation . Experimentation
29+ namespace Microsoft . VisualStudio . LanguageServices . KeybindingReset
2930{
3031 /// <summary>
3132 /// Detects if keybindings have been messed up by ReSharper disable, and offers the user the ability
@@ -42,8 +43,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Experimentation
4243 /// but at this time ReSharper is the only one we know of that has this behavior.
4344 /// If we find other extensions that do this in the future, we'll re-use this same mechanism
4445 /// </para>
45- [ Export ( typeof ( IExperiment ) ) ]
46- internal sealed class KeybindingResetDetector : ForegroundThreadAffinitizedObject , IExperiment , IOleCommandTarget
46+ [ Export ( typeof ( KeybindingResetDetector ) ) ]
47+ internal sealed class KeybindingResetDetector : ForegroundThreadAffinitizedObject , IOleCommandTarget
4748 {
4849 private const string KeybindingsFwLink = "https://go.microsoft.com/fwlink/?linkid=864209" ;
4950 private const string ReSharperExtensionName = "ReSharper Ultimate" ;
@@ -54,11 +55,17 @@ internal sealed class KeybindingResetDetector : ForegroundThreadAffinitizedObjec
5455 private const uint ResumeId = 707 ;
5556 private const uint SuspendId = 708 ;
5657 private const uint ToggleSuspendId = 709 ;
57- private static readonly Guid ReSharperPackageGuid = new ( "0C6E6407-13FC-4878-869A-C8B4016C57FE" ) ;
58- private static readonly Guid ReSharperCommandGroup = new ( "{47F03277-5055-4922-899C-0F7F30D26BF1}" ) ;
5958
60- private readonly VisualStudioWorkspace _workspace ;
59+ private static readonly Guid s_resharperPackageGuid = new ( "0C6E6407-13FC-4878-869A-C8B4016C57FE" ) ;
60+ private static readonly Guid s_resharperCommandGroup = new ( "47F03277-5055-4922-899C-0F7F30D26BF1" ) ;
61+
62+ private static readonly ImmutableArray < OptionKey > s_statusOptions = ImmutableArray . Create < OptionKey > (
63+ KeybindingResetOptions . ReSharperStatus ,
64+ KeybindingResetOptions . NeedsReset ) ;
65+
66+ private readonly IGlobalOptionService _optionService ;
6167 private readonly System . IServiceProvider _serviceProvider ;
68+ private readonly VisualStudioInfoBarService _infoBarService ;
6269
6370 // All mutable fields are UI-thread affinitized
6471
@@ -82,17 +89,22 @@ internal sealed class KeybindingResetDetector : ForegroundThreadAffinitizedObjec
8289
8390 [ ImportingConstructor ]
8491 [ Obsolete ( MefConstruction . ImportingConstructorMessage , error : true ) ]
85- public KeybindingResetDetector ( IThreadingContext threadingContext , VisualStudioWorkspace workspace , SVsServiceProvider serviceProvider )
92+ public KeybindingResetDetector (
93+ IThreadingContext threadingContext ,
94+ IGlobalOptionService optionService ,
95+ VisualStudioInfoBarService infoBarService ,
96+ SVsServiceProvider serviceProvider )
8697 : base ( threadingContext )
8798 {
88- _workspace = workspace ;
99+ _optionService = optionService ;
100+ _infoBarService = infoBarService ;
89101 _serviceProvider = serviceProvider ;
90102 }
91103
92104 public Task InitializeAsync ( )
93105 {
94106 // Immediately bail if the user has asked to never see this bar again.
95- if ( _workspace . Options . GetOption ( KeybindingResetOptions . NeverShowAgain ) )
107+ if ( _optionService . GetOption ( KeybindingResetOptions . NeverShowAgain ) )
96108 {
97109 return Task . CompletedTask ;
98110 }
@@ -104,14 +116,13 @@ private void InitializeCore()
104116 {
105117 AssertIsForeground ( ) ;
106118
107- // Ensure one of the flights is enabled, otherwise bail
108- if ( ! _workspace . Options . GetOption ( KeybindingResetOptions . EnabledFeatureFlag ) )
119+ if ( ! _optionService . GetOption ( KeybindingResetOptions . EnabledFeatureFlag ) )
109120 {
110121 return ;
111122 }
112123
113124 var vsShell = IServiceProviderExtensions . GetService < SVsShell , IVsShell > ( _serviceProvider ) ;
114- var hr = vsShell . IsPackageInstalled ( ReSharperPackageGuid , out var extensionEnabled ) ;
125+ var hr = vsShell . IsPackageInstalled ( s_resharperPackageGuid , out var extensionEnabled ) ;
115126 if ( ErrorHandler . Failed ( hr ) )
116127 {
117128 FatalError . ReportAndCatch ( Marshal . GetExceptionForHR ( hr ) ) ;
@@ -161,14 +172,14 @@ private void StartUpdateStateMachine()
161172
162173 private async Task UpdateStateMachineWorkerAsync ( CancellationToken cancellationToken )
163174 {
164- var options = _workspace . Options ;
165- var lastStatus = options . GetOption ( KeybindingResetOptions . ReSharperStatus ) ;
175+ var options = _optionService . GetOptions ( s_statusOptions ) ;
176+ var lastStatus = ( ReSharperStatus ) options [ 0 ] ;
177+ var needsReset = ( bool ) options [ 1 ] ;
166178
167179 ReSharperStatus currentStatus ;
168180 try
169181 {
170- currentStatus = await IsReSharperRunningAsync ( cancellationToken )
171- . ConfigureAwait ( false ) ;
182+ currentStatus = await IsReSharperRunningAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
172183 }
173184 catch ( OperationCanceledException )
174185 {
@@ -180,16 +191,14 @@ private async Task UpdateStateMachineWorkerAsync(CancellationToken cancellationT
180191 return ;
181192 }
182193
183- options = options . WithChangedOption ( KeybindingResetOptions . ReSharperStatus , currentStatus ) ;
184-
185194 switch ( lastStatus )
186195 {
187196 case ReSharperStatus . NotInstalledOrDisabled :
188197 case ReSharperStatus . Suspended :
189198 if ( currentStatus == ReSharperStatus . Enabled )
190199 {
191200 // N->E or S->E. If ReSharper was just installed and is enabled, reset NeedsReset.
192- options = options . WithChangedOption ( KeybindingResetOptions . NeedsReset , false ) ;
201+ needsReset = false ;
193202 }
194203
195204 // Else is N->N, N->S, S->N, S->S. N->S can occur if the user suspends ReSharper, then disables
@@ -201,19 +210,16 @@ private async Task UpdateStateMachineWorkerAsync(CancellationToken cancellationT
201210 if ( currentStatus != ReSharperStatus . Enabled )
202211 {
203212 // E->N or E->S. Set NeedsReset. Pop the gold bar to the user.
204- options = options . WithChangedOption ( KeybindingResetOptions . NeedsReset , true ) ;
213+ needsReset = true ;
205214 }
206215
207216 // Else is E->E. No actions to take
208217 break ;
209218 }
210219
211- // Apply the new options.
212- // We need to switch to UI thread to invoke TryApplyChanges.
213- await ThreadingContext . JoinableTaskFactory . SwitchToMainThreadAsync ( cancellationToken ) ;
214- _workspace . TryApplyChanges ( _workspace . CurrentSolution . WithOptions ( options ) ) ;
220+ _optionService . SetGlobalOptions ( s_statusOptions , ImmutableArray . Create < object > ( currentStatus , needsReset ) ) ;
215221
216- if ( options . GetOption ( KeybindingResetOptions . NeedsReset ) )
222+ if ( needsReset )
217223 {
218224 ShowGoldBar ( ) ;
219225 }
@@ -231,8 +237,7 @@ private void ShowGoldBar()
231237
232238 var message = ServicesVSResources . We_notice_you_suspended_0_Reset_keymappings_to_continue_to_navigate_and_refactor ;
233239 KeybindingsResetLogger . Log ( "InfoBarShown" ) ;
234- var infoBarService = _workspace . Services . GetRequiredService < IInfoBarService > ( ) ;
235- infoBarService . ShowInfoBar (
240+ _infoBarService . ShowInfoBar (
236241 string . Format ( message , ReSharperExtensionName ) ,
237242 new InfoBarUI ( title : ServicesVSResources . Reset_Visual_Studio_default_keymapping ,
238243 kind : InfoBarUI . UIKind . Button ,
@@ -307,7 +312,7 @@ async Task<OLECMDF> QueryStatusAsync(uint cmdId)
307312
308313 await ThreadingContext . JoinableTaskFactory . SwitchToMainThreadAsync ( cancellationToken ) ;
309314
310- var hr = _oleCommandTarget . QueryStatus ( ReSharperCommandGroup , ( uint ) cmds . Length , cmds , IntPtr . Zero ) ;
315+ var hr = _oleCommandTarget . QueryStatus ( s_resharperCommandGroup , ( uint ) cmds . Length , cmds , IntPtr . Zero ) ;
311316 if ( ErrorHandler . Failed ( hr ) )
312317 {
313318 FatalError . ReportAndCatch ( Marshal . GetExceptionForHR ( hr ) ) ;
@@ -349,8 +354,7 @@ private void RestoreVsKeybindings()
349354
350355 KeybindingsResetLogger . Log ( "KeybindingsReset" ) ;
351356
352- _workspace . TryApplyChanges ( _workspace . CurrentSolution . WithOptions ( _workspace . Options
353- . WithChangedOption ( KeybindingResetOptions . NeedsReset , false ) ) ) ;
357+ _optionService . SetGlobalOption ( KeybindingResetOptions . NeedsReset , false ) ;
354358 }
355359
356360 private void OpenExtensionsHyperlink ( )
@@ -360,15 +364,13 @@ private void OpenExtensionsHyperlink()
360364 VisualStudioNavigateToLinkService . StartBrowser ( KeybindingsFwLink ) ;
361365
362366 KeybindingsResetLogger . Log ( "ExtensionsLink" ) ;
363- _workspace . TryApplyChanges ( _workspace . CurrentSolution . WithOptions ( _workspace . Options
364- . WithChangedOption ( KeybindingResetOptions . NeedsReset , false ) ) ) ;
367+ _optionService . SetGlobalOption ( KeybindingResetOptions . NeedsReset , false ) ;
365368 }
366369
367370 private void NeverShowAgain ( )
368371 {
369- _workspace . TryApplyChanges ( _workspace . CurrentSolution . WithOptions ( _workspace . Options
370- . WithChangedOption ( KeybindingResetOptions . NeverShowAgain , true )
371- . WithChangedOption ( KeybindingResetOptions . NeedsReset , false ) ) ) ;
372+ _optionService . SetGlobalOption ( KeybindingResetOptions . NeverShowAgain , true ) ;
373+ _optionService . SetGlobalOption ( KeybindingResetOptions . NeedsReset , false ) ;
372374 KeybindingsResetLogger . Log ( "NeverShowAgain" ) ;
373375
374376 // The only external references to this object are as callbacks, which are removed by the Shutdown method.
@@ -393,7 +395,7 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv
393395 {
394396 // Technically can be called on any thread, though VS will only ever call it on the UI thread.
395397 ThisCanBeCalledOnAnyThread ( ) ;
396- if ( pguidCmdGroup == ReSharperCommandGroup && nCmdID >= ResumeId && nCmdID <= ToggleSuspendId )
398+ if ( pguidCmdGroup == s_resharperCommandGroup && nCmdID >= ResumeId && nCmdID <= ToggleSuspendId )
397399 {
398400 // Don't delay command processing to update resharper status
399401 StartUpdateStateMachine ( ) ;
0 commit comments