diff --git a/src/Microsoft.VisualStudio.Editor.Razor/LiveShareWorkspaceProvider.cs b/src/Microsoft.VisualStudio.Editor.Razor/LiveShareWorkspaceProvider.cs new file mode 100644 index 0000000000..0eac9d38b6 --- /dev/null +++ b/src/Microsoft.VisualStudio.Editor.Razor/LiveShareWorkspaceProvider.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.VisualStudio.Editor.Razor +{ + internal abstract class LiveShareWorkspaceProvider + { + public abstract bool TryGetWorkspace(ITextBuffer textBuffer, out Workspace workspace); + } +} diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs index 0e9079b84e..64171020a8 100644 --- a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs +++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultVisualStudioWorkspaceAccessor.cs @@ -19,12 +19,14 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor private readonly IBufferGraphFactoryService _bufferGraphService; private readonly TextBufferProjectService _projectService; private readonly Workspace _defaultWorkspace; + private readonly LiveShareWorkspaceProvider _liveShareWorkspaceProvider; [ImportingConstructor] public DefaultVisualStudioWorkspaceAccessor( IBufferGraphFactoryService bufferGraphService, TextBufferProjectService projectService, - [Import(typeof(VisualStudioWorkspace))] Workspace defaultWorkspace) + [Import(typeof(VisualStudioWorkspace))] Workspace defaultWorkspace, + [Import(typeof(LiveShareWorkspaceProvider), AllowDefault = true)] LiveShareWorkspaceProvider liveShareWorkspaceProvider) { if (bufferGraphService == null) { @@ -44,6 +46,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor _bufferGraphService = bufferGraphService; _projectService = projectService; _defaultWorkspace = defaultWorkspace; + _liveShareWorkspaceProvider = liveShareWorkspaceProvider; } public override bool TryGetWorkspace(ITextBuffer textBuffer, out Workspace workspace) @@ -56,12 +59,18 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor // We do a best effort approach in this method to get the workspace that belongs to the TextBuffer. // The approaches we take to find the workspace are: // - // 1. Look for a C# projection buffer associated with the Razor buffer. If we can find one we let + // 1. If we have a live share workspace provider, ask it for the workspace. + // 2. Look for a C# projection buffer associated with the Razor buffer. If we can find one we let // Roslyn take control of finding the Workspace (projectionBuffer.GetWorkspace()). If not, // fall back to determining if we can use the default workspace. - // 2. Look to see if this ITextBuffer is associated with a host project. If we find that our Razor + // 3. Look to see if this ITextBuffer is associated with a host project. If we find that our Razor // buffer has a host project, we make the assumption that we should use the default VisualStudioWorkspace. + if (TryGetWorkspaceFromLiveShare(textBuffer, out workspace)) + { + return true; + } + if (TryGetWorkspaceFromProjectionBuffer(textBuffer, out workspace)) { return true; @@ -76,6 +85,19 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor return false; } + // Internal for testing + internal bool TryGetWorkspaceFromLiveShare(ITextBuffer textBuffer, out Workspace workspace) + { + if (_liveShareWorkspaceProvider != null && + _liveShareWorkspaceProvider.TryGetWorkspace(textBuffer, out workspace)) + { + return true; + } + + workspace = null; + return false; + } + // Internal virtual for testing internal virtual bool TryGetWorkspaceFromProjectionBuffer(ITextBuffer textBuffer, out Workspace workspace) { diff --git a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultVisualStudioWorkspaceAccessorTest.cs b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultVisualStudioWorkspaceAccessorTest.cs index 95a0cb7105..4905f8572d 100644 --- a/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultVisualStudioWorkspaceAccessorTest.cs +++ b/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/DefaultVisualStudioWorkspaceAccessorTest.cs @@ -14,6 +14,27 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor { public class DefaultVisualStudioWorkspaceAccessorTest { + private static readonly LiveShareWorkspaceProvider NoLiveShare = null; + + [Fact] + public void TryGetWorkspace_PrioritizesLiveShareWhenResolvingWorkspaces() + { + // Arrange + var expectedWorkspace = TestWorkspace.Create(); + var liveShareWorkspaceProvider = new Mock(); + liveShareWorkspaceProvider.Setup(provider => provider.TryGetWorkspace(It.IsAny(), out expectedWorkspace)) + .Returns(true); + var workspaceAccessor = new TestWorkspaceAccessor(canGetWorkspaceFromProjectionBuffer: true, canGetWorkspaceFromHostProject: true, liveShareWorkspaceProvider.Object); + var textBuffer = Mock.Of(); + + // Act + var result = workspaceAccessor.TryGetWorkspace(textBuffer, out var workspace); + + // Assert + Assert.True(result); + Assert.Same(expectedWorkspace, workspace); + } + [Fact] public void TryGetWorkspace_CanGetWorkspaceFromProjectionBuffersOnly() { @@ -56,6 +77,57 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor Assert.True(result); } + [Fact] + public void TryGetWorkspaceFromLiveShare_NoLiveShareProvider_ReturnsFalse() + { + // Arrange + var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(Mock.Of(), Mock.Of(), TestWorkspace.Create(), NoLiveShare); + var textBuffer = Mock.Of(); + + // Act + var result = workspaceAccessor.TryGetWorkspaceFromLiveShare(textBuffer, out var workspace); + + // Assert + Assert.False(result); + } + + [Fact] + public void TryGetWorkspaceFromLiveShare_CanNotFindWorkspace_ReturnsFalse() + { + // Arrange + Workspace nullWorkspace = null; + var liveShareWorkspaceProvider = new Mock(); + liveShareWorkspaceProvider.Setup(provider => provider.TryGetWorkspace(It.IsAny(), out nullWorkspace)) + .Returns(false); + var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(Mock.Of(), Mock.Of(), TestWorkspace.Create(), liveShareWorkspaceProvider.Object); + var textBuffer = Mock.Of(); + + // Act + var result = workspaceAccessor.TryGetWorkspaceFromLiveShare(textBuffer, out var workspace); + + // Assert + Assert.False(result); + } + + [Fact] + public void TryGetWorkspaceFromLiveShare_CanFindWorkspace_ReturnsTrue() + { + // Arrange + var expectedWorkspace = TestWorkspace.Create(); + var liveShareWorkspaceProvider = new Mock(); + liveShareWorkspaceProvider.Setup(provider => provider.TryGetWorkspace(It.IsAny(), out expectedWorkspace)) + .Returns(true); + var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(Mock.Of(), Mock.Of(), TestWorkspace.Create(), liveShareWorkspaceProvider.Object); + var textBuffer = Mock.Of(); + + // Act + var result = workspaceAccessor.TryGetWorkspaceFromLiveShare(textBuffer, out var workspace); + + // Assert + Assert.True(result); + Assert.Same(expectedWorkspace, workspace); + } + [Fact] public void TryGetWorkspaceFromProjectionBuffer_NoProjectionBuffer_ReturnsFalse() { @@ -66,7 +138,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor var bufferGraphService = new Mock(); bufferGraphService.Setup(service => service.CreateBufferGraph(It.IsAny())) .Returns(bufferGraph.Object); - var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(bufferGraphService.Object, Mock.Of(), TestWorkspace.Create()); + var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(bufferGraphService.Object, Mock.Of(), TestWorkspace.Create(), NoLiveShare); var textBuffer = Mock.Of(); // Act @@ -80,7 +152,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor public void TryGetWorkspaceFromHostProject_NoHostProject_ReturnsFalse() { // Arrange - var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(Mock.Of(), Mock.Of(), TestWorkspace.Create()); + var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(Mock.Of(), Mock.Of(), TestWorkspace.Create(), NoLiveShare); var textBuffer = Mock.Of(); // Act @@ -97,7 +169,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor var textBuffer = Mock.Of(); var projectService = Mock.Of(service => service.GetHostProject(textBuffer) == new object()); var defaultWorkspace = TestWorkspace.Create(); - var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(Mock.Of(), projectService, defaultWorkspace); + var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(Mock.Of(), projectService, defaultWorkspace, NoLiveShare); // Act var result = workspaceAccessor.TryGetWorkspaceFromHostProject(textBuffer, out var workspace); @@ -113,10 +185,19 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor private readonly bool _canGetWorkspaceFromHostProject; internal TestWorkspaceAccessor(bool canGetWorkspaceFromProjectionBuffer, bool canGetWorkspaceFromHostProject) : + this(canGetWorkspaceFromProjectionBuffer, canGetWorkspaceFromHostProject, NoLiveShare) + { + } + + internal TestWorkspaceAccessor( + bool canGetWorkspaceFromProjectionBuffer, + bool canGetWorkspaceFromHostProject, + LiveShareWorkspaceProvider liveShareWorkspaceProvider) : base( Mock.Of(), Mock.Of(), - TestWorkspace.Create()) + TestWorkspace.Create(), + liveShareWorkspaceProvider) { _canGetWorkspaceFromProjectionBuffer = canGetWorkspaceFromProjectionBuffer; _canGetWorkspaceFromHostProject = canGetWorkspaceFromHostProject; @@ -147,4 +228,4 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor } } } -} +} \ No newline at end of file