diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/AbstractSignatureHelpCommandHandler.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/AbstractSignatureHelpCommandHandler.cs index f854ed36aa280..8106e43c6597b 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(SignatureHelpViewOptions.ShowSignatureHelp, languageName)) + !_globalOptions.GetOption(SignatureHelpViewOptionsStorage.ShowSignatureHelp, languageName)) { controller = null; return false; diff --git a/src/EditorFeatures/Core/Options/NavigationBarViewOptions.cs b/src/EditorFeatures/Core/Options/NavigationBarViewOptions.cs deleted file mode 100644 index cde4d9cbf4e2e..0000000000000 --- a/src/EditorFeatures/Core/Options/NavigationBarViewOptions.cs +++ /dev/null @@ -1,20 +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 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 new file mode 100644 index 0000000000000..1d7ec8496b7ff --- /dev/null +++ b/src/EditorFeatures/Core/Options/NavigationBarViewOptionsStorage.cs @@ -0,0 +1,15 @@ +// 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 deleted file mode 100644 index 0383174fb5338..0000000000000 --- a/src/EditorFeatures/Core/Options/SignatureHelpViewOptions.cs +++ /dev/null @@ -1,20 +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 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 new file mode 100644 index 0000000000000..3d02f2959d98e --- /dev/null +++ b/src/EditorFeatures/Core/Options/SignatureHelpViewOptionsStorage.cs @@ -0,0 +1,15 @@ +// 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 7e4556adaff71..98422df0558b6 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(SignatureHelpViewOptions.ShowSignatureHelp, LanguageNames.CSharp), False) + state.Workspace.GetService(Of IGlobalOptionService).SetGlobalOption(New OptionKey(SignatureHelpViewOptionsStorage.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 0257bc75979e0..cbe2eaeb82cd0 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(SignatureHelpViewOptions.ShowSignatureHelp, LanguageNames.VisualBasic), False) + state.Workspace.GetService(Of IGlobalOptionService).SetGlobalOption(New OptionKey(SignatureHelpViewOptionsStorage.ShowSignatureHelp, LanguageNames.VisualBasic), False) state.SendTypeChars("(") Await state.AssertNoSignatureHelpSession() 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 580473cd337ed..5692075529be5 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(DiagnosticOptions.LogTelemetryForBackgroundAnalyzerExecution); + var logTelemetry = GlobalOptions.GetOption(DiagnosticOptionsStorage.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 8f88c40e10cba..725dda47f9acd 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/CompletionOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/CompletionOptionsStorage.cs @@ -47,11 +47,13 @@ public static CompletionOptions GetCompletionOptions(this IGlobalOptionService o CompletionOptions.Default.SnippetCompletion, new FeatureFlagStorageLocation("Roslyn.SnippetCompletion")); - // 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 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 TriggerOnTyping = new(nameof(CompletionOptions), nameof(TriggerOnTyping), CompletionOptions.Default.TriggerOnTyping); + public static readonly PerLanguageOption2 TriggerOnTyping = new( + "CompletionOptions", "TriggerOnTyping", CompletionOptions.Default.TriggerOnTyping, + new LocalClientSettingsStorageLocation("TextEditor.%LANGUAGE%.Auto List Members")); 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 1a5ef527389ae..e3f727145d68f 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(DiagnosticOptions.LspPullDiagnosticsFeatureFlag) ? DiagnosticMode.Pull : DiagnosticMode.Push; + return globalOptions.GetOption(DiagnosticOptionsStorage.LspPullDiagnosticsFeatureFlag) ? DiagnosticMode.Pull : DiagnosticMode.Push; } // Otherwise, defer to the workspace+option to determine what mode we're in. diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs b/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticOptionsStorage.cs similarity index 95% rename from src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs rename to src/Features/LanguageServer/Protocol/Features/Options/DiagnosticOptionsStorage.cs index 1422e927ed734..506fbce515ed9 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticOptionsStorage.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics { - internal sealed class DiagnosticOptions + internal sealed class DiagnosticOptionsStorage { private const string FeatureName = "DiagnosticOptions"; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/FeatureFlagStorageLocation.cs b/src/Features/LanguageServer/Protocol/Features/Options/FeatureFlagStorageLocation.cs similarity index 100% rename from src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/FeatureFlagStorageLocation.cs rename to src/Features/LanguageServer/Protocol/Features/Options/FeatureFlagStorageLocation.cs diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 4669c384bf3b3..330cf0445e184 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(DiagnosticOptions.LspPullDiagnosticsFeatureFlag), false); + testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(new OptionKey(DiagnosticOptionsStorage.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(DiagnosticOptions.LspPullDiagnosticsFeatureFlag), true); + testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(new OptionKey(DiagnosticOptionsStorage.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 276ea9e1fb086..69ea931e44093 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(DiagnosticOptions.LspPullDiagnosticsFeatureFlag, checkboxValue.Value); + this.OptionStore.SetOption(DiagnosticOptionsStorage.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 e1daa6d2474fe..c4c33af48d2fd 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 != NavigationBarViewOptions.ShowNavigationBar) + e.Option != NavigationBarViewOptionsStorage.ShowNavigationBar) { return; } @@ -87,7 +87,7 @@ private void AddOrRemoveDropdown() return; } - var enabled = _globalOptions.GetOption(NavigationBarViewOptions.ShowNavigationBar, _languageService.RoslynLanguageName); + var enabled = _globalOptions.GetOption(NavigationBarViewOptionsStorage.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 deleted file mode 100644 index 2d46f1c5ecc3e..0000000000000 --- a/src/VisualStudio/Core/Def/Options/LanguageSettingsPersister.cs +++ /dev/null @@ -1,278 +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. - -#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 deleted file mode 100644 index e58eedebe3818..0000000000000 --- a/src/VisualStudio/Core/Def/Options/LanguageSettingsPersisterProvider.cs +++ /dev/null @@ -1,66 +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 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/RoamingVisualStudioProfileOptionPersister.cs b/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs similarity index 88% rename from src/VisualStudio/Core/Def/Options/RoamingVisualStudioProfileOptionPersister.cs rename to src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs index 7d4eb04f6ff3c..806bc4cca84f1 100644 --- a/src/VisualStudio/Core/Def/Options/RoamingVisualStudioProfileOptionPersister.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs @@ -20,9 +20,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options { /// - /// Serializes settings marked with to and from the user's roaming profile. + /// Serializes settings marked with to and from VS Settings storage. /// - internal sealed class RoamingVisualStudioProfileOptionPersister : IOptionPersister + internal sealed class VisualStudioSettingsOptionPersister : 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 RoamingVisualStudioProfileOptionPersister(IGlobalOptionService globalOptionService, ISettingsManager? settingsManager) + public VisualStudioSettingsOptionPersister(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 roamingSerializations) + private object? GetFirstOrDefaultValue(OptionKey optionKey, IEnumerable storageLocations) { Contract.ThrowIfNull(_settingManager); - // There can be more than 1 roaming location in the order of their priority. + // There can be more than 1 storage 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 for breaking changes to persistence of some options. In such a case, there + // This functionality exists to accomodate 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 roamingSerialization in roamingSerializations) + foreach (var storageLocation in storageLocations) { - var storageKey = roamingSerialization.GetKeyNameForLanguage(optionKey.Language); + var storageKey = storageLocation.GetKeyNameForLanguage(optionKey.Language); RecordObservedValueToWatchForChanges(optionKey, storageKey); @@ -127,16 +127,14 @@ public bool TryFetch(OptionKey optionKey, out object? value) return false; } - // Do we roam this at all? - var roamingSerializations = optionKey.Option.StorageLocations.OfType(); - - if (!roamingSerializations.Any()) + var storageLocations = optionKey.Option.StorageLocations.OfType(); + if (!storageLocations.Any()) { value = null; return false; } - value = GetFirstOrDefaultValue(optionKey, roamingSerializations); + value = GetFirstOrDefaultValue(optionKey, storageLocations); // VS's ISettingsManager has some quirks around storing enums. Specifically, // it *can* persist and retrieve enums, but only if you properly call @@ -250,14 +248,13 @@ public bool TryPersist(OptionKey optionKey, object? value) } // Do we roam this at all? - var roamingSerialization = optionKey.Option.StorageLocations.OfType().FirstOrDefault(); - - if (roamingSerialization == null) + var storageLocation = optionKey.Option.StorageLocations.OfType().FirstOrDefault(); + if (storageLocation == null) { return false; } - var storageKey = roamingSerialization.GetKeyNameForLanguage(optionKey.Language); + var storageKey = storageLocation.GetKeyNameForLanguage(optionKey.Language); RecordObservedValueToWatchForChanges(optionKey, storageKey); @@ -275,7 +272,7 @@ public bool TryPersist(OptionKey optionKey, object? value) } } - _settingManager.SetValueAsync(storageKey, value, isMachineLocal: false); + _settingManager.SetValueAsync(storageKey, value, storageLocation.IsMachineLocal); return true; } } diff --git a/src/VisualStudio/Core/Def/Options/RoamingVisualStudioProfileOptionPersisterProvider.cs b/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersisterProvider.cs similarity index 83% rename from src/VisualStudio/Core/Def/Options/RoamingVisualStudioProfileOptionPersisterProvider.cs rename to src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersisterProvider.cs index 21388ceaf887d..79970bbae5ce3 100644 --- a/src/VisualStudio/Core/Def/Options/RoamingVisualStudioProfileOptionPersisterProvider.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersisterProvider.cs @@ -17,15 +17,15 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options { [Export(typeof(IOptionPersisterProvider))] - internal sealed class RoamingVisualStudioProfileOptionPersisterProvider : IOptionPersisterProvider + internal sealed class VisualStudioSettingsOptionPersisterProvider : IOptionPersisterProvider { private readonly IAsyncServiceProvider _serviceProvider; private readonly IGlobalOptionService _optionService; - private RoamingVisualStudioProfileOptionPersister? _lazyPersister; + private VisualStudioSettingsOptionPersister? _lazyPersister; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RoamingVisualStudioProfileOptionPersisterProvider( + public VisualStudioSettingsOptionPersisterProvider( [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 RoamingVisualStudioProfileOptionPersister(_optionService, settingsManager); + _lazyPersister ??= new VisualStudioSettingsOptionPersister(_optionService, settingsManager); return _lazyPersister; } } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs index ce805131200d3..5246701e55e5f 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(DiagnosticOptions.LogTelemetryForBackgroundAnalyzerExecution); + var logDelta = _globalOptions.GetOption(DiagnosticOptionsStorage.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 d5f447dece3a1..c4a4c4a7b9410 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(DiagnosticOptions.LogTelemetryForBackgroundAnalyzerExecution); + var logDelta = _globalOptions.GetOption(DiagnosticOptionsStorage.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 7e741a5e85e0f..2bc6205268b82 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNavigationBar.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpNavigationBar.cs @@ -128,10 +128,10 @@ void Bar() { } public async Task VerifyOption() { var globalOptions = await TestServices.Shell.GetComponentModelServiceAsync(HangMitigatingCancellationToken); - globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptions.ShowNavigationBar, LanguageNames.CSharp), false); + globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptionsStorage.ShowNavigationBar, LanguageNames.CSharp), false); Assert.False(await TestServices.Editor.IsNavigationBarEnabledAsync(HangMitigatingCancellationToken)); - globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptions.ShowNavigationBar, LanguageNames.CSharp), true); + globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptionsStorage.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 de1ca9334b525..fd70e44acec22 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, NavigationBarViewOptions.ShowNavigationBar); + ResetPerLanguageOption(globalOptions, NavigationBarViewOptionsStorage.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 148296fa6a7b6..6b584a5795d36 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(NavigationBarViewOptions.ShowNavigationBar, LanguageNames.VisualBasic), false); + globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptionsStorage.ShowNavigationBar, LanguageNames.VisualBasic), false); Assert.False(await TestServices.Editor.IsNavigationBarEnabledAsync(HangMitigatingCancellationToken)); - globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptions.ShowNavigationBar, LanguageNames.VisualBasic), true); + globalOptions.SetGlobalOption(new OptionKey(NavigationBarViewOptionsStorage.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 485781f385fab..50b0a2f6313b7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -250,7 +250,6 @@ - @@ -390,6 +389,8 @@ + + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs index ea388ce1453e3..8de387a714922 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs @@ -39,20 +39,21 @@ public Provider() public static PerLanguageOption2 UseTabs = new(FeatureName, FormattingOptionGroups.IndentationAndSpacing, nameof(UseTabs), LineFormattingOptions.Default.UseTabs, - storageLocation: new EditorConfigStorageLocation( - "indent_style", - s => s == "tab", - isSet => isSet ? "tab" : "space")); + storageLocations: ImmutableArray.Create( + new EditorConfigStorageLocation("indent_style", s => s == "tab", isSet => isSet ? "tab" : "space"), + new LocalClientSettingsStorageLocation("TextEditor.%LANGUAGE%.Insert Tabs"))); - // This is also serialized by the Visual Studio-specific LanguageSettingsPersister public static PerLanguageOption2 TabSize = new(FeatureName, FormattingOptionGroups.IndentationAndSpacing, nameof(TabSize), LineFormattingOptions.Default.TabSize, - storageLocation: EditorConfigStorageLocation.ForInt32Option("tab_width")); + storageLocations: ImmutableArray.Create( + EditorConfigStorageLocation.ForInt32Option("tab_width"), + new LocalClientSettingsStorageLocation("TextEditor.%LANGUAGE%.Tab Size"))); - // This is also serialized by the Visual Studio-specific LanguageSettingsPersister public static PerLanguageOption2 IndentationSize = new(FeatureName, FormattingOptionGroups.IndentationAndSpacing, nameof(IndentationSize), LineFormattingOptions.Default.IndentationSize, - storageLocation: EditorConfigStorageLocation.ForInt32Option("indent_size")); + storageLocations: ImmutableArray.Create( + EditorConfigStorageLocation.ForInt32Option("indent_size"), + new LocalClientSettingsStorageLocation("TextEditor.%LANGUAGE%.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 new file mode 100644 index 0000000000000..3d12ba03c3427 --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/ClientSettingsStorageLocation.cs @@ -0,0 +1,49 @@ +// 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/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/LocalClientSettingsStorageLocation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/LocalClientSettingsStorageLocation.cs new file mode 100644 index 0000000000000..d4cf826b9c501 --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/LocalClientSettingsStorageLocation.cs @@ -0,0 +1,35 @@ +// 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 17bdc17c6fcc9..6c648abed60c4 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 registry hive. + /// Specifies that the option should be stored into the user's local Roslyn specific 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 435d9ab802415..ec382f18d23df 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/RoamingProfileStorageLocation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/RoamingProfileStorageLocation.cs @@ -4,41 +4,26 @@ using System; -namespace Microsoft.CodeAnalysis.Options +namespace Microsoft.CodeAnalysis.Options; + +/// +/// Specifies that the option should be stored into a roamed profile across machines. +/// +internal sealed class RoamingProfileStorageLocation : ClientSettingsStorageLocation { + public override bool IsMachineLocal => false; + + public RoamingProfileStorageLocation(string keyName) + : base(keyName) + { + } + /// - /// Specifies that the option should be stored into a roamed profile across machines. + /// Creates a that has different key names for different languages. /// - internal sealed class RoamingProfileStorageLocation : OptionStorageLocation2 + /// A function that maps from a value to the key name. + public RoamingProfileStorageLocation(Func keyNameFromLanguageName) + : base(keyNameFromLanguageName) { - 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; } }