Add `ProjectPathProvider` abstract for document tracker creation.

- First iteration of live share replaced the document tracker factory entirely; however, this will be prone to breaking changes in the future when me make changes to document tracker to not rely on a file path. To pre-emptively prevent breaking changes I added a project path provider that can be overridden in the live share case. Note that one big difference here between old and new is that instead of being a MEF service implementation for the project path resolution we're bringing that to the Workspace service level.
- Added tests to validate the two flows of the default project path provider.
This commit is contained in:
N. Taylor Mullen 2018-05-17 12:48:11 -07:00
parent 9a249ffcc1
commit 95d41507fc
6 changed files with 151 additions and 20 deletions

View File

@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.VisualStudio.Text;
namespace Microsoft.VisualStudio.Editor.Razor
{
internal class DefaultProjectPathProvider : ProjectPathProvider
{
private readonly TextBufferProjectService _projectService;
public DefaultProjectPathProvider(TextBufferProjectService projectService)
{
if (projectService == null)
{
throw new ArgumentNullException(nameof(projectService));
}
_projectService = projectService;
}
public override bool TryGetProjectPath(ITextBuffer textBuffer, out string filePath)
{
if (textBuffer == null)
{
throw new ArgumentNullException(nameof(textBuffer));
}
var project = _projectService.GetHostProject(textBuffer);
if (project == null)
{
filePath = null;
return false;
}
filePath = _projectService.GetProjectPath(project);
return true;
}
}
}

View File

@ -0,0 +1,39 @@
// 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.ComponentModel.Composition;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.VisualStudio.Editor.Razor
{
[System.Composition.Shared]
[ExportWorkspaceService(typeof(ProjectPathProvider), ServiceLayer.Default)]
internal class DefaultProjectPathProviderFactory : IWorkspaceServiceFactory
{
private readonly TextBufferProjectService _projectService;
[ImportingConstructor]
public DefaultProjectPathProviderFactory(TextBufferProjectService projectService)
{
if (projectService == null)
{
throw new ArgumentNullException(nameof(projectService));
}
_projectService = projectService;
}
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
{
if (workspaceServices == null)
{
throw new ArgumentNullException(nameof(workspaceServices));
}
return new DefaultProjectPathProvider(_projectService);
}
}
}

View File

@ -13,8 +13,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
{
internal class DefaultVisualStudioDocumentTrackerFactory : VisualStudioDocumentTrackerFactory
{
private readonly TextBufferProjectService _projectService;
private readonly ITextDocumentFactoryService _textDocumentFactory;
private readonly ProjectPathProvider _projectPathProvider;
private readonly Workspace _workspace;
private readonly ImportDocumentManager _importDocumentManager;
private readonly ForegroundDispatcher _foregroundDispatcher;
@ -25,7 +25,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
ForegroundDispatcher foregroundDispatcher,
ProjectSnapshotManager projectManager,
WorkspaceEditorSettings workspaceEditorSettings,
TextBufferProjectService projectService,
ProjectPathProvider projectPathProvider,
ITextDocumentFactoryService textDocumentFactory,
ImportDocumentManager importDocumentManager,
Workspace workspace)
@ -45,9 +45,9 @@ namespace Microsoft.VisualStudio.Editor.Razor
throw new ArgumentNullException(nameof(workspaceEditorSettings));
}
if (projectService == null)
if (projectPathProvider == null)
{
throw new ArgumentNullException(nameof(projectService));
throw new ArgumentNullException(nameof(projectPathProvider));
}
if (textDocumentFactory == null)
@ -68,7 +68,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
_foregroundDispatcher = foregroundDispatcher;
_projectManager = projectManager;
_workspaceEditorSettings = workspaceEditorSettings;
_projectService = projectService;
_projectPathProvider = projectPathProvider;
_textDocumentFactory = textDocumentFactory;
_importDocumentManager = importDocumentManager;
_workspace = workspace;
@ -87,16 +87,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
return null;
}
var filePath = textDocument.FilePath;
var project = _projectService.GetHostProject(textBuffer);
if (project == null)
if (!_projectPathProvider.TryGetProjectPath(textBuffer, out var projectPath))
{
Debug.Fail("Text buffer should belong to a project.");
return null;
}
var projectPath = _projectService.GetProjectPath(project);
var filePath = textDocument.FilePath;
var tracker = new DefaultVisualStudioDocumentTracker(_foregroundDispatcher, filePath, projectPath, _projectManager, _workspaceEditorSettings, _workspace, textBuffer, _importDocumentManager);
return tracker;

View File

@ -17,13 +17,11 @@ namespace Microsoft.VisualStudio.Editor.Razor
internal class DefaultVisualStudioDocumentTrackerFactoryFactory : ILanguageServiceFactory
{
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly TextBufferProjectService _projectService;
private readonly ITextDocumentFactoryService _textDocumentFactory;
[ImportingConstructor]
public DefaultVisualStudioDocumentTrackerFactoryFactory(
ForegroundDispatcher foregroundDispatcher,
TextBufferProjectService projectService,
ITextDocumentFactoryService textDocumentFactory)
{
if (foregroundDispatcher == null)
@ -31,18 +29,12 @@ namespace Microsoft.VisualStudio.Editor.Razor
throw new ArgumentNullException(nameof(foregroundDispatcher));
}
if (projectService == null)
{
throw new ArgumentNullException(nameof(projectService));
}
if (textDocumentFactory == null)
{
throw new ArgumentNullException(nameof(textDocumentFactory));
}
_foregroundDispatcher = foregroundDispatcher;
_projectService = projectService;
_textDocumentFactory = textDocumentFactory;
}
@ -57,11 +49,13 @@ namespace Microsoft.VisualStudio.Editor.Razor
var workspaceEditorSettings = languageServices.GetRequiredService<WorkspaceEditorSettings>();
var importDocumentManager = languageServices.GetRequiredService<ImportDocumentManager>();
var projectPathProvider = languageServices.WorkspaceServices.GetRequiredService<ProjectPathProvider>();
return new DefaultVisualStudioDocumentTrackerFactory(
_foregroundDispatcher,
projectManager,
workspaceEditorSettings,
_projectService,
projectPathProvider,
_textDocumentFactory,
importDocumentManager,
languageServices.WorkspaceServices.Workspace);

View File

@ -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.Host;
using Microsoft.VisualStudio.Text;
namespace Microsoft.VisualStudio.Editor.Razor
{
internal abstract class ProjectPathProvider : IWorkspaceService
{
public abstract bool TryGetProjectPath(ITextBuffer textBuffer, out string filePath);
}
}

View File

@ -0,0 +1,48 @@
// 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.Text;
using Moq;
using Xunit;
namespace Microsoft.VisualStudio.Editor.Razor
{
public class DefaultProjectPathProviderTest
{
[Fact]
public void TryGetProjectPath_ReturnsFalseIfNoProject()
{
// Arrange
var projectPathProvider = new DefaultProjectPathProvider(Mock.Of<TextBufferProjectService>());
var textBuffer = Mock.Of<ITextBuffer>();
// Act
var result = projectPathProvider.TryGetProjectPath(textBuffer, out var filePath);
// Assert
Assert.False(result);
Assert.Null(filePath);
}
[Fact]
public void TryGetProjectPath_ReturnsTrueIfProject()
{
// Arrange
var expectedProjectPath = "/my/project/path.csproj";
var projectService = new Mock<TextBufferProjectService>();
projectService.Setup(service => service.GetHostProject(It.IsAny<ITextBuffer>()))
.Returns(new object());
projectService.Setup(service => service.GetProjectPath(It.IsAny<object>()))
.Returns(expectedProjectPath);
var projectPathProvider = new DefaultProjectPathProvider(projectService.Object);
var textBuffer = Mock.Of<ITextBuffer>();
// Act
var result = projectPathProvider.TryGetProjectPath(textBuffer, out var filePath);
// Assert
Assert.True(result);
Assert.Equal(expectedProjectPath, filePath);
}
}
}