From 9c68f9b1036cb8142fac184612fe632fa7f6b91f Mon Sep 17 00:00:00 2001 From: Allison Chou Date: Mon, 18 Jul 2022 12:26:11 -0700 Subject: [PATCH] Revert "Generalize RoamingProfileStorageLocation to ClientSettingsStorageLocation (#62684)" This reverts commit 51cc7c9a54246991828f789771f02157e981a37f. --- .../AbstractSignatureHelpCommandHandler.cs | 2 +- .../Core/Options/NavigationBarViewOptions.cs | 20 ++ .../NavigationBarViewOptionsStorage.cs | 15 - .../Core/Options/SignatureHelpViewOptions.cs | 20 ++ .../SignatureHelpViewOptionsStorage.cs | 15 - .../CSharpSignatureHelpCommandHandlerTests.vb | 2 +- ...alBasicSignatureHelpCommandHandlerTests.vb | 2 +- .../Diagnostics/DiagnosticOptions.cs} | 2 +- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- .../Options/CompletionOptionsStorage.cs | 10 +- .../Options/DiagnosticModeExtensions.cs | 2 +- .../Diagnostics/PullDiagnosticTests.cs | 4 +- .../Options/AdvancedOptionPageControl.xaml.cs | 2 +- ...ctLanguageService`2.VsCodeWindowManager.cs | 4 +- .../Def/Options/LanguageSettingsPersister.cs | 278 ++++++++++++++++++ .../LanguageSettingsPersisterProvider.cs | 66 +++++ ...mingVisualStudioProfileOptionPersister.cs} | 33 ++- ...alStudioProfileOptionPersisterProvider.cs} | 8 +- .../VisualStudioWorkspaceImpl.cs | 2 +- .../VisualStudioWorkspaceTelemetryService.cs | 2 +- .../CSharp/CSharpNavigationBar.cs | 4 +- .../InProcess/StateResetInProcess.cs | 2 +- .../VisualBasic/BasicNavigationBar.cs | 4 +- .../Core/CompilerExtensions.projitems | 3 +- .../Core/Formatting/FormattingOptions2.cs | 17 +- .../Options/ClientSettingsStorageLocation.cs | 49 --- .../Options/FeatureFlagStorageLocation.cs | 0 .../LocalClientSettingsStorageLocation.cs | 35 --- .../LocalUserProfileStorageLocation.cs | 2 +- .../Options/RoamingProfileStorageLocation.cs | 49 +-- 30 files changed, 470 insertions(+), 186 deletions(-) create mode 100644 src/EditorFeatures/Core/Options/NavigationBarViewOptions.cs delete mode 100644 src/EditorFeatures/Core/Options/NavigationBarViewOptionsStorage.cs create mode 100644 src/EditorFeatures/Core/Options/SignatureHelpViewOptions.cs delete mode 100644 src/EditorFeatures/Core/Options/SignatureHelpViewOptionsStorage.cs rename src/Features/{LanguageServer/Protocol/Features/Options/DiagnosticOptionsStorage.cs => Core/Portable/Diagnostics/DiagnosticOptions.cs} (95%) create mode 100644 src/VisualStudio/Core/Def/Options/LanguageSettingsPersister.cs create mode 100644 src/VisualStudio/Core/Def/Options/LanguageSettingsPersisterProvider.cs rename src/VisualStudio/Core/Def/Options/{VisualStudioSettingsOptionPersister.cs => RoamingVisualStudioProfileOptionPersister.cs} (88%) rename src/VisualStudio/Core/Def/Options/{VisualStudioSettingsOptionPersisterProvider.cs => RoamingVisualStudioProfileOptionPersisterProvider.cs} (83%) delete mode 100644 src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/ClientSettingsStorageLocation.cs rename src/{Features/LanguageServer/Protocol/Features => Workspaces/SharedUtilitiesAndExtensions/Compiler/Core}/Options/FeatureFlagStorageLocation.cs (100%) delete mode 100644 src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/LocalClientSettingsStorageLocation.cs diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/AbstractSignatureHelpCommandHandler.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/AbstractSignatureHelpCommandHandler.cs index 8106e43c6597b..f854ed36aa280 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/AbstractSignatureHelpCommandHandler.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/AbstractSignatureHelpCommandHandler.cs @@ -40,7 +40,7 @@ protected bool TryGetController(EditorCommandArgs args, out Controller controlle var languageName = args.SubjectBuffer.GetLanguageName(); if (args is not InvokeSignatureHelpCommandArgs && languageName != null && - !_globalOptions.GetOption(SignatureHelpViewOptionsStorage.ShowSignatureHelp, languageName)) + !_globalOptions.GetOption(SignatureHelpViewOptions.ShowSignatureHelp, languageName)) { controller = null; return false; diff --git a/src/EditorFeatures/Core/Options/NavigationBarViewOptions.cs b/src/EditorFeatures/Core/Options/NavigationBarViewOptions.cs new file mode 100644 index 0000000000000..cde4d9cbf4e2e --- /dev/null +++ b/src/EditorFeatures/Core/Options/NavigationBarViewOptions.cs @@ -0,0 +1,20 @@ +// 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. + +using System; +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Options.Providers; + +namespace Microsoft.CodeAnalysis.Editor.Options +{ + internal sealed class NavigationBarViewOptions + { + private const string FeatureName = "NavigationBarOptions"; + + public static readonly PerLanguageOption2 ShowNavigationBar = new(FeatureName, "ShowNavigationBar", defaultValue: true); + } +} diff --git a/src/EditorFeatures/Core/Options/NavigationBarViewOptionsStorage.cs b/src/EditorFeatures/Core/Options/NavigationBarViewOptionsStorage.cs deleted file mode 100644 index 1d7ec8496b7ff..0000000000000 --- a/src/EditorFeatures/Core/Options/NavigationBarViewOptionsStorage.cs +++ /dev/null @@ -1,15 +0,0 @@ -// 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. - -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.Editor.Options; - -internal sealed class NavigationBarViewOptionsStorage -{ - private const string FeatureName = "NavigationBarOptions"; - - public static readonly PerLanguageOption2 ShowNavigationBar = new( - FeatureName, "ShowNavigationBar", defaultValue: true, new LocalClientSettingsStorageLocation("TextEditor.%LANGUAGE%.Dropdown Bar")); -} diff --git a/src/EditorFeatures/Core/Options/SignatureHelpViewOptions.cs b/src/EditorFeatures/Core/Options/SignatureHelpViewOptions.cs new file mode 100644 index 0000000000000..0383174fb5338 --- /dev/null +++ b/src/EditorFeatures/Core/Options/SignatureHelpViewOptions.cs @@ -0,0 +1,20 @@ +// 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. + +using System; +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Options.Providers; + +namespace Microsoft.CodeAnalysis.Editor.Options +{ + internal sealed class SignatureHelpViewOptions + { + private const string FeatureName = "SignatureHelpOptions"; + + public static readonly PerLanguageOption2 ShowSignatureHelp = new(FeatureName, nameof(ShowSignatureHelp), defaultValue: true); + } +} diff --git a/src/EditorFeatures/Core/Options/SignatureHelpViewOptionsStorage.cs b/src/EditorFeatures/Core/Options/SignatureHelpViewOptionsStorage.cs deleted file mode 100644 index 3d02f2959d98e..0000000000000 --- a/src/EditorFeatures/Core/Options/SignatureHelpViewOptionsStorage.cs +++ /dev/null @@ -1,15 +0,0 @@ -// 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. - -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.Editor.Options; - -internal sealed class SignatureHelpViewOptionsStorage -{ - private const string FeatureName = "SignatureHelpOptions"; - - public static readonly PerLanguageOption2 ShowSignatureHelp = new( - FeatureName, "ShowSignatureHelp", defaultValue: true, new LocalClientSettingsStorageLocation("TextEditor.%LANGUAGE%.Auto List Params")); -} diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpSignatureHelpCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpSignatureHelpCommandHandlerTests.vb index 98422df0558b6..7e4556adaff71 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpSignatureHelpCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpSignatureHelpCommandHandlerTests.vb @@ -884,7 +884,7 @@ class C , showCompletionInArgumentLists:=showCompletionInArgumentLists) ' disable implicit sig help then type a trigger character -> no session should be available - state.Workspace.GetService(Of IGlobalOptionService).SetGlobalOption(New OptionKey(SignatureHelpViewOptionsStorage.ShowSignatureHelp, LanguageNames.CSharp), False) + state.Workspace.GetService(Of IGlobalOptionService).SetGlobalOption(New OptionKey(SignatureHelpViewOptions.ShowSignatureHelp, LanguageNames.CSharp), False) state.SendTypeChars("(") Await state.AssertNoSignatureHelpSession() diff --git a/src/EditorFeatures/Test2/IntelliSense/VisualBasicSignatureHelpCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/VisualBasicSignatureHelpCommandHandlerTests.vb index cbe2eaeb82cd0..0257bc75979e0 100644 --- a/src/EditorFeatures/Test2/IntelliSense/VisualBasicSignatureHelpCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/VisualBasicSignatureHelpCommandHandlerTests.vb @@ -283,7 +283,7 @@ End Class ) ' disable implicit sig help then type a trigger character -> no session should be available - state.Workspace.GetService(Of IGlobalOptionService).SetGlobalOption(New OptionKey(SignatureHelpViewOptionsStorage.ShowSignatureHelp, LanguageNames.VisualBasic), False) + state.Workspace.GetService(Of IGlobalOptionService).SetGlobalOption(New OptionKey(SignatureHelpViewOptions.ShowSignatureHelp, LanguageNames.VisualBasic), False) state.SendTypeChars("(") Await state.AssertNoSignatureHelpSession() diff --git a/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticOptionsStorage.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs similarity index 95% rename from src/Features/LanguageServer/Protocol/Features/Options/DiagnosticOptionsStorage.cs rename to src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs index 506fbce515ed9..1422e927ed734 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticOptionsStorage.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics { - internal sealed class DiagnosticOptionsStorage + internal sealed class DiagnosticOptions { private const string FeatureName = "DiagnosticOptions"; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 5692075529be5..580473cd337ed 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -90,7 +90,7 @@ private async Task AnalyzeDocumentForKindAsync(TextDocument document, AnalysisKi { var analysisScope = new DocumentAnalysisScope(document, span: null, nonCachedStateSets.SelectAsArray(s => s.Analyzer), kind); var executor = new DocumentAnalysisExecutor(analysisScope, compilationWithAnalyzers, _diagnosticAnalyzerRunner, logPerformanceInfo: true, onAnalysisException: OnAnalysisException); - var logTelemetry = GlobalOptions.GetOption(DiagnosticOptionsStorage.LogTelemetryForBackgroundAnalyzerExecution); + var logTelemetry = GlobalOptions.GetOption(DiagnosticOptions.LogTelemetryForBackgroundAnalyzerExecution); foreach (var stateSet in nonCachedStateSets) { var computedData = await ComputeDocumentAnalysisDataAsync(executor, stateSet, logTelemetry, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/Protocol/Features/Options/CompletionOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/CompletionOptionsStorage.cs index 725dda47f9acd..8f88c40e10cba 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/CompletionOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/CompletionOptionsStorage.cs @@ -47,13 +47,11 @@ public static CompletionOptions GetCompletionOptions(this IGlobalOptionService o CompletionOptions.Default.SnippetCompletion, new FeatureFlagStorageLocation("Roslyn.SnippetCompletion")); - public static readonly PerLanguageOption2 HideAdvancedMembers = new( - "CompletionOptions", "HideAdvancedMembers", CompletionOptions.Default.HideAdvancedMembers, - new LocalClientSettingsStorageLocation("TextEditor.%LANGUAGE%.Hide Advanced Auto List Members")); + // This is serialized by the Visual Studio-specific LanguageSettingsPersister + public static readonly PerLanguageOption2 HideAdvancedMembers = new(nameof(CompletionOptions), nameof(HideAdvancedMembers), CompletionOptions.Default.HideAdvancedMembers); - public static readonly PerLanguageOption2 TriggerOnTyping = new( - "CompletionOptions", "TriggerOnTyping", CompletionOptions.Default.TriggerOnTyping, - new LocalClientSettingsStorageLocation("TextEditor.%LANGUAGE%.Auto List Members")); + // This is serialized by the Visual Studio-specific LanguageSettingsPersister + public static readonly PerLanguageOption2 TriggerOnTyping = new(nameof(CompletionOptions), nameof(TriggerOnTyping), CompletionOptions.Default.TriggerOnTyping); public static readonly PerLanguageOption2 TriggerOnTypingLetters = new(nameof(CompletionOptions), nameof(TriggerOnTypingLetters), CompletionOptions.Default.TriggerOnTypingLetters, storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.TriggerOnTypingLetters")); diff --git a/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticModeExtensions.cs b/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticModeExtensions.cs index e3f727145d68f..1a5ef527389ae 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticModeExtensions.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticModeExtensions.cs @@ -16,7 +16,7 @@ public static DiagnosticMode GetDiagnosticMode(this IGlobalOptionService globalO // If the workspace diagnostic mode is set to Default, defer to the feature flag service. if (diagnosticModeOption == DiagnosticMode.Default) { - return globalOptions.GetOption(DiagnosticOptionsStorage.LspPullDiagnosticsFeatureFlag) ? DiagnosticMode.Pull : DiagnosticMode.Push; + return globalOptions.GetOption(DiagnosticOptions.LspPullDiagnosticsFeatureFlag) ? DiagnosticMode.Pull : DiagnosticMode.Push; } // Otherwise, defer to the workspace+option to determine what mode we're in. diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 330cf0445e184..4669c384bf3b3 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -99,7 +99,7 @@ public async Task TestNoDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOf await OpenDocumentAsync(testLspServer, document); // Ensure we get no diagnostics when feature flag is off. - testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(new OptionKey(DiagnosticOptionsStorage.LspPullDiagnosticsFeatureFlag), false); + testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(new OptionKey(DiagnosticOptions.LspPullDiagnosticsFeatureFlag), false); await Assert.ThrowsAsync(async () => await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics)); } @@ -117,7 +117,7 @@ public async Task TestDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOn(b var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); await OpenDocumentAsync(testLspServer, document); - testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(new OptionKey(DiagnosticOptionsStorage.LspPullDiagnosticsFeatureFlag), true); + testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(new OptionKey(DiagnosticOptions.LspPullDiagnosticsFeatureFlag), true); var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics); Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs index 69ea931e44093..276ea9e1fb086 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs @@ -229,7 +229,7 @@ private void Enable_pull_diagnostics_experimental_requires_restart_CheckedChange if (checkboxValue != null) { // Update the actual value of the feature flag to ensure CPS is informed of the new feature flag value. - this.OptionStore.SetOption(DiagnosticOptionsStorage.LspPullDiagnosticsFeatureFlag, checkboxValue.Value); + this.OptionStore.SetOption(DiagnosticOptions.LspPullDiagnosticsFeatureFlag, checkboxValue.Value); } // Update the workspace option. diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs index c4c33af48d2fd..e1daa6d2474fe 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs @@ -51,7 +51,7 @@ private void SetupView(IVsTextView view) private void GlobalOptionChanged(object sender, OptionChangedEventArgs e) { if (e.Language != _languageService.RoslynLanguageName || - e.Option != NavigationBarViewOptionsStorage.ShowNavigationBar) + e.Option != NavigationBarViewOptions.ShowNavigationBar) { return; } @@ -87,7 +87,7 @@ private void AddOrRemoveDropdown() return; } - var enabled = _globalOptions.GetOption(NavigationBarViewOptionsStorage.ShowNavigationBar, _languageService.RoslynLanguageName); + var enabled = _globalOptions.GetOption(NavigationBarViewOptions.ShowNavigationBar, _languageService.RoslynLanguageName); if (enabled) { if (IsOurDropdownBar(dropdownManager, out var existingDropdownBar)) diff --git a/src/VisualStudio/Core/Def/Options/LanguageSettingsPersister.cs b/src/VisualStudio/Core/Def/Options/LanguageSettingsPersister.cs new file mode 100644 index 0000000000000..2d46f1c5ecc3e --- /dev/null +++ b/src/VisualStudio/Core/Def/Options/LanguageSettingsPersister.cs @@ -0,0 +1,278 @@ +// 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 System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.Editor.Options; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; +using Microsoft.VisualStudio.LanguageServices.Setup; +using Microsoft.VisualStudio.TextManager.Interop; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options +{ + /// + /// An that syncs core language settings against the settings that exist for all languages + /// in Visual Studio and whose backing store is provided by the shell. This includes things like default tab size, tabs vs. spaces, etc. + /// + /// TODO: replace with free-threaded impl: https://github.com/dotnet/roslyn/issues/56815 + /// + internal sealed class LanguageSettingsPersister : ForegroundThreadAffinitizedObject, IVsTextManagerEvents4, IOptionPersister + { + private readonly IVsTextManager4 _textManager; + private readonly IGlobalOptionService _globalOptions; + +#pragma warning disable IDE0052 // Remove unread private members - https://github.com/dotnet/roslyn/issues/46167 + private readonly ComEventSink _textManagerEvents2Sink; +#pragma warning restore IDE0052 // Remove unread private members + + /// + /// The mapping between language names and Visual Studio language service GUIDs. + /// + /// + /// This is a map between string and rather than just to + /// to avoid a bunch of JIT during startup. Generics of value types like will have to JIT + /// but the ngen image will exist for the basic map between two reference types, since those are reused. + private readonly IBidirectionalMap> _languageMap; + + /// + /// We make sure this code is from the UI by asking for all in + /// + public LanguageSettingsPersister( + IThreadingContext threadingContext, + IVsTextManager4 textManager, + IGlobalOptionService globalOptions) + : base(threadingContext, assertIsForeground: true) + { + _textManager = textManager; + _globalOptions = globalOptions; + + var languageMap = BidirectionalMap>.Empty; + + InitializeSettingsForLanguage(LanguageNames.CSharp, Guids.CSharpLanguageServiceId); + InitializeSettingsForLanguage(LanguageNames.VisualBasic, Guids.VisualBasicLanguageServiceId); + InitializeSettingsForLanguage(InternalLanguageNames.TypeScript, new Guid("4a0dddb5-7a95-4fbf-97cc-616d07737a77")); + InitializeSettingsForLanguage("F#", new Guid("BC6DD5A5-D4D6-4dab-A00D-A51242DBAF1B")); + InitializeSettingsForLanguage("Xaml", new Guid("CD53C9A1-6BC2-412B-BE36-CC715ED8DD41")); + + void InitializeSettingsForLanguage(string languageName, Guid languageGuid) + { + var languagePreferences = new LANGPREFERENCES3[1]; + languagePreferences[0].guidLang = languageGuid; + + // The function can potentially fail if that language service isn't installed + if (ErrorHandler.Succeeded(_textManager.GetUserPreferences4(pViewPrefs: null, pLangPrefs: languagePreferences, pColorPrefs: null))) + { + RefreshLanguageSettings(languagePreferences, languageName); + languageMap = languageMap.Add(languageName, Tuple.Create(languageGuid)); + } + else + { + FatalError.ReportWithDumpAndCatch(new InvalidOperationException("GetUserPreferences4 failed"), ErrorSeverity.Diagnostic); + } + } + + _languageMap = languageMap; + _textManagerEvents2Sink = ComEventSink.Advise(_textManager, this); + } + + private readonly IOption[] _supportedOptions = new IOption[] + { + FormattingOptions.UseTabs, + FormattingOptions.TabSize, + FormattingOptions.SmartIndent, + FormattingOptions.IndentationSize, + CompletionOptionsStorage.HideAdvancedMembers, + CompletionOptionsStorage.TriggerOnTyping, + SignatureHelpViewOptions.ShowSignatureHelp, + NavigationBarViewOptions.ShowNavigationBar + }; + + int IVsTextManagerEvents4.OnUserPreferencesChanged4( + VIEWPREFERENCES3[] viewPrefs, + LANGPREFERENCES3[] langPrefs, + FONTCOLORPREFERENCES2[] colorPrefs) + { + if (langPrefs != null) + { + RefreshLanguageSettings(langPrefs); + } + + return VSConstants.S_OK; + } + + private void RefreshLanguageSettings(LANGPREFERENCES3[] langPrefs) + { + this.AssertIsForeground(); + if (_languageMap.TryGetKey(Tuple.Create(langPrefs[0].guidLang), out var languageName)) + { + RefreshLanguageSettings(langPrefs, languageName); + } + } + + private void RefreshLanguageSettings(LANGPREFERENCES3[] langPrefs, string languageName) + { + this.AssertIsForeground(); + + foreach (var option in _supportedOptions) + { + var keyWithLanguage = new OptionKey(option, languageName); + var newValue = GetValueForOption(option, langPrefs[0]); + + _globalOptions.RefreshOption(keyWithLanguage, newValue); + } + } + + private static object GetValueForOption(IOption option, LANGPREFERENCES3 languagePreference) + { + if (option == FormattingOptions.UseTabs) + { + return languagePreference.fInsertTabs != 0; + } + else if (option == FormattingOptions.TabSize) + { + return Convert.ToInt32(languagePreference.uTabSize); + } + else if (option == FormattingOptions.IndentationSize) + { + return Convert.ToInt32(languagePreference.uIndentSize); + } + else if (option == FormattingOptions.SmartIndent) + { + switch (languagePreference.IndentStyle) + { + case vsIndentStyle.vsIndentStyleNone: + return FormattingOptions.IndentStyle.None; + case vsIndentStyle.vsIndentStyleDefault: + return FormattingOptions.IndentStyle.Block; + default: + return FormattingOptions.IndentStyle.Smart; + } + } + else if (option == CompletionOptionsStorage.HideAdvancedMembers) + { + return languagePreference.fHideAdvancedAutoListMembers != 0; + } + else if (option == CompletionOptionsStorage.TriggerOnTyping) + { + return languagePreference.fAutoListMembers != 0; + } + else if (option == SignatureHelpViewOptions.ShowSignatureHelp) + { + return languagePreference.fAutoListParams != 0; + } + else if (option == NavigationBarViewOptions.ShowNavigationBar) + { + return languagePreference.fDropdownBar != 0; + } + else + { + throw new ArgumentException("Unexpected option.", nameof(option)); + } + } + + private static void SetValueForOption(IOption option, ref LANGPREFERENCES3 languagePreference, object value) + { + if (option == FormattingOptions.UseTabs) + { + languagePreference.fInsertTabs = Convert.ToUInt32((bool)value ? 1 : 0); + } + else if (option == FormattingOptions.TabSize) + { + languagePreference.uTabSize = Convert.ToUInt32(value); + } + else if (option == FormattingOptions.IndentationSize) + { + languagePreference.uIndentSize = Convert.ToUInt32(value); + } + else if (option == FormattingOptions.SmartIndent) + { + switch ((FormattingOptions.IndentStyle)value) + { + case FormattingOptions.IndentStyle.None: + languagePreference.IndentStyle = vsIndentStyle.vsIndentStyleNone; + break; + case FormattingOptions.IndentStyle.Block: + languagePreference.IndentStyle = vsIndentStyle.vsIndentStyleDefault; + break; + default: + languagePreference.IndentStyle = vsIndentStyle.vsIndentStyleSmart; + break; + } + } + else if (option == CompletionOptionsStorage.HideAdvancedMembers) + { + languagePreference.fHideAdvancedAutoListMembers = Convert.ToUInt32((bool)value ? 1 : 0); + } + else if (option == CompletionOptionsStorage.TriggerOnTyping) + { + languagePreference.fAutoListMembers = Convert.ToUInt32((bool)value ? 1 : 0); + } + else if (option == SignatureHelpViewOptions.ShowSignatureHelp) + { + languagePreference.fAutoListParams = Convert.ToUInt32((bool)value ? 1 : 0); + } + else if (option == NavigationBarViewOptions.ShowNavigationBar) + { + languagePreference.fDropdownBar = Convert.ToUInt32((bool)value ? 1 : 0); + } + else + { + throw new ArgumentException("Unexpected option.", nameof(option)); + } + } + + public bool TryFetch(OptionKey optionKey, out object value) + { + // This particular serializer is a bit strange, since we have to initially read things out on the UI thread. + // Therefore, we refresh the values in the constructor, meaning that this should never get called for our values. + + if (_supportedOptions.Contains(optionKey.Option) && _languageMap.ContainsKey(optionKey.Language)) + { + FatalError.ReportWithDumpAndCatch(new InvalidOperationException("Unexpected call to " + nameof(LanguageSettingsPersister) + "." + nameof(TryFetch)), ErrorSeverity.Diagnostic); + } + + value = null; + return false; + } + + public bool TryPersist(OptionKey optionKey, object value) + { + if (!_supportedOptions.Contains(optionKey.Option)) + { + return false; + } + + if (!_languageMap.TryGetValue(optionKey.Language, out var languageServiceGuid)) + { + return false; + } + + var languagePreferences = new LANGPREFERENCES3[1]; + languagePreferences[0].guidLang = languageServiceGuid.Item1; + Marshal.ThrowExceptionForHR(_textManager.GetUserPreferences4(null, languagePreferences, null)); + + SetValueForOption(optionKey.Option, ref languagePreferences[0], value); + _ = SetUserPreferencesMaybeAsync(languagePreferences); + + // Even if we didn't call back, say we completed the persist + return true; + } + + private async Task SetUserPreferencesMaybeAsync(LANGPREFERENCES3[] languagePreferences) + { + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); + Marshal.ThrowExceptionForHR(_textManager.SetUserPreferences4(pViewPrefs: null, pLangPrefs: languagePreferences, pColorPrefs: null)); + } + } +} diff --git a/src/VisualStudio/Core/Def/Options/LanguageSettingsPersisterProvider.cs b/src/VisualStudio/Core/Def/Options/LanguageSettingsPersisterProvider.cs new file mode 100644 index 0000000000000..e58eedebe3818 --- /dev/null +++ b/src/VisualStudio/Core/Def/Options/LanguageSettingsPersisterProvider.cs @@ -0,0 +1,66 @@ +// 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. + +using System; +using System.ComponentModel.Composition; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.VisualStudio.TextManager.Interop; +using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; +using SAsyncServiceProvider = Microsoft.VisualStudio.Shell.Interop.SAsyncServiceProvider; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options +{ + [Export(typeof(IOptionPersisterProvider))] + internal sealed class LanguageSettingsPersisterProvider : IOptionPersisterProvider + { + private readonly IThreadingContext _threadingContext; + private readonly IAsyncServiceProvider _serviceProvider; + private readonly IGlobalOptionService _optionService; + private LanguageSettingsPersister? _lazyPersister; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public LanguageSettingsPersisterProvider( + IThreadingContext threadingContext, + [Import(typeof(SAsyncServiceProvider))] IAsyncServiceProvider serviceProvider, + IGlobalOptionService optionService) + { + _threadingContext = threadingContext; + _serviceProvider = serviceProvider; + _optionService = optionService; + } + + public async ValueTask GetOrCreatePersisterAsync(CancellationToken cancellationToken) + { + if (_lazyPersister is not null) + { + return _lazyPersister; + } + + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + // Asynchronously load dependent package prior to calling synchronous APIs on IVsTextManager4 + _ = await _serviceProvider.GetServiceAsync(typeof(SVsManagedFontAndColorInformation)).ConfigureAwait(true); + + var textManager = (IVsTextManager4?)await _serviceProvider.GetServiceAsync(typeof(SVsTextManager)).ConfigureAwait(true); + Assumes.Present(textManager); + + _lazyPersister ??= new LanguageSettingsPersister(_threadingContext, textManager, _optionService); + return _lazyPersister; + } + + /// + /// Shim to allow asynchronous loading of a package prior to calling synchronous (blocking) APIs that use it. + /// + [Guid("48d069e8-1993-4752-baf3-232236a3ea4f")] + private class SVsManagedFontAndColorInformation + { + } + } +} diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs b/src/VisualStudio/Core/Def/Options/RoamingVisualStudioProfileOptionPersister.cs similarity index 88% rename from src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs rename to src/VisualStudio/Core/Def/Options/RoamingVisualStudioProfileOptionPersister.cs index 806bc4cca84f1..7d4eb04f6ff3c 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs +++ b/src/VisualStudio/Core/Def/Options/RoamingVisualStudioProfileOptionPersister.cs @@ -20,9 +20,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options { /// - /// Serializes settings marked with to and from VS Settings storage. + /// Serializes settings marked with to and from the user's roaming profile. /// - internal sealed class VisualStudioSettingsOptionPersister : IOptionPersister + internal sealed class RoamingVisualStudioProfileOptionPersister : IOptionPersister { // NOTE: This service is not public or intended for use by teams/individuals outside of Microsoft. Any data stored is subject to deletion without warning. [Guid("9B164E40-C3A2-4363-9BC5-EB4039DEF653")] @@ -42,7 +42,7 @@ private class SVsSettingsPersistenceManager { }; /// /// We make sure this code is from the UI by asking for all in /// - public VisualStudioSettingsOptionPersister(IGlobalOptionService globalOptionService, ISettingsManager? settingsManager) + public RoamingVisualStudioProfileOptionPersister(IGlobalOptionService globalOptionService, ISettingsManager? settingsManager) { Contract.ThrowIfNull(globalOptionService); @@ -91,21 +91,21 @@ private System.Threading.Tasks.Task OnSettingChangedAsync(object sender, Propert return System.Threading.Tasks.Task.CompletedTask; } - private object? GetFirstOrDefaultValue(OptionKey optionKey, IEnumerable storageLocations) + private object? GetFirstOrDefaultValue(OptionKey optionKey, IEnumerable roamingSerializations) { Contract.ThrowIfNull(_settingManager); - // There can be more than 1 storage location in the order of their priority. + // There can be more than 1 roaming location in the order of their priority. // When fetching a value, we iterate all of them until we find the first one that exists. // When persisting a value, we always use the first location. - // This functionality exists to accomodate breaking changes to persistence of some options. In such a case, there + // This functionality exists for breaking changes to persistence of some options. In such a case, there // will be a new location added to the beginning with a new name. When fetching a value, we might find the old // location (and can upgrade the value accordingly) but we only write to the new location so that // we don't interfere with older versions. This will essentially "fork" the user's options at the time of upgrade. - foreach (var storageLocation in storageLocations) + foreach (var roamingSerialization in roamingSerializations) { - var storageKey = storageLocation.GetKeyNameForLanguage(optionKey.Language); + var storageKey = roamingSerialization.GetKeyNameForLanguage(optionKey.Language); RecordObservedValueToWatchForChanges(optionKey, storageKey); @@ -127,14 +127,16 @@ public bool TryFetch(OptionKey optionKey, out object? value) return false; } - var storageLocations = optionKey.Option.StorageLocations.OfType(); - if (!storageLocations.Any()) + // Do we roam this at all? + var roamingSerializations = optionKey.Option.StorageLocations.OfType(); + + if (!roamingSerializations.Any()) { value = null; return false; } - value = GetFirstOrDefaultValue(optionKey, storageLocations); + value = GetFirstOrDefaultValue(optionKey, roamingSerializations); // VS's ISettingsManager has some quirks around storing enums. Specifically, // it *can* persist and retrieve enums, but only if you properly call @@ -248,13 +250,14 @@ public bool TryPersist(OptionKey optionKey, object? value) } // Do we roam this at all? - var storageLocation = optionKey.Option.StorageLocations.OfType().FirstOrDefault(); - if (storageLocation == null) + var roamingSerialization = optionKey.Option.StorageLocations.OfType().FirstOrDefault(); + + if (roamingSerialization == null) { return false; } - var storageKey = storageLocation.GetKeyNameForLanguage(optionKey.Language); + var storageKey = roamingSerialization.GetKeyNameForLanguage(optionKey.Language); RecordObservedValueToWatchForChanges(optionKey, storageKey); @@ -272,7 +275,7 @@ public bool TryPersist(OptionKey optionKey, object? value) } } - _settingManager.SetValueAsync(storageKey, value, storageLocation.IsMachineLocal); + _settingManager.SetValueAsync(storageKey, value, isMachineLocal: false); return true; } } diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersisterProvider.cs b/src/VisualStudio/Core/Def/Options/RoamingVisualStudioProfileOptionPersisterProvider.cs similarity index 83% rename from src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersisterProvider.cs rename to src/VisualStudio/Core/Def/Options/RoamingVisualStudioProfileOptionPersisterProvider.cs index 79970bbae5ce3..21388ceaf887d 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersisterProvider.cs +++ b/src/VisualStudio/Core/Def/Options/RoamingVisualStudioProfileOptionPersisterProvider.cs @@ -17,15 +17,15 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options { [Export(typeof(IOptionPersisterProvider))] - internal sealed class VisualStudioSettingsOptionPersisterProvider : IOptionPersisterProvider + internal sealed class RoamingVisualStudioProfileOptionPersisterProvider : IOptionPersisterProvider { private readonly IAsyncServiceProvider _serviceProvider; private readonly IGlobalOptionService _optionService; - private VisualStudioSettingsOptionPersister? _lazyPersister; + private RoamingVisualStudioProfileOptionPersister? _lazyPersister; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public VisualStudioSettingsOptionPersisterProvider( + public RoamingVisualStudioProfileOptionPersisterProvider( [Import(typeof(SAsyncServiceProvider))] IAsyncServiceProvider serviceProvider, IGlobalOptionService optionService) { @@ -42,7 +42,7 @@ public async ValueTask GetOrCreatePersisterAsync(CancellationT var settingsManager = (ISettingsManager?)await _serviceProvider.GetServiceAsync(typeof(SVsSettingsPersistenceManager)).ConfigureAwait(true); - _lazyPersister ??= new VisualStudioSettingsOptionPersister(_optionService, settingsManager); + _lazyPersister ??= new RoamingVisualStudioProfileOptionPersister(_optionService, settingsManager); return _lazyPersister; } } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs index 5246701e55e5f..ce805131200d3 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -247,7 +247,7 @@ public async Task InitializeUIAffinitizedServicesAsync(IAsyncServiceProvider asy // Switch to a background thread to avoid loading option providers on UI thread (telemetry is reading options). await TaskScheduler.Default; - var logDelta = _globalOptions.GetOption(DiagnosticOptionsStorage.LogTelemetryForBackgroundAnalyzerExecution); + var logDelta = _globalOptions.GetOption(DiagnosticOptions.LogTelemetryForBackgroundAnalyzerExecution); var telemetryService = (VisualStudioWorkspaceTelemetryService)Services.GetRequiredService(); telemetryService.InitializeTelemetrySession(telemetrySession, logDelta); diff --git a/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs b/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs index c4a4c4a7b9410..d5f447dece3a1 100644 --- a/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs +++ b/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs @@ -57,7 +57,7 @@ protected override void TelemetrySessionInitialized() Contract.ThrowIfNull(settings); // Only log "delta" property for block end events if feature flag is enabled. - var logDelta = _globalOptions.GetOption(DiagnosticOptionsStorage.LogTelemetryForBackgroundAnalyzerExecution); + var logDelta = _globalOptions.GetOption(DiagnosticOptions.LogTelemetryForBackgroundAnalyzerExecution); // initialize session in the remote service _ = await client.TryInvokeAsync( diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNavigationBar.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNavigationBar.cs index 2bc6205268b82..7e741a5e85e0f 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNavigationBar.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNavigationBar.cs @@ -128,10 +128,10 @@ struct S public async Task VerifyOption() { var globalOptions = await TestServices.Shell.GetComponentModelServiceAsync(HangMitigatingCancellationToken); - globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptionsStorage.ShowNavigationBar, LanguageNames.CSharp), false); + globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptions.ShowNavigationBar, LanguageNames.CSharp), false); Assert.False(await TestServices.Editor.IsNavigationBarEnabledAsync(HangMitigatingCancellationToken)); - globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptionsStorage.ShowNavigationBar, LanguageNames.CSharp), true); + globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptions.ShowNavigationBar, LanguageNames.CSharp), true); Assert.True(await TestServices.Editor.IsNavigationBarEnabledAsync(HangMitigatingCancellationToken)); } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs index fd70e44acec22..de1ca9334b525 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs @@ -45,7 +45,7 @@ public async Task ResetGlobalOptionsAsync(CancellationToken cancellationToken) var globalOptions = await GetComponentModelServiceAsync(cancellationToken); ResetOption(globalOptions, MetadataAsSourceOptionsStorage.NavigateToDecompiledSources); ResetOption(globalOptions, WorkspaceConfigurationOptionsStorage.EnableOpeningSourceGeneratedFilesInWorkspace); - ResetPerLanguageOption(globalOptions, NavigationBarViewOptionsStorage.ShowNavigationBar); + ResetPerLanguageOption(globalOptions, NavigationBarViewOptions.ShowNavigationBar); ResetPerLanguageOption(globalOptions, VisualStudioNavigationOptions.NavigateToObjectBrowser); ResetPerLanguageOption(globalOptions, FeatureOnOffOptions.AddImportsOnPaste); ResetPerLanguageOption(globalOptions, FeatureOnOffOptions.PrettyListing); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicNavigationBar.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicNavigationBar.cs index 6b584a5795d36..148296fa6a7b6 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicNavigationBar.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicNavigationBar.cs @@ -104,10 +104,10 @@ public async Task VerifyOption() { var globalOptions = await TestServices.Shell.GetComponentModelServiceAsync(HangMitigatingCancellationToken); - globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptionsStorage.ShowNavigationBar, LanguageNames.VisualBasic), false); + globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptions.ShowNavigationBar, LanguageNames.VisualBasic), false); Assert.False(await TestServices.Editor.IsNavigationBarEnabledAsync(HangMitigatingCancellationToken)); - globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptionsStorage.ShowNavigationBar, LanguageNames.VisualBasic), true); + globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptions.ShowNavigationBar, LanguageNames.VisualBasic), true); Assert.True(await TestServices.Editor.IsNavigationBarEnabledAsync(HangMitigatingCancellationToken)); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index 1f3be8b79d45e..88351e745b66d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -250,6 +250,7 @@ + @@ -389,8 +390,6 @@ - - diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs index 8de387a714922..ea388ce1453e3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs @@ -39,21 +39,20 @@ public Provider() public static PerLanguageOption2 UseTabs = new(FeatureName, FormattingOptionGroups.IndentationAndSpacing, nameof(UseTabs), LineFormattingOptions.Default.UseTabs, - storageLocations: ImmutableArray.Create( - new EditorConfigStorageLocation("indent_style", s => s == "tab", isSet => isSet ? "tab" : "space"), - new LocalClientSettingsStorageLocation("TextEditor.%LANGUAGE%.Insert Tabs"))); + storageLocation: new EditorConfigStorageLocation( + "indent_style", + s => s == "tab", + isSet => isSet ? "tab" : "space")); + // This is also serialized by the Visual Studio-specific LanguageSettingsPersister public static PerLanguageOption2 TabSize = new(FeatureName, FormattingOptionGroups.IndentationAndSpacing, nameof(TabSize), LineFormattingOptions.Default.TabSize, - storageLocations: ImmutableArray.Create( - EditorConfigStorageLocation.ForInt32Option("tab_width"), - new LocalClientSettingsStorageLocation("TextEditor.%LANGUAGE%.Tab Size"))); + storageLocation: EditorConfigStorageLocation.ForInt32Option("tab_width")); + // This is also serialized by the Visual Studio-specific LanguageSettingsPersister public static PerLanguageOption2 IndentationSize = new(FeatureName, FormattingOptionGroups.IndentationAndSpacing, nameof(IndentationSize), LineFormattingOptions.Default.IndentationSize, - storageLocations: ImmutableArray.Create( - EditorConfigStorageLocation.ForInt32Option("indent_size"), - new LocalClientSettingsStorageLocation("TextEditor.%LANGUAGE%.Indent Size"))); + storageLocation: EditorConfigStorageLocation.ForInt32Option("indent_size")); public static PerLanguageOption2 NewLine = new(FeatureName, FormattingOptionGroups.NewLine, nameof(NewLine), LineFormattingOptions.Default.NewLine, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/ClientSettingsStorageLocation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/ClientSettingsStorageLocation.cs deleted file mode 100644 index 3d12ba03c3427..0000000000000 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/ClientSettingsStorageLocation.cs +++ /dev/null @@ -1,49 +0,0 @@ -// 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. - -using System; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Options; - -/// -/// Specifies that the option is stored in setting storage of the client. -/// -/// -/// TODO (https://github.com/dotnet/roslyn/issues/62683): The options that use this storage are global client options. -/// This storage should really be in the VS layer but currently option storage is coupled with option definition and thus the storage is needed here. -/// -internal abstract class ClientSettingsStorageLocation : OptionStorageLocation2 -{ - private readonly Func _keyNameFromLanguageName; - - public ClientSettingsStorageLocation(string keyName) - => _keyNameFromLanguageName = _ => keyName; - - /// - /// Creates a that has different key names for different languages. - /// - /// A function that maps from a value to the key name. - public ClientSettingsStorageLocation(Func keyNameFromLanguageName) - => _keyNameFromLanguageName = keyNameFromLanguageName; - - public abstract bool IsMachineLocal { get; } - - public string GetKeyNameForLanguage(string? languageName) - { - var keyName = _keyNameFromLanguageName(languageName); - - if (languageName != null) - { - keyName = keyName.Replace("%LANGUAGE%", languageName switch - { - LanguageNames.CSharp => "CSharp", - LanguageNames.VisualBasic => "VisualBasic", - _ => languageName // handles F#, TypeScript and Xaml - }); - } - - return keyName; - } -} diff --git a/src/Features/LanguageServer/Protocol/Features/Options/FeatureFlagStorageLocation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/FeatureFlagStorageLocation.cs similarity index 100% rename from src/Features/LanguageServer/Protocol/Features/Options/FeatureFlagStorageLocation.cs rename to src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/FeatureFlagStorageLocation.cs diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/LocalClientSettingsStorageLocation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/LocalClientSettingsStorageLocation.cs deleted file mode 100644 index d4cf826b9c501..0000000000000 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/LocalClientSettingsStorageLocation.cs +++ /dev/null @@ -1,35 +0,0 @@ -// 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. - -using System; - -namespace Microsoft.CodeAnalysis.Options; - -/// -/// Specifies that the option should be stored into local client settings storage. -/// -/// -/// Unlike LocalUserRegistryOptionPersister, which accesses the registry directly this storage is managed by VS Settings component. -/// -/// TODO (https://github.com/dotnet/roslyn/issues/62683): The options that use this storage are global client options. This storage should really be in the VS layer but currently -/// option storage is coupled with option definition and thus the storage is needed here. -/// -internal sealed class LocalClientSettingsStorageLocation : ClientSettingsStorageLocation -{ - public override bool IsMachineLocal => true; - - public LocalClientSettingsStorageLocation(string keyName) - : base(keyName) - { - } - - /// - /// Creates a that has different key names for different languages. - /// - /// A function that maps from a value to the key name. - public LocalClientSettingsStorageLocation(Func keyNameFromLanguageName) - : base(keyNameFromLanguageName) - { - } -} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/LocalUserProfileStorageLocation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/LocalUserProfileStorageLocation.cs index 6c648abed60c4..17bdc17c6fcc9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/LocalUserProfileStorageLocation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/LocalUserProfileStorageLocation.cs @@ -5,7 +5,7 @@ namespace Microsoft.CodeAnalysis.Options { /// - /// Specifies that the option should be stored into the user's local Roslyn specific registry hive. + /// Specifies that the option should be stored into the user's local registry hive. /// internal sealed class LocalUserProfileStorageLocation : OptionStorageLocation2 { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/RoamingProfileStorageLocation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/RoamingProfileStorageLocation.cs index ec382f18d23df..435d9ab802415 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/RoamingProfileStorageLocation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/RoamingProfileStorageLocation.cs @@ -4,26 +4,41 @@ using System; -namespace Microsoft.CodeAnalysis.Options; - -/// -/// Specifies that the option should be stored into a roamed profile across machines. -/// -internal sealed class RoamingProfileStorageLocation : ClientSettingsStorageLocation +namespace Microsoft.CodeAnalysis.Options { - public override bool IsMachineLocal => false; - - public RoamingProfileStorageLocation(string keyName) - : base(keyName) - { - } - /// - /// Creates a that has different key names for different languages. + /// Specifies that the option should be stored into a roamed profile across machines. /// - /// A function that maps from a value to the key name. - public RoamingProfileStorageLocation(Func keyNameFromLanguageName) - : base(keyNameFromLanguageName) + internal sealed class RoamingProfileStorageLocation : OptionStorageLocation2 { + private readonly Func _keyNameFromLanguageName; + + public string GetKeyNameForLanguage(string? languageName) + { + var unsubstitutedKeyName = _keyNameFromLanguageName(languageName); + + if (languageName == null) + { + return unsubstitutedKeyName; + } + else + { + var substituteLanguageName = languageName == LanguageNames.CSharp ? "CSharp" : + languageName == LanguageNames.VisualBasic ? "VisualBasic" : + languageName; + + return unsubstitutedKeyName.Replace("%LANGUAGE%", substituteLanguageName); + } + } + + public RoamingProfileStorageLocation(string keyName) + => _keyNameFromLanguageName = _ => keyName; + + /// + /// Creates a that has different key names for different languages. + /// + /// A function that maps from a value to the key name. + public RoamingProfileStorageLocation(Func keyNameFromLanguageName) + => _keyNameFromLanguageName = keyNameFromLanguageName; } }