diff --git a/src/Microsoft.VisualStudio.Editor.Razor/ContextChangeKind.cs b/src/Microsoft.VisualStudio.Editor.Razor/ContextChangeKind.cs index 867fa9857f..2a95099676 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/ContextChangeKind.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/ContextChangeKind.cs @@ -8,5 +8,6 @@ namespace Microsoft.VisualStudio.Editor.Razor ProjectChanged, EditorSettingsChanged, TagHelpersChanged, + ImportsChanged, } } \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Editor.Razor/DefaultRazorDocumentManager.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultRazorDocumentManager.cs new file mode 100644 index 0000000000..719434f23d --- /dev/null +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultRazorDocumentManager.cs @@ -0,0 +1,134 @@ +// 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.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.VisualStudio.Editor.Razor +{ + [System.Composition.Shared] + [Export(typeof(RazorDocumentManager))] + internal class DefaultRazorDocumentManager : RazorDocumentManager + { + private readonly RazorEditorFactoryService _editorFactoryService; + private readonly TextBufferProjectService _projectService; + + [ImportingConstructor] + public DefaultRazorDocumentManager( + RazorEditorFactoryService editorFactoryService, + TextBufferProjectService projectService) + { + if (editorFactoryService == null) + { + throw new ArgumentNullException(nameof(editorFactoryService)); + } + + if (projectService == null) + { + throw new ArgumentNullException(nameof(projectService)); + } + + _editorFactoryService = editorFactoryService; + _projectService = projectService; + } + + public override void OnTextViewOpened(ITextView textView, IList subjectBuffers) + { + if (textView == null) + { + throw new ArgumentNullException(nameof(textView)); + } + + if (subjectBuffers == null) + { + throw new ArgumentNullException(nameof(subjectBuffers)); + } + + for (var i = 0; i < subjectBuffers.Count; i++) + { + var textBuffer = subjectBuffers[i]; + if (!textBuffer.IsRazorBuffer()) + { + continue; + } + + if (!IsSupportedProject(textBuffer)) + { + return; + } + + if (!_editorFactoryService.TryGetDocumentTracker(textBuffer, out var documentTracker) || + !(documentTracker is DefaultVisualStudioDocumentTracker tracker)) + { + Debug.Fail("Tracker should always be available given our expectations of the VS workflow."); + return; + } + + tracker.AddTextView(textView); + + if (documentTracker.TextViews.Count == 1) + { + tracker.Subscribe(); + } + } + } + + public override void OnTextViewClosed(ITextView textView, IList subjectBuffers) + { + if (textView == null) + { + throw new ArgumentNullException(nameof(textView)); + } + + if (subjectBuffers == null) + { + throw new ArgumentNullException(nameof(subjectBuffers)); + } + + // This means a Razor buffer has be detached from this ITextView or the ITextView is closing. Since we keep a + // list of all of the open text views for each text buffer, we need to update the tracker. + // + // Notice that this method is called *after* changes are applied to the text buffer(s). We need to check every + // one of them for a tracker because the content type could have changed. + for (var i = 0; i < subjectBuffers.Count; i++) + { + var textBuffer = subjectBuffers[i]; + + DefaultVisualStudioDocumentTracker documentTracker; + if (textBuffer.Properties.TryGetProperty(typeof(VisualStudioDocumentTracker), out documentTracker)) + { + documentTracker.RemoveTextView(textView); + + if (documentTracker.TextViews.Count == 0) + { + documentTracker.Unsubscribe(); + } + } + } + } + + private bool IsSupportedProject(ITextBuffer textBuffer) + { + // Fundamentally we have a Razor half of the world as soon as the document is open - and then later + // the C# half of the world will be initialized. This code is in general pretty tolerant of + // unexpected /impossible states. + // + // We also want to successfully shut down if the buffer is something other than .cshtml. + object project = null; + var isSupportedProject = false; + + // We expect the document to have a hierarchy even if it's not a real 'project'. + // However the hierarchy can be null when the document is in the process of closing. + if ((project = _projectService.GetHostProject(textBuffer)) != null) + { + isSupportedProject = _projectService.IsSupportedProject(project); + } + + return isSupportedProject; + } + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTracker.cs similarity index 74% rename from src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs rename to src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTracker.cs index 1cc76169aa..3fb3896ca5 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTracker.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTracker.cs @@ -5,35 +5,31 @@ using System; using System.Collections.Generic; using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.Editor; using Microsoft.CodeAnalysis.Razor.ProjectSystem; -using Microsoft.VisualStudio.Editor.Razor; -using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor +namespace Microsoft.VisualStudio.Editor.Razor { internal class DefaultVisualStudioDocumentTracker : VisualStudioDocumentTracker { private readonly string _filePath; + private readonly string _projectPath; private readonly ProjectSnapshotManager _projectManager; private readonly EditorSettingsManagerInternal _editorSettingsManager; - private readonly TextBufferProjectService _projectService; private readonly ITextBuffer _textBuffer; private readonly List _textViews; private readonly Workspace _workspace; private bool _isSupportedProject; private ProjectSnapshot _project; - private string _projectPath; public override event EventHandler ContextChanged; public DefaultVisualStudioDocumentTracker( string filePath, + string projectPath, ProjectSnapshotManager projectManager, - TextBufferProjectService projectService, EditorSettingsManagerInternal editorSettingsManager, Workspace workspace, ITextBuffer textBuffer) @@ -43,16 +39,16 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(filePath)); } + if (projectPath == null) + { + throw new ArgumentNullException(nameof(projectPath)); + } + if (projectManager == null) { throw new ArgumentNullException(nameof(projectManager)); } - if (projectService == null) - { - throw new ArgumentNullException(nameof(projectService)); - } - if (editorSettingsManager == null) { throw new ArgumentNullException(nameof(editorSettingsManager)); @@ -69,8 +65,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor } _filePath = filePath; + _projectPath = projectPath; _projectManager = projectManager; - _projectService = projectService; _editorSettingsManager = editorSettingsManager; _textBuffer = textBuffer; _workspace = workspace; // For now we assume that the workspace is the always default VS workspace. @@ -108,11 +104,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor if (!_textViews.Contains(textView)) { _textViews.Add(textView); - - if (_textViews.Count == 1) - { - Subscribe(); - } } } @@ -126,11 +117,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor if (_textViews.Contains(textView)) { _textViews.Remove(textView); - - if (_textViews.Count == 0) - { - Unsubscribe(); - } } } @@ -147,42 +133,18 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor return null; } - private void Subscribe() + public void Subscribe() { - // Fundamentally we have a Razor half of the world as as soon as the document is open - and then later - // the C# half of the world will be initialized. This code is in general pretty tolerant of - // unexpected /impossible states. - // - // We also want to successfully shut down if the buffer is something other than .cshtml. - IVsHierarchy hierarchy = null; - string projectPath = null; - var isSupportedProject = false; - - if (_textBuffer.ContentType.IsOfType(RazorLanguage.ContentType) && - - // We expect the document to have a hierarchy even if it's not a real 'project'. - // However the hierarchy can be null when the document is in the process of closing. - (hierarchy = _projectService.GetHierarchy(_textBuffer)) != null) - { - projectPath = _projectService.GetProjectPath(hierarchy); - isSupportedProject = _projectService.IsSupportedProject(hierarchy); - } - - if (!isSupportedProject || projectPath == null) - { - return; - } - - _isSupportedProject = isSupportedProject; - _projectPath = projectPath; - _project = _projectManager.GetProjectWithFilePath(projectPath); - _projectManager.Changed += ProjectManager_Changed; _editorSettingsManager.Changed += EditorSettingsManager_Changed; + _projectManager.Changed += ProjectManager_Changed; + + _isSupportedProject = true; + _project = _projectManager.GetProjectWithFilePath(_projectPath); OnContextChanged(_project, ContextChangeKind.ProjectChanged); } - private void Unsubscribe() + public void Unsubscribe() { _projectManager.Changed -= ProjectManager_Changed; _editorSettingsManager.Changed -= EditorSettingsManager_Changed; @@ -190,6 +152,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor // Detached from project. _isSupportedProject = false; _project = null; + OnContextChanged(project: null, kind: ContextChangeKind.ProjectChanged); } diff --git a/src/Microsoft.VisualStudio.Editor.Razor/RazorDocumentManager.cs b/src/Microsoft.VisualStudio.Editor.Razor/RazorDocumentManager.cs new file mode 100644 index 0000000000..1a27d94714 --- /dev/null +++ b/src/Microsoft.VisualStudio.Editor.Razor/RazorDocumentManager.cs @@ -0,0 +1,16 @@ +// 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.Collections.Generic; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.VisualStudio.Editor.Razor +{ + internal abstract class RazorDocumentManager + { + public abstract void OnTextViewOpened(ITextView textView, IList subjectBuffers); + + public abstract void OnTextViewClosed(ITextView textView, IList subjectBuffers); + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/TextBufferProjectService.cs b/src/Microsoft.VisualStudio.Editor.Razor/TextBufferProjectService.cs similarity index 52% rename from src/Microsoft.VisualStudio.LanguageServices.Razor/TextBufferProjectService.cs rename to src/Microsoft.VisualStudio.Editor.Razor/TextBufferProjectService.cs index 902b8b4a68..84d5fab5d1 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/TextBufferProjectService.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/TextBufferProjectService.cs @@ -1,15 +1,16 @@ // 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.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; -namespace Microsoft.VisualStudio.LanguageServices.Razor +namespace Microsoft.VisualStudio.Editor.Razor { internal abstract class TextBufferProjectService { - public abstract IVsHierarchy GetHierarchy(ITextBuffer textBuffer); + public abstract object GetHostProject(ITextBuffer textBuffer); - public abstract bool IsSupportedProject(IVsHierarchy hierarchy); + public abstract bool IsSupportedProject(object project); + + public abstract string GetProjectPath(object project); } } diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultTextBufferProjectService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultTextBufferProjectService.cs index f81d6fa4d5..3d8089af01 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultTextBufferProjectService.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultTextBufferProjectService.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel.Composition; using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.Editor.Razor; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; @@ -41,7 +42,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor _documentTable = new RunningDocumentTable(services); } - public override IVsHierarchy GetHierarchy(ITextBuffer textBuffer) + public override object GetHostProject(ITextBuffer textBuffer) { if (textBuffer == null) { @@ -63,24 +64,27 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor return hierarchy; } - public override string GetProjectPath(IVsHierarchy hierarchy) + public override string GetProjectPath(object project) { - if (hierarchy == null) + if (project == null) { - throw new ArgumentNullException(nameof(hierarchy)); + throw new ArgumentNullException(nameof(project)); } + var hierarchy = (IVsHierarchy)project; + ErrorHandler.ThrowOnFailure(((IVsProject)hierarchy).GetMkDocument((uint)VSConstants.VSITEMID.Root, out var path), VSConstants.E_NOTIMPL); return path; } - public override bool IsSupportedProject(IVsHierarchy hierarchy) + public override bool IsSupportedProject(object project) { - if (hierarchy == null) + if (project == null) { - throw new ArgumentNullException(nameof(hierarchy)); + throw new ArgumentNullException(nameof(project)); } + var hierarchy = (IVsHierarchy)project; try { return hierarchy.IsCapabilityMatch(DotNetCoreCapability); diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs index 53c2c074a2..c56f7d024f 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/DefaultVisualStudioDocumentTrackerFactory.cs @@ -69,7 +69,16 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor } var filePath = textDocument.FilePath; - var tracker = new DefaultVisualStudioDocumentTracker(filePath, _projectManager, _projectService, _editorSettingsManager, _workspace, textBuffer); + var project = _projectService.GetHostProject(textBuffer); + if (project == null) + { + Debug.Fail("Text buffer should belong to a project."); + return null; + } + + var projectPath = _projectService.GetProjectPath(project); + + var tracker = new DefaultVisualStudioDocumentTracker(filePath, projectPath, _projectManager, _editorSettingsManager, _workspace, textBuffer); return tracker; } diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/RazorTextViewConnectionListener.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/RazorTextViewConnectionListener.cs index 860a8680e1..df330d1000 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/RazorTextViewConnectionListener.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/RazorTextViewConnectionListener.cs @@ -4,12 +4,9 @@ using System; using System.Collections.ObjectModel; using System.ComponentModel.Composition; -using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Razor; using Microsoft.VisualStudio.Editor.Razor; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; @@ -22,69 +19,53 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor internal class RazorTextViewConnectionListener : IWpfTextViewConnectionListener { private readonly ForegroundDispatcher _foregroundDispatcher; - private readonly TextBufferProjectService _projectService; - private readonly RazorEditorFactoryService _editorFactoryService; private readonly Workspace _workspace; + private readonly RazorDocumentManager _documentManager; [ImportingConstructor] public RazorTextViewConnectionListener( - TextBufferProjectService projectService, - RazorEditorFactoryService editorFactoryService, - [Import(typeof(VisualStudioWorkspace))] Workspace workspace) + [Import(typeof(VisualStudioWorkspace))] Workspace workspace, + RazorDocumentManager documentManager) { - if (projectService == null) - { - throw new ArgumentNullException(nameof(projectService)); - } - - if (editorFactoryService == null) - { - throw new ArgumentNullException(nameof(editorFactoryService)); - } - if (workspace == null) { throw new ArgumentNullException(nameof(workspace)); } - _projectService = projectService; - _editorFactoryService = editorFactoryService; - _workspace = workspace; + if (documentManager == null) + { + throw new ArgumentNullException(nameof(documentManager)); + } + _workspace = workspace; + _documentManager = documentManager; _foregroundDispatcher = workspace.Services.GetRequiredService(); } // This is only for testing. We want to avoid using the actual Roslyn GetService methods in unit tests. internal RazorTextViewConnectionListener( ForegroundDispatcher foregroundDispatcher, - TextBufferProjectService projectService, - RazorEditorFactoryService editorFactoryService, - [Import(typeof(VisualStudioWorkspace))] Workspace workspace) + Workspace workspace, + RazorDocumentManager documentManager) { if (foregroundDispatcher == null) { throw new ArgumentNullException(nameof(foregroundDispatcher)); } - if (projectService == null) - { - throw new ArgumentNullException(nameof(projectService)); - } - - if (editorFactoryService == null) - { - throw new ArgumentNullException(nameof(editorFactoryService)); - } - if (workspace == null) { throw new ArgumentNullException(nameof(workspace)); } + if (documentManager == null) + { + throw new ArgumentNullException(nameof(documentManager)); + } + _foregroundDispatcher = foregroundDispatcher; - _projectService = projectService; - _editorFactoryService = editorFactoryService; _workspace = workspace; + _documentManager = documentManager; } public Workspace Workspace => _workspace; @@ -103,29 +84,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor _foregroundDispatcher.AssertForegroundThread(); - for (var i = 0; i < subjectBuffers.Count; i++) - { - var textBuffer = subjectBuffers[i]; - if (!textBuffer.IsRazorBuffer()) - { - continue; - } - - var hierarchy = _projectService.GetHierarchy(textBuffer); - if (!_projectService.IsSupportedProject(hierarchy)) - { - return; - } - - if (!_editorFactoryService.TryGetDocumentTracker(textBuffer, out var documentTracker) || - !(documentTracker is DefaultVisualStudioDocumentTracker tracker)) - { - Debug.Fail("Tracker should always be available given our expectations of the VS workflow."); - return; - } - - tracker.AddTextView(textView); - } + _documentManager.OnTextViewOpened(textView, subjectBuffers); } public void SubjectBuffersDisconnected(IWpfTextView textView, ConnectionReason reason, Collection subjectBuffers) @@ -142,21 +101,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor _foregroundDispatcher.AssertForegroundThread(); - // This means a Razor buffer has be detached from this ITextView or the ITextView is closing. Since we keep a - // list of all of the open text views for each text buffer, we need to update the tracker. - // - // Notice that this method is called *after* changes are applied to the text buffer(s). We need to check every - // one of them for a tracker because the content type could have changed. - for (var i = 0; i < subjectBuffers.Count; i++) - { - var textBuffer = subjectBuffers[i]; - - DefaultVisualStudioDocumentTracker documentTracker; - if (textBuffer.Properties.TryGetProperty(typeof(VisualStudioDocumentTracker), out documentTracker)) - { - documentTracker.RemoveTextView(textView); - } - } + _documentManager.OnTextViewClosed(textView, subjectBuffers); } } } diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/TextBufferProjectService.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/TextBufferProjectService.cs deleted file mode 100644 index 89a5eb49cd..0000000000 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Editor/TextBufferProjectService.cs +++ /dev/null @@ -1,17 +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 Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor -{ - internal abstract class TextBufferProjectService - { - public abstract IVsHierarchy GetHierarchy(ITextBuffer textBuffer); - - public abstract bool IsSupportedProject(IVsHierarchy hierarchy); - - public abstract string GetProjectPath(IVsHierarchy hierarchy); - } -} diff --git a/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultRazorDocumentManagerTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultRazorDocumentManagerTest.cs new file mode 100644 index 0000000000..d2b42f15e9 --- /dev/null +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultRazorDocumentManagerTest.cs @@ -0,0 +1,224 @@ +// 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.Collections.Generic; +using System.Collections.ObjectModel; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.CodeAnalysis.Razor.Editor; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; +using Moq; +using Xunit; + +namespace Microsoft.VisualStudio.Editor.Razor +{ + public class DefaultRazorDocumentManagerTest + { + private IContentType RazorContentType { get; } = Mock.Of(c => c.IsOfType(RazorLanguage.ContentType) == true); + + private IContentType NonRazorContentType { get; } = Mock.Of(c => c.IsOfType(It.IsAny()) == false); + + private string FilePath => "C:/Some/Path/TestDocumentTracker.cshtml"; + + private string ProjectPath => "C:/Some/Path/TestProject.csproj"; + + private ProjectSnapshotManager ProjectManager => Mock.Of(p => p.Projects == new List()); + + private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal(); + + private Workspace Workspace => new AdhocWorkspace(); + + private TextBufferProjectService SupportedProjectService { get; } = Mock.Of( + s => s.GetHostProject(It.IsAny()) == Mock.Of() && + s.IsSupportedProject(It.IsAny()) == true && + s.GetProjectPath(It.IsAny()) == "C:/Some/Path/TestProject.csproj"); + + private TextBufferProjectService UnsupportedProjectService { get; } = Mock.Of(s => s.IsSupportedProject(It.IsAny()) == false); + + [Fact] + public void OnTextViewOpened_ForNonRazorCoreProject_DoesNothing() + { + // Arrange + var editorFactoryService = new Mock(MockBehavior.Strict); + var documentManager = new DefaultRazorDocumentManager(editorFactoryService.Object, UnsupportedProjectService); + var textView = Mock.Of(); + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + }; + + // Act & Assert + documentManager.OnTextViewOpened(textView, buffers); + } + + [Fact] + public void OnTextViewOpened_ForNonRazorTextBuffer_DoesNothing() + { + // Arrange + var editorFactoryService = new Mock(MockBehavior.Strict); + var documentManager = new DefaultRazorDocumentManager(editorFactoryService.Object, SupportedProjectService); + var textView = Mock.Of(); + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == NonRazorContentType && b.Properties == new PropertyCollection()), + }; + + // Act & Assert + documentManager.OnTextViewOpened(textView, buffers); + } + + [Fact] + public void OnTextViewOpened_ForRazorTextBuffer_AddsTextViewToTracker() + { + // Arrange + var textView = Mock.Of(); + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + }; + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, buffers[0]) as VisualStudioDocumentTracker; + var editorFactoryService = Mock.Of(factoryService => factoryService.TryGetDocumentTracker(It.IsAny(), out documentTracker) == true); + var documentManager = new DefaultRazorDocumentManager(editorFactoryService, SupportedProjectService); + + // Act + documentManager.OnTextViewOpened(textView, buffers); + + // Assert + Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView)); + } + + [Fact] + public void OnTextViewOpened_SubscribesAfterFirstTextViewOpened() + { + // Arrange + var textView = Mock.Of(); + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + Mock.Of(b => b.ContentType == NonRazorContentType && b.Properties == new PropertyCollection()), + }; + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, buffers[0]) as VisualStudioDocumentTracker; + var editorFactoryService = Mock.Of(f => f.TryGetDocumentTracker(It.IsAny(), out documentTracker) == true); + var documentManager = new DefaultRazorDocumentManager(editorFactoryService, SupportedProjectService); + + // Assert 1 + Assert.False(documentTracker.IsSupportedProject); + + // Act + documentManager.OnTextViewOpened(textView, buffers); + + // Assert 2 + Assert.True(documentTracker.IsSupportedProject); + } + + [Fact] + public void OnTextViewClosed_FoNonRazorCoreProject_DoesNothing() + { + // Arrange + var documentManager = new DefaultRazorDocumentManager(Mock.Of(), UnsupportedProjectService); + var textView = Mock.Of(); + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + }; + + // Act + documentManager.OnTextViewClosed(textView, buffers); + + // Assert + Assert.False(buffers[0].Properties.ContainsProperty(typeof(VisualStudioDocumentTracker))); + } + + [Fact] + public void OnTextViewClosed_TextViewWithoutDocumentTracker_DoesNothing() + { + // Arrange + var documentManager = new DefaultRazorDocumentManager(Mock.Of(), SupportedProjectService); + var textView = Mock.Of(); + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + }; + + // Act + documentManager.OnTextViewClosed(textView, buffers); + + // Assert + Assert.False(buffers[0].Properties.ContainsProperty(typeof(VisualStudioDocumentTracker))); + } + + [Fact] + public void OnTextViewClosed_ForAnyTextBufferWithTracker_RemovesTextView() + { + // Arrange + var textView1 = Mock.Of(); + var textView2 = Mock.Of(); + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + Mock.Of(b => b.ContentType == NonRazorContentType && b.Properties == new PropertyCollection()), + }; + + // Preload the buffer's properties with a tracker, so it's like we've already tracked this one. + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, buffers[0]); + documentTracker.AddTextView(textView1); + documentTracker.AddTextView(textView2); + buffers[0].Properties.AddProperty(typeof(VisualStudioDocumentTracker), documentTracker); + + documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, buffers[1]); + documentTracker.AddTextView(textView1); + documentTracker.AddTextView(textView2); + buffers[1].Properties.AddProperty(typeof(VisualStudioDocumentTracker), documentTracker); + + var editorFactoryService = Mock.Of(); + var documentManager = new DefaultRazorDocumentManager(editorFactoryService, SupportedProjectService); + + // Act + documentManager.OnTextViewClosed(textView2, buffers); + + // Assert + documentTracker = buffers[0].Properties.GetProperty(typeof(VisualStudioDocumentTracker)); + Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView1)); + + documentTracker = buffers[1].Properties.GetProperty(typeof(VisualStudioDocumentTracker)); + Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView1)); + } + + [Fact] + public void OnTextViewClosed_UnsubscribesAfterLastTextViewClosed() + { + // Arrange + var textView1 = Mock.Of(); + var textView2 = Mock.Of(); + var buffers = new Collection() + { + Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), + Mock.Of(b => b.ContentType == NonRazorContentType && b.Properties == new PropertyCollection()), + }; + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, buffers[0]); + buffers[0].Properties.AddProperty(typeof(VisualStudioDocumentTracker), documentTracker); + var editorFactoryService = Mock.Of(); + var documentManager = new DefaultRazorDocumentManager(editorFactoryService, SupportedProjectService); + + // Populate the text views + documentTracker.Subscribe(); + documentTracker.AddTextView(textView1); + documentTracker.AddTextView(textView2); + + // Act 1 + documentManager.OnTextViewClosed(textView2, buffers); + + // Assert 1 + Assert.True(documentTracker.IsSupportedProject); + + // Act + documentManager.OnTextViewClosed(textView1, buffers); + + // Assert 2 + Assert.False(documentTracker.IsSupportedProject); + } + } +} diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/DefaultVisualStudioDocumentTrackerTest.cs b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioDocumentTrackerTest.cs similarity index 71% rename from test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/DefaultVisualStudioDocumentTrackerTest.cs rename to test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioDocumentTrackerTest.cs index 9a6e4552fe..6bc12c3af3 100644 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/DefaultVisualStudioDocumentTrackerTest.cs +++ b/test/Microsoft.VisualStudio.Editor.Razor.Test/DefaultVisualStudioDocumentTrackerTest.cs @@ -6,14 +6,13 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.Editor; using Microsoft.CodeAnalysis.Razor.ProjectSystem; -using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; using Moq; using Xunit; -namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor +namespace Microsoft.VisualStudio.Editor.Razor { public class DefaultVisualStudioDocumentTrackerTest { @@ -23,12 +22,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor private string FilePath => "C:/Some/Path/TestDocumentTracker.cshtml"; - private ProjectSnapshotManager ProjectManager => Mock.Of(p => p.Projects == new List()); + private string ProjectPath => "C:/Some/Path/TestProject.csproj"; - private TextBufferProjectService ProjectService => Mock.Of( - s => s.GetHierarchy(It.IsAny()) == Mock.Of() && - s.IsSupportedProject(It.IsAny()) == true && - s.GetProjectPath(It.IsAny()) == "C:/Some/Path/TestProject.csproj"); + private ProjectSnapshotManager ProjectManager => Mock.Of(p => p.Projects == new List()); private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal(); @@ -38,11 +34,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor public void EditorSettingsManager_Changed_TriggersContextChanged() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectManager, ProjectService, EditorSettingsManager, Workspace, TextBuffer); + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer); var called = false; documentTracker.ContextChanged += (sender, args) => { called = true; + Assert.Equal(ContextChangeKind.EditorSettingsChanged, args.Kind); }; // Act @@ -52,11 +49,55 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor Assert.True(called); } + [Fact] + public void Subscribe_SetsSupportedProjectAndTriggersContextChanged() + { + // Arrange + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer); + var called = false; + documentTracker.ContextChanged += (sender, args) => + { + called = true; + Assert.Equal(ContextChangeKind.ProjectChanged, args.Kind); + }; + + // Act + documentTracker.Subscribe(); + + // Assert + Assert.True(called); + Assert.True(documentTracker.IsSupportedProject); + } + + [Fact] + public void Unsubscribe_ResetsSupportedProjectAndTriggersContextChanged() + { + // Arrange + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer); + + // Subscribe once to set supported project + documentTracker.Subscribe(); + + var called = false; + documentTracker.ContextChanged += (sender, args) => + { + called = true; + Assert.Equal(ContextChangeKind.ProjectChanged, args.Kind); + }; + + // Act + documentTracker.Unsubscribe(); + + // Assert + Assert.False(documentTracker.IsSupportedProject); + Assert.True(called); + } + [Fact] public void AddTextView_AddsToTextViewCollection() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectManager, ProjectService, EditorSettingsManager, Workspace, TextBuffer); + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer); var textView = Mock.Of(); // Act @@ -66,28 +107,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView)); } - [Fact] - public void AddTextView_SubscribesAfterFirstTextViewAdded() - { - // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectManager, ProjectService, EditorSettingsManager, Workspace, TextBuffer); - var textView = Mock.Of(); - - // Assert - 1 - Assert.False(documentTracker.IsSupportedProject); - - // Act - documentTracker.AddTextView(textView); - - // Assert - 2 - Assert.True(documentTracker.IsSupportedProject); - } - [Fact] public void AddTextView_DoesNotAddDuplicateTextViews() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectManager, ProjectService, EditorSettingsManager, Workspace, TextBuffer); + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer); var textView = Mock.Of(); // Act @@ -102,7 +126,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor public void AddTextView_AddsMultipleTextViewsToCollection() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectManager, ProjectService, EditorSettingsManager, Workspace, TextBuffer); + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer); var textView1 = Mock.Of(); var textView2 = Mock.Of(); @@ -121,7 +145,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor public void RemoveTextView_RemovesTextViewFromCollection_SingleItem() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectManager, ProjectService, EditorSettingsManager, Workspace, TextBuffer); + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer); var textView = Mock.Of(); documentTracker.AddTextView(textView); @@ -136,7 +160,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor public void RemoveTextView_RemovesTextViewFromCollection_MultipleItems() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectManager, ProjectService, EditorSettingsManager, Workspace, TextBuffer); + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer); var textView1 = Mock.Of(); var textView2 = Mock.Of(); var textView3 = Mock.Of(); @@ -158,7 +182,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor public void RemoveTextView_NoopsWhenRemovingTextViewNotInCollection() { // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectManager, ProjectService, EditorSettingsManager, Workspace, TextBuffer); + var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer); var textView1 = Mock.Of(); documentTracker.AddTextView(textView1); var textView2 = Mock.Of(); @@ -169,28 +193,5 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor // Assert Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView1)); } - - [Fact] - public void RemoveTextView_UnsubscribesAfterLastTextViewRemoved() - { - // Arrange - var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectManager, ProjectService, EditorSettingsManager, Workspace, TextBuffer); - var textView1 = Mock.Of(); - var textView2 = Mock.Of(); - documentTracker.AddTextView(textView1); - documentTracker.AddTextView(textView2); - - // Act - 1 - documentTracker.RemoveTextView(textView1); - - // Assert - 1 - Assert.True(documentTracker.IsSupportedProject); - - // Act - 2 - documentTracker.RemoveTextView(textView2); - - // Assert - 2 - Assert.False(documentTracker.IsSupportedProject); - } } } diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/RazorTextViewConnectionListenerTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/RazorTextViewConnectionListenerTest.cs index 2b727a2139..36bc0aa4a0 100644 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/RazorTextViewConnectionListenerTest.cs +++ b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Editor/RazorTextViewConnectionListenerTest.cs @@ -1,17 +1,11 @@ // 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.Collections.Generic; using System.Collections.ObjectModel; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Razor; -using Microsoft.CodeAnalysis.Razor.Editor; -using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.VisualStudio.Editor.Razor; -using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Utilities; using Moq; using Xunit; @@ -19,149 +13,42 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor { public class RazorTextViewConnectionListenerTest : ForegroundDispatcherTestBase { - private ProjectSnapshotManager ProjectManager { get; } = Mock.Of(p => p.Projects == new List()); - - private TextBufferProjectService ProjectService { get; } = Mock.Of( - s => s.GetHierarchy(It.IsAny()) == Mock.Of() && - s.IsSupportedProject(It.IsAny()) == true && - s.GetProjectPath(It.IsAny()) == "C:/Some/Path/TestProject.csproj"); - - private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal(); - - private Workspace Workspace { get; } = new AdhocWorkspace(); - - private IContentType RazorContentType { get; } = Mock.Of(c => c.IsOfType(RazorLanguage.ContentType) == true); - - private IContentType NonRazorContentType { get; } = Mock.Of(c => c.IsOfType(It.IsAny()) == false); - - private TextBufferProjectService SupportedProjectService { get; } = Mock.Of(s => s.IsSupportedProject(It.IsAny()) == true); - - private TextBufferProjectService UnsupportedProjectService { get; } = Mock.Of(s => s.IsSupportedProject(It.IsAny()) == false); - [ForegroundFact] - public void SubjectBuffersConnected_ForNonRazorCoreProject_DoesNothing() - { - // Arrange - var editorFactoryService = new Mock(MockBehavior.Strict); - var factory = new RazorTextViewConnectionListener(Dispatcher, UnsupportedProjectService, editorFactoryService.Object, Workspace); - var textView = Mock.Of(); - var buffers = new Collection() - { - Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), - }; - - // Act & Assert - factory.SubjectBuffersConnected(textView, ConnectionReason.BufferGraphChange, buffers); - } - - [ForegroundFact] - public void SubjectBuffersConnected_ForNonRazorTextBuffer_DoesNothing() - { - // Arrange - var editorFactoryService = new Mock(MockBehavior.Strict); - var factory = new RazorTextViewConnectionListener(Dispatcher, SupportedProjectService, editorFactoryService.Object, Workspace); - var textView = Mock.Of(); - var buffers = new Collection() - { - Mock.Of(b => b.ContentType == NonRazorContentType && b.Properties == new PropertyCollection()), - }; - - // Act & Assert - factory.SubjectBuffersConnected(textView, ConnectionReason.BufferGraphChange, buffers); - } - - [ForegroundFact] - public void SubjectBuffersConnected_ForRazorTextBuffer_AddsTextViewToTracker() + public void SubjectBuffersConnected_CallsRazorDocumentManager_OnTextViewOpened() { // Arrange var textView = Mock.Of(); - var buffers = new Collection() - { - Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), - }; - VisualStudioDocumentTracker documentTracker = new DefaultVisualStudioDocumentTracker("AFile", ProjectManager, ProjectService, EditorSettingsManager, Workspace, buffers[0]); - var editorFactoryService = Mock.Of(factoryService => factoryService.TryGetDocumentTracker(It.IsAny(), out documentTracker) == true); - var textViewListener = new RazorTextViewConnectionListener(Dispatcher, SupportedProjectService, editorFactoryService, Workspace); + var buffers = new Collection(); + var workspace = new AdhocWorkspace(); + var documentManager = new Mock(MockBehavior.Strict); + documentManager.Setup(d => d.OnTextViewOpened(textView, buffers)).Verifiable(); + + var listener = new RazorTextViewConnectionListener(Dispatcher, workspace, documentManager.Object); // Act - textViewListener.SubjectBuffersConnected(textView, ConnectionReason.BufferGraphChange, buffers); + listener.SubjectBuffersConnected(textView, ConnectionReason.BufferGraphChange, buffers); // Assert - Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView)); + documentManager.Verify(); } [ForegroundFact] - public void SubjectBuffersDisconnected_ForAnyTextBufferWithTracker_RemovesTextView() + public void SubjectBuffersDisonnected_CallsRazorDocumentManager_OnTextViewClosed() { // Arrange - var textView1 = Mock.Of(); - var textView2 = Mock.Of(); - - var buffers = new Collection() - { - Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), - Mock.Of(b => b.ContentType == NonRazorContentType && b.Properties == new PropertyCollection()), - }; - - // Preload the buffer's properties with a tracker, so it's like we've already tracked this one. - var tracker = new DefaultVisualStudioDocumentTracker("C:/File/Path/To/Tracker1.cshtml", ProjectManager, ProjectService, EditorSettingsManager, Workspace, buffers[0]); - tracker.AddTextView(textView1); - tracker.AddTextView(textView2); - buffers[0].Properties.AddProperty(typeof(VisualStudioDocumentTracker), tracker); - - tracker = new DefaultVisualStudioDocumentTracker("C:/File/Path/To/Tracker1.cshtml", ProjectManager, ProjectService, EditorSettingsManager, Workspace, buffers[1]); - tracker.AddTextView(textView1); - tracker.AddTextView(textView2); - buffers[1].Properties.AddProperty(typeof(VisualStudioDocumentTracker), tracker); - var textViewListener = new RazorTextViewConnectionListener(Dispatcher, SupportedProjectService, Mock.Of(), Workspace); - - // Act - textViewListener.SubjectBuffersDisconnected(textView2, ConnectionReason.BufferGraphChange, buffers); - - // Assert - tracker = buffers[0].Properties.GetProperty(typeof(VisualStudioDocumentTracker)); - Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView1)); - - tracker = buffers[1].Properties.GetProperty(typeof(VisualStudioDocumentTracker)); - Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView1)); - } - - [ForegroundFact] - public void SubjectBuffersDisconnected_FoNonRazorCoreProject_DoesNothing() - { - // Arrange - var textViewListener = new RazorTextViewConnectionListener(Dispatcher, UnsupportedProjectService, Mock.Of(), Workspace); var textView = Mock.Of(); - var buffers = new Collection() - { - Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), - }; + var buffers = new Collection(); + var workspace = new AdhocWorkspace(); + var documentManager = new Mock(MockBehavior.Strict); + documentManager.Setup(d => d.OnTextViewClosed(textView, buffers)).Verifiable(); + + var listener = new RazorTextViewConnectionListener(Dispatcher, workspace, documentManager.Object); // Act - textViewListener.SubjectBuffersDisconnected(textView, ConnectionReason.BufferGraphChange, buffers); + listener.SubjectBuffersDisconnected(textView, ConnectionReason.BufferGraphChange, buffers); // Assert - Assert.False(buffers[0].Properties.ContainsProperty(typeof(VisualStudioDocumentTracker))); - } - - [ForegroundFact] - public void SubjectBuffersDisconnected_ForAnyTextBufferWithoutTracker_DoesNothing() - { - // Arrange - var textViewListener = new RazorTextViewConnectionListener(Dispatcher, SupportedProjectService, Mock.Of(), Workspace); - - var textView = Mock.Of(); - - var buffers = new Collection() - { - Mock.Of(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), - }; - - // Act - textViewListener.SubjectBuffersDisconnected(textView, ConnectionReason.BufferGraphChange, buffers); - - // Assert - Assert.False(buffers[0].Properties.ContainsProperty(typeof(VisualStudioDocumentTracker))); + documentManager.Verify(); } } }