Skip to content

Commit 9525fa5

Browse files
authored
Options refactoring (#55686)
1 parent 9e0b1ad commit 9525fa5

File tree

15 files changed

+295
-163
lines changed

15 files changed

+295
-163
lines changed

src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,32 +27,34 @@ namespace Microsoft.CodeAnalysis.Editor
2727
internal class EditorLayerExtensionManager : IWorkspaceServiceFactory
2828
{
2929
private readonly List<IExtensionErrorHandler> _errorHandlers;
30+
private readonly IGlobalOptionService _optionService;
3031

3132
[ImportingConstructor]
3233
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
3334
public EditorLayerExtensionManager(
35+
IGlobalOptionService optionService,
3436
[ImportMany] IEnumerable<IExtensionErrorHandler> errorHandlers)
3537
{
38+
_optionService = optionService;
3639
_errorHandlers = errorHandlers.ToList();
3740
}
3841

3942
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
4043
{
41-
var optionService = workspaceServices.GetRequiredService<IOptionService>();
4244
var errorReportingService = workspaceServices.GetRequiredService<IErrorReportingService>();
4345
var errorLoggerService = workspaceServices.GetRequiredService<IErrorLoggerService>();
44-
return new ExtensionManager(optionService, errorReportingService, errorLoggerService, _errorHandlers);
46+
return new ExtensionManager(_optionService, errorReportingService, errorLoggerService, _errorHandlers);
4547
}
4648

4749
internal class ExtensionManager : AbstractExtensionManager
4850
{
4951
private readonly List<IExtensionErrorHandler> _errorHandlers;
50-
private readonly IOptionService _optionsService;
52+
private readonly IGlobalOptionService _optionsService;
5153
private readonly IErrorReportingService _errorReportingService;
5254
private readonly IErrorLoggerService _errorLoggerService;
5355

5456
public ExtensionManager(
55-
IOptionService optionsService,
57+
IGlobalOptionService optionsService,
5658
IErrorReportingService errorReportingService,
5759
IErrorLoggerService errorLoggerService,
5860
List<IExtensionErrorHandler> errorHandlers)

src/VisualStudio/Core/Def/Experimentation/IExperiment.cs

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/VisualStudio/Core/Def/Implementation/FindReferences/StreamingFindUsagesPresenter.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
1616
using Microsoft.CodeAnalysis.FindUsages;
1717
using Microsoft.CodeAnalysis.Host.Mef;
18+
using Microsoft.CodeAnalysis.Options;
1819
using Microsoft.CodeAnalysis.PooledObjects;
1920
using Microsoft.CodeAnalysis.Shared.TestHooks;
2021
using Microsoft.VisualStudio.LanguageServices.Implementation.FindReferences;
@@ -43,6 +44,7 @@ internal partial class StreamingFindUsagesPresenter :
4344
public readonly IClassificationFormatMap ClassificationFormatMap;
4445

4546
private readonly Workspace _workspace;
47+
private readonly IGlobalOptionService _optionService;
4648

4749
private readonly HashSet<AbstractTableDataSourceFindUsagesContext> _currentContexts =
4850
new();
@@ -53,6 +55,7 @@ internal partial class StreamingFindUsagesPresenter :
5355
public StreamingFindUsagesPresenter(
5456
IThreadingContext threadingContext,
5557
VisualStudioWorkspace workspace,
58+
IGlobalOptionService optionService,
5659
Shell.SVsServiceProvider serviceProvider,
5760
ClassificationTypeMap typeMap,
5861
IEditorFormatMapService formatMapService,
@@ -62,6 +65,7 @@ public StreamingFindUsagesPresenter(
6265
: this(workspace,
6366
threadingContext,
6467
serviceProvider,
68+
optionService,
6569
typeMap,
6670
formatMapService,
6771
classificationFormatMapService,
@@ -78,6 +82,7 @@ public StreamingFindUsagesPresenter(
7882
: this(workspace,
7983
exportProvider.GetExportedValue<IThreadingContext>(),
8084
exportProvider.GetExportedValue<Shell.SVsServiceProvider>(),
85+
exportProvider.GetExportedValue<IGlobalOptionService>(),
8186
exportProvider.GetExportedValue<ClassificationTypeMap>(),
8287
exportProvider.GetExportedValue<IEditorFormatMapService>(),
8388
exportProvider.GetExportedValue<IClassificationFormatMapService>(),
@@ -91,6 +96,7 @@ private StreamingFindUsagesPresenter(
9196
Workspace workspace,
9297
IThreadingContext threadingContext,
9398
Shell.SVsServiceProvider serviceProvider,
99+
IGlobalOptionService optionService,
94100
ClassificationTypeMap typeMap,
95101
IEditorFormatMapService formatMapService,
96102
IClassificationFormatMapService classificationFormatMapService,
@@ -99,6 +105,7 @@ private StreamingFindUsagesPresenter(
99105
: base(threadingContext, assertIsForeground: false)
100106
{
101107
_workspace = workspace;
108+
_optionService = optionService;
102109
_serviceProvider = serviceProvider;
103110
TypeMap = typeMap;
104111
FormatMapService = formatMapService;
@@ -177,7 +184,7 @@ private AbstractTableDataSourceFindUsagesContext StartSearchWorker(
177184
// We need this because we disable the Definition column when we're not showing references
178185
// (i.e. GoToImplementation/GoToDef). However, we want to restore the user's choice if they
179186
// then do another FindAllReferences.
180-
var desiredGroupingPriority = _workspace.Options.GetOption(FindUsagesOptions.DefinitionGroupingPriority);
187+
var desiredGroupingPriority = _optionService.GetOption(FindUsagesOptions.DefinitionGroupingPriority);
181188
if (desiredGroupingPriority < 0)
182189
{
183190
StoreCurrentGroupingPriority(window);
@@ -219,9 +226,7 @@ private AbstractTableDataSourceFindUsagesContext StartSearchWithoutReferences(
219226

220227
private void StoreCurrentGroupingPriority(IFindAllReferencesWindow window)
221228
{
222-
var definitionColumn = window.GetDefinitionColumn();
223-
_workspace.TryApplyChanges(_workspace.CurrentSolution.WithOptions(_workspace.Options
224-
.WithChangedOption(FindUsagesOptions.DefinitionGroupingPriority, definitionColumn.GroupingPriority)));
229+
_optionService.SetGlobalOption(FindUsagesOptions.DefinitionGroupingPriority, window.GetDefinitionColumn().GroupingPriority);
225230
}
226231

227232
private void SetDefinitionGroupingPriority(IFindAllReferencesWindow window, int priority)

src/VisualStudio/Core/Def/Implementation/InfoBar/VisualStudioInfoBarService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020

2121
namespace Microsoft.VisualStudio.LanguageServices.Implementation
2222
{
23-
[ExportWorkspaceService(typeof(IInfoBarService), layer: ServiceLayer.Host), Shared]
24-
internal class VisualStudioInfoBarService : ForegroundThreadAffinitizedObject, IInfoBarService
23+
[Export(typeof(VisualStudioInfoBarService)), Shared]
24+
internal sealed class VisualStudioInfoBarService : ForegroundThreadAffinitizedObject
2525
{
2626
private readonly SVsServiceProvider _serviceProvider;
2727
private readonly IAsynchronousOperationListener _listener;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable disable
6+
7+
using System;
8+
using System.Composition;
9+
using Microsoft.CodeAnalysis.Extensions;
10+
using Microsoft.CodeAnalysis.Host.Mef;
11+
12+
namespace Microsoft.VisualStudio.LanguageServices.Implementation
13+
{
14+
[ExportWorkspaceService(typeof(IInfoBarService), layer: ServiceLayer.Host), Shared]
15+
internal sealed class VisualStudioInfoBarWorkspaceService : IInfoBarService
16+
{
17+
private readonly VisualStudioInfoBarService _service;
18+
19+
[ImportingConstructor]
20+
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
21+
public VisualStudioInfoBarWorkspaceService(VisualStudioInfoBarService service)
22+
=> _service = service;
23+
24+
public void ShowInfoBar(string message, params InfoBarUI[] items)
25+
=> _service.ShowInfoBar(message, items);
26+
}
27+
}

src/VisualStudio/Core/Def/Implementation/Experimentation/KeybindingResetDetector.cs renamed to src/VisualStudio/Core/Def/Implementation/KeybindingReset/KeybindingResetDetector.cs

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@
55
#nullable disable
66

77
using System;
8+
using System.Collections.Immutable;
89
using System.ComponentModel.Composition;
910
using System.Diagnostics;
1011
using System.Runtime.InteropServices;
1112
using System.Threading;
1213
using System.Threading.Tasks;
1314
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
1415
using Microsoft.CodeAnalysis.ErrorReporting;
15-
using Microsoft.CodeAnalysis.Experimentation;
1616
using Microsoft.CodeAnalysis.Extensions;
1717
using Microsoft.CodeAnalysis.Host.Mef;
18-
using Microsoft.VisualStudio.LanguageServices.Experimentation;
18+
using Microsoft.CodeAnalysis.Options;
19+
using Microsoft.VisualStudio.LanguageServices.Implementation;
1920
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
2021
using Microsoft.VisualStudio.LanguageServices.Utilities;
2122
using Microsoft.VisualStudio.OLE.Interop;
@@ -25,7 +26,7 @@
2526
using Roslyn.Utilities;
2627
using 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

Comments
 (0)