[Design] Create Template engine from project snapshot
This commit is contained in:
parent
12e61d75a7
commit
f23ff9452c
|
|
@ -3,10 +3,8 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Mvc1_X = Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X;
|
||||
using MvcLatest = Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
|
||||
|
|
@ -14,14 +12,21 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
internal class DefaultTemplateEngineFactoryService : RazorTemplateEngineFactoryService
|
||||
{
|
||||
private const string MvcAssemblyName = "Microsoft.AspNetCore.Mvc.Razor";
|
||||
private static readonly Version LatestSupportedMvc = new Version(2, 1, 0);
|
||||
private readonly static MvcExtensibilityConfiguration DefaultConfiguration = new MvcExtensibilityConfiguration(
|
||||
ProjectExtensibilityConfigurationKind.Fallback,
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Razor.Language", new Version("2.0.0.0"))),
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("2.0.0.0"))));
|
||||
|
||||
private readonly HostLanguageServices _services;
|
||||
private readonly ProjectSnapshotManager _projectManager;
|
||||
|
||||
public DefaultTemplateEngineFactoryService(HostLanguageServices services)
|
||||
public DefaultTemplateEngineFactoryService(ProjectSnapshotManager projectManager)
|
||||
{
|
||||
_services = services;
|
||||
if (projectManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
_projectManager = projectManager;
|
||||
}
|
||||
|
||||
public override RazorTemplateEngine Create(string projectPath, Action<IRazorEngineBuilder> configure)
|
||||
|
|
@ -31,9 +36,12 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
throw new ArgumentNullException(nameof(projectPath));
|
||||
}
|
||||
|
||||
// In 15.5 we expect projectPath to be a directory, NOT the path to the csproj.
|
||||
var project = FindProject(projectPath);
|
||||
var configuration = (project?.Configuration as MvcExtensibilityConfiguration) ?? DefaultConfiguration;
|
||||
|
||||
RazorEngine engine;
|
||||
var mvcVersion = GetMvcVersion(projectPath);
|
||||
if (mvcVersion?.Major == 1)
|
||||
if (configuration.RazorAssembly.Identity.Version.Major == 1)
|
||||
{
|
||||
engine = RazorEngine.CreateDesignTime(b =>
|
||||
{
|
||||
|
|
@ -41,7 +49,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
|
||||
Mvc1_X.RazorExtensions.Register(b);
|
||||
|
||||
if (mvcVersion?.Minor >= 1)
|
||||
if (configuration.MvcAssembly.Identity.Version.Minor >= 1)
|
||||
{
|
||||
Mvc1_X.RazorExtensions.RegisterViewComponentTagHelpers(b);
|
||||
}
|
||||
|
|
@ -53,12 +61,6 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
}
|
||||
else
|
||||
{
|
||||
if (mvcVersion?.Major != LatestSupportedMvc.Major)
|
||||
{
|
||||
// TODO: Log unknown Mvc version. Something like
|
||||
// Could not construct Razor engine for Mvc version '{mvcVersion}'. Falling back to Razor engine for Mvc '{LatestSupportedMvc}'.
|
||||
}
|
||||
|
||||
engine = RazorEngine.CreateDesignTime(b =>
|
||||
{
|
||||
configure?.Invoke(b);
|
||||
|
|
@ -72,28 +74,19 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
}
|
||||
}
|
||||
|
||||
private Version GetMvcVersion(string projectPath)
|
||||
private ProjectSnapshot FindProject(string directory)
|
||||
{
|
||||
var workspace = _services.WorkspaceServices.Workspace;
|
||||
directory = NormalizeDirectoryPath(directory);
|
||||
|
||||
var project = workspace.CurrentSolution.Projects.FirstOrDefault(p =>
|
||||
var projects = _projectManager.Projects;
|
||||
for (var i = 0; i < projects.Count; i++)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(p.FilePath);
|
||||
return string.Equals(
|
||||
NormalizeDirectoryPath(directory),
|
||||
NormalizeDirectoryPath(projectPath),
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
|
||||
if (project != null)
|
||||
{
|
||||
var compilation = CSharpCompilation.Create(project.AssemblyName).AddReferences(project.MetadataReferences);
|
||||
|
||||
foreach (var identity in compilation.ReferencedAssemblyNames)
|
||||
var project = projects[i];
|
||||
if (project.UnderlyingProject.FilePath != null)
|
||||
{
|
||||
if (identity.Name == MvcAssemblyName)
|
||||
if (string.Equals(directory, NormalizeDirectoryPath(Path.GetDirectoryName(project.UnderlyingProject.FilePath)), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return identity.Version;
|
||||
return project;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
|
|
@ -12,7 +13,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultTemplateEngineFactoryService(languageServices);
|
||||
return new DefaultTemplateEngineFactoryService(languageServices.GetRequiredService<ProjectSnapshotManager>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Inner = Microsoft.CodeAnalysis.Razor.RazorTemplateEngineFactoryService;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
|
|
@ -15,6 +17,38 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
[Export(typeof(RazorTemplateEngineFactoryService))]
|
||||
internal class LegacyTemplateEngineFactoryService : RazorTemplateEngineFactoryService
|
||||
{
|
||||
private readonly Inner _inner;
|
||||
private readonly Workspace _workspace;
|
||||
|
||||
[ImportingConstructor]
|
||||
public LegacyTemplateEngineFactoryService([Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
{
|
||||
if (workspace == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
_workspace = workspace;
|
||||
_inner = workspace.Services.GetLanguageServices(RazorLanguage.Name).GetRequiredService<Inner>();
|
||||
}
|
||||
|
||||
// internal for testing
|
||||
internal LegacyTemplateEngineFactoryService(Workspace workspace, Inner inner)
|
||||
{
|
||||
if (workspace == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
if (inner == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(inner));
|
||||
}
|
||||
|
||||
_workspace = workspace;
|
||||
_inner = inner;
|
||||
}
|
||||
|
||||
public override RazorTemplateEngine Create(string projectPath, Action<IRazorEngineBuilder> configure)
|
||||
{
|
||||
if (projectPath == null)
|
||||
|
|
@ -22,17 +56,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
throw new ArgumentNullException(nameof(projectPath));
|
||||
}
|
||||
|
||||
var engine = RazorEngine.CreateDesignTime(b =>
|
||||
{
|
||||
configure?.Invoke(b);
|
||||
|
||||
// For now we're hardcoded to use MVC's extensibility.
|
||||
RazorExtensions.Register(b);
|
||||
});
|
||||
|
||||
var templateEngine = new MvcRazorTemplateEngine(engine, RazorProject.Create(projectPath));
|
||||
templateEngine.Options.ImportsFileName = "_ViewImports.cshtml";
|
||||
return templateEngine;
|
||||
return _inner.Create(projectPath, configure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,18 +8,43 @@ using Microsoft.CodeAnalysis.Host;
|
|||
using Mvc1_X = Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X;
|
||||
using MvcLatest = Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Xunit;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public class DefaultTemplateEngineFactoryServiceTest
|
||||
{
|
||||
public DefaultTemplateEngineFactoryServiceTest()
|
||||
{
|
||||
Workspace = new AdhocWorkspace();
|
||||
|
||||
var info = ProjectInfo.Create(ProjectId.CreateNewId("Test"), VersionStamp.Default, "Test", "Test", LanguageNames.CSharp, filePath: "/TestPath/SomePath/Test.csproj");
|
||||
Project = Workspace.CurrentSolution.AddProject(info).GetProject(info.Id);
|
||||
}
|
||||
|
||||
// We don't actually look at the project, we rely on the ProjectStateManager
|
||||
public Project Project { get; }
|
||||
|
||||
public Workspace Workspace { get; }
|
||||
|
||||
[Fact]
|
||||
public void Create_CreatesDesignTimeTemplateEngine_ForLatest()
|
||||
{
|
||||
// Arrange
|
||||
var mvcReference = GetAssemblyMetadataReference("Microsoft.AspNetCore.Mvc.Razor", "2.0.0");
|
||||
var services = GetServices(mvcReference);
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(services);
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.ProjectAdded(Project);
|
||||
projectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(Project)
|
||||
{
|
||||
Configuration = new MvcExtensibilityConfiguration(
|
||||
ProjectExtensibilityConfigurationKind.ApproximateMatch,
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("2.0.0.0"))),
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("2.0.0.0")))),
|
||||
});
|
||||
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(projectManager);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
|
|
@ -38,9 +63,17 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
public void Create_CreatesDesignTimeTemplateEngine_ForVersion1_1()
|
||||
{
|
||||
// Arrange
|
||||
var mvcReference = GetAssemblyMetadataReference("Microsoft.AspNetCore.Mvc.Razor", "1.1.3");
|
||||
var services = GetServices(mvcReference);
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(services);
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.ProjectAdded(Project);
|
||||
projectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(Project)
|
||||
{
|
||||
Configuration = new MvcExtensibilityConfiguration(
|
||||
ProjectExtensibilityConfigurationKind.ApproximateMatch,
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("1.1.3.0"))),
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("1.1.3.0")))),
|
||||
});
|
||||
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(projectManager);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
|
|
@ -59,9 +92,17 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
public void Create_DoesNotSupportViewComponentTagHelpers_ForVersion1_0()
|
||||
{
|
||||
// Arrange
|
||||
var mvcReference = GetAssemblyMetadataReference("Microsoft.AspNetCore.Mvc.Razor", "1.0.0");
|
||||
var services = GetServices(mvcReference);
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(services);
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.ProjectAdded(Project);
|
||||
projectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(Project)
|
||||
{
|
||||
Configuration = new MvcExtensibilityConfiguration(
|
||||
ProjectExtensibilityConfigurationKind.ApproximateMatch,
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("1.0.0.0"))),
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("1.0.0.0")))),
|
||||
});
|
||||
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(projectManager);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
|
|
@ -76,12 +117,20 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_UnknownMvcVersion_UsesLatest()
|
||||
public void Create_HigherMvcVersion_UsesLatest()
|
||||
{
|
||||
// Arrange
|
||||
var mvcReference = GetAssemblyMetadataReference("Microsoft.AspNetCore.Mvc.Razor", "3.0.0");
|
||||
var services = GetServices(mvcReference);
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(services);
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.ProjectAdded(Project);
|
||||
projectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(Project)
|
||||
{
|
||||
Configuration = new MvcExtensibilityConfiguration(
|
||||
ProjectExtensibilityConfigurationKind.ApproximateMatch,
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("3.0.0.0"))),
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("3.0.0.0")))),
|
||||
});
|
||||
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(projectManager);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
|
|
@ -100,9 +149,9 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
public void Create_UnknownProjectPath_UsesLatest()
|
||||
{
|
||||
// Arrange
|
||||
var mvcReference = GetAssemblyMetadataReference("Microsoft.AspNetCore.Mvc.Razor", "1.1.0");
|
||||
var services = GetServices(mvcReference);
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(services);
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(projectManager);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/DifferentPath/", b =>
|
||||
|
|
@ -121,9 +170,10 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
public void Create_MvcReferenceNotFound_UsesLatest()
|
||||
{
|
||||
// Arrange
|
||||
var mvcReference = GetAssemblyMetadataReference("Microsoft.Something.Else", "1.0.0");
|
||||
var services = GetServices(mvcReference);
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(services);
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.ProjectAdded(Project);
|
||||
|
||||
var factoryService = new DefaultTemplateEngineFactoryService(projectManager);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/DifferentPath/", b =>
|
||||
|
|
@ -138,39 +188,22 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
||||
private HostLanguageServices GetServices(MetadataReference mvcReference)
|
||||
{
|
||||
var project = ProjectInfo
|
||||
.Create(ProjectId.CreateNewId(), VersionStamp.Default, "TestProject", "TestAssembly", LanguageNames.CSharp)
|
||||
.WithFilePath("/TestPath/SomePath/MyProject.csproj")
|
||||
.WithMetadataReferences(new[] { mvcReference });
|
||||
|
||||
var workspace = new AdhocWorkspace();
|
||||
workspace.AddProject(project);
|
||||
|
||||
return workspace.Services.GetLanguageServices(LanguageNames.CSharp);
|
||||
}
|
||||
|
||||
private MetadataReference GetAssemblyMetadataReference(string assemblyName, string version)
|
||||
{
|
||||
var code = $@"
|
||||
using System.Reflection;
|
||||
[assembly: AssemblyVersion(""{version}"")]
|
||||
";
|
||||
|
||||
var syntaxTree = CSharpSyntaxTree.ParseText(code);
|
||||
|
||||
var compilation = CSharpCompilation.Create(
|
||||
assemblyName,
|
||||
syntaxTrees: new[] { syntaxTree },
|
||||
references: new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) });
|
||||
|
||||
return compilation.ToMetadataReference();
|
||||
}
|
||||
|
||||
private class MyCoolNewFeature : IRazorEngineFeature
|
||||
{
|
||||
public RazorEngine Engine { get; set; }
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(Workspace workspace)
|
||||
: base(
|
||||
Mock.Of<ForegroundDispatcher>(),
|
||||
Mock.Of<ErrorReporter>(),
|
||||
Mock.Of<ProjectSnapshotWorker>(),
|
||||
Enumerable.Empty<ProjectSnapshotChangeTrigger>(),
|
||||
workspace)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue