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

View File

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