Added necessary infrastructure for Tag helper project system imports

tracking
This commit is contained in:
Ajay Bhargav Baaskaran 2017-11-20 19:25:46 -08:00
parent ece080466b
commit 90120f6a3b
12 changed files with 509 additions and 341 deletions

View File

@ -8,5 +8,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
ProjectChanged, ProjectChanged,
EditorSettingsChanged, EditorSettingsChanged,
TagHelpersChanged, TagHelpersChanged,
ImportsChanged,
} }
} }

View File

@ -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<ITextBuffer> 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<ITextBuffer> 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;
}
}
}

View File

@ -5,35 +5,31 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Editor; using Microsoft.CodeAnalysis.Razor.Editor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.Editor.Razor;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor;
namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor namespace Microsoft.VisualStudio.Editor.Razor
{ {
internal class DefaultVisualStudioDocumentTracker : VisualStudioDocumentTracker internal class DefaultVisualStudioDocumentTracker : VisualStudioDocumentTracker
{ {
private readonly string _filePath; private readonly string _filePath;
private readonly string _projectPath;
private readonly ProjectSnapshotManager _projectManager; private readonly ProjectSnapshotManager _projectManager;
private readonly EditorSettingsManagerInternal _editorSettingsManager; private readonly EditorSettingsManagerInternal _editorSettingsManager;
private readonly TextBufferProjectService _projectService;
private readonly ITextBuffer _textBuffer; private readonly ITextBuffer _textBuffer;
private readonly List<ITextView> _textViews; private readonly List<ITextView> _textViews;
private readonly Workspace _workspace; private readonly Workspace _workspace;
private bool _isSupportedProject; private bool _isSupportedProject;
private ProjectSnapshot _project; private ProjectSnapshot _project;
private string _projectPath;
public override event EventHandler<ContextChangeEventArgs> ContextChanged; public override event EventHandler<ContextChangeEventArgs> ContextChanged;
public DefaultVisualStudioDocumentTracker( public DefaultVisualStudioDocumentTracker(
string filePath, string filePath,
string projectPath,
ProjectSnapshotManager projectManager, ProjectSnapshotManager projectManager,
TextBufferProjectService projectService,
EditorSettingsManagerInternal editorSettingsManager, EditorSettingsManagerInternal editorSettingsManager,
Workspace workspace, Workspace workspace,
ITextBuffer textBuffer) ITextBuffer textBuffer)
@ -43,16 +39,16 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(filePath)); throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(filePath));
} }
if (projectPath == null)
{
throw new ArgumentNullException(nameof(projectPath));
}
if (projectManager == null) if (projectManager == null)
{ {
throw new ArgumentNullException(nameof(projectManager)); throw new ArgumentNullException(nameof(projectManager));
} }
if (projectService == null)
{
throw new ArgumentNullException(nameof(projectService));
}
if (editorSettingsManager == null) if (editorSettingsManager == null)
{ {
throw new ArgumentNullException(nameof(editorSettingsManager)); throw new ArgumentNullException(nameof(editorSettingsManager));
@ -69,8 +65,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
} }
_filePath = filePath; _filePath = filePath;
_projectPath = projectPath;
_projectManager = projectManager; _projectManager = projectManager;
_projectService = projectService;
_editorSettingsManager = editorSettingsManager; _editorSettingsManager = editorSettingsManager;
_textBuffer = textBuffer; _textBuffer = textBuffer;
_workspace = workspace; // For now we assume that the workspace is the always default VS workspace. _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)) if (!_textViews.Contains(textView))
{ {
_textViews.Add(textView); _textViews.Add(textView);
if (_textViews.Count == 1)
{
Subscribe();
}
} }
} }
@ -126,11 +117,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
if (_textViews.Contains(textView)) if (_textViews.Contains(textView))
{ {
_textViews.Remove(textView); _textViews.Remove(textView);
if (_textViews.Count == 0)
{
Unsubscribe();
}
} }
} }
@ -147,42 +133,18 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
return null; 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; _editorSettingsManager.Changed += EditorSettingsManager_Changed;
_projectManager.Changed += ProjectManager_Changed;
_isSupportedProject = true;
_project = _projectManager.GetProjectWithFilePath(_projectPath);
OnContextChanged(_project, ContextChangeKind.ProjectChanged); OnContextChanged(_project, ContextChangeKind.ProjectChanged);
} }
private void Unsubscribe() public void Unsubscribe()
{ {
_projectManager.Changed -= ProjectManager_Changed; _projectManager.Changed -= ProjectManager_Changed;
_editorSettingsManager.Changed -= EditorSettingsManager_Changed; _editorSettingsManager.Changed -= EditorSettingsManager_Changed;
@ -190,6 +152,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
// Detached from project. // Detached from project.
_isSupportedProject = false; _isSupportedProject = false;
_project = null; _project = null;
OnContextChanged(project: null, kind: ContextChangeKind.ProjectChanged); OnContextChanged(project: null, kind: ContextChangeKind.ProjectChanged);
} }

View File

@ -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<ITextBuffer> subjectBuffers);
public abstract void OnTextViewClosed(ITextView textView, IList<ITextBuffer> subjectBuffers);
}
}

View File

@ -1,15 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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; using Microsoft.VisualStudio.Text;
namespace Microsoft.VisualStudio.LanguageServices.Razor namespace Microsoft.VisualStudio.Editor.Razor
{ {
internal abstract class TextBufferProjectService 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);
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.Editor.Razor;
using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text;
@ -41,7 +42,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
_documentTable = new RunningDocumentTable(services); _documentTable = new RunningDocumentTable(services);
} }
public override IVsHierarchy GetHierarchy(ITextBuffer textBuffer) public override object GetHostProject(ITextBuffer textBuffer)
{ {
if (textBuffer == null) if (textBuffer == null)
{ {
@ -63,24 +64,27 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
return hierarchy; 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); ErrorHandler.ThrowOnFailure(((IVsProject)hierarchy).GetMkDocument((uint)VSConstants.VSITEMID.Root, out var path), VSConstants.E_NOTIMPL);
return path; 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 try
{ {
return hierarchy.IsCapabilityMatch(DotNetCoreCapability); return hierarchy.IsCapabilityMatch(DotNetCoreCapability);

View File

@ -69,7 +69,16 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
} }
var filePath = textDocument.FilePath; 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; return tracker;
} }

View File

@ -4,12 +4,9 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.Diagnostics;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor;
using Microsoft.VisualStudio.Editor.Razor; using Microsoft.VisualStudio.Editor.Razor;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.Utilities;
@ -22,69 +19,53 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
internal class RazorTextViewConnectionListener : IWpfTextViewConnectionListener internal class RazorTextViewConnectionListener : IWpfTextViewConnectionListener
{ {
private readonly ForegroundDispatcher _foregroundDispatcher; private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly TextBufferProjectService _projectService;
private readonly RazorEditorFactoryService _editorFactoryService;
private readonly Workspace _workspace; private readonly Workspace _workspace;
private readonly RazorDocumentManager _documentManager;
[ImportingConstructor] [ImportingConstructor]
public RazorTextViewConnectionListener( public RazorTextViewConnectionListener(
TextBufferProjectService projectService, [Import(typeof(VisualStudioWorkspace))] Workspace workspace,
RazorEditorFactoryService editorFactoryService, RazorDocumentManager documentManager)
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
{ {
if (projectService == null)
{
throw new ArgumentNullException(nameof(projectService));
}
if (editorFactoryService == null)
{
throw new ArgumentNullException(nameof(editorFactoryService));
}
if (workspace == null) if (workspace == null)
{ {
throw new ArgumentNullException(nameof(workspace)); throw new ArgumentNullException(nameof(workspace));
} }
_projectService = projectService; if (documentManager == null)
_editorFactoryService = editorFactoryService; {
_workspace = workspace; throw new ArgumentNullException(nameof(documentManager));
}
_workspace = workspace;
_documentManager = documentManager;
_foregroundDispatcher = workspace.Services.GetRequiredService<ForegroundDispatcher>(); _foregroundDispatcher = workspace.Services.GetRequiredService<ForegroundDispatcher>();
} }
// This is only for testing. We want to avoid using the actual Roslyn GetService methods in unit tests. // This is only for testing. We want to avoid using the actual Roslyn GetService methods in unit tests.
internal RazorTextViewConnectionListener( internal RazorTextViewConnectionListener(
ForegroundDispatcher foregroundDispatcher, ForegroundDispatcher foregroundDispatcher,
TextBufferProjectService projectService, Workspace workspace,
RazorEditorFactoryService editorFactoryService, RazorDocumentManager documentManager)
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
{ {
if (foregroundDispatcher == null) if (foregroundDispatcher == null)
{ {
throw new ArgumentNullException(nameof(foregroundDispatcher)); throw new ArgumentNullException(nameof(foregroundDispatcher));
} }
if (projectService == null)
{
throw new ArgumentNullException(nameof(projectService));
}
if (editorFactoryService == null)
{
throw new ArgumentNullException(nameof(editorFactoryService));
}
if (workspace == null) if (workspace == null)
{ {
throw new ArgumentNullException(nameof(workspace)); throw new ArgumentNullException(nameof(workspace));
} }
if (documentManager == null)
{
throw new ArgumentNullException(nameof(documentManager));
}
_foregroundDispatcher = foregroundDispatcher; _foregroundDispatcher = foregroundDispatcher;
_projectService = projectService;
_editorFactoryService = editorFactoryService;
_workspace = workspace; _workspace = workspace;
_documentManager = documentManager;
} }
public Workspace Workspace => _workspace; public Workspace Workspace => _workspace;
@ -103,29 +84,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
_foregroundDispatcher.AssertForegroundThread(); _foregroundDispatcher.AssertForegroundThread();
for (var i = 0; i < subjectBuffers.Count; i++) _documentManager.OnTextViewOpened(textView, subjectBuffers);
{
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);
}
} }
public void SubjectBuffersDisconnected(IWpfTextView textView, ConnectionReason reason, Collection<ITextBuffer> subjectBuffers) public void SubjectBuffersDisconnected(IWpfTextView textView, ConnectionReason reason, Collection<ITextBuffer> subjectBuffers)
@ -142,21 +101,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
_foregroundDispatcher.AssertForegroundThread(); _foregroundDispatcher.AssertForegroundThread();
// This means a Razor buffer has be detached from this ITextView or the ITextView is closing. Since we keep a _documentManager.OnTextViewClosed(textView, subjectBuffers);
// 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);
}
}
} }
} }
} }

View File

@ -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);
}
}

View File

@ -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<IContentType>(c => c.IsOfType(RazorLanguage.ContentType) == true);
private IContentType NonRazorContentType { get; } = Mock.Of<IContentType>(c => c.IsOfType(It.IsAny<string>()) == false);
private string FilePath => "C:/Some/Path/TestDocumentTracker.cshtml";
private string ProjectPath => "C:/Some/Path/TestProject.csproj";
private ProjectSnapshotManager ProjectManager => Mock.Of<ProjectSnapshotManager>(p => p.Projects == new List<ProjectSnapshot>());
private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal();
private Workspace Workspace => new AdhocWorkspace();
private TextBufferProjectService SupportedProjectService { get; } = Mock.Of<TextBufferProjectService>(
s => s.GetHostProject(It.IsAny<ITextBuffer>()) == Mock.Of<object>() &&
s.IsSupportedProject(It.IsAny<object>()) == true &&
s.GetProjectPath(It.IsAny<object>()) == "C:/Some/Path/TestProject.csproj");
private TextBufferProjectService UnsupportedProjectService { get; } = Mock.Of<TextBufferProjectService>(s => s.IsSupportedProject(It.IsAny<object>()) == false);
[Fact]
public void OnTextViewOpened_ForNonRazorCoreProject_DoesNothing()
{
// Arrange
var editorFactoryService = new Mock<RazorEditorFactoryService>(MockBehavior.Strict);
var documentManager = new DefaultRazorDocumentManager(editorFactoryService.Object, UnsupportedProjectService);
var textView = Mock.Of<ITextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(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<RazorEditorFactoryService>(MockBehavior.Strict);
var documentManager = new DefaultRazorDocumentManager(editorFactoryService.Object, SupportedProjectService);
var textView = Mock.Of<ITextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(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<ITextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(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<RazorEditorFactoryService>(factoryService => factoryService.TryGetDocumentTracker(It.IsAny<ITextBuffer>(), 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<ITextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()),
Mock.Of<ITextBuffer>(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<RazorEditorFactoryService>(f => f.TryGetDocumentTracker(It.IsAny<ITextBuffer>(), 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<RazorEditorFactoryService>(), UnsupportedProjectService);
var textView = Mock.Of<ITextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(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<RazorEditorFactoryService>(), SupportedProjectService);
var textView = Mock.Of<ITextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(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<ITextView>();
var textView2 = Mock.Of<ITextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()),
Mock.Of<ITextBuffer>(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<RazorEditorFactoryService>();
var documentManager = new DefaultRazorDocumentManager(editorFactoryService, SupportedProjectService);
// Act
documentManager.OnTextViewClosed(textView2, buffers);
// Assert
documentTracker = buffers[0].Properties.GetProperty<DefaultVisualStudioDocumentTracker>(typeof(VisualStudioDocumentTracker));
Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView1));
documentTracker = buffers[1].Properties.GetProperty<DefaultVisualStudioDocumentTracker>(typeof(VisualStudioDocumentTracker));
Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView1));
}
[Fact]
public void OnTextViewClosed_UnsubscribesAfterLastTextViewClosed()
{
// Arrange
var textView1 = Mock.Of<ITextView>();
var textView2 = Mock.Of<ITextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()),
Mock.Of<ITextBuffer>(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<RazorEditorFactoryService>();
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);
}
}
}

View File

@ -6,14 +6,13 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Editor; using Microsoft.CodeAnalysis.Razor.Editor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.Utilities;
using Moq; using Moq;
using Xunit; using Xunit;
namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor namespace Microsoft.VisualStudio.Editor.Razor
{ {
public class DefaultVisualStudioDocumentTrackerTest public class DefaultVisualStudioDocumentTrackerTest
{ {
@ -23,12 +22,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
private string FilePath => "C:/Some/Path/TestDocumentTracker.cshtml"; private string FilePath => "C:/Some/Path/TestDocumentTracker.cshtml";
private ProjectSnapshotManager ProjectManager => Mock.Of<ProjectSnapshotManager>(p => p.Projects == new List<ProjectSnapshot>()); private string ProjectPath => "C:/Some/Path/TestProject.csproj";
private TextBufferProjectService ProjectService => Mock.Of<TextBufferProjectService>( private ProjectSnapshotManager ProjectManager => Mock.Of<ProjectSnapshotManager>(p => p.Projects == new List<ProjectSnapshot>());
s => s.GetHierarchy(It.IsAny<ITextBuffer>()) == Mock.Of<IVsHierarchy>() &&
s.IsSupportedProject(It.IsAny<IVsHierarchy>()) == true &&
s.GetProjectPath(It.IsAny<IVsHierarchy>()) == "C:/Some/Path/TestProject.csproj");
private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal(); private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal();
@ -38,11 +34,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
public void EditorSettingsManager_Changed_TriggersContextChanged() public void EditorSettingsManager_Changed_TriggersContextChanged()
{ {
// Arrange // Arrange
var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectManager, ProjectService, EditorSettingsManager, Workspace, TextBuffer); var documentTracker = new DefaultVisualStudioDocumentTracker(FilePath, ProjectPath, ProjectManager, EditorSettingsManager, Workspace, TextBuffer);
var called = false; var called = false;
documentTracker.ContextChanged += (sender, args) => documentTracker.ContextChanged += (sender, args) =>
{ {
called = true; called = true;
Assert.Equal(ContextChangeKind.EditorSettingsChanged, args.Kind);
}; };
// Act // Act
@ -52,11 +49,55 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
Assert.True(called); 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] [Fact]
public void AddTextView_AddsToTextViewCollection() public void AddTextView_AddsToTextViewCollection()
{ {
// Arrange // 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<ITextView>(); var textView = Mock.Of<ITextView>();
// Act // Act
@ -66,28 +107,11 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView)); 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<ITextView>();
// Assert - 1
Assert.False(documentTracker.IsSupportedProject);
// Act
documentTracker.AddTextView(textView);
// Assert - 2
Assert.True(documentTracker.IsSupportedProject);
}
[Fact] [Fact]
public void AddTextView_DoesNotAddDuplicateTextViews() public void AddTextView_DoesNotAddDuplicateTextViews()
{ {
// Arrange // 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<ITextView>(); var textView = Mock.Of<ITextView>();
// Act // Act
@ -102,7 +126,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
public void AddTextView_AddsMultipleTextViewsToCollection() public void AddTextView_AddsMultipleTextViewsToCollection()
{ {
// Arrange // 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<ITextView>(); var textView1 = Mock.Of<ITextView>();
var textView2 = Mock.Of<ITextView>(); var textView2 = Mock.Of<ITextView>();
@ -121,7 +145,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
public void RemoveTextView_RemovesTextViewFromCollection_SingleItem() public void RemoveTextView_RemovesTextViewFromCollection_SingleItem()
{ {
// Arrange // 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<ITextView>(); var textView = Mock.Of<ITextView>();
documentTracker.AddTextView(textView); documentTracker.AddTextView(textView);
@ -136,7 +160,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
public void RemoveTextView_RemovesTextViewFromCollection_MultipleItems() public void RemoveTextView_RemovesTextViewFromCollection_MultipleItems()
{ {
// Arrange // 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<ITextView>(); var textView1 = Mock.Of<ITextView>();
var textView2 = Mock.Of<ITextView>(); var textView2 = Mock.Of<ITextView>();
var textView3 = Mock.Of<ITextView>(); var textView3 = Mock.Of<ITextView>();
@ -158,7 +182,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
public void RemoveTextView_NoopsWhenRemovingTextViewNotInCollection() public void RemoveTextView_NoopsWhenRemovingTextViewNotInCollection()
{ {
// Arrange // 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<ITextView>(); var textView1 = Mock.Of<ITextView>();
documentTracker.AddTextView(textView1); documentTracker.AddTextView(textView1);
var textView2 = Mock.Of<ITextView>(); var textView2 = Mock.Of<ITextView>();
@ -169,28 +193,5 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
// Assert // Assert
Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView1)); 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<ITextView>();
var textView2 = Mock.Of<ITextView>();
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);
}
} }
} }

View File

@ -1,17 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 System.Collections.ObjectModel;
using Microsoft.CodeAnalysis; 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.Editor.Razor;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Utilities;
using Moq; using Moq;
using Xunit; using Xunit;
@ -19,149 +13,42 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
{ {
public class RazorTextViewConnectionListenerTest : ForegroundDispatcherTestBase public class RazorTextViewConnectionListenerTest : ForegroundDispatcherTestBase
{ {
private ProjectSnapshotManager ProjectManager { get; } = Mock.Of<ProjectSnapshotManager>(p => p.Projects == new List<ProjectSnapshot>());
private TextBufferProjectService ProjectService { get; } = Mock.Of<TextBufferProjectService>(
s => s.GetHierarchy(It.IsAny<ITextBuffer>()) == Mock.Of<IVsHierarchy>() &&
s.IsSupportedProject(It.IsAny<IVsHierarchy>()) == true &&
s.GetProjectPath(It.IsAny<IVsHierarchy>()) == "C:/Some/Path/TestProject.csproj");
private EditorSettingsManagerInternal EditorSettingsManager => new DefaultEditorSettingsManagerInternal();
private Workspace Workspace { get; } = new AdhocWorkspace();
private IContentType RazorContentType { get; } = Mock.Of<IContentType>(c => c.IsOfType(RazorLanguage.ContentType) == true);
private IContentType NonRazorContentType { get; } = Mock.Of<IContentType>(c => c.IsOfType(It.IsAny<string>()) == false);
private TextBufferProjectService SupportedProjectService { get; } = Mock.Of<TextBufferProjectService>(s => s.IsSupportedProject(It.IsAny<IVsHierarchy>()) == true);
private TextBufferProjectService UnsupportedProjectService { get; } = Mock.Of<TextBufferProjectService>(s => s.IsSupportedProject(It.IsAny<IVsHierarchy>()) == false);
[ForegroundFact] [ForegroundFact]
public void SubjectBuffersConnected_ForNonRazorCoreProject_DoesNothing() public void SubjectBuffersConnected_CallsRazorDocumentManager_OnTextViewOpened()
{
// Arrange
var editorFactoryService = new Mock<RazorEditorFactoryService>(MockBehavior.Strict);
var factory = new RazorTextViewConnectionListener(Dispatcher, UnsupportedProjectService, editorFactoryService.Object, Workspace);
var textView = Mock.Of<IWpfTextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(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<RazorEditorFactoryService>(MockBehavior.Strict);
var factory = new RazorTextViewConnectionListener(Dispatcher, SupportedProjectService, editorFactoryService.Object, Workspace);
var textView = Mock.Of<IWpfTextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(b => b.ContentType == NonRazorContentType && b.Properties == new PropertyCollection()),
};
// Act & Assert
factory.SubjectBuffersConnected(textView, ConnectionReason.BufferGraphChange, buffers);
}
[ForegroundFact]
public void SubjectBuffersConnected_ForRazorTextBuffer_AddsTextViewToTracker()
{ {
// Arrange // Arrange
var textView = Mock.Of<IWpfTextView>(); var textView = Mock.Of<IWpfTextView>();
var buffers = new Collection<ITextBuffer>() var buffers = new Collection<ITextBuffer>();
{ var workspace = new AdhocWorkspace();
Mock.Of<ITextBuffer>(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), var documentManager = new Mock<RazorDocumentManager>(MockBehavior.Strict);
}; documentManager.Setup(d => d.OnTextViewOpened(textView, buffers)).Verifiable();
VisualStudioDocumentTracker documentTracker = new DefaultVisualStudioDocumentTracker("AFile", ProjectManager, ProjectService, EditorSettingsManager, Workspace, buffers[0]);
var editorFactoryService = Mock.Of<RazorEditorFactoryService>(factoryService => factoryService.TryGetDocumentTracker(It.IsAny<ITextBuffer>(), out documentTracker) == true); var listener = new RazorTextViewConnectionListener(Dispatcher, workspace, documentManager.Object);
var textViewListener = new RazorTextViewConnectionListener(Dispatcher, SupportedProjectService, editorFactoryService, Workspace);
// Act // Act
textViewListener.SubjectBuffersConnected(textView, ConnectionReason.BufferGraphChange, buffers); listener.SubjectBuffersConnected(textView, ConnectionReason.BufferGraphChange, buffers);
// Assert // Assert
Assert.Collection(documentTracker.TextViews, v => Assert.Same(v, textView)); documentManager.Verify();
} }
[ForegroundFact] [ForegroundFact]
public void SubjectBuffersDisconnected_ForAnyTextBufferWithTracker_RemovesTextView() public void SubjectBuffersDisonnected_CallsRazorDocumentManager_OnTextViewClosed()
{ {
// Arrange // Arrange
var textView1 = Mock.Of<IWpfTextView>();
var textView2 = Mock.Of<IWpfTextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()),
Mock.Of<ITextBuffer>(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<RazorEditorFactoryService>(), Workspace);
// Act
textViewListener.SubjectBuffersDisconnected(textView2, ConnectionReason.BufferGraphChange, buffers);
// Assert
tracker = buffers[0].Properties.GetProperty<DefaultVisualStudioDocumentTracker>(typeof(VisualStudioDocumentTracker));
Assert.Collection(tracker.TextViews, v => Assert.Same(v, textView1));
tracker = buffers[1].Properties.GetProperty<DefaultVisualStudioDocumentTracker>(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<RazorEditorFactoryService>(), Workspace);
var textView = Mock.Of<IWpfTextView>(); var textView = Mock.Of<IWpfTextView>();
var buffers = new Collection<ITextBuffer>() var buffers = new Collection<ITextBuffer>();
{ var workspace = new AdhocWorkspace();
Mock.Of<ITextBuffer>(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()), var documentManager = new Mock<RazorDocumentManager>(MockBehavior.Strict);
}; documentManager.Setup(d => d.OnTextViewClosed(textView, buffers)).Verifiable();
var listener = new RazorTextViewConnectionListener(Dispatcher, workspace, documentManager.Object);
// Act // Act
textViewListener.SubjectBuffersDisconnected(textView, ConnectionReason.BufferGraphChange, buffers); listener.SubjectBuffersDisconnected(textView, ConnectionReason.BufferGraphChange, buffers);
// Assert // Assert
Assert.False(buffers[0].Properties.ContainsProperty(typeof(VisualStudioDocumentTracker))); documentManager.Verify();
}
[ForegroundFact]
public void SubjectBuffersDisconnected_ForAnyTextBufferWithoutTracker_DoesNothing()
{
// Arrange
var textViewListener = new RazorTextViewConnectionListener(Dispatcher, SupportedProjectService, Mock.Of<RazorEditorFactoryService>(), Workspace);
var textView = Mock.Of<IWpfTextView>();
var buffers = new Collection<ITextBuffer>()
{
Mock.Of<ITextBuffer>(b => b.ContentType == RazorContentType && b.Properties == new PropertyCollection()),
};
// Act
textViewListener.SubjectBuffersDisconnected(textView, ConnectionReason.BufferGraphChange, buffers);
// Assert
Assert.False(buffers[0].Properties.ContainsProperty(typeof(VisualStudioDocumentTracker)));
} }
} }
} }