diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternal.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternal.cs deleted file mode 100644 index 941884616e..0000000000 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternal.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.CodeAnalysis.Razor.Editor -{ - internal class DefaultEditorSettingsManagerInternal : EditorSettingsManagerInternal - { - public override event EventHandler Changed; - - private readonly ForegroundDispatcher _foregroundDispatcher; - private readonly object SettingsAccessorLock = new object(); - private EditorSettings _settings; - - public DefaultEditorSettingsManagerInternal(ForegroundDispatcher dispatcher) - { - if (dispatcher == null) - { - throw new ArgumentNullException(nameof(dispatcher)); - } - - _foregroundDispatcher = dispatcher; - _settings = EditorSettings.Default; - } - - public override EditorSettings Current - { - get - { - lock (SettingsAccessorLock) - { - return _settings; - } - } - } - - public override void Update(EditorSettings updatedSettings) - { - if (updatedSettings == null) - { - throw new ArgumentNullException(nameof(updatedSettings)); - } - - lock (SettingsAccessorLock) - { - if (!_settings.Equals(updatedSettings)) - { - _settings = updatedSettings; - OnChanged(); - } - } - } - - private void OnChanged() - { - _foregroundDispatcher.AssertForegroundThread(); - - var args = new EditorSettingsChangedEventArgs(Current); - Changed?.Invoke(this, args); - } - } -} diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/EditorSettingsManagerInternal.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/WorkspaceEditorSettings.cs similarity index 74% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/EditorSettingsManagerInternal.cs rename to src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/WorkspaceEditorSettings.cs index 9dd5a4f5aa..6736dfc39d 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/EditorSettingsManagerInternal.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/WorkspaceEditorSettings.cs @@ -6,12 +6,10 @@ using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Razor.Editor { - internal abstract class EditorSettingsManagerInternal : ILanguageService + internal abstract class WorkspaceEditorSettings : ILanguageService { public abstract event EventHandler Changed; public abstract EditorSettings Current { get; } - - public abstract void Update(EditorSettings updateSettings); } } diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultEditorSettingsManager.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultEditorSettingsManager.cs index 389aff0b77..cba5851308 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/DefaultEditorSettingsManager.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultEditorSettingsManager.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.ComponentModel.Composition; using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.Editor; @@ -12,26 +13,55 @@ namespace Microsoft.VisualStudio.Editor.Razor [Export(typeof(EditorSettingsManager))] internal class DefaultEditorSettingsManager : EditorSettingsManager { - private readonly EditorSettingsManagerInternal _editorSettingsManager; + public override event EventHandler Changed; + + private readonly object SettingsAccessorLock = new object(); + private readonly ForegroundDispatcher _foregroundDispatcher; + private EditorSettings _settings; [ImportingConstructor] - public DefaultEditorSettingsManager(VisualStudioWorkspaceAccessor workspaceAccessor) + public DefaultEditorSettingsManager(ForegroundDispatcher foregroundDispatcher) { - var razorLanguageServices = workspaceAccessor.Workspace.Services.GetLanguageServices(RazorLanguage.Name); - _editorSettingsManager = razorLanguageServices.GetRequiredService(); + _foregroundDispatcher = foregroundDispatcher; + _settings = EditorSettings.Default; } - public override event EventHandler Changed + public override EditorSettings Current { - add => _editorSettingsManager.Changed += value; - remove => _editorSettingsManager.Changed -= value; + get + { + lock (SettingsAccessorLock) + { + return _settings; + } + } } - public override EditorSettings Current => _editorSettingsManager.Current; - - public override void Update(EditorSettings updateSettings) + public override void Update(EditorSettings updatedSettings) { - _editorSettingsManager.Update(updateSettings); + if (updatedSettings == null) + { + throw new ArgumentNullException(nameof(updatedSettings)); + } + + _foregroundDispatcher.AssertForegroundThread(); + + lock (SettingsAccessorLock) + { + if (!_settings.Equals(updatedSettings)) + { + _settings = updatedSettings; + OnChanged(); + } + } + } + + private void OnChanged() + { + _foregroundDispatcher.AssertForegroundThread(); + + var args = new EditorSettingsChangedEventArgs(Current); + Changed?.Invoke(this, args); } } } diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTracker.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTracker.cs index 3e147c005f..f1b8593f6f 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTracker.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTracker.cs @@ -19,7 +19,7 @@ namespace Microsoft.VisualStudio.Editor.Razor private readonly string _filePath; private readonly string _projectPath; private readonly ProjectSnapshotManager _projectManager; - private readonly EditorSettingsManagerInternal _editorSettingsManager; + private readonly WorkspaceEditorSettings _workspaceEditorSettings; private readonly ITextBuffer _textBuffer; private readonly ImportDocumentManager _importDocumentManager; private readonly List _textViews; @@ -34,7 +34,7 @@ namespace Microsoft.VisualStudio.Editor.Razor string filePath, string projectPath, ProjectSnapshotManager projectManager, - EditorSettingsManagerInternal editorSettingsManager, + WorkspaceEditorSettings workspaceEditorSettings, Workspace workspace, ITextBuffer textBuffer, ImportDocumentManager importDocumentManager) @@ -59,9 +59,9 @@ namespace Microsoft.VisualStudio.Editor.Razor throw new ArgumentNullException(nameof(projectManager)); } - if (editorSettingsManager == null) + if (workspaceEditorSettings == null) { - throw new ArgumentNullException(nameof(editorSettingsManager)); + throw new ArgumentNullException(nameof(workspaceEditorSettings)); } if (workspace == null) @@ -83,7 +83,7 @@ namespace Microsoft.VisualStudio.Editor.Razor _filePath = filePath; _projectPath = projectPath; _projectManager = projectManager; - _editorSettingsManager = editorSettingsManager; + _workspaceEditorSettings = workspaceEditorSettings; _textBuffer = textBuffer; _importDocumentManager = importDocumentManager; _workspace = workspace; // For now we assume that the workspace is the always default VS workspace. @@ -93,7 +93,7 @@ namespace Microsoft.VisualStudio.Editor.Razor internal override ProjectExtensibilityConfiguration Configuration => _project?.Configuration; - public override EditorSettings EditorSettings => _editorSettingsManager.Current; + public override EditorSettings EditorSettings => _workspaceEditorSettings.Current; public override IReadOnlyList TagHelpers => _project?.TagHelpers ?? Array.Empty(); @@ -154,7 +154,7 @@ namespace Microsoft.VisualStudio.Editor.Razor { _importDocumentManager.OnSubscribed(this); - _editorSettingsManager.Changed += EditorSettingsManager_Changed; + _workspaceEditorSettings.Changed += EditorSettingsManager_Changed; _projectManager.Changed += ProjectManager_Changed; _importDocumentManager.Changed += Import_Changed; @@ -169,7 +169,7 @@ namespace Microsoft.VisualStudio.Editor.Razor _importDocumentManager.OnUnsubscribed(this); _projectManager.Changed -= ProjectManager_Changed; - _editorSettingsManager.Changed -= EditorSettingsManager_Changed; + _workspaceEditorSettings.Changed -= EditorSettingsManager_Changed; _importDocumentManager.Changed -= Import_Changed; // Detached from project. diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTrackerFactory.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTrackerFactory.cs index df91f80484..7005ce059e 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTrackerFactory.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTrackerFactory.cs @@ -19,12 +19,12 @@ namespace Microsoft.VisualStudio.Editor.Razor private readonly ImportDocumentManager _importDocumentManager; private readonly ForegroundDispatcher _foregroundDispatcher; private readonly ProjectSnapshotManager _projectManager; - private readonly EditorSettingsManagerInternal _editorSettingsManager; + private readonly WorkspaceEditorSettings _workspaceEditorSettings; public DefaultVisualStudioDocumentTrackerFactory( ForegroundDispatcher foregroundDispatcher, ProjectSnapshotManager projectManager, - EditorSettingsManagerInternal editorSettingsManager, + WorkspaceEditorSettings workspaceEditorSettings, TextBufferProjectService projectService, ITextDocumentFactoryService textDocumentFactory, ImportDocumentManager importDocumentManager, @@ -40,9 +40,9 @@ namespace Microsoft.VisualStudio.Editor.Razor throw new ArgumentNullException(nameof(projectManager)); } - if (editorSettingsManager == null) + if (workspaceEditorSettings == null) { - throw new ArgumentNullException(nameof(editorSettingsManager)); + throw new ArgumentNullException(nameof(workspaceEditorSettings)); } if (projectService == null) @@ -67,7 +67,7 @@ namespace Microsoft.VisualStudio.Editor.Razor _foregroundDispatcher = foregroundDispatcher; _projectManager = projectManager; - _editorSettingsManager = editorSettingsManager; + _workspaceEditorSettings = workspaceEditorSettings; _projectService = projectService; _textDocumentFactory = textDocumentFactory; _importDocumentManager = importDocumentManager; @@ -97,7 +97,7 @@ namespace Microsoft.VisualStudio.Editor.Razor var projectPath = _projectService.GetProjectPath(project); - var tracker = new DefaultVisualStudioDocumentTracker(_foregroundDispatcher, filePath, projectPath, _projectManager, _editorSettingsManager, _workspace, textBuffer, _importDocumentManager); + var tracker = new DefaultVisualStudioDocumentTracker(_foregroundDispatcher, filePath, projectPath, _projectManager, _workspaceEditorSettings, _workspace, textBuffer, _importDocumentManager); return tracker; } diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTrackerFactoryFactory.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTrackerFactoryFactory.cs index 684d858582..f8ebf6503d 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTrackerFactoryFactory.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTrackerFactoryFactory.cs @@ -44,14 +44,14 @@ namespace Microsoft.VisualStudio.Editor.Razor } var projectManager = languageServices.GetRequiredService(); - var editorSettingsManager = languageServices.GetRequiredService(); + var workspaceEditorSettings = languageServices.GetRequiredService(); var projectService = languageServices.GetRequiredService(); var importDocumentManager = languageServices.GetRequiredService(); return new DefaultVisualStudioDocumentTrackerFactory( _foregroundDispatcher, projectManager, - editorSettingsManager, + workspaceEditorSettings, projectService, _textDocumentFactory, importDocumentManager, diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultWorkspaceEditorSettings.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultWorkspaceEditorSettings.cs new file mode 100644 index 0000000000..0e49fbb8f4 --- /dev/null +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultWorkspaceEditorSettings.cs @@ -0,0 +1,94 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.CodeAnalysis.Razor.Editor; + +namespace Microsoft.VisualStudio.Editor.Razor +{ + internal class DefaultWorkspaceEditorSettings : WorkspaceEditorSettings + { + private readonly EditorSettingsManager _editorSettingsManager; + private readonly EventHandler _onChanged; + private EventHandler _changed; + private readonly ForegroundDispatcher _foregroundDispatcher; + private int _listenerCount = 0; + + public DefaultWorkspaceEditorSettings(ForegroundDispatcher foregroundDispatcher, EditorSettingsManager editorSettingsManager) + { + if (foregroundDispatcher == null) + { + throw new ArgumentNullException(nameof(foregroundDispatcher)); + } + + if (editorSettingsManager == null) + { + throw new ArgumentNullException(nameof(editorSettingsManager)); + } + + _foregroundDispatcher = foregroundDispatcher; + _editorSettingsManager = editorSettingsManager; + _onChanged = OnChanged; + } + + public override event EventHandler Changed + { + add + { + _foregroundDispatcher.AssertForegroundThread(); + + _listenerCount++; + _changed += value; + + if (_listenerCount == 1) + { + // We bind to the editor settings manager only when we have listeners to avoid leaking memory. + // Basically we're relying on anyone listening to us to have an understanding of when they're going + // to be torn down. In Razor's case this will just be the document tracker factory (which does know). + AttachToEditorSettingsManager(); + } + } + remove + { + _foregroundDispatcher.AssertForegroundThread(); + + _listenerCount--; + _changed -= value; + + if (_listenerCount == 0) + { + // We detatch from the editor settings manager when no one is listening to allow us to be garbage + // collected in the case that the workspace is tearing down. + DetachFromEditorSettingsManager(); + } + } + } + + // Internal for testing + internal virtual void AttachToEditorSettingsManager() + { + _editorSettingsManager.Changed += _onChanged; + } + + // Internal for testing + internal virtual void DetachFromEditorSettingsManager() + { + _editorSettingsManager.Changed -= _onChanged; + } + + public override EditorSettings Current => _editorSettingsManager.Current; + + // Internal for testing + internal void OnChanged(object sender, EditorSettingsChangedEventArgs e) + { + _foregroundDispatcher.AssertForegroundThread(); + + Debug.Assert(_changed != null, nameof(OnChanged) + " should not be invoked when there are no listeners."); + + var args = new EditorSettingsChangedEventArgs(Current); + _changed?.Invoke(this, args); + } + } +} diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternalFactory.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultWorkspaceEditorSettingsFactory.cs similarity index 52% rename from src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternalFactory.cs rename to src/Microsoft.VisualStudio.Editor.Razor/DefaultWorkspaceEditorSettingsFactory.cs index a5a2f83750..6bc448b43f 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/Editor/DefaultEditorSettingsManagerInternalFactory.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultWorkspaceEditorSettingsFactory.cs @@ -5,24 +5,33 @@ using System; using System.Composition; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.CodeAnalysis.Razor.Editor; -namespace Microsoft.CodeAnalysis.Razor.Editor +namespace Microsoft.VisualStudio.Editor.Razor { [Shared] - [ExportLanguageServiceFactory(typeof(EditorSettingsManagerInternal), RazorLanguage.Name)] - internal class DefaultEditorSettingsManagerInternalFactory : ILanguageServiceFactory + [ExportLanguageServiceFactory(typeof(WorkspaceEditorSettings), RazorLanguage.Name)] + internal class DefaultWorkspaceEditorSettingsFactory : ILanguageServiceFactory { private readonly ForegroundDispatcher _foregroundDispatcher; + private readonly EditorSettingsManager _editorSettingsManager; [ImportingConstructor] - public DefaultEditorSettingsManagerInternalFactory(ForegroundDispatcher foregroundDispatcher) + public DefaultWorkspaceEditorSettingsFactory(ForegroundDispatcher foregroundDispatcher, EditorSettingsManager editorSettingsManager) { if (foregroundDispatcher == null) { throw new ArgumentNullException(nameof(foregroundDispatcher)); } + if (editorSettingsManager == null) + { + throw new ArgumentNullException(nameof(editorSettingsManager)); + } + _foregroundDispatcher = foregroundDispatcher; + _editorSettingsManager = editorSettingsManager; } public ILanguageService CreateLanguageService(HostLanguageServices languageServices) @@ -32,7 +41,7 @@ namespace Microsoft.CodeAnalysis.Razor.Editor throw new ArgumentNullException(nameof(languageServices)); } - return new DefaultEditorSettingsManagerInternal(_foregroundDispatcher); + return new DefaultWorkspaceEditorSettings(_foregroundDispatcher, _editorSettingsManager); } } } diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test/Editor/DefaultEditorSettingsManagerInternalTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultEditorSettingsManagerTest.cs similarity index 78% rename from test/Microsoft.VisualStudio.Editor.Razor.Test/Editor/DefaultEditorSettingsManagerInternalTest.cs rename to test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultEditorSettingsManagerTest.cs index 86524df683..fdf27056d3 100644 --- a/test/Microsoft.VisualStudio.Editor.Razor.Test/Editor/DefaultEditorSettingsManagerInternalTest.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultEditorSettingsManagerTest.cs @@ -1,17 +1,18 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.CodeAnalysis.Razor.Editor; using Xunit; -namespace Microsoft.CodeAnalysis.Razor.Editor +namespace Microsoft.VisualStudio.Editor.Razor { - public class DefaultEditorSettingsManagerInternalTest : ForegroundDispatcherTestBase + public class DefaultEditorSettingsManagerTest : ForegroundDispatcherTestBase { [Fact] public void InitialSettingsAreDefault() { // Act - var manager = new DefaultEditorSettingsManagerInternal(Dispatcher); + var manager = new DefaultEditorSettingsManager(Dispatcher); // Assert Assert.Equal(EditorSettings.Default, manager.Current); @@ -21,7 +22,7 @@ namespace Microsoft.CodeAnalysis.Razor.Editor public void Update_TriggersChangedIfEditorSettingsAreDifferent() { // Arrange - var manager = new DefaultEditorSettingsManagerInternal(Dispatcher); + var manager = new DefaultEditorSettingsManager(Dispatcher); var called = false; manager.Changed += (caller, args) => { @@ -41,7 +42,7 @@ namespace Microsoft.CodeAnalysis.Razor.Editor public void Update_DoesNotTriggerChangedIfEditorSettingsAreSame() { // Arrange - var manager = new DefaultEditorSettingsManagerInternal(Dispatcher); + var manager = new DefaultEditorSettingsManager(Dispatcher); var called = false; manager.Changed += (caller, args) => { diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultRazorDocumentManagerTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultRazorDocumentManagerTest.cs index d950f52590..3cd9f5fb81 100644 --- a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultRazorDocumentManagerTest.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultRazorDocumentManagerTest.cs @@ -27,7 +27,7 @@ namespace Microsoft.VisualStudio.Editor.Razor private ProjectSnapshotManager ProjectManager => Mock.Of(p => p.Projects == new List()); - private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal(Dispatcher); + private WorkspaceEditorSettings WorkspaceEditorSettings => new DefaultWorkspaceEditorSettings(Dispatcher, Mock.Of()); private ImportDocumentManager ImportDocumentManager => Mock.Of(); @@ -81,7 +81,7 @@ namespace Microsoft.VisualStudio.Editor.Razor { Mock.Of(b => b.ContentType == RazorCoreContentType && b.Properties == new PropertyCollection()), }; - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, buffers[0], ImportDocumentManager) as VisualStudioDocumentTracker; + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, buffers[0], ImportDocumentManager) as VisualStudioDocumentTracker; var editorFactoryService = Mock.Of(factoryService => factoryService.TryGetDocumentTracker(It.IsAny(), out documentTracker) == true); var documentManager = new DefaultRazorDocumentManager(Dispatcher, editorFactoryService, SupportedProjectService); @@ -102,7 +102,7 @@ namespace Microsoft.VisualStudio.Editor.Razor Mock.Of(b => b.ContentType == RazorCoreContentType && b.Properties == new PropertyCollection()), Mock.Of(b => b.ContentType == NonRazorCoreContentType && b.Properties == new PropertyCollection()), }; - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, buffers[0], ImportDocumentManager) as VisualStudioDocumentTracker; + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, buffers[0], ImportDocumentManager) as VisualStudioDocumentTracker; var editorFactoryService = Mock.Of(f => f.TryGetDocumentTracker(It.IsAny(), out documentTracker) == true); var documentManager = new DefaultRazorDocumentManager(Dispatcher, editorFactoryService, SupportedProjectService); @@ -165,12 +165,12 @@ namespace Microsoft.VisualStudio.Editor.Razor }; // Preload the buffer's properties with a tracker, so it's like we've already tracked this one. - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, buffers[0], ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, buffers[0], ImportDocumentManager); documentTracker.AddTextView(textView1); documentTracker.AddTextView(textView2); buffers[0].Properties.AddProperty(typeof(VisualStudioDocumentTracker), documentTracker); - documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, buffers[1], ImportDocumentManager); + documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, buffers[1], ImportDocumentManager); documentTracker.AddTextView(textView1); documentTracker.AddTextView(textView2); buffers[1].Properties.AddProperty(typeof(VisualStudioDocumentTracker), documentTracker); @@ -200,7 +200,7 @@ namespace Microsoft.VisualStudio.Editor.Razor Mock.Of(b => b.ContentType == RazorCoreContentType && b.Properties == new PropertyCollection()), Mock.Of(b => b.ContentType == NonRazorCoreContentType && b.Properties == new PropertyCollection()), }; - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, buffers[0], ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, buffers[0], ImportDocumentManager); buffers[0].Properties.AddProperty(typeof(VisualStudioDocumentTracker), documentTracker); var editorFactoryService = Mock.Of(); var documentManager = new DefaultRazorDocumentManager(Dispatcher, editorFactoryService, SupportedProjectService); diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioDocumentTrackerTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioDocumentTrackerTest.cs index 7712cd49eb..c37040a3a3 100644 --- a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioDocumentTrackerTest.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioDocumentTrackerTest.cs @@ -27,7 +27,7 @@ namespace Microsoft.VisualStudio.Editor.Razor private ProjectSnapshotManager ProjectManager => Mock.Of(p => p.Projects == new List()); - private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal(Dispatcher); + private WorkspaceEditorSettings WorkspaceEditorSettings => new DefaultWorkspaceEditorSettings(Dispatcher, Mock.Of()); private Workspace Workspace => TestWorkspace.Create(); @@ -37,7 +37,7 @@ namespace Microsoft.VisualStudio.Editor.Razor public void EditorSettingsManager_Changed_TriggersContextChanged() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); var called = false; documentTracker.ContextChanged += (sender, args) => { @@ -62,7 +62,7 @@ namespace Microsoft.VisualStudio.Editor.Razor { project = ws.AddProject(ProjectInfo.Create(ProjectId.CreateNewId(), new VersionStamp(), "Test1", "TestAssembly", LanguageNames.CSharp, filePath: "C:/Some/Path/TestProject.csproj")); }); - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, workspace, TextBuffer, ImportDocumentManager); var projectSnapshot = new DefaultProjectSnapshot(project); var projectChangedArgs = new ProjectChangeEventArgs(projectSnapshot, ProjectChangeKind.Changed); @@ -90,7 +90,7 @@ namespace Microsoft.VisualStudio.Editor.Razor { project = ws.AddProject(ProjectInfo.Create(ProjectId.CreateNewId(), new VersionStamp(), "Test1", "TestAssembly", LanguageNames.CSharp, filePath: "C:/Some/Path/TestProject.csproj")); }); - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, workspace, TextBuffer, ImportDocumentManager); var projectSnapshot = new DefaultProjectSnapshot(project); var projectChangedArgs = new ProjectChangeEventArgs(projectSnapshot, ProjectChangeKind.TagHelpersChanged); @@ -118,7 +118,7 @@ namespace Microsoft.VisualStudio.Editor.Razor { project = ws.AddProject(ProjectInfo.Create(ProjectId.CreateNewId(), new VersionStamp(), "Test1", "TestAssembly", LanguageNames.CSharp, filePath: "C:/Some/Other/Path/TestProject.csproj")); }); - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, workspace, TextBuffer, ImportDocumentManager); var projectSnapshot = new DefaultProjectSnapshot(project); var projectChangedArgs = new ProjectChangeEventArgs(projectSnapshot, ProjectChangeKind.Changed); @@ -140,7 +140,7 @@ namespace Microsoft.VisualStudio.Editor.Razor public void Import_Changed_ImportAssociatedWithDocument_TriggersContextChanged() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); var called = false; documentTracker.ContextChanged += (sender, args) => @@ -162,7 +162,7 @@ namespace Microsoft.VisualStudio.Editor.Razor public void Import_Changed_UnrelatedImport_DoesNothing() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); documentTracker.ContextChanged += (sender, args) => { @@ -179,7 +179,7 @@ namespace Microsoft.VisualStudio.Editor.Razor public void Subscribe_SetsSupportedProjectAndTriggersContextChanged() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); var called = false; documentTracker.ContextChanged += (sender, args) => { @@ -199,7 +199,7 @@ namespace Microsoft.VisualStudio.Editor.Razor public void Unsubscribe_ResetsSupportedProjectAndTriggersContextChanged() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); // Subscribe once to set supported project documentTracker.Subscribe(); @@ -223,7 +223,7 @@ namespace Microsoft.VisualStudio.Editor.Razor public void AddTextView_AddsToTextViewCollection() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); var textView = Mock.Of(); // Act @@ -237,7 +237,7 @@ namespace Microsoft.VisualStudio.Editor.Razor public void AddTextView_DoesNotAddDuplicateTextViews() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); var textView = Mock.Of(); // Act @@ -252,7 +252,7 @@ namespace Microsoft.VisualStudio.Editor.Razor public void AddTextView_AddsMultipleTextViewsToCollection() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); var textView1 = Mock.Of(); var textView2 = Mock.Of(); @@ -271,7 +271,7 @@ namespace Microsoft.VisualStudio.Editor.Razor public void RemoveTextView_RemovesTextViewFromCollection_SingleItem() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); var textView = Mock.Of(); documentTracker.AddTextView(textView); @@ -286,7 +286,7 @@ namespace Microsoft.VisualStudio.Editor.Razor public void RemoveTextView_RemovesTextViewFromCollection_MultipleItems() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); var textView1 = Mock.Of(); var textView2 = Mock.Of(); var textView3 = Mock.Of(); @@ -308,7 +308,7 @@ namespace Microsoft.VisualStudio.Editor.Razor public void RemoveTextView_NoopsWhenRemovingTextViewNotInCollection() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer, ImportDocumentManager); + var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, Workspace, TextBuffer, ImportDocumentManager); var textView1 = Mock.Of(); documentTracker.AddTextView(textView1); var textView2 = Mock.Of(); diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultWorkspaceEditorSettingsTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultWorkspaceEditorSettingsTest.cs new file mode 100644 index 0000000000..64365755bb --- /dev/null +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultWorkspaceEditorSettingsTest.cs @@ -0,0 +1,99 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.CodeAnalysis.Razor.Editor; +using Moq; +using Xunit; + +namespace Microsoft.VisualStudio.Editor.Razor +{ + public class DefaultWorkspaceEditorSettingsTest : ForegroundDispatcherTestBase + { + [Fact] + public void InitialSettingsAreEditorSettingsManagerDefault() + { + // Arrange + var editorSettings = new EditorSettings(true, 123); + var editorSettingsManager = Mock.Of(m => m.Current == editorSettings); + + // Act + var manager = new DefaultWorkspaceEditorSettings(Dispatcher, editorSettingsManager); + + // Assert + Assert.Equal(editorSettings, manager.Current); + } + + [Fact] + public void OnChanged_TriggersChanged() + { + // Arrange + var manager = new DefaultWorkspaceEditorSettings(Dispatcher, Mock.Of()); + var called = false; + manager.Changed += (caller, args) => + { + called = true; + }; + + // Act + manager.OnChanged(null, null); + + // Assert + Assert.True(called); + } + + [Fact] + public void Attach_CalledOnceForMultipleListeners() + { + // Arrange + var manager = new TestEditorSettingsManagerInternal(Dispatcher); + + // Act + manager.Changed += (caller, args) => { }; + manager.Changed += (caller, args) => { }; + + // Assert + Assert.Equal(1, manager.AttachCount); + } + + [Fact] + public void Detach_CalledOnceWhenNoMoreListeners() + { + // Arrange + var manager = new TestEditorSettingsManagerInternal(Dispatcher); + EventHandler listener1 = (caller, args) => { }; + EventHandler listener2 = (caller, args) => { }; + manager.Changed += listener1; + manager.Changed += listener2; + + // Act + manager.Changed -= listener1; + manager.Changed -= listener2; + + // Assert + Assert.Equal(1, manager.DetachCount); + } + + private class TestEditorSettingsManagerInternal : DefaultWorkspaceEditorSettings + { + public TestEditorSettingsManagerInternal(ForegroundDispatcher foregroundDispatcher) : base(foregroundDispatcher, Mock.Of()) + { + } + + public int AttachCount { get; private set; } + + public int DetachCount { get; private set; } + + internal override void AttachToEditorSettingsManager() + { + AttachCount++; + } + + internal override void DetachFromEditorSettingsManager() + { + DetachCount++; + } + } + } +}