Decouple tooling from MVC assemblies
This commit is contained in:
parent
5008c7803c
commit
0b777dad3f
|
|
@ -10,14 +10,12 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\src\Microsoft.VisualStudio.LanguageServices.Razor\RazorDiagnosticJsonConverter.cs">
|
||||
<Link>Shared\RazorDiagnosticJsonConverter.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\src\Microsoft.VisualStudio.LanguageServices.Razor\TagHelperDescriptorJsonConverter.cs">
|
||||
<Link>Shared\TagHelperDescriptorJsonConverter.cs</Link>
|
||||
<Compile Include="..\..\src\Microsoft.VisualStudio.LanguageServices.Razor\Serialization\*.cs">
|
||||
<Link>Serialization\%(FileName)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using System.IO;
|
|||
using System.Text;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Performance
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
// 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.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
||||
{
|
||||
internal class ExtensionInitializer : RazorExtensionInitializer
|
||||
{
|
||||
public override void Initialize(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (builder.Configuration.ConfigurationName == "MVC-1.0")
|
||||
{
|
||||
RazorExtensions.Register(builder);
|
||||
}
|
||||
else if (builder.Configuration.ConfigurationName == "MVC-1.1")
|
||||
{
|
||||
RazorExtensions.Register(builder);
|
||||
RazorExtensions.RegisterViewComponentTagHelpers(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,5 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
[assembly: ProvideRazorExtensionInitializer("MVC-1.0", typeof(ExtensionInitializer))]
|
||||
[assembly: ProvideRazorExtensionInitializer("MVC-1.1", typeof(ExtensionInitializer))]
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.Features.Add(new ViewComponentTagHelperDescriptorProvider());
|
||||
|
||||
builder.Features.Add(new ViewComponentTagHelperPass());
|
||||
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
|
||||
}
|
||||
|
|
@ -88,6 +90,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
|
||||
EnsureDesignTime(builder);
|
||||
|
||||
builder.Features.Add(new ViewComponentTagHelperDescriptorProvider());
|
||||
builder.Features.Add(new ViewComponentTagHelperPass());
|
||||
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,10 +24,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
var symbol = compilation.GetAssemblyOrModuleSymbol(reference) as IAssemblySymbol;
|
||||
if (symbol != null)
|
||||
{
|
||||
if (string.Equals(symbol.Identity.Name, ViewComponentTypes.Assembly, StringComparison.Ordinal) &&
|
||||
symbol.Identity.Version > ViewComponentTypes.AssemblyVersion)
|
||||
if (string.Equals(symbol.Identity.Name, ViewComponentTypes.Assembly, StringComparison.Ordinal))
|
||||
{
|
||||
enabled = true;
|
||||
enabled = symbol.Identity.Version >= ViewComponentTypes.AssemblyVersion;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
FunctionsDirective.Register(builder);
|
||||
InheritsDirective.Register(builder);
|
||||
SectionDirective.Register(builder);
|
||||
|
||||
|
||||
builder.Features.Add(new ViewComponentTagHelperDescriptorProvider());
|
||||
|
||||
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
|
||||
|
|
@ -61,6 +61,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
InheritsDirective.Register(builder);
|
||||
SectionDirective.Register(builder);
|
||||
|
||||
builder.Features.Add(new ViewComponentTagHelperDescriptorProvider());
|
||||
|
||||
builder.AddTargetExtension(new ViewComponentTagHelperTargetExtension());
|
||||
builder.AddTargetExtension(new TemplateTargetExtension()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,31 +10,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
{
|
||||
internal class ViewComponentTypeVisitor : SymbolVisitor
|
||||
{
|
||||
private static readonly Version SupportedVCTHMvcVersion = new Version(1, 1);
|
||||
|
||||
private readonly INamedTypeSymbol _viewComponentAttribute;
|
||||
private readonly INamedTypeSymbol _nonViewComponentAttribute;
|
||||
private readonly List<INamedTypeSymbol> _results;
|
||||
|
||||
public static ViewComponentTypeVisitor Create(Compilation compilation, List<INamedTypeSymbol> results)
|
||||
{
|
||||
var enabled = false;
|
||||
foreach (var reference in compilation.References)
|
||||
{
|
||||
var symbol = compilation.GetAssemblyOrModuleSymbol(reference) as IAssemblySymbol;
|
||||
if (symbol != null)
|
||||
{
|
||||
if (string.Equals(symbol.Identity.Name, ViewComponentTypes.Assembly, StringComparison.Ordinal) &&
|
||||
symbol.Identity.Version > ViewComponentTypes.AssemblyVersion)
|
||||
{
|
||||
enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var vcAttribute = enabled ? compilation.GetTypeByMetadataName(ViewComponentTypes.ViewComponentAttribute) : null;
|
||||
var nonVCAttribute = enabled ? compilation.GetTypeByMetadataName(ViewComponentTypes.NonViewComponentAttribute) : null;
|
||||
var vcAttribute = compilation.GetTypeByMetadataName(ViewComponentTypes.ViewComponentAttribute);
|
||||
var nonVCAttribute = compilation.GetTypeByMetadataName(ViewComponentTypes.NonViewComponentAttribute);
|
||||
return new ViewComponentTypeVisitor(vcAttribute, nonVCAttribute, results);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
// 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.Composition;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
[Export(typeof(IFallbackProjectEngineFactory))]
|
||||
internal class FallbackProjectEngineFactory : IFallbackProjectEngineFactory
|
||||
{
|
||||
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
if (fileSystem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileSystem));
|
||||
}
|
||||
|
||||
// This is a very basic implementation that will provide reasonable support without crashing.
|
||||
// If the user falls into this situation, ideally they can realize that something is wrong and take
|
||||
// action.
|
||||
//
|
||||
// This has no support for:
|
||||
// - Tag Helpers
|
||||
// - Imports
|
||||
// - Default Imports
|
||||
// - and will have a very limited set of directives
|
||||
return RazorProjectEngine.Create(configuration, fileSystem, configure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
// Used to create the 'fallback' project engine when we don't have a custom implementation.
|
||||
internal interface IFallbackProjectEngineFactory : IProjectEngineFactory
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
for (var i = 0; i< projects.Count; i++)
|
||||
{
|
||||
var project = projects[i];
|
||||
if (string.Equals(filePath, project.WorkspaceProject.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
if (FilePathComparer.Instance.Equals(filePath, project.FilePath))
|
||||
{
|
||||
return project;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Remote.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Editor.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -4,11 +4,20 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal abstract class RazorProjectEngineFactoryService : ILanguageService
|
||||
{
|
||||
public abstract RazorProjectEngine Create(string projectPath, Action<RazorProjectEngineBuilder> configure);
|
||||
public abstract IProjectEngineFactory FindFactory(ProjectSnapshot project);
|
||||
|
||||
public abstract IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project);
|
||||
|
||||
public abstract RazorProjectEngine Create(ProjectSnapshot project, Action<RazorProjectEngineBuilder> configure);
|
||||
|
||||
public abstract RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure);
|
||||
|
||||
public abstract RazorProjectEngine Create(string directoryPath, Action<RazorProjectEngineBuilder> configure);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
|
|
@ -8,6 +9,8 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
public sealed class TagHelperResolutionResult
|
||||
{
|
||||
internal static TagHelperResolutionResult Empty = new TagHelperResolutionResult(Array.Empty<TagHelperDescriptor>(), Array.Empty<RazorDiagnostic>());
|
||||
|
||||
public TagHelperResolutionResult(IReadOnlyList<TagHelperDescriptor> descriptors, IReadOnlyList<RazorDiagnostic> diagnostics)
|
||||
{
|
||||
Descriptors = descriptors;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,57 @@
|
|||
// 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.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal abstract class TagHelperResolver : ILanguageService
|
||||
{
|
||||
public abstract Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project, CancellationToken cancellationToken);
|
||||
public abstract Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default);
|
||||
|
||||
protected virtual async Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, RazorProjectEngine engine)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (engine == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(engine));
|
||||
}
|
||||
|
||||
if (project.WorkspaceProject == null)
|
||||
{
|
||||
return TagHelperResolutionResult.Empty;
|
||||
}
|
||||
|
||||
var providers = engine.Engine.Features.OfType<ITagHelperDescriptorProvider>().ToArray();
|
||||
if (providers.Length == 0)
|
||||
{
|
||||
return TagHelperResolutionResult.Empty;
|
||||
}
|
||||
|
||||
var results = new List<TagHelperDescriptor>();
|
||||
var context = TagHelperDescriptorProviderContext.Create(results);
|
||||
|
||||
var compilation = await project.WorkspaceProject.GetCompilationAsync().ConfigureAwait(false);
|
||||
context.SetCompilation(compilation);
|
||||
|
||||
for (var i = 0; i < providers.Length; i++)
|
||||
{
|
||||
var provider = providers[i];
|
||||
provider.Execute(context);
|
||||
}
|
||||
|
||||
return new TagHelperResolutionResult(results, Array.Empty<RazorDiagnostic>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +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 System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Remote.Razor
|
||||
{
|
||||
internal class DefaultTagHelperResolver : TagHelperResolver
|
||||
{
|
||||
public DefaultTagHelperResolver(bool designTime)
|
||||
{
|
||||
DesignTime = designTime;
|
||||
}
|
||||
|
||||
public bool DesignTime { get; }
|
||||
|
||||
private TagHelperResolutionResult GetTagHelpers(Compilation compilation)
|
||||
{
|
||||
var descriptors = new List<TagHelperDescriptor>();
|
||||
|
||||
var providers = new ITagHelperDescriptorProvider[]
|
||||
{
|
||||
new DefaultTagHelperDescriptorProvider() { DesignTime = DesignTime, },
|
||||
new ViewComponentTagHelperDescriptorProvider(),
|
||||
};
|
||||
|
||||
var results = new List<TagHelperDescriptor>();
|
||||
var context = TagHelperDescriptorProviderContext.Create(results);
|
||||
context.SetCompilation(compilation);
|
||||
|
||||
for (var i = 0; i < providers.Length; i++)
|
||||
{
|
||||
var provider = providers[i];
|
||||
provider.Execute(context);
|
||||
}
|
||||
|
||||
var diagnostics = new List<RazorDiagnostic>();
|
||||
var resolutionResult = new TagHelperResolutionResult(results, diagnostics);
|
||||
|
||||
return resolutionResult;
|
||||
}
|
||||
|
||||
public override async Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project, CancellationToken cancellationToken)
|
||||
{
|
||||
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
|
||||
return GetTagHelpers(compilation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,12 +7,13 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Microsoft.VisualStudio.LanguageServices.Razor\RazorDiagnosticJsonConverter.cs" />
|
||||
<Compile Include="..\Microsoft.VisualStudio.LanguageServices.Razor\Serialization\*.cs">
|
||||
<Link>Serialization\%(FileName)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.Language\Microsoft.AspNetCore.Razor.Language.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,34 +10,22 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Remote.Razor
|
||||
{
|
||||
internal class RazorLanguageService : ServiceHubServiceBase
|
||||
internal class RazorLanguageService : RazorServiceBase
|
||||
{
|
||||
public RazorLanguageService(Stream stream, IServiceProvider serviceProvider)
|
||||
: base(serviceProvider, stream)
|
||||
: base(stream, serviceProvider)
|
||||
{
|
||||
Rpc.JsonSerializer.Converters.Add(new RazorDiagnosticJsonConverter());
|
||||
|
||||
// Due to this issue - https://github.com/dotnet/roslyn/issues/16900#issuecomment-277378950
|
||||
// We need to manually start the RPC connection. Otherwise we'd be opting ourselves into
|
||||
// race condition prone call paths.
|
||||
Rpc.StartListening();
|
||||
}
|
||||
|
||||
public async Task<TagHelperResolutionResult> GetTagHelpersAsync(Guid projectIdBytes, string projectDebugName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public async Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshotHandle projectHandle, string factoryTypeName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var projectId = ProjectId.CreateFromSerialized(projectIdBytes, projectDebugName);
|
||||
var project = await GetProjectSnapshotAsync(projectHandle, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var solution = await GetSolutionAsync(cancellationToken).ConfigureAwait(false);
|
||||
var project = solution.GetProject(projectId);
|
||||
|
||||
var resolver = new DefaultTagHelperResolver(designTime: true);
|
||||
var result = await resolver.GetTagHelpersAsync(project, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
return await RazorServices.TagHelperResolver.GetTagHelpersAsync(project, factoryTypeName, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IEnumerable<DirectiveDescriptor>> GetDirectivesAsync(Guid projectIdBytes, string projectDebugName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
// 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.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Remote.Razor
|
||||
{
|
||||
internal abstract class RazorServiceBase : ServiceHubServiceBase
|
||||
{
|
||||
public RazorServiceBase(Stream stream, IServiceProvider serviceProvider)
|
||||
: base(serviceProvider, stream)
|
||||
{
|
||||
RazorServices = new RazorServices();
|
||||
|
||||
Rpc.JsonSerializer.Converters.RegisterRazorConverters();
|
||||
|
||||
// Due to this issue - https://github.com/dotnet/roslyn/issues/16900#issuecomment-277378950
|
||||
// We need to manually start the RPC connection. Otherwise we'd be opting ourselves into
|
||||
// race condition prone call paths.
|
||||
Rpc.StartListening();
|
||||
}
|
||||
|
||||
protected RazorServices RazorServices { get; }
|
||||
|
||||
protected virtual async Task<ProjectSnapshot> GetProjectSnapshotAsync(ProjectSnapshotHandle projectHandle, CancellationToken cancellationToken)
|
||||
{
|
||||
if (projectHandle == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectHandle));
|
||||
}
|
||||
|
||||
var solution = await GetSolutionAsync(cancellationToken).ConfigureAwait(false);
|
||||
var workspaceProject = solution.GetProject(projectHandle.WorkspaceProjectId);
|
||||
|
||||
return new SerializedProjectSnapshot(projectHandle.FilePath, projectHandle.Configuration, workspaceProject);
|
||||
}
|
||||
|
||||
private class SerializedProjectSnapshot : ProjectSnapshot
|
||||
{
|
||||
public SerializedProjectSnapshot(string filePath, RazorConfiguration configuration, Project workspaceProject)
|
||||
{
|
||||
FilePath = filePath;
|
||||
Configuration = configuration;
|
||||
WorkspaceProject = workspaceProject;
|
||||
|
||||
IsInitialized = true;
|
||||
Version = VersionStamp.Default;
|
||||
}
|
||||
|
||||
public override RazorConfiguration Configuration { get; }
|
||||
|
||||
public override string FilePath { get; }
|
||||
|
||||
public override bool IsInitialized { get; }
|
||||
|
||||
public override VersionStamp Version { get; }
|
||||
|
||||
public override Project WorkspaceProject { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
// Provides access to Razor language and workspace services that are avialable in the OOP host.
|
||||
//
|
||||
// Since we don't have access to the workspace we only have access to some specific things
|
||||
// that we can construct directly.
|
||||
internal class RazorServices
|
||||
{
|
||||
public RazorServices()
|
||||
{
|
||||
FallbackProjectEngineFactory = new FallbackProjectEngineFactory();
|
||||
TagHelperResolver = new RemoteTagHelperResolver(FallbackProjectEngineFactory);
|
||||
}
|
||||
|
||||
public IFallbackProjectEngineFactory FallbackProjectEngineFactory { get; }
|
||||
|
||||
public RemoteTagHelperResolver TagHelperResolver { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
// 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal class RemoteTagHelperResolver : TagHelperResolver
|
||||
{
|
||||
private readonly static RazorConfiguration DefaultConfiguration = FallbackRazorConfiguration.MVC_2_0;
|
||||
|
||||
private readonly IFallbackProjectEngineFactory _fallbackFactory;
|
||||
|
||||
public RemoteTagHelperResolver(IFallbackProjectEngineFactory fallbackFactory)
|
||||
{
|
||||
if (fallbackFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fallbackFactory));
|
||||
}
|
||||
|
||||
_fallbackFactory = fallbackFactory;
|
||||
}
|
||||
|
||||
public override Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, string factoryTypeName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (project.Configuration == null || project.WorkspaceProject == null)
|
||||
{
|
||||
return Task.FromResult(TagHelperResolutionResult.Empty);
|
||||
}
|
||||
|
||||
var engine = CreateProjectEngine(project, factoryTypeName);
|
||||
return GetTagHelpersAsync(project, engine);
|
||||
}
|
||||
|
||||
internal RazorProjectEngine CreateProjectEngine(ProjectSnapshot project, string factoryTypeName)
|
||||
{
|
||||
// This section is really similar to the code DefaultProjectEngineFactoryService
|
||||
// but with a few differences that are significant in the remote scenario
|
||||
//
|
||||
// Most notably, we are going to find the Tag Helpers using a compilation, and we have
|
||||
// no editor settings.
|
||||
Action<RazorProjectEngineBuilder> configure = (b) =>
|
||||
{
|
||||
b.Features.Add(new DefaultTagHelperDescriptorProvider() { DesignTime = true });
|
||||
};
|
||||
|
||||
// The default configuration currently matches MVC-2.0. Beyond MVC-2.0 we added SDK support for
|
||||
// properly detecting project versions, so that's a good version to assume when we can't find a
|
||||
// configuration.
|
||||
var configuration = project?.Configuration ?? DefaultConfiguration;
|
||||
|
||||
// If there's no factory to handle the configuration then fall back to a very basic configuration.
|
||||
//
|
||||
// This will stop a crash from happening in this case (misconfigured project), but will still make
|
||||
// it obvious to the user that something is wrong.
|
||||
var factory = CreateFactory(configuration, factoryTypeName) ?? _fallbackFactory;
|
||||
return factory.Create(configuration, RazorProjectFileSystem.Empty, configure);
|
||||
}
|
||||
|
||||
private IProjectEngineFactory CreateFactory(RazorConfiguration configuration, string factoryTypeName)
|
||||
{
|
||||
if (factoryTypeName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (IProjectEngineFactory)Activator.CreateInstance(Type.GetType(factoryTypeName, throwOnError: true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,6 @@ using System.IO;
|
|||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Mvc1_X = Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X;
|
||||
using MvcLatest = Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
|
|
@ -16,58 +14,127 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
private readonly static RazorConfiguration DefaultConfiguration = FallbackRazorConfiguration.MVC_2_0;
|
||||
|
||||
private readonly ProjectSnapshotManager _projectManager;
|
||||
private readonly IFallbackProjectEngineFactory _defaultFactory;
|
||||
private readonly Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] _customFactories;
|
||||
|
||||
public DefaultProjectEngineFactoryService(ProjectSnapshotManager projectManager)
|
||||
public DefaultProjectEngineFactoryService(
|
||||
ProjectSnapshotManager projectManager,
|
||||
IFallbackProjectEngineFactory defaultFactory,
|
||||
Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] customFactories)
|
||||
{
|
||||
if (projectManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
if (defaultFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(defaultFactory));
|
||||
}
|
||||
|
||||
if (customFactories == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(customFactories));
|
||||
}
|
||||
|
||||
_projectManager = projectManager;
|
||||
_defaultFactory = defaultFactory;
|
||||
_customFactories = customFactories;
|
||||
}
|
||||
|
||||
public override RazorProjectEngine Create(string projectPath, Action<RazorProjectEngineBuilder> configure)
|
||||
public override IProjectEngineFactory FindFactory(ProjectSnapshot project)
|
||||
{
|
||||
if (projectPath == null)
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectPath));
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
// In 15.5 we expect projectPath to be a directory, NOT the path to the csproj.
|
||||
var project = FindProject(projectPath);
|
||||
var configuration = project?.Configuration ?? DefaultConfiguration;
|
||||
var fileSystem = RazorProjectFileSystem.Create(projectPath);
|
||||
|
||||
RazorProjectEngine projectEngine;
|
||||
if (configuration.LanguageVersion.Major == 1)
|
||||
{
|
||||
projectEngine = RazorProjectEngine.Create(configuration, fileSystem, b =>
|
||||
{
|
||||
configure?.Invoke(b);
|
||||
|
||||
Mvc1_X.RazorExtensions.Register(b);
|
||||
|
||||
if (configuration.LanguageVersion.Minor >= 1)
|
||||
{
|
||||
Mvc1_X.RazorExtensions.RegisterViewComponentTagHelpers(b);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
projectEngine = RazorProjectEngine.Create(configuration, fileSystem, b =>
|
||||
{
|
||||
configure?.Invoke(b);
|
||||
|
||||
MvcLatest.RazorExtensions.Register(b);
|
||||
});
|
||||
}
|
||||
|
||||
return projectEngine;
|
||||
return SelectFactory(project.Configuration ?? DefaultConfiguration, requireSerializable: false);
|
||||
}
|
||||
|
||||
private ProjectSnapshot FindProject(string directory)
|
||||
public override IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
return SelectFactory(project.Configuration ?? DefaultConfiguration, requireSerializable: true);
|
||||
}
|
||||
|
||||
public override RazorProjectEngine Create(ProjectSnapshot project, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
return CreateCore(project, RazorProjectFileSystem.Create(Path.GetDirectoryName(project.FilePath)), configure);
|
||||
}
|
||||
|
||||
public override RazorProjectEngine Create(string directoryPath, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
if (directoryPath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(directoryPath));
|
||||
}
|
||||
|
||||
var project = FindProjectByDirectory(directoryPath);
|
||||
return CreateCore(project, RazorProjectFileSystem.Create(directoryPath), configure);
|
||||
}
|
||||
|
||||
public override RazorProjectEngine Create(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (fileSystem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileSystem));
|
||||
}
|
||||
|
||||
return CreateCore(project, fileSystem, configure);
|
||||
}
|
||||
|
||||
private RazorProjectEngine CreateCore(ProjectSnapshot project, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
// When we're running in the editor, the editor provides a configure delegate that will include
|
||||
// the editor settings and tag helpers.
|
||||
//
|
||||
// This service is only used in process in Visual Studio, and any other callers should provide these
|
||||
// things also.
|
||||
configure = configure ?? ((b) => { });
|
||||
|
||||
// The default configuration currently matches MVC-2.0. Beyond MVC-2.0 we added SDK support for
|
||||
// properly detecting project versions, so that's a good version to assume when we can't find a
|
||||
// configuration.
|
||||
var configuration = project?.Configuration ?? DefaultConfiguration;
|
||||
|
||||
// If there's no factory to handle the configuration then fall back to a very basic configuration.
|
||||
//
|
||||
// This will stop a crash from happening in this case (misconfigured project), but will still make
|
||||
// it obvious to the user that something is wrong.
|
||||
var factory = SelectFactory(configuration) ?? _defaultFactory;
|
||||
return factory.Create(configuration, fileSystem, configure);
|
||||
}
|
||||
|
||||
private IProjectEngineFactory SelectFactory(RazorConfiguration configuration, bool requireSerializable = false)
|
||||
{
|
||||
for (var i = 0; i < _customFactories.Length; i++)
|
||||
{
|
||||
var factory = _customFactories[i];
|
||||
if (string.Equals(configuration.ConfigurationName, factory.Metadata.ConfigurationName))
|
||||
{
|
||||
return requireSerializable && !factory.Metadata.SupportsSerialization ? null : factory.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ProjectSnapshot FindProjectByDirectory(string directory)
|
||||
{
|
||||
directory = NormalizeDirectoryPath(directory);
|
||||
|
||||
|
|
@ -75,9 +142,9 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
for (var i = 0; i < projects.Count; i++)
|
||||
{
|
||||
var project = projects[i];
|
||||
if (project.WorkspaceProject?.FilePath != null)
|
||||
if (project.FilePath != null)
|
||||
{
|
||||
if (string.Equals(directory, NormalizeDirectoryPath(Path.GetDirectoryName(project.WorkspaceProject.FilePath)), StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(directory, NormalizeDirectoryPath(Path.GetDirectoryName(project.FilePath)), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return project;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
// 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.Composition;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
|
@ -11,9 +15,34 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
[ExportLanguageServiceFactory(typeof(RazorProjectEngineFactoryService), RazorLanguage.Name, ServiceLayer.Default)]
|
||||
internal class DefaultProjectEngineFactoryServiceFactory : ILanguageServiceFactory
|
||||
{
|
||||
private readonly Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] _customFactories;
|
||||
private readonly IFallbackProjectEngineFactory _fallbackFactory;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultProjectEngineFactoryServiceFactory(
|
||||
IFallbackProjectEngineFactory fallbackFactory,
|
||||
[ImportMany] IEnumerable<Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>> customFactories)
|
||||
{
|
||||
if (fallbackFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fallbackFactory));
|
||||
}
|
||||
|
||||
if (customFactories == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(customFactories));
|
||||
}
|
||||
|
||||
_fallbackFactory = fallbackFactory;
|
||||
_customFactories = customFactories.ToArray();
|
||||
}
|
||||
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultProjectEngineFactoryService(languageServices.GetRequiredService<ProjectSnapshotManager>());
|
||||
return new DefaultProjectEngineFactoryService(
|
||||
languageServices.GetRequiredService<ProjectSnapshotManager>(),
|
||||
_fallbackFactory,
|
||||
_customFactories);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,59 +2,45 @@
|
|||
// 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
internal class DefaultTagHelperResolver : TagHelperResolver
|
||||
{
|
||||
// Hack for testability. The view component visitor will normally just no op if we're not referencing
|
||||
// an appropriate version of MVC.
|
||||
internal bool ForceEnableViewComponentDiscovery { get; set; }
|
||||
private readonly RazorProjectEngineFactoryService _engineFactory;
|
||||
|
||||
public override async Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project, CancellationToken cancellationToken)
|
||||
public DefaultTagHelperResolver(RazorProjectEngineFactoryService engineFactory)
|
||||
{
|
||||
if (engineFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(engineFactory));
|
||||
}
|
||||
|
||||
_engineFactory = engineFactory;
|
||||
}
|
||||
|
||||
public override Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
|
||||
var result = GetTagHelpers(compilation);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal TagHelperResolutionResult GetTagHelpers(Compilation compilation)
|
||||
{
|
||||
var descriptors = new List<TagHelperDescriptor>();
|
||||
|
||||
var providers = new ITagHelperDescriptorProvider[]
|
||||
if (project.Configuration == null || project.WorkspaceProject == null)
|
||||
{
|
||||
new DefaultTagHelperDescriptorProvider() { DesignTime = true, },
|
||||
new ViewComponentTagHelperDescriptorProvider() { ForceEnabled = ForceEnableViewComponentDiscovery },
|
||||
};
|
||||
|
||||
var results = new List<TagHelperDescriptor>();
|
||||
var context = TagHelperDescriptorProviderContext.Create(results);
|
||||
context.SetCompilation(compilation);
|
||||
|
||||
for (var i = 0; i < providers.Length; i++)
|
||||
{
|
||||
var provider = providers[i];
|
||||
provider.Execute(context);
|
||||
return Task.FromResult(TagHelperResolutionResult.Empty);
|
||||
}
|
||||
|
||||
var diagnostics = new List<RazorDiagnostic>();
|
||||
var resolutionResult = new TagHelperResolutionResult(results, diagnostics);
|
||||
|
||||
return resolutionResult;
|
||||
var engine = _engineFactory.Create(project, RazorProjectFileSystem.Empty, b =>
|
||||
{
|
||||
b.Features.Add(new DefaultTagHelperDescriptorProvider() { DesignTime = true, });
|
||||
});
|
||||
return GetTagHelpersAsync(project, engine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultTagHelperResolver();
|
||||
return new DefaultTagHelperResolver(languageServices.GetRequiredService<RazorProjectEngineFactoryService>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.Reflection;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
[ExportCustomProjectEngineFactory("MVC-1.0", SupportsSerialization = true)]
|
||||
internal class LegacyProjectEngineFactory_1_0 : IProjectEngineFactory
|
||||
{
|
||||
private const string AssemblyName = "Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X";
|
||||
|
||||
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
// Rewrite the assembly name into a full name just like this one, but with the name of the MVC design time assembly.
|
||||
var assemblyName = new AssemblyName(typeof(LegacyProjectEngineFactory_1_0).Assembly.FullName);
|
||||
assemblyName.Name = AssemblyName;
|
||||
|
||||
var extension = new AssemblyExtension(configuration.ConfigurationName, Assembly.Load(assemblyName));
|
||||
var initializer = extension.CreateInitializer();
|
||||
|
||||
return RazorProjectEngine.Create(configuration, fileSystem, b =>
|
||||
{
|
||||
initializer.Initialize(b);
|
||||
configure?.Invoke(b);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.Reflection;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
[ExportCustomProjectEngineFactory("MVC-1.1", SupportsSerialization = true)]
|
||||
internal class LegacyProjectEngineFactory_1_1 : IProjectEngineFactory
|
||||
{
|
||||
private const string AssemblyName = "Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X";
|
||||
|
||||
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
// Rewrite the assembly name into a full name just like this one, but with the name of the MVC design time assembly.
|
||||
var assemblyName = new AssemblyName(typeof(LegacyProjectEngineFactory_1_1).Assembly.FullName);
|
||||
assemblyName.Name = AssemblyName;
|
||||
|
||||
var extension = new AssemblyExtension(configuration.ConfigurationName, Assembly.Load(assemblyName));
|
||||
var initializer = extension.CreateInitializer();
|
||||
|
||||
return RazorProjectEngine.Create(configuration, fileSystem, b =>
|
||||
{
|
||||
initializer.Initialize(b);
|
||||
configure?.Invoke(b);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// 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.Reflection;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
[ExportCustomProjectEngineFactory("MVC-2.0", SupportsSerialization = true)]
|
||||
internal class LegacyProjectEngineFactory_2_0 : IProjectEngineFactory
|
||||
{
|
||||
private const string AssemblyName = "Microsoft.AspNetCore.Mvc.Razor.Extensions";
|
||||
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
// Rewrite the assembly name into a full name just like this one, but with the name of the MVC design time assembly.
|
||||
var assemblyName = new AssemblyName(typeof(LegacyProjectEngineFactory_2_0).Assembly.FullName);
|
||||
assemblyName.Name = AssemblyName;
|
||||
|
||||
var extension = new AssemblyExtension(configuration.ConfigurationName, Assembly.Load(assemblyName));
|
||||
var initializer = extension.CreateInitializer();
|
||||
|
||||
return RazorProjectEngine.Create(configuration, fileSystem, b =>
|
||||
{
|
||||
initializer.Initialize(b);
|
||||
configure?.Invoke(b);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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.Reflection;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
// Currently we provide a fixed configuration for 2.1, but this is a point-in-time issue. We plan
|
||||
// to make the 2.1 configuration more flexible and less hardcoded.
|
||||
[ExportCustomProjectEngineFactory("MVC-2.1", SupportsSerialization = true)]
|
||||
internal class LegacyProjectEngineFactory_2_1 : IProjectEngineFactory
|
||||
{
|
||||
private const string AssemblyName = "Microsoft.AspNetCore.Mvc.Razor.Extensions";
|
||||
public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
|
||||
{
|
||||
// Rewrite the assembly name into a full name just like this one, but with the name of the MVC design time assembly.
|
||||
var assemblyName = new AssemblyName(typeof(LegacyProjectEngineFactory_2_1).Assembly.FullName);
|
||||
assemblyName.Name = AssemblyName;
|
||||
|
||||
var extension = new AssemblyExtension(configuration.ConfigurationName, Assembly.Load(assemblyName));
|
||||
var initializer = extension.CreateInitializer();
|
||||
|
||||
return RazorProjectEngine.Create(configuration, fileSystem, b =>
|
||||
{
|
||||
initializer.Initialize(b);
|
||||
configure?.Invoke(b);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,8 +14,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
// 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 System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
|
|
@ -14,23 +15,42 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
// use TagHelperResolver.
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
[Export(typeof(ITagHelperResolver))]
|
||||
internal class LegacyTagHelperResolver : OOPTagHelperResolver, ITagHelperResolver
|
||||
internal class LegacyTagHelperResolver : ITagHelperResolver
|
||||
{
|
||||
private readonly Workspace _workspace;
|
||||
|
||||
[ImportingConstructor]
|
||||
public LegacyTagHelperResolver(
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
: base(workspace)
|
||||
public LegacyTagHelperResolver([Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
{
|
||||
if (workspace == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
_workspace = workspace;
|
||||
}
|
||||
|
||||
public Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(project));
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
return base.GetTagHelpersAsync(project, CancellationToken.None);
|
||||
if (project.FilePath == null)
|
||||
{
|
||||
return Task.FromResult(TagHelperResolutionResult.Empty);
|
||||
}
|
||||
|
||||
var projectManager = _workspace.Services.GetLanguageServices(RazorLanguage.Name).GetRequiredService<ProjectSnapshotManager>();
|
||||
var projectSnapshot = projectManager.GetProjectWithFilePath(project.FilePath);
|
||||
if (projectSnapshot == null)
|
||||
{
|
||||
return Task.FromResult(TagHelperResolutionResult.Empty);
|
||||
}
|
||||
|
||||
var resolver = _workspace.Services.GetLanguageServices(RazorLanguage.Name).GetRequiredService<TagHelperResolver>();
|
||||
return resolver.GetTagHelpersAsync(projectSnapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
|
@ -15,53 +16,67 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
internal class OOPTagHelperResolver : TagHelperResolver
|
||||
{
|
||||
private readonly DefaultTagHelperResolver _defaultResolver;
|
||||
private readonly RazorProjectEngineFactoryService _engineFactory;
|
||||
private readonly ErrorReporter _errorReporter;
|
||||
private readonly Workspace _workspace;
|
||||
|
||||
public OOPTagHelperResolver(Workspace workspace)
|
||||
public OOPTagHelperResolver(RazorProjectEngineFactoryService engineFactory, ErrorReporter errorReporter, Workspace workspace)
|
||||
{
|
||||
if (engineFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(engineFactory));
|
||||
}
|
||||
|
||||
if (errorReporter == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(errorReporter));
|
||||
}
|
||||
|
||||
if (workspace == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
_engineFactory = engineFactory;
|
||||
_errorReporter = errorReporter;
|
||||
_workspace = workspace;
|
||||
_defaultResolver = new DefaultTagHelperResolver();
|
||||
|
||||
_defaultResolver = new DefaultTagHelperResolver(_engineFactory);
|
||||
}
|
||||
|
||||
public override async Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project, CancellationToken cancellationToken)
|
||||
public override async Task<TagHelperResolutionResult> GetTagHelpersAsync(ProjectSnapshot project, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (project.Configuration == null || project.WorkspaceProject == null)
|
||||
{
|
||||
return TagHelperResolutionResult.Empty;
|
||||
}
|
||||
|
||||
// Not every custom factory supports the OOP host. Our priority system should work like this:
|
||||
//
|
||||
// 1. Use custom factory out of process
|
||||
// 2. Use custom factory in process
|
||||
// 3. Use fallback factory in process
|
||||
//
|
||||
// Calling into RazorTemplateEngineFactoryService.Create will accomplish #2 and #3 in one step.
|
||||
var factory = _engineFactory.FindSerializableFactory(project);
|
||||
|
||||
try
|
||||
{
|
||||
TagHelperResolutionResult result = null;
|
||||
|
||||
// We're being defensive here because the OOP host can return null for the client/session/operation
|
||||
// when it's disconnected (user stops the process).
|
||||
var client = await RazorLanguageServiceClientFactory.CreateAsync(_workspace, cancellationToken);
|
||||
if (client != null)
|
||||
if (factory != null)
|
||||
{
|
||||
using (var session = await client.CreateSessionAsync(project.Solution))
|
||||
{
|
||||
if (session != null)
|
||||
{
|
||||
var jsonObject = await session.InvokeAsync<JObject>(
|
||||
"GetTagHelpersAsync",
|
||||
new object[] { project.Id.Id, "Foo", },
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
result = GetTagHelperResolutionResult(jsonObject);
|
||||
}
|
||||
}
|
||||
result = await ResolveTagHelpersOutOfProcessAsync(factory, project);
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
// Was unable to get tag helpers OOP, fallback to default behavior.
|
||||
result = await _defaultResolver.GetTagHelpersAsync(project, cancellationToken);
|
||||
result = await ResolveTagHelpersInProcessAsync(project);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -76,11 +91,61 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
}
|
||||
}
|
||||
|
||||
private TagHelperResolutionResult GetTagHelperResolutionResult(JObject jsonObject)
|
||||
protected virtual async Task<TagHelperResolutionResult> ResolveTagHelpersOutOfProcessAsync(IProjectEngineFactory factory, ProjectSnapshot project)
|
||||
{
|
||||
// We're being overly defensive here because the OOP host can return null for the client/session/operation
|
||||
// when it's disconnected (user stops the process).
|
||||
//
|
||||
// This will change in the future to an easier to consume API but for VS RTM this is what we have.
|
||||
try
|
||||
{
|
||||
var client = await RazorLanguageServiceClientFactory.CreateAsync(_workspace, CancellationToken.None);
|
||||
if (client != null)
|
||||
{
|
||||
using (var session = await client.CreateSessionAsync(project.WorkspaceProject.Solution))
|
||||
{
|
||||
if (session != null)
|
||||
{
|
||||
var args = new object[]
|
||||
{
|
||||
Serialize(project),
|
||||
factory == null ? null : factory.GetType().AssemblyQualifiedName,
|
||||
};
|
||||
|
||||
var json = await session.InvokeAsync<JObject>("GetTagHelpersAsync", args, CancellationToken.None).ConfigureAwait(false);
|
||||
return Deserialize(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// We silence exceptions from the OOP host because we don't want to bring down VS for an OOP failure.
|
||||
// We will retry all failures in process anyway, so if there's a real problem that isn't unique to OOP
|
||||
// then it will report a crash in VS.
|
||||
_errorReporter.ReportError(ex, project);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected virtual Task<TagHelperResolutionResult> ResolveTagHelpersInProcessAsync(ProjectSnapshot project)
|
||||
{
|
||||
return _defaultResolver.GetTagHelpersAsync(project);
|
||||
}
|
||||
|
||||
private JObject Serialize(ProjectSnapshot snapshot)
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
serializer.Converters.Add(TagHelperDescriptorJsonConverter.Instance);
|
||||
serializer.Converters.Add(RazorDiagnosticJsonConverter.Instance);
|
||||
serializer.Converters.RegisterRazorConverters();
|
||||
|
||||
return JObject.FromObject(snapshot, serializer);
|
||||
}
|
||||
|
||||
private TagHelperResolutionResult Deserialize(JObject jsonObject)
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
serializer.Converters.RegisterRazorConverters();
|
||||
|
||||
using (var reader = jsonObject.CreateReader())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,8 +14,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
var workspace = languageServices.WorkspaceServices.Workspace;
|
||||
return new OOPTagHelperResolver(workspace);
|
||||
return new OOPTagHelperResolver(
|
||||
languageServices.GetRequiredService<RazorProjectEngineFactoryService>(),
|
||||
languageServices.WorkspaceServices.GetRequiredService<ErrorReporter>(),
|
||||
languageServices.WorkspaceServices.Workspace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// 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.LanguageServices.Razor.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal static class JsonConverterCollectionExtensions
|
||||
{
|
||||
public static void RegisterRazorConverters(this JsonConverterCollection collection)
|
||||
{
|
||||
if (collection == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(collection));
|
||||
}
|
||||
|
||||
collection.Add(TagHelperDescriptorJsonConverter.Instance);
|
||||
collection.Add(RazorDiagnosticJsonConverter.Instance);
|
||||
collection.Add(RazorExtensionJsonConverter.Instance);
|
||||
collection.Add(RazorConfigurationJsonConverter.Instance);
|
||||
collection.Add(ProjectSnapshotJsonConverter.Instance);
|
||||
collection.Add(ProjectSnapshotHandleJsonConverter.Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// 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.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal sealed class ProjectSnapshotHandle
|
||||
{
|
||||
public ProjectSnapshotHandle(string filePath, RazorConfiguration configuration, ProjectId workspaceProjectId)
|
||||
{
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
Configuration = configuration;
|
||||
WorkspaceProjectId = workspaceProjectId;
|
||||
}
|
||||
|
||||
public RazorConfiguration Configuration { get; }
|
||||
|
||||
public string FilePath { get; }
|
||||
|
||||
public ProjectId WorkspaceProjectId { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
// 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.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Serialization
|
||||
{
|
||||
internal class ProjectSnapshotHandleJsonConverter : JsonConverter
|
||||
{
|
||||
public static readonly ProjectSnapshotHandleJsonConverter Instance = new ProjectSnapshotHandleJsonConverter();
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(ProjectSnapshotHandle).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType != JsonToken.StartObject)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var obj = JObject.Load(reader);
|
||||
var filePath = obj[nameof(ProjectSnapshotHandle.FilePath)].Value<string>();
|
||||
var configuration = obj[nameof(ProjectSnapshotHandle.Configuration)].ToObject<RazorConfiguration>(serializer);
|
||||
|
||||
var id = obj[nameof(ProjectSnapshotHandle.WorkspaceProjectId)].Value<string>();
|
||||
var workspaceProjectId = id == null ? null : ProjectId.CreateFromSerialized(Guid.Parse(id));
|
||||
|
||||
return new ProjectSnapshotHandle(filePath, configuration, workspaceProjectId);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var handle = (ProjectSnapshotHandle)value;
|
||||
|
||||
writer.WriteStartObject();
|
||||
|
||||
writer.WritePropertyName(nameof(ProjectSnapshotHandle.FilePath));
|
||||
writer.WriteValue(handle.FilePath);
|
||||
|
||||
if (handle.Configuration == null)
|
||||
{
|
||||
writer.WritePropertyName(nameof(ProjectSnapshotHandle.Configuration));
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WritePropertyName(nameof(ProjectSnapshotHandle.Configuration));
|
||||
serializer.Serialize(writer, handle.Configuration);
|
||||
}
|
||||
|
||||
if (handle.WorkspaceProjectId == null)
|
||||
{
|
||||
writer.WritePropertyName(nameof(ProjectSnapshotHandle.WorkspaceProjectId));
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WritePropertyName(nameof(ProjectSnapshotHandle.WorkspaceProjectId));
|
||||
writer.WriteValue(handle.WorkspaceProjectId.Id);
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// 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.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Serialization
|
||||
{
|
||||
// We can't truly serialize a snapshot because it has access to a Workspace Project\
|
||||
//
|
||||
// Instead we serialize to a ProjectSnapshotHandle and then use that to re-create the snapshot
|
||||
// inside the remote host.
|
||||
internal class ProjectSnapshotJsonConverter : JsonConverter
|
||||
{
|
||||
public static readonly ProjectSnapshotJsonConverter Instance = new ProjectSnapshotJsonConverter();
|
||||
|
||||
public override bool CanRead => false;
|
||||
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(ProjectSnapshot).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var project = (ProjectSnapshot)value;
|
||||
var handle = new ProjectSnapshotHandle(project.FilePath, project.Configuration, project.WorkspaceProject?.Id);
|
||||
|
||||
ProjectSnapshotHandleJsonConverter.Instance.WriteJson(writer, handle, serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// 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.AspNetCore.Razor.Language;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Serialization
|
||||
{
|
||||
internal class RazorConfigurationJsonConverter : JsonConverter
|
||||
{
|
||||
public static readonly RazorConfigurationJsonConverter Instance = new RazorConfigurationJsonConverter();
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(RazorConfiguration).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType != JsonToken.StartObject)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var obj = JObject.Load(reader);
|
||||
var configurationName = obj[nameof(RazorConfiguration.ConfigurationName)].Value<string>();
|
||||
var languageVersion = obj[nameof(RazorConfiguration.LanguageVersion)].Value<string>();
|
||||
var extensions = obj[nameof(RazorConfiguration.Extensions)].ToObject<RazorExtension[]>(serializer);
|
||||
|
||||
return RazorConfiguration.Create(RazorLanguageVersion.Parse(languageVersion), configurationName, extensions);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var configuration = (RazorConfiguration)value;
|
||||
|
||||
writer.WriteStartObject();
|
||||
|
||||
writer.WritePropertyName(nameof(RazorConfiguration.ConfigurationName));
|
||||
writer.WriteValue(configuration.ConfigurationName);
|
||||
|
||||
writer.WritePropertyName(nameof(RazorConfiguration.LanguageVersion));
|
||||
writer.WriteValue(configuration.LanguageVersion.ToString());
|
||||
|
||||
writer.WritePropertyName(nameof(RazorConfiguration.Extensions));
|
||||
serializer.Serialize(writer, configuration.Extensions);
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Razor.Language;
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Serialization
|
||||
{
|
||||
internal class RazorDiagnosticJsonConverter : JsonConverter
|
||||
{
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// 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.AspNetCore.Razor.Language;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Serialization
|
||||
{
|
||||
internal class RazorExtensionJsonConverter : JsonConverter
|
||||
{
|
||||
public static readonly RazorExtensionJsonConverter Instance = new RazorExtensionJsonConverter();
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(RazorExtension).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType != JsonToken.StartObject)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var obj = JObject.Load(reader);
|
||||
var extensionName = obj[nameof(RazorExtension.ExtensionName)].Value<string>();
|
||||
|
||||
return new SerializedRazorExtension(extensionName);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var extension = (RazorExtension)value;
|
||||
|
||||
writer.WriteStartObject();
|
||||
|
||||
writer.WritePropertyName(nameof(RazorExtension.ExtensionName));
|
||||
writer.WriteValue(extension.ExtensionName);
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Serialization
|
||||
{
|
||||
internal class SerializedRazorExtension : RazorExtension
|
||||
{
|
||||
public SerializedRazorExtension(string extensionName)
|
||||
{
|
||||
if (extensionName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(extensionName));
|
||||
}
|
||||
|
||||
ExtensionName = extensionName;
|
||||
}
|
||||
|
||||
public override string ExtensionName { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Razor.Language;
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Serialization
|
||||
{
|
||||
internal class TagHelperDescriptorJsonConverter : JsonConverter
|
||||
{
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
|
@ -31,28 +32,62 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
HostProject_For_1_0 = new HostProject("/TestPath/SomePath/Test.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
HostProject_For_1_1 = new HostProject("/TestPath/SomePath/Test.csproj", FallbackRazorConfiguration.MVC_1_1);
|
||||
HostProject_For_2_0 = new HostProject("/TestPath/SomePath/Test.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||
|
||||
HostProject_For_2_1 = new HostProject(
|
||||
"/TestPath/SomePath/Test.csproj",
|
||||
new ProjectSystemRazorConfiguration(RazorLanguageVersion.Version_2_1, "MVC-2.1", Array.Empty<RazorExtension>()));
|
||||
HostProject_For_UnknownConfiguration = new HostProject(
|
||||
"/TestPath/SomePath/Test.csproj",
|
||||
new ProjectSystemRazorConfiguration(RazorLanguageVersion.Version_2_1, "Blazor-0.1", Array.Empty<RazorExtension>()));
|
||||
|
||||
|
||||
CustomFactories = new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[]
|
||||
{
|
||||
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
|
||||
() => new LegacyProjectEngineFactory_1_0(),
|
||||
typeof(LegacyProjectEngineFactory_1_0).GetCustomAttribute<ExportCustomProjectEngineFactoryAttribute>()),
|
||||
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
|
||||
() => new LegacyProjectEngineFactory_1_1(),
|
||||
typeof(LegacyProjectEngineFactory_1_1).GetCustomAttribute<ExportCustomProjectEngineFactoryAttribute>()),
|
||||
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
|
||||
() => new LegacyProjectEngineFactory_2_0(),
|
||||
typeof(LegacyProjectEngineFactory_2_0).GetCustomAttribute<ExportCustomProjectEngineFactoryAttribute>()),
|
||||
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
|
||||
() => new LegacyProjectEngineFactory_2_1(),
|
||||
typeof(LegacyProjectEngineFactory_2_1).GetCustomAttribute<ExportCustomProjectEngineFactoryAttribute>()),
|
||||
};
|
||||
|
||||
FallbackFactory = new FallbackProjectEngineFactory();
|
||||
}
|
||||
|
||||
private Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] CustomFactories { get; }
|
||||
|
||||
private IFallbackProjectEngineFactory FallbackFactory { get; }
|
||||
|
||||
private HostProject HostProject_For_1_0 { get; }
|
||||
|
||||
private HostProject HostProject_For_1_1 { get; }
|
||||
|
||||
private HostProject HostProject_For_2_0 { get; }
|
||||
|
||||
private HostProject HostProject_For_2_1 { get; }
|
||||
|
||||
private HostProject HostProject_For_UnknownConfiguration { get; }
|
||||
|
||||
// We don't actually look at the project, we rely on the ProjectStateManager
|
||||
private Project WorkspaceProject { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[Fact]
|
||||
public void Create_CreatesTemplateEngine_ForLatest()
|
||||
public void Create_CreatesDesignTimeTemplateEngine_ForVersion2_1()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.HostProjectAdded(HostProject_For_2_0);
|
||||
projectManager.HostProjectAdded(HostProject_For_2_1);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager);
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
|
|
@ -62,6 +97,30 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
// Assert
|
||||
Assert.Single(engine.Engine.Features.OfType<MyCoolNewFeature>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperDescriptorProvider>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.MvcViewDocumentClassifierPass>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_CreatesDesignTimeTemplateEngine_ForVersion2_0()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.HostProjectAdded(HostProject_For_2_0);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
{
|
||||
b.Features.Add(new MyCoolNewFeature());
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Single(engine.Engine.Features.OfType<MyCoolNewFeature>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperDescriptorProvider>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.MvcViewDocumentClassifierPass>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
|
@ -74,7 +133,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
projectManager.HostProjectAdded(HostProject_For_1_1);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager);
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
|
|
@ -84,6 +143,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
// Assert
|
||||
Assert.Single(engine.Engine.Features.OfType<MyCoolNewFeature>());
|
||||
Assert.Single(engine.Engine.Features.OfType<Mvc1_X.ViewComponentTagHelperDescriptorProvider>());
|
||||
Assert.Single(engine.Engine.Features.OfType<Mvc1_X.MvcViewDocumentClassifierPass>());
|
||||
Assert.Single(engine.Engine.Features.OfType<Mvc1_X.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
|
@ -96,7 +156,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
projectManager.HostProjectAdded(HostProject_For_1_0);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager);
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
|
|
@ -106,17 +166,18 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
// Assert
|
||||
Assert.Single(engine.Engine.Features.OfType<MyCoolNewFeature>());
|
||||
Assert.Single(engine.Engine.Features.OfType<Mvc1_X.MvcViewDocumentClassifierPass>());
|
||||
Assert.Empty(engine.Engine.Features.OfType<Mvc1_X.ViewComponentTagHelperPass>());
|
||||
Assert.Empty(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperDescriptorProvider>());
|
||||
Assert.Empty(engine.Engine.Features.OfType<MvcLatest.MvcViewDocumentClassifierPass>());
|
||||
Assert.Empty(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_UnknownProjectPath_UsesLatest()
|
||||
public void Create_UnknownProject_UsesVersion2_0()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager);
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/DifferentPath/", b =>
|
||||
|
|
@ -126,30 +187,33 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
// Assert
|
||||
Assert.Single(engine.Engine.Features.OfType<MyCoolNewFeature>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperDescriptorProvider>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.MvcViewDocumentClassifierPass>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_MvcReferenceNotFound_UsesLatest()
|
||||
public void Create_ForUnknownConfiguration_UsesFallbackFactory()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.HostProjectAdded(HostProject_For_2_0);
|
||||
projectManager.HostProjectAdded(HostProject_For_UnknownConfiguration);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager);
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/DifferentPath/", b =>
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
{
|
||||
b.Features.Add(new MyCoolNewFeature());
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Single(engine.Engine.Features.OfType<MyCoolNewFeature>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.MvcViewDocumentClassifierPass>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
||||
Assert.Empty(engine.Engine.Features.OfType<DefaultTagHelperDescriptorProvider>());
|
||||
Assert.Empty(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperDescriptorProvider>());
|
||||
Assert.Empty(engine.Engine.Features.OfType<MvcLatest.MvcViewDocumentClassifierPass>());
|
||||
Assert.Empty(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
||||
private class MyCoolNewFeature : IRazorEngineFeature
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.VisualStudio.Editor.Razor.Test.Common\Microsoft.VisualStudio.Editor.Razor.Test.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.VisualStudio.LanguageServices.Razor\Microsoft.VisualStudio.LanguageServices.Razor.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Runtime\Microsoft.AspNetCore.Razor.Runtime.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor\Microsoft.AspNetCore.Razor.csproj" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
// 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.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
{
|
||||
public class OOPTagHelperResolverTest
|
||||
{
|
||||
public OOPTagHelperResolverTest()
|
||||
{
|
||||
HostProject_For_2_0 = new HostProject("Test.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||
HostProject_For_NonSerializableConfiguration = new HostProject(
|
||||
"Test.csproj",
|
||||
new ProjectSystemRazorConfiguration(RazorLanguageVersion.Version_2_1, "Blazor-0.1", Array.Empty<RazorExtension>()));
|
||||
|
||||
CustomFactories = new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[]
|
||||
{
|
||||
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
|
||||
() => new LegacyProjectEngineFactory_2_0(),
|
||||
typeof(LegacyProjectEngineFactory_2_0).GetCustomAttribute<ExportCustomProjectEngineFactoryAttribute>()),
|
||||
|
||||
// We don't really use this factory, we just use it to ensure that the call is going to go out of process.
|
||||
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>(
|
||||
() => new LegacyProjectEngineFactory_2_1(),
|
||||
new ExportCustomProjectEngineFactoryAttribute("Blazor-0.1") { SupportsSerialization = false, }),
|
||||
};
|
||||
|
||||
FallbackFactory = new FallbackProjectEngineFactory();
|
||||
|
||||
Workspace = new AdhocWorkspace();
|
||||
|
||||
var info = ProjectInfo.Create(ProjectId.CreateNewId("Test"), VersionStamp.Default, "Test", "Test", LanguageNames.CSharp, filePath: "Test.csproj");
|
||||
WorkspaceProject = Workspace.CurrentSolution.AddProject(info).GetProject(info.Id);
|
||||
|
||||
ErrorReporter = new DefaultErrorReporter();
|
||||
ProjectManager = new TestProjectSnapshotManager(Workspace);
|
||||
EngineFactory = new DefaultProjectEngineFactoryService(ProjectManager, FallbackFactory, CustomFactories);
|
||||
}
|
||||
|
||||
private ErrorReporter ErrorReporter { get; }
|
||||
|
||||
private RazorProjectEngineFactoryService EngineFactory { get; }
|
||||
|
||||
private Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] CustomFactories { get; }
|
||||
|
||||
private IFallbackProjectEngineFactory FallbackFactory { get; }
|
||||
|
||||
private HostProject HostProject_For_2_0 { get; }
|
||||
|
||||
private HostProject HostProject_For_NonSerializableConfiguration { get; }
|
||||
|
||||
private ProjectSnapshotManagerBase ProjectManager { get; }
|
||||
|
||||
private Project WorkspaceProject { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[Fact]
|
||||
public async Task GetTagHelpersAsync_WithNonInitializedProject_Noops()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject_For_2_0);
|
||||
|
||||
var project = ProjectManager.GetProjectWithFilePath("Test.csproj");
|
||||
|
||||
var resolver = new TestTagHelperResolver(EngineFactory, ErrorReporter, Workspace);
|
||||
|
||||
var result = await resolver.GetTagHelpersAsync(project);
|
||||
|
||||
// Assert
|
||||
Assert.Same(TagHelperResolutionResult.Empty, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetTagHelpersAsync_WithSerializableCustomFactory_GoesOutOfProcess()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject_For_2_0);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var project = ProjectManager.GetProjectWithFilePath("Test.csproj");
|
||||
|
||||
var resolver = new TestTagHelperResolver(EngineFactory, ErrorReporter, Workspace)
|
||||
{
|
||||
OnResolveOutOfProcess = (f, p) =>
|
||||
{
|
||||
Assert.Same(CustomFactories[0].Value, f);
|
||||
Assert.Same(project, p);
|
||||
|
||||
return Task.FromResult(TagHelperResolutionResult.Empty);
|
||||
},
|
||||
};
|
||||
|
||||
var result = await resolver.GetTagHelpersAsync(project);
|
||||
|
||||
// Assert
|
||||
Assert.Same(TagHelperResolutionResult.Empty, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetTagHelpersAsync_WithNonSerializableCustomFactory_StaysInProcess()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject_For_NonSerializableConfiguration);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var project = ProjectManager.GetProjectWithFilePath("Test.csproj");
|
||||
|
||||
var resolver = new TestTagHelperResolver(EngineFactory, ErrorReporter, Workspace)
|
||||
{
|
||||
OnResolveInProcess = (p) =>
|
||||
{
|
||||
Assert.Same(project, p);
|
||||
|
||||
return Task.FromResult(TagHelperResolutionResult.Empty);
|
||||
},
|
||||
};
|
||||
|
||||
var result = await resolver.GetTagHelpersAsync(project);
|
||||
|
||||
// Assert
|
||||
Assert.Same(TagHelperResolutionResult.Empty, result);
|
||||
|
||||
}
|
||||
|
||||
private class TestTagHelperResolver : OOPTagHelperResolver
|
||||
{
|
||||
public TestTagHelperResolver(RazorProjectEngineFactoryService engineFactory, ErrorReporter errorReporter, Workspace workspace)
|
||||
: base(engineFactory, errorReporter, workspace)
|
||||
{
|
||||
}
|
||||
|
||||
public Func<IProjectEngineFactory, ProjectSnapshot, Task<TagHelperResolutionResult>> OnResolveOutOfProcess { get; set; }
|
||||
|
||||
public Func<ProjectSnapshot, Task<TagHelperResolutionResult>> OnResolveInProcess { get; set; }
|
||||
|
||||
protected override Task<TagHelperResolutionResult> ResolveTagHelpersOutOfProcessAsync(IProjectEngineFactory factory, ProjectSnapshot project)
|
||||
{
|
||||
Assert.NotNull(OnResolveOutOfProcess);
|
||||
return OnResolveOutOfProcess(factory, project);
|
||||
}
|
||||
|
||||
protected override Task<TagHelperResolutionResult> ResolveTagHelpersInProcessAsync(ProjectSnapshot project)
|
||||
{
|
||||
Assert.NotNull(OnResolveInProcess);
|
||||
return OnResolveInProcess(project);
|
||||
}
|
||||
}
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(Workspace workspace)
|
||||
: base(
|
||||
Mock.Of<ForegroundDispatcher>(),
|
||||
Mock.Of<ErrorReporter>(),
|
||||
Mock.Of<ProjectSnapshotWorker>(),
|
||||
Enumerable.Empty<ProjectSnapshotChangeTrigger>(),
|
||||
workspace)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Serialization
|
||||
{
|
||||
public class ProjectSnapshotHandleSerializationTest
|
||||
{
|
||||
public ProjectSnapshotHandleSerializationTest()
|
||||
{
|
||||
var converters = new JsonConverterCollection();
|
||||
converters.RegisterRazorConverters();
|
||||
Converters = converters.ToArray();
|
||||
}
|
||||
|
||||
public JsonConverter[] Converters { get; }
|
||||
|
||||
[Fact]
|
||||
public void ProjectSnapshotHandleJsonConverter_Serialization_CanKindaRoundTrip()
|
||||
{
|
||||
// Arrange
|
||||
var snapshot = new ProjectSnapshotHandle(
|
||||
"Test.csproj",
|
||||
new ProjectSystemRazorConfiguration(
|
||||
RazorLanguageVersion.Version_1_1,
|
||||
"Test",
|
||||
new[]
|
||||
{
|
||||
new ProjectSystemRazorExtension("Test-Extension1"),
|
||||
new ProjectSystemRazorExtension("Test-Extension2"),
|
||||
}),
|
||||
ProjectId.CreateFromSerialized(Guid.NewGuid(), "Test"));
|
||||
|
||||
// Act
|
||||
var json = JsonConvert.SerializeObject(snapshot, Converters);
|
||||
var obj = JsonConvert.DeserializeObject<ProjectSnapshotHandle>(json, Converters);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(snapshot.FilePath, obj.FilePath);
|
||||
Assert.Equal(snapshot.Configuration.ConfigurationName, obj.Configuration.ConfigurationName);
|
||||
Assert.Collection(
|
||||
snapshot.Configuration.Extensions,
|
||||
e => Assert.Equal("Test-Extension1", e.ExtensionName),
|
||||
e => Assert.Equal("Test-Extension2", e.ExtensionName));
|
||||
Assert.Equal(snapshot.Configuration.LanguageVersion, obj.Configuration.LanguageVersion);
|
||||
Assert.Equal(snapshot.WorkspaceProjectId.Id, obj.WorkspaceProjectId.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectSnapshotHandleJsonConverter_SerializationWithNulls_CanKindaRoundTrip()
|
||||
{
|
||||
// Arrange
|
||||
var snapshot = new ProjectSnapshotHandle("Test.csproj", null, null);
|
||||
|
||||
// Act
|
||||
var json = JsonConvert.SerializeObject(snapshot, Converters);
|
||||
var obj = JsonConvert.DeserializeObject<ProjectSnapshotHandle>(json, Converters);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(snapshot.FilePath, obj.FilePath);
|
||||
Assert.Null(obj.Configuration);
|
||||
Assert.Null(obj.WorkspaceProjectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Serialization
|
||||
{
|
||||
public class RazorConfigurationSerializationTest
|
||||
{
|
||||
public RazorConfigurationSerializationTest()
|
||||
{
|
||||
var converters = new JsonConverterCollection();
|
||||
converters.RegisterRazorConverters();
|
||||
Converters = converters.ToArray();
|
||||
}
|
||||
|
||||
public JsonConverter[] Converters { get; }
|
||||
|
||||
[Fact]
|
||||
public void RazorConfigurationJsonConverter_Serialization_CanRoundTrip()
|
||||
{
|
||||
// Arrange
|
||||
var configuration = new ProjectSystemRazorConfiguration(
|
||||
RazorLanguageVersion.Version_1_1,
|
||||
"Test",
|
||||
new[]
|
||||
{
|
||||
new ProjectSystemRazorExtension("Test-Extension1"),
|
||||
new ProjectSystemRazorExtension("Test-Extension2"),
|
||||
});
|
||||
|
||||
// Act
|
||||
var json = JsonConvert.SerializeObject(configuration, Converters);
|
||||
var obj = JsonConvert.DeserializeObject<RazorConfiguration>(json, Converters);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(configuration.ConfigurationName, obj.ConfigurationName);
|
||||
Assert.Collection(
|
||||
configuration.Extensions,
|
||||
e => Assert.Equal("Test-Extension1", e.ExtensionName),
|
||||
e => Assert.Equal("Test-Extension2", e.ExtensionName));
|
||||
Assert.Equal(configuration.LanguageVersion, obj.LanguageVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Serialization
|
||||
{
|
||||
public class RazorExtensionSerializationTest
|
||||
{
|
||||
public RazorExtensionSerializationTest()
|
||||
{
|
||||
var converters = new JsonConverterCollection();
|
||||
converters.RegisterRazorConverters();
|
||||
Converters = converters.ToArray();
|
||||
}
|
||||
|
||||
public JsonConverter[] Converters { get; }
|
||||
|
||||
[Fact]
|
||||
public void RazorExensionJsonConverter_Serialization_CanRoundTrip()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new ProjectSystemRazorExtension("Test");
|
||||
|
||||
// Act
|
||||
var json = JsonConvert.SerializeObject(extension, Converters);
|
||||
var obj = JsonConvert.DeserializeObject<RazorExtension>(json, Converters);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(extension.ExtensionName, obj.ExtensionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -177,7 +177,8 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
.Select(reference => reference.Display)
|
||||
.Select(filter => Path.GetFileNameWithoutExtension(filter));
|
||||
var projectFilters = project.AllProjectReferences.Select(filter => solution.GetProject(filter.ProjectId).AssemblyName);
|
||||
var resolutionResult = await _tagHelperResolver.GetTagHelpersAsync(project, CancellationToken.None);
|
||||
|
||||
var resolutionResult = await _tagHelperResolver.GetTagHelpersAsync(projectViewModel.Snapshot.Project);
|
||||
|
||||
var files = GetCshtmlDocuments(project);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue