Merge branch 'rel/vs15.7' into dev
# Conflicts: # build/dependencies.props # src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshot.cs # src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotManager.cs # src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotWorker.cs # src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/DefaultProjectSnapshotWorkerFactory.cs # src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectSnapshot.cs # src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectSnapshotUpdateContext.cs # src/Microsoft.VisualStudio.Editor.Razor/DefaultVisualStudioDocumentTracker.cs # src/Microsoft.VisualStudio.LanguageServices.Razor/Legacy/LegacyTagHelperResolver.cs # src/Microsoft.VisualStudio.LanguageServices.Razor/Microsoft.VisualStudio.LanguageServices.Razor.csproj # src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectSystem/Rules/RazorConfiguration.cs # src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectSystem/Rules/RazorExtension.cs # src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectSystem/Rules/RazorGeneral.cs # src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectSystem/Rules/RazorProjectProperties.cs # test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/ProjectSystem/DefaultProjectSnapshotTest.cs # test/Microsoft.VisualStudio.Editor.Razor.Test/Microsoft.VisualStudio.Editor.Razor.Test.csproj
This commit is contained in:
commit
5c456fbd04
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -70,7 +70,10 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<LanguageServiceAssembly Include="$(LanguageServiceOutputPath)%(LanguageServiceAssemblyNames.Identity)" />
|
||||
<LanguageServiceExtensionAssembly Include="$(RepositoryRoot)src\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X\bin\$(Configuration)\net46\Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.dll" />
|
||||
<LanguageServiceExtensionAssembly Include="$(RepositoryRoot)src\Microsoft.AspNetCore.Mvc.Razor.Extensions\bin\$(Configuration)\net46\Microsoft.AspNetCore.Mvc.Razor.Extensions.dll" />
|
||||
<LanguageServiceAssembly Include="$(LanguageServiceOutputPath)%(LanguageServiceAssemblyNames.Identity)" Condition="Exists('$(LanguageServiceOutputPath)%(LanguageServiceAssemblyNames.Identity)')" />
|
||||
<LanguageServiceAssembly Include="%(LanguageServiceExtensionAssembly.Identity)" />
|
||||
<LanguageServicePDB Include="%(LanguageServiceAssembly.RootDir)%(Directory)%(FileName).pdb" Condition="Exists('%(LanguageServiceAssembly.RootDir)%(Directory)%(FileName).pdb')" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
<VSIX_MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>
|
||||
<VSIX_MicrosoftCodeAnalysisRemoteRazorServiceHubPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftCodeAnalysisRemoteRazorServiceHubPackageVersion>
|
||||
<VSIX_MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>
|
||||
<VSIX_MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>
|
||||
<VSIX_MicrosoftVisualStudioLanguageServicesPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftVisualStudioLanguageServicesPackageVersion>
|
||||
<VSIX_MicrosoftVisualStudioLanguageServicesRazorRemoteClientPackageVersion>2.7.0-beta3-62512-06</VSIX_MicrosoftVisualStudioLanguageServicesRazorRemoteClientPackageVersion>
|
||||
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@ using System.Linq;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public sealed class RazorConfiguration
|
||||
public abstract class RazorConfiguration
|
||||
{
|
||||
public static readonly RazorConfiguration Default = new RazorConfiguration(
|
||||
public static readonly RazorConfiguration Default = new DefaultRazorConfiguration(
|
||||
RazorLanguageVersion.Latest,
|
||||
"unnamed",
|
||||
Array.Empty<RazorExtension>());
|
||||
|
||||
public RazorConfiguration(
|
||||
RazorLanguageVersion languageVersion,
|
||||
public static RazorConfiguration Create(
|
||||
RazorLanguageVersion languageVersion,
|
||||
string configurationName,
|
||||
IEnumerable<RazorExtension> extensions)
|
||||
{
|
||||
|
|
@ -34,15 +34,32 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
throw new ArgumentNullException(nameof(extensions));
|
||||
}
|
||||
|
||||
LanguageVersion = languageVersion;
|
||||
ConfigurationName = configurationName;
|
||||
Extensions = extensions.ToArray();
|
||||
return new DefaultRazorConfiguration(languageVersion, configurationName, extensions.ToArray());
|
||||
}
|
||||
|
||||
public string ConfigurationName { get; }
|
||||
public abstract string ConfigurationName { get; }
|
||||
|
||||
public IReadOnlyList<RazorExtension> Extensions { get; }
|
||||
public abstract IReadOnlyList<RazorExtension> Extensions { get; }
|
||||
|
||||
public RazorLanguageVersion LanguageVersion { get; }
|
||||
public abstract RazorLanguageVersion LanguageVersion { get; }
|
||||
|
||||
private class DefaultRazorConfiguration : RazorConfiguration
|
||||
{
|
||||
public DefaultRazorConfiguration(
|
||||
RazorLanguageVersion languageVersion,
|
||||
string configurationName,
|
||||
RazorExtension[] extensions)
|
||||
{
|
||||
LanguageVersion = languageVersion;
|
||||
ConfigurationName = configurationName;
|
||||
Extensions = extensions;
|
||||
}
|
||||
|
||||
public override string ConfigurationName { get; }
|
||||
|
||||
public override IReadOnlyList<RazorExtension> Extensions { get; }
|
||||
|
||||
public override RazorLanguageVersion LanguageVersion { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Razor.Language;
|
|||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
|
|
@ -115,7 +115,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
}
|
||||
|
||||
var version = RazorLanguageVersion.Parse(Version.Value());
|
||||
var configuration = new RazorConfiguration(version, Configuration.Value(), extensions);
|
||||
var configuration = RazorConfiguration.Create(version, Configuration.Value(), extensions);
|
||||
|
||||
var result = ExecuteCore(
|
||||
configuration: configuration,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using System.IO;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tools
|
||||
|
|
@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
}
|
||||
|
||||
var version = RazorLanguageVersion.Parse(Version.Value());
|
||||
var configuration = new RazorConfiguration(version, Configuration.Value(), extensions);
|
||||
var configuration = RazorConfiguration.Create(version, Configuration.Value(), extensions);
|
||||
|
||||
var result = ExecuteCore(
|
||||
configuration: configuration,
|
||||
|
|
@ -151,7 +151,7 @@ namespace Microsoft.AspNetCore.Razor.Tools
|
|||
GetVirtualRazorProjectSystem(inputItems),
|
||||
RazorProjectFileSystem.Create(projectDirectory),
|
||||
});
|
||||
|
||||
|
||||
var engine = RazorProjectEngine.Create(configuration, compositeFileSystem, b =>
|
||||
{
|
||||
b.Features.Add(new StaticTagHelperFeature() { TagHelpers = tagHelpers, });
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Microsoft.VisualStudio.LanguageServices.Razor\RazorDiagnosticJsonConverter.cs">
|
||||
<Link>Shared\RazorDiagnosticJsonConverter.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Microsoft.VisualStudio.LanguageServices.Razor\TagHelperDescriptorJsonConverter.cs">
|
||||
<Compile Include="..\Microsoft.VisualStudio.LanguageServices.Razor\Serialization\TagHelperDescriptorJsonConverter.cs">
|
||||
<Link>Shared\TagHelperDescriptorJsonConverter.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Microsoft.VisualStudio.LanguageServices.Razor\Serialization\RazorDiagnosticJsonConverter.cs">
|
||||
<Link>Shared\RazorDiagnosticJsonConverter.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
|
|
@ -9,11 +10,31 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
public override void ReportError(Exception exception)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, Project project)
|
||||
public override void ReportError(Exception exception, ProjectSnapshot project)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, Project workspaceProject)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,16 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal abstract class ErrorReporter : IWorkspaceService
|
||||
{
|
||||
public abstract void ReportError(Exception exception);
|
||||
|
||||
public abstract void ReportError(Exception exception, ProjectSnapshot project);
|
||||
|
||||
public abstract void ReportError(Exception exception, Project project);
|
||||
public abstract void ReportError(Exception exception, Project workspaceProject);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,142 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
// This is hardcoded for now. A more complete design would fan out to a list of providers.
|
||||
internal class DefaultProjectExtensibilityConfigurationFactory : ProjectExtensibilityConfigurationFactory
|
||||
{
|
||||
private const string MvcAssemblyName = "Microsoft.AspNetCore.Mvc.Razor";
|
||||
private const string RazorV1AssemblyName = "Microsoft.AspNetCore.Razor";
|
||||
private const string RazorV2AssemblyName = "Microsoft.AspNetCore.Razor.Language";
|
||||
|
||||
// Using MaxValue here so that we ignore patch and build numbers. We only want to compare major/minor.
|
||||
private static readonly Version MaxSupportedRazorVersion = new Version(2, 0, Int32.MaxValue, Int32.MaxValue);
|
||||
private static readonly Version MaxSupportedMvcVersion = new Version(2, 0, Int32.MaxValue, Int32.MaxValue);
|
||||
|
||||
private static readonly Version DefaultRazorVersion = new Version(2, 0, 0, 0);
|
||||
private static readonly Version DefaultMvcVersion = new Version(2, 0, 0, 0);
|
||||
|
||||
public async override Task<ProjectExtensibilityConfiguration> GetConfigurationAsync(Project project, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
var compilation = await project.GetCompilationAsync(cancellationToken);
|
||||
return GetConfiguration(compilation.ReferencedAssemblyNames);
|
||||
}
|
||||
|
||||
// internal/separate for testing.
|
||||
internal ProjectExtensibilityConfiguration GetConfiguration(IEnumerable<AssemblyIdentity> references)
|
||||
{
|
||||
// Avoiding ToDictionary here because we don't want a crash if there is a duplicate name.
|
||||
var assemblies = new Dictionary<string, AssemblyIdentity>();
|
||||
foreach (var assembly in references)
|
||||
{
|
||||
assemblies[assembly.Name] = assembly;
|
||||
}
|
||||
|
||||
// First we look for the V2+ Razor Assembly. If we find this then its version is the correct Razor version.
|
||||
AssemblyIdentity razorAssembly;
|
||||
if (assemblies.TryGetValue(RazorV2AssemblyName, out razorAssembly))
|
||||
{
|
||||
if (razorAssembly.Version == null || razorAssembly.Version > MaxSupportedRazorVersion)
|
||||
{
|
||||
// This is a newer Razor version than we know, treat it as a fallback case.
|
||||
razorAssembly = null;
|
||||
}
|
||||
}
|
||||
else if (assemblies.TryGetValue(RazorV1AssemblyName, out razorAssembly))
|
||||
{
|
||||
// This assembly only counts as the 'Razor' assembly if it's a version lower than 2.0.0.
|
||||
if (razorAssembly.Version == null || razorAssembly.Version >= new Version(2, 0, 0, 0))
|
||||
{
|
||||
razorAssembly = null;
|
||||
}
|
||||
}
|
||||
|
||||
AssemblyIdentity mvcAssembly;
|
||||
if (assemblies.TryGetValue(MvcAssemblyName, out mvcAssembly))
|
||||
{
|
||||
if (mvcAssembly.Version == null || mvcAssembly.Version > MaxSupportedMvcVersion)
|
||||
{
|
||||
// This is a newer MVC version than we know, treat it as a fallback case.
|
||||
mvcAssembly = null;
|
||||
}
|
||||
}
|
||||
|
||||
RazorLanguageVersion languageVersion = null;
|
||||
if (razorAssembly != null && mvcAssembly != null)
|
||||
{
|
||||
languageVersion = GetLanguageVersion(razorAssembly);
|
||||
|
||||
// This means we've definitely found a supported Razor version and an MVC version.
|
||||
return new MvcExtensibilityConfiguration(
|
||||
languageVersion,
|
||||
ProjectExtensibilityConfigurationKind.ApproximateMatch,
|
||||
new ProjectExtensibilityAssembly(razorAssembly),
|
||||
new ProjectExtensibilityAssembly(mvcAssembly));
|
||||
}
|
||||
|
||||
// If we get here it means we didn't find everything, so we have to guess.
|
||||
if (razorAssembly == null || razorAssembly.Version == null)
|
||||
{
|
||||
razorAssembly = new AssemblyIdentity(RazorV2AssemblyName, DefaultRazorVersion);
|
||||
}
|
||||
|
||||
if (mvcAssembly == null || mvcAssembly.Version == null)
|
||||
{
|
||||
mvcAssembly = new AssemblyIdentity(MvcAssemblyName, DefaultMvcVersion);
|
||||
}
|
||||
|
||||
if (languageVersion == null)
|
||||
{
|
||||
languageVersion = GetLanguageVersion(razorAssembly);
|
||||
}
|
||||
|
||||
return new MvcExtensibilityConfiguration(
|
||||
languageVersion,
|
||||
ProjectExtensibilityConfigurationKind.Fallback,
|
||||
new ProjectExtensibilityAssembly(razorAssembly),
|
||||
new ProjectExtensibilityAssembly(mvcAssembly));
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static RazorLanguageVersion GetLanguageVersion(AssemblyIdentity razorAssembly)
|
||||
{
|
||||
// This is inferred from the assembly for now, the Razor language version will eventually flow from MSBuild.
|
||||
|
||||
var razorAssemblyVersion = razorAssembly.Version;
|
||||
if (razorAssemblyVersion.Major == 1)
|
||||
{
|
||||
if (razorAssemblyVersion.Minor >= 1)
|
||||
{
|
||||
return RazorLanguageVersion.Version_1_1;
|
||||
}
|
||||
|
||||
return RazorLanguageVersion.Version_1_0;
|
||||
}
|
||||
|
||||
if (razorAssemblyVersion.Major == 2)
|
||||
{
|
||||
if (razorAssemblyVersion.Minor >= 1)
|
||||
{
|
||||
return RazorLanguageVersion.Version_2_1;
|
||||
}
|
||||
|
||||
return RazorLanguageVersion.Version_2_0;
|
||||
}
|
||||
|
||||
// Couldn't determine version based off of assembly, fallback to latest.
|
||||
return RazorLanguageVersion.Latest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +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;
|
||||
using System.Composition;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
[Shared]
|
||||
[ExportLanguageServiceFactory(typeof(ProjectExtensibilityConfigurationFactory), RazorLanguage.Name)]
|
||||
internal class DefaultProjectExtensibilityConfigurationFactoryFactory : ILanguageServiceFactory
|
||||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
if (languageServices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(languageServices));
|
||||
}
|
||||
|
||||
return new DefaultProjectExtensibilityConfigurationFactory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,33 +18,62 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// at once.
|
||||
internal class DefaultProjectSnapshot : ProjectSnapshot
|
||||
{
|
||||
public DefaultProjectSnapshot(Project underlyingProject)
|
||||
public DefaultProjectSnapshot(HostProject hostProject, Project workspaceProject, VersionStamp? version = null)
|
||||
{
|
||||
if (underlyingProject == null)
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(underlyingProject));
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
UnderlyingProject = underlyingProject;
|
||||
HostProject = hostProject;
|
||||
WorkspaceProject = workspaceProject; // Might be null
|
||||
|
||||
FilePath = hostProject.FilePath;
|
||||
Version = version ?? VersionStamp.Default;
|
||||
}
|
||||
|
||||
private DefaultProjectSnapshot(Project underlyingProject, DefaultProjectSnapshot other)
|
||||
private DefaultProjectSnapshot(HostProject hostProject, DefaultProjectSnapshot other)
|
||||
{
|
||||
if (underlyingProject == null)
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(underlyingProject));
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
UnderlyingProject = underlyingProject;
|
||||
|
||||
ComputedVersion = other.ComputedVersion;
|
||||
Configuration = other.Configuration;
|
||||
|
||||
FilePath = other.FilePath;
|
||||
TagHelpers = other.TagHelpers;
|
||||
HostProject = hostProject;
|
||||
WorkspaceProject = other.WorkspaceProject;
|
||||
|
||||
Version = other.Version.GetNewerVersion();
|
||||
}
|
||||
|
||||
private DefaultProjectSnapshot(Project workspaceProject, DefaultProjectSnapshot other)
|
||||
{
|
||||
if (workspaceProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceProject));
|
||||
}
|
||||
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
ComputedVersion = other.ComputedVersion;
|
||||
|
||||
FilePath = other.FilePath;
|
||||
TagHelpers = other.TagHelpers;
|
||||
HostProject = other.HostProject;
|
||||
WorkspaceProject = workspaceProject;
|
||||
|
||||
Version = other.Version.GetNewerVersion();
|
||||
}
|
||||
|
||||
private DefaultProjectSnapshot(ProjectSnapshotUpdateContext update, DefaultProjectSnapshot other)
|
||||
|
|
@ -59,16 +88,28 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
UnderlyingProject = other.UnderlyingProject;
|
||||
ComputedVersion = update.Version;
|
||||
|
||||
ComputedVersion = update.UnderlyingProject.Version;
|
||||
Configuration = update.Configuration;
|
||||
FilePath = other.FilePath;
|
||||
HostProject = other.HostProject;
|
||||
TagHelpers = update.TagHelpers ?? Array.Empty<TagHelperDescriptor>();
|
||||
WorkspaceProject = other.WorkspaceProject;
|
||||
|
||||
// This doesn't represent a new version of the underlying data. Keep the same version.
|
||||
Version = other.Version;
|
||||
}
|
||||
|
||||
public override ProjectExtensibilityConfiguration Configuration { get; }
|
||||
public override RazorConfiguration Configuration => HostProject.Configuration;
|
||||
|
||||
public override Project UnderlyingProject { get; }
|
||||
public override string FilePath { get; }
|
||||
|
||||
public override HostProject HostProject { get; }
|
||||
|
||||
public override bool IsInitialized => WorkspaceProject != null;
|
||||
|
||||
public override VersionStamp Version { get; }
|
||||
|
||||
public override Project WorkspaceProject { get; }
|
||||
|
||||
public override IReadOnlyList<TagHelperDescriptor> TagHelpers { get; } = Array.Empty<TagHelperDescriptor>();
|
||||
|
||||
|
|
@ -77,19 +118,40 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// We know the project is dirty if we don't have a computed result, or it was computed for a different version.
|
||||
// Since the PSM updates the snapshots synchronously, the snapshot can never be older than the computed state.
|
||||
public bool IsDirty => ComputedVersion == null || ComputedVersion.Value != UnderlyingProject.Version;
|
||||
public bool IsDirty => ComputedVersion == null || ComputedVersion.Value != Version;
|
||||
|
||||
public DefaultProjectSnapshot WithProjectChange(Project project)
|
||||
public ProjectSnapshotUpdateContext CreateUpdateContext()
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
return new DefaultProjectSnapshot(project, this);
|
||||
return new ProjectSnapshotUpdateContext(FilePath, HostProject, WorkspaceProject, Version);
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot WithProjectChange(ProjectSnapshotUpdateContext update)
|
||||
public DefaultProjectSnapshot WithHostProject(HostProject hostProject)
|
||||
{
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
return new DefaultProjectSnapshot(hostProject, this);
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot RemoveWorkspaceProject()
|
||||
{
|
||||
// We want to get rid of all of the computed state since it's not really valid.
|
||||
return new DefaultProjectSnapshot(HostProject, null, Version.GetNewerVersion());
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot WithWorkspaceProject(Project workspaceProject)
|
||||
{
|
||||
if (workspaceProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceProject));
|
||||
}
|
||||
|
||||
return new DefaultProjectSnapshot(workspaceProject, this);
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot WithComputedUpdate(ProjectSnapshotUpdateContext update)
|
||||
{
|
||||
if (update == null)
|
||||
{
|
||||
|
|
@ -99,7 +161,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return new DefaultProjectSnapshot(update, this);
|
||||
}
|
||||
|
||||
public bool HasConfigurationChanged(ProjectSnapshot original)
|
||||
public bool HasConfigurationChanged(DefaultProjectSnapshot original)
|
||||
{
|
||||
if (original == null)
|
||||
{
|
||||
|
|
@ -119,4 +181,4 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return !Enumerable.SequenceEqual(TagHelpers, original.TagHelpers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,22 @@ using System.Linq;
|
|||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
// The implementation of project snapshot manager abstracts over the Roslyn Project (WorkspaceProject)
|
||||
// and information from the host's underlying project system (HostProject), to provide a unified and
|
||||
// immutable view of the underlying project systems.
|
||||
//
|
||||
// The HostProject support all of the configuration that the Razor SDK exposes via the project system
|
||||
// (language version, extensions, named configuration).
|
||||
//
|
||||
// The WorkspaceProject is needed to support our use of Roslyn Compilations for Tag Helpers and other
|
||||
// C# based constructs.
|
||||
//
|
||||
// The implementation will create a ProjectSnapshot for each HostProject. Put another way, when we
|
||||
// see a WorkspaceProject get created, we only care if we already have a HostProject for the same
|
||||
// filepath.
|
||||
//
|
||||
// Our underlying HostProject infrastructure currently does not handle multiple TFMs (project with
|
||||
// $(TargetFrameworks), so we just bind to the first WorkspaceProject we see for each HostProject.
|
||||
internal class DefaultProjectSnapshotManager : ProjectSnapshotManagerBase
|
||||
{
|
||||
public override event EventHandler<ProjectChangeEventArgs> Changed;
|
||||
|
|
@ -17,8 +33,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
private readonly ProjectSnapshotWorkerQueue _workerQueue;
|
||||
private readonly ProjectSnapshotWorker _worker;
|
||||
|
||||
private readonly Dictionary<ProjectId, DefaultProjectSnapshot> _projects;
|
||||
|
||||
private readonly Dictionary<string, DefaultProjectSnapshot> _projects;
|
||||
|
||||
public DefaultProjectSnapshotManager(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
ErrorReporter errorReporter,
|
||||
|
|
@ -57,7 +73,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
_triggers = triggers.ToArray();
|
||||
Workspace = workspace;
|
||||
|
||||
_projects = new Dictionary<ProjectId, DefaultProjectSnapshot>();
|
||||
_projects = new Dictionary<string, DefaultProjectSnapshot>(FilePathComparer.Instance);
|
||||
|
||||
_workerQueue = new ProjectSnapshotWorkerQueue(_foregroundDispatcher, this, worker);
|
||||
|
||||
for (var i = 0; i < _triggers.Length; i++)
|
||||
|
|
@ -70,63 +87,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
get
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
return _projects.Values.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot FindProject(ProjectId id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(id));
|
||||
}
|
||||
|
||||
_projects.TryGetValue(id, out var project);
|
||||
return project;
|
||||
}
|
||||
|
||||
public override Workspace Workspace { get; }
|
||||
|
||||
public override void ProjectAdded(Project underlyingProject)
|
||||
{
|
||||
if (underlyingProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(underlyingProject));
|
||||
}
|
||||
|
||||
var snapshot = new DefaultProjectSnapshot(underlyingProject);
|
||||
_projects[underlyingProject.Id] = snapshot;
|
||||
|
||||
// New projects always start dirty, need to compute state in the background.
|
||||
NotifyBackgroundWorker(snapshot.UnderlyingProject);
|
||||
|
||||
// We need to notify listeners about every project add.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Added));
|
||||
}
|
||||
|
||||
public override void ProjectChanged(Project underlyingProject)
|
||||
{
|
||||
if (underlyingProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(underlyingProject));
|
||||
}
|
||||
|
||||
if (_projects.TryGetValue(underlyingProject.Id, out var original))
|
||||
{
|
||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
||||
// underlying project is newer.
|
||||
var snapshot = original.WithProjectChange(underlyingProject);
|
||||
_projects[underlyingProject.Id] = snapshot;
|
||||
|
||||
if (snapshot.IsDirty)
|
||||
{
|
||||
// We don't need to notify listeners yet because we don't have any **new** computed state. However we do
|
||||
// need to trigger the background work to asynchronously compute the effect of the updates.
|
||||
NotifyBackgroundWorker(snapshot.UnderlyingProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ProjectUpdated(ProjectSnapshotUpdateContext update)
|
||||
{
|
||||
if (update == null)
|
||||
|
|
@ -134,25 +101,203 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
throw new ArgumentNullException(nameof(update));
|
||||
}
|
||||
|
||||
if (_projects.TryGetValue(update.UnderlyingProject.Id, out var original))
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_projects.TryGetValue(update.WorkspaceProject.FilePath, out var original))
|
||||
{
|
||||
if (!original.IsInitialized)
|
||||
{
|
||||
// If the project has been uninitialized, just ignore the update.
|
||||
return;
|
||||
}
|
||||
|
||||
// This is an update to the project's computed values, so everything should be overwritten
|
||||
var snapshot = original.WithProjectChange(update);
|
||||
_projects[update.UnderlyingProject.Id] = snapshot;
|
||||
var snapshot = original.WithComputedUpdate(update);
|
||||
_projects[update.WorkspaceProject.FilePath] = snapshot;
|
||||
|
||||
if (snapshot.IsDirty)
|
||||
{
|
||||
// It's possible that the snapshot can still be dirty if we got a project update while computing state in
|
||||
// the background. We need to trigger the background work to asynchronously compute the effect of the updates.
|
||||
NotifyBackgroundWorker(snapshot.UnderlyingProject);
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
|
||||
if (!object.Equals(snapshot.ComputedVersion, original.ComputedVersion))
|
||||
{
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.TagHelpersChanged));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void HostProjectAdded(HostProject hostProject)
|
||||
{
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
// We don't expect to see a HostProject initialized multiple times for the same path. Just ignore it.
|
||||
if (_projects.ContainsKey(hostProject.FilePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// It's possible that Workspace has already created a project for this, but it's not deterministic
|
||||
// So if possible find a WorkspaceProject.
|
||||
var workspaceProject = GetWorkspaceProject(hostProject.FilePath);
|
||||
|
||||
var snapshot = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
||||
_projects[hostProject.FilePath] = snapshot;
|
||||
|
||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
||||
{
|
||||
// Start computing background state if the project is fully initialized.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
|
||||
// We need to notify listeners about every project add.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Added));
|
||||
}
|
||||
|
||||
public override void HostProjectChanged(HostProject hostProject)
|
||||
{
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_projects.TryGetValue(hostProject.FilePath, out var original))
|
||||
{
|
||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
||||
// underlying project is newer.
|
||||
var snapshot = original.WithHostProject(hostProject);
|
||||
_projects[hostProject.FilePath] = snapshot;
|
||||
|
||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
||||
{
|
||||
// Start computing background state if the project is fully initialized.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
|
||||
// Now we need to know if the changes that we applied are significant. If that's the case then
|
||||
// we need to notify listeners.
|
||||
if (snapshot.HasConfigurationChanged(original))
|
||||
// Notify listeners right away because if the HostProject changes then it's likely that the Razor
|
||||
// configuration changed.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
|
||||
}
|
||||
}
|
||||
|
||||
public override void HostProjectRemoved(HostProject hostProject)
|
||||
{
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_projects.TryGetValue(hostProject.FilePath, out var snapshot))
|
||||
{
|
||||
_projects.Remove(hostProject.FilePath);
|
||||
|
||||
// We need to notify listeners about every project removal.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Removed));
|
||||
}
|
||||
}
|
||||
|
||||
public override void HostProjectBuildComplete(HostProject hostProject)
|
||||
{
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_projects.TryGetValue(hostProject.FilePath, out var original))
|
||||
{
|
||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
||||
// underlying project is newer.
|
||||
var snapshot = original.WithHostProject(hostProject);
|
||||
_projects[hostProject.FilePath] = snapshot;
|
||||
|
||||
// Notify the background worker so it can trigger tag helper discovery.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
}
|
||||
|
||||
public override void WorkspaceProjectAdded(Project workspaceProject)
|
||||
{
|
||||
if (workspaceProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceProject));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (!IsSupportedWorkspaceProject(workspaceProject))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The WorkspaceProject initialization never triggers a "Project Add" from out point of view, we
|
||||
// only care if the new WorkspaceProject matches an existing HostProject.
|
||||
if (_projects.TryGetValue(workspaceProject.FilePath, out var original))
|
||||
{
|
||||
// If this is a multi-targeting project then we are only interested in a single workspace project. If we already
|
||||
// found one in the past just ignore this one.
|
||||
if (original.WorkspaceProject == null)
|
||||
{
|
||||
var snapshot = original.WithWorkspaceProject(workspaceProject);
|
||||
_projects[workspaceProject.FilePath] = snapshot;
|
||||
|
||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
||||
{
|
||||
// We don't need to notify listeners yet because we don't have any **new** computed state.
|
||||
//
|
||||
// However we do need to trigger the background work to asynchronously compute the effect of the updates.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
|
||||
// Notify listeners right away since WorkspaceProject was just added, the project is now initialized.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void WorkspaceProjectChanged(Project workspaceProject)
|
||||
{
|
||||
if (workspaceProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceProject));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (!IsSupportedWorkspaceProject(workspaceProject))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We also need to check the projectId here. If this is a multi-targeting project then we are only interested
|
||||
// in a single workspace project. Just use the one that showed up first.
|
||||
if (_projects.TryGetValue(workspaceProject.FilePath, out var original) &&
|
||||
(original.WorkspaceProject == null ||
|
||||
original.WorkspaceProject.Id == workspaceProject.Id))
|
||||
{
|
||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
||||
// underlying project is newer.
|
||||
var snapshot = original.WithWorkspaceProject(workspaceProject);
|
||||
_projects[workspaceProject.FilePath] = snapshot;
|
||||
|
||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
||||
{
|
||||
// We don't need to notify listeners yet because we don't have any **new** computed state. However we do
|
||||
// need to trigger the background work to asynchronously compute the effect of the updates.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
|
||||
if (snapshot.HaveTagHelpersChanged(original))
|
||||
{
|
||||
|
|
@ -161,58 +306,136 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
}
|
||||
|
||||
public override void ProjectRemoved(Project underlyingProject)
|
||||
public override void WorkspaceProjectRemoved(Project workspaceProject)
|
||||
{
|
||||
if (underlyingProject == null)
|
||||
if (workspaceProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(underlyingProject));
|
||||
throw new ArgumentNullException(nameof(workspaceProject));
|
||||
}
|
||||
|
||||
if (_projects.TryGetValue(underlyingProject.Id, out var snapshot))
|
||||
{
|
||||
_projects.Remove(underlyingProject.Id);
|
||||
|
||||
// We need to notify listeners about every project removal.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Removed));
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (!IsSupportedWorkspaceProject(workspaceProject))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_projects.TryGetValue(workspaceProject.FilePath, out var original))
|
||||
{
|
||||
// We also need to check the projectId here. If this is a multi-targeting project then we are only interested
|
||||
// in a single workspace project. Make sure the WorkspaceProject we're using is the one that's being removed.
|
||||
if (original.WorkspaceProject?.Id != workspaceProject.Id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DefaultProjectSnapshot snapshot;
|
||||
|
||||
// So if the WorkspaceProject got removed, we should double check to make sure that there aren't others
|
||||
// hanging around. This could happen if a project is multi-targeting and one of the TFMs is removed.
|
||||
var otherWorkspaceProject = GetWorkspaceProject(workspaceProject.FilePath);
|
||||
if (otherWorkspaceProject != null && otherWorkspaceProject.Id != workspaceProject.Id)
|
||||
{
|
||||
// OK there's another WorkspaceProject, use that.
|
||||
//
|
||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
||||
// underlying project is newer.
|
||||
snapshot = original.WithWorkspaceProject(otherWorkspaceProject);
|
||||
_projects[workspaceProject.FilePath] = snapshot;
|
||||
|
||||
if (snapshot.IsInitialized && snapshot.IsDirty)
|
||||
{
|
||||
// We don't need to notify listeners yet because we don't have any **new** computed state. However we do
|
||||
// need to trigger the background work to asynchronously compute the effect of the updates.
|
||||
NotifyBackgroundWorker(snapshot.CreateUpdateContext());
|
||||
}
|
||||
|
||||
// Notify listeners of a change because it's a different WorkspaceProject.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
snapshot = original.RemoveWorkspaceProject();
|
||||
_projects[workspaceProject.FilePath] = snapshot;
|
||||
|
||||
// Notify listeners of a change because we've removed computed state.
|
||||
NotifyListeners(new ProjectChangeEventArgs(snapshot, ProjectChangeKind.Changed));
|
||||
}
|
||||
}
|
||||
|
||||
public override void ProjectBuildComplete(Project underlyingProject)
|
||||
public override void ReportError(Exception exception)
|
||||
{
|
||||
if (underlyingProject == null)
|
||||
if (exception == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(underlyingProject));
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
if (_projects.TryGetValue(underlyingProject.Id, out var original))
|
||||
{
|
||||
// Doing an update to the project should keep computed values, but mark the project as dirty if the
|
||||
// underlying project is newer.
|
||||
var snapshot = original.WithProjectChange(underlyingProject);
|
||||
_projects[underlyingProject.Id] = snapshot;
|
||||
|
||||
// Notify the background worker so it can trigger tag helper discovery.
|
||||
NotifyBackgroundWorker(underlyingProject);
|
||||
}
|
||||
_errorReporter.ReportError(exception);
|
||||
}
|
||||
|
||||
public override void ProjectsCleared()
|
||||
public override void ReportError(Exception exception, ProjectSnapshot project)
|
||||
{
|
||||
foreach (var kvp in _projects.ToArray())
|
||||
if (exception == null)
|
||||
{
|
||||
_projects.Remove(kvp.Key);
|
||||
|
||||
// We need to notify listeners about every project removal.
|
||||
NotifyListeners(new ProjectChangeEventArgs(kvp.Value, ProjectChangeKind.Removed));
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
_errorReporter.ReportError(exception, project);
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, HostProject hostProject)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
var project = hostProject?.FilePath == null ? null : this.GetProjectWithFilePath(hostProject.FilePath);
|
||||
_errorReporter.ReportError(exception, project);
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, Project workspaceProject)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
_errorReporter.ReportError(exception, workspaceProject);
|
||||
}
|
||||
|
||||
// We're only interested in CSharp projects that have a FilePath. We rely on the FilePath to
|
||||
// unify the Workspace Project with our HostProject concept.
|
||||
private bool IsSupportedWorkspaceProject(Project workspaceProject) => workspaceProject.Language == LanguageNames.CSharp && workspaceProject.FilePath != null;
|
||||
|
||||
private Project GetWorkspaceProject(string filePath)
|
||||
{
|
||||
var solution = Workspace.CurrentSolution;
|
||||
if (solution == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var workspaceProject in solution.Projects)
|
||||
{
|
||||
if (IsSupportedWorkspaceProject(workspaceProject) &&
|
||||
FilePathComparer.Instance.Equals(filePath, workspaceProject.FilePath))
|
||||
{
|
||||
// We don't try to handle mulitple TFMs anwhere in Razor, just take the first WorkspaceProject that is a match.
|
||||
return workspaceProject;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// virtual so it can be overridden in tests
|
||||
protected virtual void NotifyBackgroundWorker(Project project)
|
||||
protected virtual void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
_workerQueue.Enqueue(project);
|
||||
_workerQueue.Enqueue(context);
|
||||
}
|
||||
|
||||
// virtual so it can be overridden in tests
|
||||
|
|
@ -226,15 +449,5 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
handler(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception)
|
||||
{
|
||||
_errorReporter.ReportError(exception);
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, Project project)
|
||||
{
|
||||
_errorReporter.ReportError(exception, project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,32 +9,22 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
internal class DefaultProjectSnapshotWorker : ProjectSnapshotWorker
|
||||
{
|
||||
private readonly ProjectExtensibilityConfigurationFactory _configurationFactory;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly TagHelperResolver _tagHelperResolver;
|
||||
|
||||
public DefaultProjectSnapshotWorker(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
ProjectExtensibilityConfigurationFactory configurationFactory,
|
||||
TagHelperResolver tagHelperResolver)
|
||||
public DefaultProjectSnapshotWorker(ForegroundDispatcher foregroundDispatcher, TagHelperResolver tagHelperResolver)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (configurationFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurationFactory));
|
||||
}
|
||||
|
||||
if (tagHelperResolver == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tagHelperResolver));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_configurationFactory = configurationFactory;
|
||||
_tagHelperResolver = tagHelperResolver;
|
||||
}
|
||||
|
||||
|
|
@ -54,16 +44,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return ProjectUpdatesCoreAsync(update);
|
||||
}
|
||||
|
||||
protected virtual void OnProcessingUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
private async Task ProjectUpdatesCoreAsync(object state)
|
||||
{
|
||||
var update = (ProjectSnapshotUpdateContext)state;
|
||||
|
||||
// We'll have more things to process here, but for now we're just hardcoding the configuration.
|
||||
OnProcessingUpdate();
|
||||
|
||||
var configuration = await _configurationFactory.GetConfigurationAsync(update.UnderlyingProject);
|
||||
update.Configuration = configuration;
|
||||
|
||||
var result = await _tagHelperResolver.GetTagHelpersAsync(update.UnderlyingProject, CancellationToken.None);
|
||||
var snapshot = new DefaultProjectSnapshot(update.HostProject, update.WorkspaceProject, update.Version);
|
||||
var result = await _tagHelperResolver.GetTagHelpersAsync(snapshot, CancellationToken.None);
|
||||
update.TagHelpers = result.Descriptors;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,10 +26,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultProjectSnapshotWorker(
|
||||
_foregroundDispatcher,
|
||||
languageServices.GetRequiredService<ProjectExtensibilityConfigurationFactory>(),
|
||||
languageServices.GetRequiredService<TagHelperResolver>());
|
||||
return new DefaultProjectSnapshotWorker(_foregroundDispatcher, languageServices.GetRequiredService<TagHelperResolver>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class FallbackRazorConfiguration : RazorConfiguration
|
||||
{
|
||||
public static readonly RazorConfiguration MVC_1_0 = new FallbackRazorConfiguration(
|
||||
RazorLanguageVersion.Version_1_0,
|
||||
"MVC-1.0",
|
||||
new[] { new FallbackRazorExtension("MVC-1.0"), });
|
||||
|
||||
public static readonly RazorConfiguration MVC_1_1 = new FallbackRazorConfiguration(
|
||||
RazorLanguageVersion.Version_1_1,
|
||||
"MVC-1.1",
|
||||
new[] { new FallbackRazorExtension("MVC-1.1"), });
|
||||
|
||||
public static readonly RazorConfiguration MVC_2_0 = new FallbackRazorConfiguration(
|
||||
RazorLanguageVersion.Version_2_0,
|
||||
"MVC-2.0",
|
||||
new[] { new FallbackRazorExtension("MVC-2.0"), });
|
||||
|
||||
public static RazorConfiguration SelectConfiguration(Version version)
|
||||
{
|
||||
if (version.Major == 1 && version.Minor == 0)
|
||||
{
|
||||
return MVC_1_0;
|
||||
}
|
||||
else if (version.Major == 1 && version.Minor == 1)
|
||||
{
|
||||
return MVC_1_1;
|
||||
}
|
||||
else if (version.Major == 2 && version.Minor == 0)
|
||||
{
|
||||
return MVC_2_0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MVC_2_0;
|
||||
}
|
||||
}
|
||||
|
||||
public FallbackRazorConfiguration(
|
||||
RazorLanguageVersion languageVersion,
|
||||
string configurationName,
|
||||
RazorExtension[] extensions)
|
||||
{
|
||||
if (languageVersion == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(languageVersion));
|
||||
}
|
||||
|
||||
if (configurationName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurationName));
|
||||
}
|
||||
|
||||
if (extensions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(extensions));
|
||||
}
|
||||
|
||||
LanguageVersion = languageVersion;
|
||||
ConfigurationName = configurationName;
|
||||
Extensions = extensions;
|
||||
}
|
||||
|
||||
public override string ConfigurationName { get; }
|
||||
|
||||
public override IReadOnlyList<RazorExtension> Extensions { get; }
|
||||
|
||||
public override RazorLanguageVersion LanguageVersion { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class FallbackRazorExtension : RazorExtension
|
||||
{
|
||||
public FallbackRazorExtension(string extensionName)
|
||||
{
|
||||
if (extensionName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(extensionName));
|
||||
}
|
||||
|
||||
ExtensionName = extensionName;
|
||||
}
|
||||
|
||||
public override string ExtensionName { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class HostProject
|
||||
{
|
||||
public HostProject(string projectFilePath, RazorConfiguration razorConfiguration)
|
||||
{
|
||||
if (projectFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectFilePath));
|
||||
}
|
||||
|
||||
if (razorConfiguration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(razorConfiguration));
|
||||
}
|
||||
|
||||
FilePath = projectFilePath;
|
||||
Configuration = razorConfiguration;
|
||||
}
|
||||
|
||||
public RazorConfiguration Configuration { get; }
|
||||
|
||||
public string FilePath { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class MvcExtensibilityConfiguration : ProjectExtensibilityConfiguration
|
||||
{
|
||||
public MvcExtensibilityConfiguration(
|
||||
RazorLanguageVersion languageVersion,
|
||||
ProjectExtensibilityConfigurationKind kind,
|
||||
ProjectExtensibilityAssembly razorAssembly,
|
||||
ProjectExtensibilityAssembly mvcAssembly)
|
||||
{
|
||||
if (razorAssembly == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(razorAssembly));
|
||||
}
|
||||
|
||||
if (mvcAssembly == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mvcAssembly));
|
||||
}
|
||||
|
||||
Kind = kind;
|
||||
RazorAssembly = razorAssembly;
|
||||
MvcAssembly = mvcAssembly;
|
||||
LanguageVersion = languageVersion;
|
||||
|
||||
Assemblies = new[] { RazorAssembly, MvcAssembly, };
|
||||
}
|
||||
|
||||
public override IReadOnlyList<ProjectExtensibilityAssembly> Assemblies { get; }
|
||||
|
||||
// MVC: '2.0.0' (fallback) | Razor Language '2.0.0'
|
||||
// or
|
||||
// MVC: '2.1.3' | Razor Language '2.1.3'
|
||||
public override string DisplayName => $"MVC: {MvcAssembly.Identity.Version.ToString(3)}" + (Kind == ProjectExtensibilityConfigurationKind.Fallback? " (fallback)" : string.Empty) + " | " + LanguageVersion;
|
||||
|
||||
public override ProjectExtensibilityConfigurationKind Kind { get; }
|
||||
|
||||
public override ProjectExtensibilityAssembly RazorAssembly { get; }
|
||||
|
||||
public override RazorLanguageVersion LanguageVersion { get; }
|
||||
|
||||
public ProjectExtensibilityAssembly MvcAssembly { get; }
|
||||
|
||||
public override bool Equals(ProjectExtensibilityConfiguration other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're intentionally ignoring the 'Kind' here. That's mostly for diagnostics and doesn't influence any behavior.
|
||||
return LanguageVersion == other.LanguageVersion &&
|
||||
Enumerable.SequenceEqual(
|
||||
Assemblies.OrderBy(a => a.Identity.Name).Select(a => a.Identity),
|
||||
other.Assemblies.OrderBy(a => a.Identity.Name).Select(a => a.Identity),
|
||||
AssemblyIdentityEqualityComparer.NameAndVersion);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCodeCombiner();
|
||||
foreach (var assembly in Assemblies.OrderBy(a => a.Identity.Name))
|
||||
{
|
||||
hash.Add(assembly);
|
||||
}
|
||||
|
||||
hash.Add(LanguageVersion);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return DisplayName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +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;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal abstract class ProjectExtensibilityConfiguration : IEquatable<ProjectExtensibilityConfiguration>
|
||||
{
|
||||
public abstract IReadOnlyList<ProjectExtensibilityAssembly> Assemblies { get; }
|
||||
|
||||
public abstract string DisplayName { get; }
|
||||
|
||||
public abstract ProjectExtensibilityConfigurationKind Kind { get; }
|
||||
|
||||
public abstract ProjectExtensibilityAssembly RazorAssembly { get; }
|
||||
|
||||
public abstract RazorLanguageVersion LanguageVersion { get; }
|
||||
|
||||
public abstract bool Equals(ProjectExtensibilityConfiguration other);
|
||||
|
||||
public abstract override int GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as ProjectExtensibilityConfiguration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal abstract class ProjectExtensibilityConfigurationFactory : ILanguageService
|
||||
{
|
||||
public abstract Task<ProjectExtensibilityConfiguration> GetConfigurationAsync(Project project, CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +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.
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes how closely the configuration of Razor tooling matches the actual project dependencies.
|
||||
/// </summary>
|
||||
internal enum ProjectExtensibilityConfigurationKind
|
||||
{
|
||||
ApproximateMatch,
|
||||
Fallback,
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,18 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
internal abstract class ProjectSnapshot
|
||||
{
|
||||
public abstract ProjectExtensibilityConfiguration Configuration { get; }
|
||||
public abstract RazorConfiguration Configuration { get; }
|
||||
|
||||
public abstract Project UnderlyingProject { get; }
|
||||
public abstract string FilePath { get; }
|
||||
|
||||
public abstract bool IsInitialized { get; }
|
||||
|
||||
public abstract IReadOnlyList<TagHelperDescriptor> TagHelpers { get; }
|
||||
|
||||
public abstract VersionStamp Version { get; }
|
||||
|
||||
public abstract Project WorkspaceProject { get; }
|
||||
|
||||
public abstract HostProject HostProject { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,20 +9,28 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
public abstract Workspace Workspace { get; }
|
||||
|
||||
public abstract void ProjectAdded(Project underlyingProject);
|
||||
|
||||
public abstract void ProjectChanged(Project underlyingProject);
|
||||
|
||||
public abstract void ProjectUpdated(ProjectSnapshotUpdateContext update);
|
||||
|
||||
public abstract void ProjectRemoved(Project underlyingProject);
|
||||
public abstract void HostProjectAdded(HostProject hostProject);
|
||||
|
||||
public abstract void ProjectBuildComplete(Project underlyingProject);
|
||||
public abstract void HostProjectChanged(HostProject hostProject);
|
||||
|
||||
public abstract void ProjectsCleared();
|
||||
public abstract void HostProjectRemoved(HostProject hostProject);
|
||||
|
||||
public abstract void HostProjectBuildComplete(HostProject hostProject);
|
||||
|
||||
public abstract void WorkspaceProjectAdded(Project workspaceProject);
|
||||
|
||||
public abstract void WorkspaceProjectChanged(Project workspaceProject);
|
||||
|
||||
public abstract void WorkspaceProjectRemoved(Project workspaceProject);
|
||||
|
||||
public abstract void ReportError(Exception exception);
|
||||
|
||||
public abstract void ReportError(Exception exception, Project project);
|
||||
public abstract void ReportError(Exception exception, ProjectSnapshot project);
|
||||
|
||||
public abstract void ReportError(Exception exception, HostProject hostProject);
|
||||
|
||||
public abstract void ReportError(Exception exception, Project workspaceProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.UnderlyingProject.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
if (FilePathComparer.Instance.Equals(filePath, project.FilePath))
|
||||
{
|
||||
return project;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,20 +9,37 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
internal class ProjectSnapshotUpdateContext
|
||||
{
|
||||
public ProjectSnapshotUpdateContext(Project underlyingProject)
|
||||
public ProjectSnapshotUpdateContext(string filePath, HostProject hostProject, Project workspaceProject, VersionStamp version)
|
||||
{
|
||||
if (underlyingProject == null)
|
||||
if (filePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(underlyingProject));
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
||||
|
||||
UnderlyingProject = underlyingProject;
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
if (workspaceProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceProject));
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
HostProject = hostProject;
|
||||
WorkspaceProject = workspaceProject;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public Project UnderlyingProject { get; }
|
||||
public string FilePath { get; }
|
||||
|
||||
public ProjectExtensibilityConfiguration Configuration { get; set; }
|
||||
public HostProject HostProject { get; }
|
||||
|
||||
public Project WorkspaceProject { get; }
|
||||
|
||||
public IReadOnlyList<TagHelperDescriptor> TagHelpers { get; set; }
|
||||
|
||||
public VersionStamp Version { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -16,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
private readonly DefaultProjectSnapshotManager _projectManager;
|
||||
private readonly ProjectSnapshotWorker _projectWorker;
|
||||
|
||||
private readonly Dictionary<ProjectId, Project> _projects;
|
||||
private readonly Dictionary<string, ProjectSnapshotUpdateContext> _projects;
|
||||
private Timer _timer;
|
||||
|
||||
public ProjectSnapshotWorkerQueue(ForegroundDispatcher foregroundDispatcher, DefaultProjectSnapshotManager projectManager, ProjectSnapshotWorker projectWorker)
|
||||
|
|
@ -40,7 +39,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
_projectManager = projectManager;
|
||||
_projectWorker = projectWorker;
|
||||
|
||||
_projects = new Dictionary<ProjectId, Project>();
|
||||
_projects = new Dictionary<string, ProjectSnapshotUpdateContext>(FilePathComparer.Instance);
|
||||
}
|
||||
|
||||
public bool HasPendingNotifications
|
||||
|
|
@ -93,11 +92,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
}
|
||||
|
||||
public void Enqueue(Project project)
|
||||
public void Enqueue(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
if (project == null)
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
|
@ -106,7 +105,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
// We only want to store the last 'seen' version of any given project. That way when we pick one to process
|
||||
// it's always the best version to use.
|
||||
_projects[project.Id] = project;
|
||||
_projects[context.FilePath] = context;
|
||||
|
||||
StartWorker();
|
||||
}
|
||||
|
|
@ -133,7 +132,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
OnStartingBackgroundWork();
|
||||
|
||||
Project[] work;
|
||||
ProjectSnapshotUpdateContext[] work;
|
||||
lock (_projects)
|
||||
{
|
||||
work = _projects.Values.ToArray();
|
||||
|
|
@ -145,7 +144,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
try
|
||||
{
|
||||
updates[i] = (new ProjectSnapshotUpdateContext(work[i]), null);
|
||||
updates[i] = (work[i], null);
|
||||
await _projectWorker.ProcessUpdateAsync(updates[i].context);
|
||||
}
|
||||
catch (Exception projectException)
|
||||
|
|
@ -196,7 +195,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
else
|
||||
{
|
||||
_projectManager.ReportError(update.exception, update.context?.UnderlyingProject);
|
||||
_projectManager.ReportError(update.exception, update.context?.WorkspaceProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class ProjectSystemRazorConfiguration : RazorConfiguration
|
||||
{
|
||||
public ProjectSystemRazorConfiguration(
|
||||
RazorLanguageVersion languageVersion,
|
||||
string configurationName,
|
||||
RazorExtension[] extensions)
|
||||
{
|
||||
if (languageVersion == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(languageVersion));
|
||||
}
|
||||
|
||||
if (configurationName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurationName));
|
||||
}
|
||||
|
||||
if (extensions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(extensions));
|
||||
}
|
||||
|
||||
LanguageVersion = languageVersion;
|
||||
ConfigurationName = configurationName;
|
||||
Extensions = extensions;
|
||||
}
|
||||
|
||||
public override string ConfigurationName { get; }
|
||||
|
||||
public override IReadOnlyList<RazorExtension> Extensions { get; }
|
||||
|
||||
public override RazorLanguageVersion LanguageVersion { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class ProjectSystemRazorExtension : RazorExtension
|
||||
{
|
||||
public ProjectSystemRazorExtension(string extensionName)
|
||||
{
|
||||
if (extensionName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(extensionName));
|
||||
}
|
||||
|
||||
ExtensionName = extensionName;
|
||||
}
|
||||
|
||||
public override string ExtensionName { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -23,51 +23,43 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
Debug.Assert(solution != null);
|
||||
|
||||
_projectManager.ProjectsCleared();
|
||||
|
||||
foreach (var project in solution.Projects)
|
||||
{
|
||||
if (project.Language == LanguageNames.CSharp)
|
||||
{
|
||||
_projectManager.ProjectAdded(project);
|
||||
}
|
||||
_projectManager.WorkspaceProjectAdded(project);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal void Workspace_WorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
|
||||
{
|
||||
Project underlyingProject;
|
||||
Project project;
|
||||
switch (e.Kind)
|
||||
{
|
||||
case WorkspaceChangeKind.ProjectAdded:
|
||||
{
|
||||
underlyingProject = e.NewSolution.GetProject(e.ProjectId);
|
||||
Debug.Assert(underlyingProject != null);
|
||||
project = e.NewSolution.GetProject(e.ProjectId);
|
||||
Debug.Assert(project != null);
|
||||
|
||||
if (underlyingProject.Language == LanguageNames.CSharp)
|
||||
{
|
||||
_projectManager.ProjectAdded(underlyingProject);
|
||||
}
|
||||
_projectManager.WorkspaceProjectAdded(project);
|
||||
break;
|
||||
}
|
||||
|
||||
case WorkspaceChangeKind.ProjectChanged:
|
||||
case WorkspaceChangeKind.ProjectReloaded:
|
||||
{
|
||||
underlyingProject = e.NewSolution.GetProject(e.ProjectId);
|
||||
Debug.Assert(underlyingProject != null);
|
||||
project = e.NewSolution.GetProject(e.ProjectId);
|
||||
Debug.Assert(project != null);
|
||||
|
||||
_projectManager.ProjectChanged(underlyingProject);
|
||||
_projectManager.WorkspaceProjectChanged(project);
|
||||
break;
|
||||
}
|
||||
|
||||
case WorkspaceChangeKind.ProjectRemoved:
|
||||
{
|
||||
underlyingProject = e.OldSolution.GetProject(e.ProjectId);
|
||||
Debug.Assert(underlyingProject != null);
|
||||
project = e.OldSolution.GetProject(e.ProjectId);
|
||||
Debug.Assert(project != null);
|
||||
|
||||
_projectManager.ProjectRemoved(underlyingProject);
|
||||
_projectManager.WorkspaceProjectRemoved(project);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -76,6 +68,15 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
case WorkspaceChangeKind.SolutionCleared:
|
||||
case WorkspaceChangeKind.SolutionReloaded:
|
||||
case WorkspaceChangeKind.SolutionRemoved:
|
||||
|
||||
if (e.OldSolution != null)
|
||||
{
|
||||
foreach (var p in e.OldSolution.Projects)
|
||||
{
|
||||
_projectManager.WorkspaceProjectRemoved(p);
|
||||
}
|
||||
}
|
||||
|
||||
InitializeSolution(e.NewSolution);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
internal static class FilePathComparer
|
||||
{
|
||||
private static StringComparer _instance;
|
||||
|
||||
public static StringComparer Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null && RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
_instance = StringComparer.Ordinal;
|
||||
}
|
||||
else if (_instance == null)
|
||||
{
|
||||
_instance = StringComparer.OrdinalIgnoreCase;
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,74 @@
|
|||
// 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.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;
|
||||
HostProject = new HostProject(filePath, configuration);
|
||||
WorkspaceProject = workspaceProject;
|
||||
TagHelpers = Array.Empty<TagHelperDescriptor>();
|
||||
|
||||
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; }
|
||||
|
||||
public override HostProject HostProject { get; }
|
||||
|
||||
public override IReadOnlyList<TagHelperDescriptor> TagHelpers { 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,85 +7,177 @@ using Microsoft.AspNetCore.Razor.Language;
|
|||
using Microsoft.CodeAnalysis;
|
||||
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
|
||||
{
|
||||
internal class DefaultProjectEngineFactoryService : RazorProjectEngineFactoryService
|
||||
{
|
||||
private readonly static MvcExtensibilityConfiguration DefaultConfiguration = new MvcExtensibilityConfiguration(
|
||||
RazorLanguageVersion.Version_2_0,
|
||||
ProjectExtensibilityConfigurationKind.Fallback,
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Razor.Language", new Version("2.0.0.0"))),
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("2.0.0.0"))));
|
||||
private readonly static RazorConfiguration DefaultConfiguration = FallbackRazorConfiguration.MVC_2_0;
|
||||
|
||||
private readonly ProjectSnapshotManager _projectManager;
|
||||
private readonly Workspace _workspace;
|
||||
private readonly IFallbackProjectEngineFactory _defaultFactory;
|
||||
private readonly Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] _customFactories;
|
||||
private ProjectSnapshotManager _projectManager;
|
||||
|
||||
public DefaultProjectEngineFactoryService(ProjectSnapshotManager projectManager)
|
||||
public DefaultProjectEngineFactoryService(
|
||||
Workspace workspace,
|
||||
IFallbackProjectEngineFactory defaultFactory,
|
||||
Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] customFactories)
|
||||
{
|
||||
if (workspace == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
if (defaultFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(defaultFactory));
|
||||
}
|
||||
|
||||
if (customFactories == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(customFactories));
|
||||
}
|
||||
|
||||
_workspace = workspace;
|
||||
_defaultFactory = defaultFactory;
|
||||
_customFactories = customFactories;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal 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 as MvcExtensibilityConfiguration) ?? DefaultConfiguration;
|
||||
var razorLanguageVersion = configuration.LanguageVersion;
|
||||
|
||||
var razorConfiguration = new RazorConfiguration(razorLanguageVersion, "unnamed", Array.Empty<RazorExtension>());
|
||||
var fileSystem = RazorProjectFileSystem.Create(projectPath);
|
||||
|
||||
RazorProjectEngine projectEngine;
|
||||
if (razorLanguageVersion.Major == 1)
|
||||
{
|
||||
projectEngine = RazorProjectEngine.Create(razorConfiguration, fileSystem, b =>
|
||||
{
|
||||
configure?.Invoke(b);
|
||||
|
||||
Mvc1_X.RazorExtensions.Register(b);
|
||||
|
||||
if (configuration.MvcAssembly.Identity.Version.Minor >= 1)
|
||||
{
|
||||
Mvc1_X.RazorExtensions.RegisterViewComponentTagHelpers(b);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
projectEngine = RazorProjectEngine.Create(razorConfiguration, 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);
|
||||
|
||||
if (_projectManager == null)
|
||||
{
|
||||
_projectManager = _workspace.Services.GetLanguageServices(RazorLanguage.Name).GetRequiredService<ProjectSnapshotManager>();
|
||||
}
|
||||
|
||||
var projects = _projectManager.Projects;
|
||||
for (var i = 0; i < projects.Count; i++)
|
||||
{
|
||||
var project = projects[i];
|
||||
if (project.UnderlyingProject.FilePath != null)
|
||||
if (project.FilePath != null)
|
||||
{
|
||||
if (string.Equals(directory, NormalizeDirectoryPath(Path.GetDirectoryName(project.UnderlyingProject.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.WorkspaceServices.Workspace,
|
||||
_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>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
_textViews = new List<ITextView>();
|
||||
}
|
||||
|
||||
internal override ProjectExtensibilityConfiguration Configuration => _project?.Configuration;
|
||||
public override RazorConfiguration Configuration => _project?.Configuration;
|
||||
|
||||
public override EditorSettings EditorSettings => _workspaceEditorSettings.Current;
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
public override bool IsSupportedProject => _isSupportedProject;
|
||||
|
||||
public override Project Project => _workspace.CurrentSolution.GetProject(_project.UnderlyingProject.Id);
|
||||
public override Project Project => _workspace.CurrentSolution.GetProject(_project.WorkspaceProject.Id);
|
||||
|
||||
public override ITextBuffer TextBuffer => _textBuffer;
|
||||
|
||||
|
|
@ -196,7 +196,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
internal void ProjectManager_Changed(object sender, ProjectChangeEventArgs e)
|
||||
{
|
||||
if (_projectPath != null &&
|
||||
string.Equals(_projectPath, e.Project.UnderlyingProject.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
string.Equals(_projectPath, e.Project.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (e.Kind == ProjectChangeKind.TagHelpersChanged)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor.Editor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Microsoft.VisualStudio.Text.Editor;
|
||||
|
||||
|
|
@ -16,7 +15,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
public abstract event EventHandler<ContextChangeEventArgs> ContextChanged;
|
||||
|
||||
internal abstract ProjectExtensibilityConfiguration Configuration { get; }
|
||||
public abstract RazorConfiguration Configuration { get; }
|
||||
|
||||
public abstract EditorSettings EditorSettings { get; }
|
||||
|
||||
|
|
|
|||
|
|
@ -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,128 @@
|
|||
// 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.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Dataflow;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.VisualStudio.LanguageServices;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
// Somewhat similar to https://github.com/dotnet/project-system/blob/fa074d228dcff6dae9e48ce43dd4a3a5aa22e8f0/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/LanguageServiceHost.cs
|
||||
//
|
||||
// This class is responsible for intializing the Razor ProjectSnapshotManager for cases where
|
||||
// MSBuild provides configuration support (>= 2.1).
|
||||
[AppliesTo("DotNetCoreRazor & DotNetCoreRazorConfiguration")]
|
||||
[Export(ExportContractNames.Scopes.UnconfiguredProject, typeof(IProjectDynamicLoadComponent))]
|
||||
internal class DefaultRazorProjectHost : RazorProjectHostBase
|
||||
{
|
||||
private IDisposable _subscription;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultRazorProjectHost(
|
||||
IUnconfiguredProjectCommonServices commonServices,
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
: base(commonServices, workspace)
|
||||
{
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal DefaultRazorProjectHost(
|
||||
IUnconfiguredProjectCommonServices commonServices,
|
||||
Workspace workspace,
|
||||
ProjectSnapshotManagerBase projectManager)
|
||||
: base(commonServices, workspace, projectManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task InitializeCoreAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await base.InitializeCoreAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Don't try to evaluate any properties here since the project is still loading and we require access
|
||||
// to the UI thread to push our updates.
|
||||
//
|
||||
// Just subscribe and handle the notification later.
|
||||
// Don't try to evaluate any properties here since the project is still loading and we require access
|
||||
// to the UI thread to push our updates.
|
||||
//
|
||||
// Just subscribe and handle the notification later.
|
||||
var receiver = new ActionBlock<IProjectVersionedValue<IProjectSubscriptionUpdate>>(OnProjectChanged);
|
||||
_subscription = CommonServices.ActiveConfiguredProjectSubscription.JointRuleSource.SourceBlock.LinkTo(
|
||||
receiver,
|
||||
initialDataAsNew: true,
|
||||
suppressVersionOnlyUpdates: true,
|
||||
ruleNames: new string[] { Rules.RazorGeneral.SchemaName, Rules.RazorConfiguration.SchemaName, Rules.RazorExtension.SchemaName });
|
||||
}
|
||||
|
||||
protected override async Task DisposeCoreAsync(bool initialized)
|
||||
{
|
||||
await base.DisposeCoreAsync(initialized).ConfigureAwait(false);
|
||||
|
||||
if (initialized)
|
||||
{
|
||||
_subscription.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal async Task OnProjectChanged(IProjectVersionedValue<IProjectSubscriptionUpdate> update)
|
||||
{
|
||||
if (IsDisposing || IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await CommonServices.TasksService.LoadedProjectAsync(async () =>
|
||||
{
|
||||
await ExecuteWithLock(async () =>
|
||||
{
|
||||
var languageVersion = update.Value.CurrentState[Rules.RazorGeneral.SchemaName].Properties[Rules.RazorGeneral.RazorLangVersionProperty];
|
||||
var defaultConfiguration = update.Value.CurrentState[Rules.RazorGeneral.SchemaName].Properties[Rules.RazorGeneral.RazorDefaultConfigurationProperty];
|
||||
|
||||
RazorConfiguration configuration = null;
|
||||
if (!string.IsNullOrEmpty(languageVersion) && !string.IsNullOrEmpty(defaultConfiguration))
|
||||
{
|
||||
if (!RazorLanguageVersion.TryParse(languageVersion, out var parsedVersion))
|
||||
{
|
||||
parsedVersion = RazorLanguageVersion.Latest;
|
||||
}
|
||||
|
||||
var extensions = update.Value.CurrentState[Rules.RazorExtension.PrimaryDataSourceItemType].Items.Select(e =>
|
||||
{
|
||||
return new ProjectSystemRazorExtension(e.Key);
|
||||
}).ToArray();
|
||||
|
||||
var configurations = update.Value.CurrentState[Rules.RazorConfiguration.PrimaryDataSourceItemType].Items.Select(c =>
|
||||
{
|
||||
var includedExtensions = c.Value[Rules.RazorConfiguration.ExtensionsProperty]
|
||||
.Split(';')
|
||||
.Select(name => extensions.Where(e => e.ExtensionName == name).FirstOrDefault())
|
||||
.Where(e => e != null)
|
||||
.ToArray();
|
||||
|
||||
return new ProjectSystemRazorConfiguration(parsedVersion, c.Key, includedExtensions);
|
||||
}).ToArray();
|
||||
|
||||
configuration = configurations.Where(c => c.ConfigurationName == defaultConfiguration).FirstOrDefault();
|
||||
}
|
||||
|
||||
if (configuration == null)
|
||||
{
|
||||
// Ok we can't find a language version. Let's assume this project isn't using Razor then.
|
||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
|
||||
await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false);
|
||||
});
|
||||
}, registerFaultHandler: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
// 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.IO;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Dataflow;
|
||||
using Microsoft.VisualStudio.LanguageServices;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using ResolvedCompilationReference = Microsoft.CodeAnalysis.Razor.ProjectSystem.ManageProjectSystemSchema.ResolvedCompilationReference;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
// Somewhat similar to https://github.com/dotnet/project-system/blob/fa074d228dcff6dae9e48ce43dd4a3a5aa22e8f0/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/LanguageServices/LanguageServiceHost.cs
|
||||
//
|
||||
// This class is responsible for intializing the Razor ProjectSnapshotManager for cases where
|
||||
// MSBuild does not provides configuration support (SDK < 2.1).
|
||||
[AppliesTo("(DotNetCoreRazor | DotNetCoreWeb) & !DotNetCoreRazorConfiguration")]
|
||||
[Export(ExportContractNames.Scopes.UnconfiguredProject, typeof(IProjectDynamicLoadComponent))]
|
||||
internal class FallbackRazorProjectHost : RazorProjectHostBase
|
||||
{
|
||||
private const string MvcAssemblyName = "Microsoft.AspNetCore.Mvc.Razor";
|
||||
private const string MvcAssemblyFileName = "Microsoft.AspNetCore.Mvc.Razor.dll";
|
||||
|
||||
private IDisposable _subscription;
|
||||
|
||||
[ImportingConstructor]
|
||||
public FallbackRazorProjectHost(
|
||||
IUnconfiguredProjectCommonServices commonServices,
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
: base(commonServices, workspace)
|
||||
{
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal FallbackRazorProjectHost(
|
||||
IUnconfiguredProjectCommonServices commonServices,
|
||||
Workspace workspace,
|
||||
ProjectSnapshotManagerBase projectManager)
|
||||
: base(commonServices, workspace, projectManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task InitializeCoreAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await base.InitializeCoreAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Don't try to evaluate any properties here since the project is still loading and we require access
|
||||
// to the UI thread to push our updates.
|
||||
//
|
||||
// Just subscribe and handle the notification later.
|
||||
var receiver = new ActionBlock<IProjectVersionedValue<IProjectSubscriptionUpdate>>(OnProjectChanged);
|
||||
_subscription = CommonServices.ActiveConfiguredProjectSubscription.JointRuleSource.SourceBlock.LinkTo(
|
||||
receiver,
|
||||
initialDataAsNew: true,
|
||||
suppressVersionOnlyUpdates: true,
|
||||
ruleNames: new string[] { ResolvedCompilationReference.SchemaName },
|
||||
linkOptions: new DataflowLinkOptions() { PropagateCompletion = true });
|
||||
}
|
||||
|
||||
protected override async Task DisposeCoreAsync(bool initialized)
|
||||
{
|
||||
await base.DisposeCoreAsync(initialized).ConfigureAwait(false);
|
||||
|
||||
if (initialized)
|
||||
{
|
||||
_subscription.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal async Task OnProjectChanged(IProjectVersionedValue<IProjectSubscriptionUpdate> update)
|
||||
{
|
||||
if (IsDisposing || IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await CommonServices.TasksService.LoadedProjectAsync(async () =>
|
||||
{
|
||||
await ExecuteWithLock(async () =>
|
||||
{
|
||||
string mvcReferenceFullPath = null;
|
||||
var references = update.Value.CurrentState[ResolvedCompilationReference.SchemaName].Items;
|
||||
foreach (var reference in references)
|
||||
{
|
||||
if (reference.Key.EndsWith(MvcAssemblyFileName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
mvcReferenceFullPath = reference.Key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mvcReferenceFullPath == null)
|
||||
{
|
||||
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
|
||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var version = GetAssemblyVersion(mvcReferenceFullPath);
|
||||
if (version == null)
|
||||
{
|
||||
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
|
||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var configuration = FallbackRazorConfiguration.SelectConfiguration(version);
|
||||
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
|
||||
await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false);
|
||||
});
|
||||
}, registerFaultHandler: true);
|
||||
}
|
||||
|
||||
// virtual for overriding in tests
|
||||
protected virtual Version GetAssemblyVersion(string filePath)
|
||||
{
|
||||
return ReadAssemblyVersion(filePath);
|
||||
}
|
||||
|
||||
private static Version ReadAssemblyVersion(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
|
||||
using (var reader = new PEReader(stream))
|
||||
{
|
||||
var metadataReader = reader.GetMetadataReader();
|
||||
|
||||
var assemblyDefinition = metadataReader.GetAssemblyDefinition();
|
||||
return assemblyDefinition.Version;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We're purposely silencing any kinds of I/O exceptions here, just in case something wacky is going on.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.VisualStudio.ProjectSystem;
|
||||
using Microsoft.VisualStudio.ProjectSystem.References;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
// This defines the set of services that we frequently need for working with UnconfiguredProject.
|
||||
//
|
||||
// We're following a somewhat common pattern for code that uses CPS. It's really easy to end up
|
||||
// relying on service location inside CPS, which can be hard to test. This approach makes it easy
|
||||
// for us to build reusable mocks instead.
|
||||
internal interface IUnconfiguredProjectCommonServices
|
||||
{
|
||||
ConfiguredProject ActiveConfiguredProject { get; }
|
||||
|
||||
IAssemblyReferencesService ActiveConfiguredProjectAssemblyReferences { get; }
|
||||
|
||||
IPackageReferencesService ActiveConfiguredProjectPackageReferences { get; }
|
||||
|
||||
Rules.RazorProjectProperties ActiveConfiguredProjectRazorProperties { get; }
|
||||
|
||||
IActiveConfiguredProjectSubscriptionService ActiveConfiguredProjectSubscription { get; }
|
||||
|
||||
IProjectAsynchronousTasksService TasksService { get; }
|
||||
|
||||
IProjectThreadingService ThreadingService { get; }
|
||||
|
||||
UnconfiguredProject UnconfiguredProject { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// 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.ProjectSystem
|
||||
{
|
||||
// Well-Known Schema and property names defined by the ManagedProjectSystem
|
||||
internal static class ManageProjectSystemSchema
|
||||
{
|
||||
public static class ResolvedCompilationReference
|
||||
{
|
||||
public static readonly string SchemaName = "ResolvedCompilationReference";
|
||||
|
||||
public static readonly string ItemName = "ResolvedCompilationReference";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
// 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.VisualStudio.LanguageServices;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal abstract class RazorProjectHostBase : OnceInitializedOnceDisposedAsync, IProjectDynamicLoadComponent
|
||||
{
|
||||
private readonly Workspace _workspace;
|
||||
private readonly AsyncSemaphore _lock;
|
||||
|
||||
private ProjectSnapshotManagerBase _projectManager;
|
||||
private HostProject _current;
|
||||
|
||||
public RazorProjectHostBase(
|
||||
IUnconfiguredProjectCommonServices commonServices,
|
||||
[Import(typeof(VisualStudioWorkspace))] Workspace workspace)
|
||||
: base(commonServices.ThreadingService.JoinableTaskContext)
|
||||
{
|
||||
if (commonServices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(commonServices));
|
||||
}
|
||||
|
||||
if (workspace == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
CommonServices = commonServices;
|
||||
_workspace = workspace;
|
||||
|
||||
_lock = new AsyncSemaphore(initialCount: 1);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
protected RazorProjectHostBase(
|
||||
IUnconfiguredProjectCommonServices commonServices,
|
||||
Workspace workspace,
|
||||
ProjectSnapshotManagerBase projectManager)
|
||||
: base(commonServices.ThreadingService.JoinableTaskContext)
|
||||
{
|
||||
if (commonServices == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(commonServices));
|
||||
}
|
||||
|
||||
if (workspace == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspace));
|
||||
}
|
||||
|
||||
if (projectManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectManager));
|
||||
}
|
||||
|
||||
CommonServices = commonServices;
|
||||
_workspace = workspace;
|
||||
_projectManager = projectManager;
|
||||
|
||||
_lock = new AsyncSemaphore(initialCount: 1);
|
||||
}
|
||||
|
||||
protected IUnconfiguredProjectCommonServices CommonServices { get; }
|
||||
|
||||
// internal for tests. The product will call through the IProjectDynamicLoadComponent interface.
|
||||
internal Task LoadAsync()
|
||||
{
|
||||
return InitializeAsync();
|
||||
}
|
||||
|
||||
protected override Task InitializeCoreAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
CommonServices.UnconfiguredProject.ProjectRenaming += UnconfiguredProject_ProjectRenaming;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override async Task DisposeCoreAsync(bool initialized)
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
CommonServices.UnconfiguredProject.ProjectRenaming -= UnconfiguredProject_ProjectRenaming;
|
||||
|
||||
await ExecuteWithLock(async () =>
|
||||
{
|
||||
if (_current != null)
|
||||
{
|
||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for tests
|
||||
internal async Task OnProjectRenamingAsync()
|
||||
{
|
||||
// When a project gets renamed we expect any rules watched by the derived class to fire.
|
||||
//
|
||||
// However, the project snapshot manager uses the project Fullpath as the key. We want to just
|
||||
// reinitialize the HostProject with the same configuration and settings here, but the updated
|
||||
// FilePath.
|
||||
await ExecuteWithLock(async () =>
|
||||
{
|
||||
if (_current != null)
|
||||
{
|
||||
var old = _current;
|
||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
|
||||
var filePath = CommonServices.UnconfiguredProject.FullPath;
|
||||
await UpdateProjectUnsafeAsync(new HostProject(filePath, old.Configuration)).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Should only be called from the UI thread.
|
||||
private ProjectSnapshotManagerBase GetProjectManager()
|
||||
{
|
||||
CommonServices.ThreadingService.VerifyOnUIThread();
|
||||
|
||||
if (_projectManager == null)
|
||||
{
|
||||
_projectManager = (ProjectSnapshotManagerBase)_workspace.Services.GetLanguageServices(RazorLanguage.Name).GetRequiredService<ProjectSnapshotManager>();
|
||||
}
|
||||
|
||||
return _projectManager;
|
||||
}
|
||||
|
||||
// Must be called inside the lock.
|
||||
protected async Task UpdateProjectUnsafeAsync(HostProject project)
|
||||
{
|
||||
await CommonServices.ThreadingService.SwitchToUIThread();
|
||||
var projectManager = GetProjectManager();
|
||||
|
||||
if (_current == null && project == null)
|
||||
{
|
||||
// This is a no-op. This project isn't using Razor.
|
||||
}
|
||||
else if (_current == null && project != null)
|
||||
{
|
||||
projectManager.HostProjectAdded(project);
|
||||
}
|
||||
else if (_current != null && project == null)
|
||||
{
|
||||
projectManager.HostProjectRemoved(_current);
|
||||
}
|
||||
else
|
||||
{
|
||||
projectManager.HostProjectChanged(project);
|
||||
}
|
||||
|
||||
_current = project;
|
||||
}
|
||||
|
||||
protected async Task ExecuteWithLock(Func<Task> func)
|
||||
{
|
||||
using (JoinableCollection.Join())
|
||||
{
|
||||
using (await _lock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
var task = JoinableFactory.RunAsync(func);
|
||||
await task.Task.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Task IProjectDynamicLoadComponent.LoadAsync()
|
||||
{
|
||||
return InitializeAsync();
|
||||
}
|
||||
|
||||
Task IProjectDynamicLoadComponent.UnloadAsync()
|
||||
{
|
||||
return DisposeAsync();
|
||||
}
|
||||
|
||||
private async Task UnconfiguredProject_ProjectRenaming(object sender, ProjectRenamedEventArgs args)
|
||||
{
|
||||
await OnProjectRenamingAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Rule
|
||||
Description="Configuration Properties"
|
||||
DisplayName="Configuration Properties"
|
||||
Name="RazorConfiguration"
|
||||
PageTemplate="generic"
|
||||
xmlns="http://schemas.microsoft.com/build/2009/properties">
|
||||
<Rule.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
HasConfigurationCondition="True"
|
||||
ItemType="RazorConfiguration" />
|
||||
</Rule.DataSource>
|
||||
|
||||
<Rule.Categories>
|
||||
<Category
|
||||
Name="General"
|
||||
DisplayName="General" />
|
||||
</Rule.Categories>
|
||||
|
||||
<StringProperty
|
||||
Category="General"
|
||||
Description="Razor Extensions"
|
||||
DisplayName="Razor Extensions"
|
||||
Name="Extensions"
|
||||
ReadOnly="True"
|
||||
Visible="True" />
|
||||
|
||||
</Rule>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Rule
|
||||
Description="Extension Properties"
|
||||
DisplayName="Extension Properties"
|
||||
Name="RazorExtension"
|
||||
PageTemplate="generic"
|
||||
xmlns="http://schemas.microsoft.com/build/2009/properties">
|
||||
<Rule.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
HasConfigurationCondition="True"
|
||||
ItemType="RazorExtension" />
|
||||
</Rule.DataSource>
|
||||
|
||||
<Rule.Categories>
|
||||
<Category
|
||||
Name="General"
|
||||
DisplayName="General" />
|
||||
</Rule.Categories>
|
||||
|
||||
<StringProperty
|
||||
Category="General"
|
||||
Description="Razor Extension Assembly Name"
|
||||
DisplayName="Razor Extension Assembly Name"
|
||||
Name="AssemblyName"
|
||||
ReadOnly="True"
|
||||
Visible="True" />
|
||||
|
||||
<StringProperty
|
||||
Category="General"
|
||||
Description="Razor Extension Assembly File Path"
|
||||
DisplayName="Razor Extension Assembly File Path"
|
||||
Name="AssemblyFilePath"
|
||||
ReadOnly="True"
|
||||
Visible="True" />
|
||||
|
||||
</Rule>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Rule
|
||||
Description="Razor Properties"
|
||||
DisplayName="Razor Properties"
|
||||
Name="RazorGeneral"
|
||||
PageTemplate="generic"
|
||||
xmlns="http://schemas.microsoft.com/build/2009/properties">
|
||||
<Rule.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
HasConfigurationCondition="True" />
|
||||
</Rule.DataSource>
|
||||
|
||||
<Rule.Categories>
|
||||
<Category
|
||||
Name="General"
|
||||
DisplayName="General" />
|
||||
</Rule.Categories>
|
||||
|
||||
<StringProperty
|
||||
Category="General"
|
||||
Description="Razor Language Version"
|
||||
DisplayName="Razor Language Version"
|
||||
Name="RazorLangVersion"
|
||||
ReadOnly="True"
|
||||
Visible="True" />
|
||||
|
||||
<StringProperty
|
||||
Category="General"
|
||||
Description="Razor Configuration Name"
|
||||
DisplayName="Razor Configuration Name"
|
||||
Name="RazorDefaultConfiguration"
|
||||
ReadOnly="True"
|
||||
Visible="True" />
|
||||
|
||||
</Rule>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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.ComponentModel.Composition;
|
||||
|
|
@ -31,4 +31,4 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem.Rules
|
|||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Microsoft.VisualStudio.ProjectSystem.References;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
[Export(typeof(IUnconfiguredProjectCommonServices))]
|
||||
internal class UnconfiguredProjectCommonServices : IUnconfiguredProjectCommonServices
|
||||
{
|
||||
private readonly ActiveConfiguredProject<ConfiguredProject> _activeConfiguredProject;
|
||||
private readonly ActiveConfiguredProject<IAssemblyReferencesService> _activeConfiguredProjectAssemblyReferences;
|
||||
private readonly ActiveConfiguredProject<IPackageReferencesService> _activeConfiguredProjectPackageReferences;
|
||||
private readonly ActiveConfiguredProject<Rules.RazorProjectProperties> _activeConfiguredProjectProperties;
|
||||
|
||||
[ImportingConstructor]
|
||||
public UnconfiguredProjectCommonServices(
|
||||
[Import(ExportContractNames.Scopes.UnconfiguredProject)] IProjectAsynchronousTasksService tasksService,
|
||||
IProjectThreadingService threadingService,
|
||||
UnconfiguredProject unconfiguredProject,
|
||||
IActiveConfiguredProjectSubscriptionService activeConfiguredProjectSubscription,
|
||||
ActiveConfiguredProject<ConfiguredProject> activeConfiguredProject,
|
||||
ActiveConfiguredProject<IAssemblyReferencesService> activeConfiguredProjectAssemblyReferences,
|
||||
ActiveConfiguredProject<IPackageReferencesService> activeConfiguredProjectPackageReferences,
|
||||
ActiveConfiguredProject<Rules.RazorProjectProperties> activeConfiguredProjectRazorProperties)
|
||||
{
|
||||
if (tasksService == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tasksService));
|
||||
}
|
||||
|
||||
if (threadingService == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(threadingService));
|
||||
}
|
||||
|
||||
if (unconfiguredProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(unconfiguredProject));
|
||||
}
|
||||
|
||||
if (activeConfiguredProjectSubscription == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ActiveConfiguredProjectSubscription));
|
||||
}
|
||||
|
||||
if (activeConfiguredProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(activeConfiguredProject));
|
||||
}
|
||||
|
||||
if (activeConfiguredProjectAssemblyReferences == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(activeConfiguredProjectAssemblyReferences));
|
||||
}
|
||||
|
||||
if (activeConfiguredProjectPackageReferences == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(activeConfiguredProjectPackageReferences));
|
||||
}
|
||||
|
||||
if (activeConfiguredProjectRazorProperties == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(activeConfiguredProjectRazorProperties));
|
||||
}
|
||||
|
||||
TasksService = tasksService;
|
||||
ThreadingService = threadingService;
|
||||
UnconfiguredProject = unconfiguredProject;
|
||||
ActiveConfiguredProjectSubscription = activeConfiguredProjectSubscription;
|
||||
_activeConfiguredProject = activeConfiguredProject;
|
||||
_activeConfiguredProjectAssemblyReferences = activeConfiguredProjectAssemblyReferences;
|
||||
_activeConfiguredProjectPackageReferences = activeConfiguredProjectPackageReferences;
|
||||
_activeConfiguredProjectProperties = activeConfiguredProjectRazorProperties;
|
||||
}
|
||||
|
||||
public ConfiguredProject ActiveConfiguredProject => _activeConfiguredProject.Value;
|
||||
|
||||
public IAssemblyReferencesService ActiveConfiguredProjectAssemblyReferences => _activeConfiguredProjectAssemblyReferences.Value;
|
||||
|
||||
public IPackageReferencesService ActiveConfiguredProjectPackageReferences => _activeConfiguredProjectPackageReferences.Value;
|
||||
|
||||
public Rules.RazorProjectProperties ActiveConfiguredProjectRazorProperties => _activeConfiguredProjectProperties.Value;
|
||||
|
||||
public IActiveConfiguredProjectSubscriptionService ActiveConfiguredProjectSubscription { get; }
|
||||
|
||||
public IProjectAsynchronousTasksService TasksService { get; }
|
||||
|
||||
public IProjectThreadingService ThreadingService { get; }
|
||||
|
||||
public UnconfiguredProject UnconfiguredProject { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Shell.Interop;
|
||||
|
||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||
|
|
@ -40,7 +41,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
}
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, Project project)
|
||||
public override void ReportError(Exception exception, ProjectSnapshot project)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
|
|
@ -53,7 +54,25 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
var hr = activityLog.LogEntry(
|
||||
(uint)__ACTIVITYLOG_ENTRYTYPE.ALE_ERROR,
|
||||
"Razor Language Services",
|
||||
$"Error encountered from project '{project?.Name}':{Environment.NewLine}{exception}");
|
||||
$"Error encountered from project '{project?.FilePath}':{Environment.NewLine}{exception}");
|
||||
ErrorHandler.ThrowOnFailure(hr);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, Project workspaceProject)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var activityLog = GetActivityLog();
|
||||
if (activityLog != null)
|
||||
{
|
||||
var hr = activityLog.LogEntry(
|
||||
(uint)__ACTIVITYLOG_ENTRYTYPE.ALE_ERROR,
|
||||
"Razor Language Services",
|
||||
$"Error encountered from project '{workspaceProject?.Name}' '{workspaceProject?.FilePath}':{Environment.NewLine}{exception}");
|
||||
ErrorHandler.ThrowOnFailure(hr);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,16 +85,14 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
// This gets called when the project has finished building.
|
||||
public int UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel)
|
||||
{
|
||||
var projectName = _projectService.GetProjectName(pHierProj);
|
||||
var projectPath = _projectService.GetProjectPath(pHierProj);
|
||||
|
||||
// Get the corresponding roslyn project by matching the project name and the project path.
|
||||
foreach (var project in _projectManager.Workspace.CurrentSolution.Projects)
|
||||
foreach (var projectSnapshot in _projectManager.Projects)
|
||||
{
|
||||
if (string.Equals(projectName, project.Name, StringComparison.Ordinal) &&
|
||||
string.Equals(projectPath, project.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(projectPath, projectSnapshot.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_projectManager.ProjectBuildComplete(project);
|
||||
_projectManager.HostProjectBuildComplete(projectSnapshot.HostProject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,16 +96,14 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
|||
return;
|
||||
}
|
||||
|
||||
var projectName = _projectService.GetProjectName(projectItem);
|
||||
var projectPath = _projectService.GetProjectPath(projectItem);
|
||||
|
||||
// Get the corresponding roslyn project by matching the project name and the project path.
|
||||
foreach (var project in _projectManager.Workspace.CurrentSolution.Projects)
|
||||
foreach (var projectSnapshot in _projectManager.Projects)
|
||||
{
|
||||
if (string.Equals(projectName, project.Name, StringComparison.Ordinal) &&
|
||||
string.Equals(projectPath, project.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(projectPath, projectSnapshot.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_projectManager.ProjectBuildComplete(project);
|
||||
_projectManager.HostProjectBuildComplete(projectSnapshot.HostProject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,22 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
|||
internal static string FormatRazorLanguageServiceProjectError(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("RazorLanguageServiceProjectError"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Error encountered from project '{0}':
|
||||
/// {1}
|
||||
/// </summary>
|
||||
internal static string RazorLanguageServiceProjectSnapshotError
|
||||
{
|
||||
get => GetString("RazorLanguageServiceProjectSnapshotError");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error encountered from project '{0}':
|
||||
/// {1}
|
||||
/// </summary>
|
||||
internal static string FormatRazorLanguageServiceProjectSnapshotError(object p0, object p1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("RazorLanguageServiceProjectSnapshotError"), p0, p1);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -126,4 +126,8 @@
|
|||
<data name="RazorLanguageServiceProjectError" xml:space="preserve">
|
||||
<value>Razor Language Service error encountered from project '{0}'.</value>
|
||||
</data>
|
||||
<data name="RazorLanguageServiceProjectSnapshotError" xml:space="preserve">
|
||||
<value>Error encountered from project '{0}':
|
||||
{1}</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Diagnostics;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using MonoDevelop.Core;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
||||
|
|
@ -36,5 +37,18 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
|||
Resources.FormatRazorLanguageServiceProjectError(project?.Name),
|
||||
exception);
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, ProjectSnapshot project)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
Debug.Fail("Null exceptions should not be reported.");
|
||||
return;
|
||||
}
|
||||
|
||||
LoggingService.LogError(
|
||||
Resources.FormatRazorLanguageServiceProjectSnapshotError(project?.FilePath, exception),
|
||||
exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="$(VSIX_MicrosoftCodeAnalysisWorkspacesCommonPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(VSIX_MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="$(VSIX_MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
|
||||
<PackageReference Include="Moq" Version="$(MoqPackageVersion)" />
|
||||
|
|
|
|||
|
|
@ -1,245 +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;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
public class DefaultProjectExtensibilityConfigurationFactoryTest
|
||||
{
|
||||
public static TheoryData LanguageVersionMappingData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<AssemblyIdentity, RazorLanguageVersion>
|
||||
{
|
||||
{ new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("1.0.0.0")), RazorLanguageVersion.Version_1_0 },
|
||||
{ new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("1.1.0.0")), RazorLanguageVersion.Version_1_1 },
|
||||
{ new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("2.0.0.0")), RazorLanguageVersion.Version_2_0 },
|
||||
{ new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("2.1.0.0")), RazorLanguageVersion.Version_2_1 },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(LanguageVersionMappingData))]
|
||||
public void GetLanguageVersion_MapsExactVersionsCorrectly(AssemblyIdentity assemblyIdentity, RazorLanguageVersion expectedVersion)
|
||||
{
|
||||
// Act
|
||||
var languageVersion = DefaultProjectExtensibilityConfigurationFactory.GetLanguageVersion(assemblyIdentity);
|
||||
|
||||
// Assert
|
||||
Assert.Same(expectedVersion, languageVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageVersion_MapsFuture_1_0_VersionsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var assemblyIdentity = new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("1.3.0.0"));
|
||||
|
||||
// Act
|
||||
var languageVersion = DefaultProjectExtensibilityConfigurationFactory.GetLanguageVersion(assemblyIdentity);
|
||||
|
||||
// Assert
|
||||
Assert.Same(RazorLanguageVersion.Version_1_1, languageVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageVersion_MapsFuture_2_0_VersionsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var assemblyIdentity = new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("2.3.0.0"));
|
||||
|
||||
// Act
|
||||
var languageVersion = DefaultProjectExtensibilityConfigurationFactory.GetLanguageVersion(assemblyIdentity);
|
||||
|
||||
// Assert
|
||||
Assert.Same(RazorLanguageVersion.Latest, languageVersion);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("1.0.0.0", "1.0.0.0")]
|
||||
[InlineData("1.1.0.0", "1.1.0.0")]
|
||||
[InlineData("2.0.0.0", "2.0.0.0")]
|
||||
[InlineData("2.0.2.0", "2.0.2.0")]
|
||||
public void GetConfiguration_FindsSupportedConfiguration_ForNewRazor(string razorVersion, string mvcVersion)
|
||||
{
|
||||
// Arrange
|
||||
var references = new AssemblyIdentity[]
|
||||
{
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Razor.Language", new Version(razorVersion)),
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version(mvcVersion)),
|
||||
};
|
||||
|
||||
var factory = new DefaultProjectExtensibilityConfigurationFactory();
|
||||
|
||||
// Act
|
||||
var result = factory.GetConfiguration(references);
|
||||
|
||||
// Assert
|
||||
var configuration = Assert.IsType<MvcExtensibilityConfiguration>(result);
|
||||
Assert.Equal(ProjectExtensibilityConfigurationKind.ApproximateMatch, configuration.Kind);
|
||||
Assert.Equal(razorVersion, configuration.RazorAssembly.Identity.Version.ToString());
|
||||
Assert.Equal(mvcVersion, configuration.MvcAssembly.Identity.Version.ToString());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("1.0.0.0", "1.0.0.0")]
|
||||
[InlineData("1.1.0.0", "1.1.0.0")]
|
||||
[InlineData("1.9.9.9", "2.0.0.0")] // MVC version is ignored
|
||||
public void GetConfiguration_FindsSupportedConfiguration_ForOldRazor(string razorVersion, string mvcVersion)
|
||||
{
|
||||
// Arrange
|
||||
var references = new AssemblyIdentity[]
|
||||
{
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version(razorVersion)),
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version(mvcVersion)),
|
||||
};
|
||||
|
||||
var factory = new DefaultProjectExtensibilityConfigurationFactory();
|
||||
|
||||
// Act
|
||||
var result = factory.GetConfiguration(references);
|
||||
|
||||
// Assert
|
||||
var configuration = Assert.IsType<MvcExtensibilityConfiguration>(result);
|
||||
Assert.Equal(ProjectExtensibilityConfigurationKind.ApproximateMatch, configuration.Kind);
|
||||
Assert.Equal(razorVersion, configuration.RazorAssembly.Identity.Version.ToString());
|
||||
Assert.Equal(mvcVersion, configuration.MvcAssembly.Identity.Version.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetConfiguration_RazorVersion_NewAssemblyWinsOverOld()
|
||||
{
|
||||
// Arrange
|
||||
var references = new AssemblyIdentity[]
|
||||
{
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("1.0.0.0")),
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Razor.Language", new Version("2.0.0.0")),
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("2.0.0.0")),
|
||||
};
|
||||
|
||||
var factory = new DefaultProjectExtensibilityConfigurationFactory();
|
||||
|
||||
// Act
|
||||
var result = factory.GetConfiguration(references);
|
||||
|
||||
// Assert
|
||||
var configuration = Assert.IsType<MvcExtensibilityConfiguration>(result);
|
||||
Assert.Equal(ProjectExtensibilityConfigurationKind.ApproximateMatch, configuration.Kind);
|
||||
Assert.Equal("2.0.0.0", configuration.RazorAssembly.Identity.Version.ToString());
|
||||
Assert.Equal("2.0.0.0", configuration.MvcAssembly.Identity.Version.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetConfiguration_RazorVersion_OldAssemblyIgnoredPastV1()
|
||||
{
|
||||
// Arrange
|
||||
var references = new AssemblyIdentity[]
|
||||
{
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("2.0.0.0")),
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("2.0.0.0")),
|
||||
};
|
||||
|
||||
var factory = new DefaultProjectExtensibilityConfigurationFactory();
|
||||
|
||||
// Act
|
||||
var result = factory.GetConfiguration(references);
|
||||
|
||||
// Assert
|
||||
var configuration = Assert.IsType<MvcExtensibilityConfiguration>(result);
|
||||
Assert.Equal(ProjectExtensibilityConfigurationKind.Fallback, configuration.Kind);
|
||||
Assert.Equal("2.0.0.0", configuration.RazorAssembly.Identity.Version.ToString());
|
||||
Assert.Equal("2.0.0.0", configuration.MvcAssembly.Identity.Version.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetConfiguration_NoRazorVersion_ChoosesDefault()
|
||||
{
|
||||
// Arrange
|
||||
var references = new AssemblyIdentity[]
|
||||
{
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("2.0.0.0")),
|
||||
};
|
||||
|
||||
var factory = new DefaultProjectExtensibilityConfigurationFactory();
|
||||
|
||||
// Act
|
||||
var result = factory.GetConfiguration(references);
|
||||
|
||||
// Assert
|
||||
var configuration = Assert.IsType<MvcExtensibilityConfiguration>(result);
|
||||
Assert.Equal(ProjectExtensibilityConfigurationKind.Fallback, configuration.Kind);
|
||||
Assert.Equal("2.0.0.0", configuration.RazorAssembly.Identity.Version.ToString());
|
||||
Assert.Equal("2.0.0.0", configuration.MvcAssembly.Identity.Version.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetConfiguration_UnsupportedRazorVersion_ChoosesDefault()
|
||||
{
|
||||
// Arrange
|
||||
var references = new AssemblyIdentity[]
|
||||
{
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Razor.Language", new Version("3.0.0.0")),
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("2.0.0.0")),
|
||||
};
|
||||
|
||||
var factory = new DefaultProjectExtensibilityConfigurationFactory();
|
||||
|
||||
// Act
|
||||
var result = factory.GetConfiguration(references);
|
||||
|
||||
// Assert
|
||||
var configuration = Assert.IsType<MvcExtensibilityConfiguration>(result);
|
||||
Assert.Equal(ProjectExtensibilityConfigurationKind.Fallback, configuration.Kind);
|
||||
Assert.Equal("2.0.0.0", configuration.RazorAssembly.Identity.Version.ToString());
|
||||
Assert.Equal("2.0.0.0", configuration.MvcAssembly.Identity.Version.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetConfiguration_NoMvcVersion_ChoosesDefault()
|
||||
{
|
||||
// Arrange
|
||||
var references = new AssemblyIdentity[]
|
||||
{
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Razor.Language", new Version("2.0.0.0")),
|
||||
};
|
||||
|
||||
var factory = new DefaultProjectExtensibilityConfigurationFactory();
|
||||
|
||||
// Act
|
||||
var result = factory.GetConfiguration(references);
|
||||
|
||||
// Assert
|
||||
var configuration = Assert.IsType<MvcExtensibilityConfiguration>(result);
|
||||
Assert.Equal(ProjectExtensibilityConfigurationKind.Fallback, configuration.Kind);
|
||||
Assert.Equal("2.0.0.0", configuration.RazorAssembly.Identity.Version.ToString());
|
||||
Assert.Equal("2.0.0.0", configuration.MvcAssembly.Identity.Version.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetConfiguration_UnsupportedMvcVersion_ChoosesDefault()
|
||||
{
|
||||
// Arrange
|
||||
var references = new AssemblyIdentity[]
|
||||
{
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Razor.Language", new Version("2.0.0.0")),
|
||||
new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("3.0.0.0")),
|
||||
};
|
||||
|
||||
var factory = new DefaultProjectExtensibilityConfigurationFactory();
|
||||
|
||||
// Act
|
||||
var result = factory.GetConfiguration(references);
|
||||
|
||||
// Assert
|
||||
var configuration = Assert.IsType<MvcExtensibilityConfiguration>(result);
|
||||
Assert.Equal(ProjectExtensibilityConfigurationKind.Fallback, configuration.Kind);
|
||||
Assert.Equal("2.0.0.0", configuration.RazorAssembly.Identity.Version.ToString());
|
||||
Assert.Equal("2.0.0.0", configuration.MvcAssembly.Identity.Version.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,331 +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.Linq;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
public class DefaultProjectSnapshotManagerTest
|
||||
{
|
||||
public DefaultProjectSnapshotManagerTest()
|
||||
{
|
||||
Workspace = TestWorkspace.Create();
|
||||
ProjectManager = new TestProjectSnapshotManager(Enumerable.Empty<ProjectSnapshotChangeTrigger>(), Workspace);
|
||||
}
|
||||
|
||||
private TestProjectSnapshotManager ProjectManager { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[Fact]
|
||||
public void ProjectAdded_AddsProject_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectAdded(project);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(project.Id);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectChanged_MadeDirty_RetainsComputedState_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
ProjectManager.ProjectAdded(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Adding some computed state
|
||||
var configuration = Mock.Of<ProjectExtensibilityConfiguration>();
|
||||
ProjectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(project) { Configuration = configuration });
|
||||
ProjectManager.Reset();
|
||||
|
||||
project = project.WithAssemblyName("Test1"); // Simulate a project change
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectChanged(project);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(project.Id);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.Same(configuration, snapshot.Configuration);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectChanged_BackgroundUpdate_MadeClean_WithSignificantChanges_NotifiesListeners_AndDoesNotStartBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
ProjectManager.ProjectAdded(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var configuration = Mock.Of<ProjectExtensibilityConfiguration>();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(project) { Configuration = configuration });
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(project.Id);
|
||||
Assert.False(snapshot.IsDirty);
|
||||
Assert.Same(configuration, snapshot.Configuration);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectChanged_BackgroundUpdate_MadeClean_WithoutSignificantChanges_NotifiesListeners_AndDoesNotStartBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
ProjectManager.ProjectAdded(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var configuration = Mock.Of<ProjectExtensibilityConfiguration>();
|
||||
ProjectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(project) { Configuration = configuration });
|
||||
ProjectManager.Reset();
|
||||
|
||||
project = project.WithAssemblyName("Test1"); // Simulate a project change
|
||||
ProjectManager.ProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(project) { Configuration = configuration });
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(project.Id);
|
||||
Assert.False(snapshot.IsDirty);
|
||||
Assert.Same(configuration, snapshot.Configuration);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectChanged_BackgroundUpdate_StillDirty_WithSignificantChanges_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
ProjectManager.ProjectAdded(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var configuration = Mock.Of<ProjectExtensibilityConfiguration>();
|
||||
|
||||
// Compute an update for "Test"
|
||||
var update = new ProjectSnapshotUpdateContext(project) { Configuration = configuration };
|
||||
|
||||
project = project.WithAssemblyName("Test1"); // Simulate a project change
|
||||
ProjectManager.ProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(update);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(project.Id);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.Same(configuration, snapshot.Configuration);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectChanged_BackgroundUpdate_StillDirty_WithoutSignificantChanges_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
ProjectManager.ProjectAdded(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var configuration = Mock.Of<ProjectExtensibilityConfiguration>();
|
||||
ProjectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(project) { Configuration = configuration });
|
||||
|
||||
project = project.WithAssemblyName("Test1"); // Simulate a project change
|
||||
ProjectManager.ProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Compute an update for "Test1"
|
||||
var update = new ProjectSnapshotUpdateContext(project) { Configuration = configuration };
|
||||
|
||||
project = project.WithAssemblyName("Test2"); // Simulate a project change
|
||||
ProjectManager.ProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(update); // Still dirty because the project changed while computing the update
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(project.Id);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.Same(configuration, snapshot.Configuration);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectChanged_IgnoresUnknownProject()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectChanged(project);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectChanged_WithComputedState_IgnoresUnknownProject()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(project));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectBuildComplete_KnownProject_NotifiesBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
ProjectManager.ProjectAdded(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectBuildComplete(project);
|
||||
|
||||
// Assert
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectBuildComplete_IgnoresUnknownProject()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectBuildComplete(project);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectRemoved_RemovesProject_NotifiesListeners_DoesNotStartBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
|
||||
ProjectManager.ProjectAdded(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectRemoved(project);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectRemoved_IgnoresUnknownProject()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectRemoved(project);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProjectsCleared_RemovesProject_NotifiesListeners_DoesNotStartBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
var project = Workspace.CurrentSolution.AddProject("Test", "Test", LanguageNames.CSharp);
|
||||
|
||||
ProjectManager.ProjectAdded(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectsCleared();
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(IEnumerable<ProjectSnapshotChangeTrigger> triggers, Workspace workspace)
|
||||
: base(Mock.Of<ForegroundDispatcher>(), Mock.Of<ErrorReporter>(), Mock.Of<ProjectSnapshotWorker>(), triggers, workspace)
|
||||
{
|
||||
}
|
||||
|
||||
public bool ListenersNotified { get; private set; }
|
||||
|
||||
public bool WorkerStarted { get; private set; }
|
||||
|
||||
public DefaultProjectSnapshot GetSnapshot(ProjectId id)
|
||||
{
|
||||
return Projects.Cast<DefaultProjectSnapshot>().FirstOrDefault(s => s.UnderlyingProject.Id == id);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ListenersNotified = false;
|
||||
WorkerStarted = false;
|
||||
}
|
||||
|
||||
protected override void NotifyListeners(ProjectChangeEventArgs e)
|
||||
{
|
||||
ListenersNotified = true;
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(Project project)
|
||||
{
|
||||
WorkerStarted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,12 +2,7 @@
|
|||
// 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.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
|
|
@ -15,19 +10,20 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public class DefaultProjectSnapshotTest
|
||||
{
|
||||
[Fact]
|
||||
public void WithProjectChange_WithProject_CreatesSnapshot_UpdatesUnderlyingProject()
|
||||
public void WithWorkspaceProject_CreatesSnapshot_UpdatesUnderlyingProject()
|
||||
{
|
||||
// Arrange
|
||||
var underlyingProject = GetProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(underlyingProject);
|
||||
var hostProject = new HostProject("Test.cshtml", FallbackRazorConfiguration.MVC_2_0);
|
||||
var workspaceProject = GetWorkspaceProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
||||
|
||||
var anotherProject = GetProject("Test1");
|
||||
var anotherProject = GetWorkspaceProject("Test1");
|
||||
|
||||
// Act
|
||||
var snapshot = original.WithProjectChange(anotherProject);
|
||||
var snapshot = original.WithWorkspaceProject(anotherProject);
|
||||
|
||||
// Assert
|
||||
Assert.Same(anotherProject, snapshot.UnderlyingProject);
|
||||
Assert.Same(anotherProject, snapshot.WorkspaceProject);
|
||||
Assert.Equal(original.ComputedVersion, snapshot.ComputedVersion);
|
||||
Assert.Equal(original.Configuration, snapshot.Configuration);
|
||||
Assert.Equal(original.TagHelpers, snapshot.TagHelpers);
|
||||
|
|
@ -37,23 +33,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void WithProjectChange_WithProject_CreatesSnapshot_UpdatesValues()
|
||||
{
|
||||
// Arrange
|
||||
var underlyingProject = GetProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(underlyingProject);
|
||||
var hostProject = new HostProject("Test.cshtml", FallbackRazorConfiguration.MVC_2_0);
|
||||
var workspaceProject = GetWorkspaceProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
||||
|
||||
var anotherProject = GetProject("Test1");
|
||||
var update = new ProjectSnapshotUpdateContext(anotherProject)
|
||||
var anotherProject = GetWorkspaceProject("Test1");
|
||||
var update = new ProjectSnapshotUpdateContext(original.FilePath, hostProject, anotherProject, original.Version)
|
||||
{
|
||||
Configuration = Mock.Of<ProjectExtensibilityConfiguration>(),
|
||||
TagHelpers = Array.Empty<TagHelperDescriptor>(),
|
||||
};
|
||||
|
||||
// Act
|
||||
var snapshot = original.WithProjectChange(update);
|
||||
var snapshot = original.WithComputedUpdate(update);
|
||||
|
||||
// Assert
|
||||
Assert.Same(original.UnderlyingProject, snapshot.UnderlyingProject);
|
||||
Assert.Equal(update.UnderlyingProject.Version, snapshot.ComputedVersion);
|
||||
Assert.Same(update.Configuration, snapshot.Configuration);
|
||||
Assert.Same(original.WorkspaceProject, snapshot.WorkspaceProject);
|
||||
Assert.Same(update.TagHelpers, snapshot.TagHelpers);
|
||||
}
|
||||
|
||||
|
|
@ -61,12 +55,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void HaveTagHelpersChanged_NoUpdatesToTagHelpers_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var underlyingProject = GetProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(underlyingProject);
|
||||
var hostProject = new HostProject("Test1.csproj", RazorConfiguration.Default);
|
||||
var workspaceProject = GetWorkspaceProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
||||
|
||||
var anotherProject = GetProject("Test1");
|
||||
var update = new ProjectSnapshotUpdateContext(anotherProject);
|
||||
var snapshot = original.WithProjectChange(update);
|
||||
var anotherProject = GetWorkspaceProject("Test1");
|
||||
var update = new ProjectSnapshotUpdateContext("Test1.csproj", hostProject, anotherProject, VersionStamp.Default);
|
||||
var snapshot = original.WithComputedUpdate(update);
|
||||
|
||||
// Act
|
||||
var result = snapshot.HaveTagHelpersChanged(original);
|
||||
|
|
@ -79,11 +74,12 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
public void HaveTagHelpersChanged_TagHelpersUpdated_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var underlyingProject = GetProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(underlyingProject);
|
||||
var hostProject = new HostProject("Test1.csproj", RazorConfiguration.Default);
|
||||
var workspaceProject = GetWorkspaceProject("Test1");
|
||||
var original = new DefaultProjectSnapshot(hostProject, workspaceProject);
|
||||
|
||||
var anotherProject = GetProject("Test1");
|
||||
var update = new ProjectSnapshotUpdateContext(anotherProject)
|
||||
var anotherProject = GetWorkspaceProject("Test1");
|
||||
var update = new ProjectSnapshotUpdateContext("Test1.csproj", hostProject, anotherProject, VersionStamp.Default)
|
||||
{
|
||||
TagHelpers = new[]
|
||||
{
|
||||
|
|
@ -91,7 +87,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
TagHelperDescriptorBuilder.Create("Two", "TestAssembly").Build(),
|
||||
},
|
||||
};
|
||||
var snapshot = original.WithProjectChange(update);
|
||||
var snapshot = original.WithComputedUpdate(update);
|
||||
|
||||
// Act
|
||||
var result = snapshot.HaveTagHelpersChanged(original);
|
||||
|
|
@ -100,7 +96,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
Assert.True(result);
|
||||
}
|
||||
|
||||
private Project GetProject(string name)
|
||||
private Project GetWorkspaceProject(string name)
|
||||
{
|
||||
Project project = null;
|
||||
TestWorkspace.Create(workspace =>
|
||||
|
|
|
|||
|
|
@ -14,33 +14,53 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
public WorkspaceProjectSnapshotChangeTriggerTest()
|
||||
{
|
||||
Solution emptySolution = null;
|
||||
Project project1 = null;
|
||||
Project project2 = null;
|
||||
Project project3 = null;
|
||||
Solution solutionWithTwoProjects = null;
|
||||
Solution solutionWithOneProject = null;
|
||||
Workspace = TestWorkspace.Create();
|
||||
EmptySolution = Workspace.CurrentSolution.GetIsolatedSolution();
|
||||
|
||||
Workspace = TestWorkspace.Create(ws =>
|
||||
{
|
||||
emptySolution = ws.CurrentSolution.GetIsolatedSolution();
|
||||
project1 = ws.CurrentSolution.AddProject("One", "One", LanguageNames.CSharp);
|
||||
project2 = project1.Solution.AddProject("Two", "Two", LanguageNames.CSharp);
|
||||
solutionWithTwoProjects = project2.Solution;
|
||||
var projectId1 = ProjectId.CreateNewId("One");
|
||||
var projectId2 = ProjectId.CreateNewId("Two");
|
||||
var projectId3 = ProjectId.CreateNewId("Three");
|
||||
|
||||
project3 = emptySolution.GetIsolatedSolution().AddProject("Three", "Three", LanguageNames.CSharp);
|
||||
solutionWithOneProject = project3.Solution;
|
||||
});
|
||||
SolutionWithTwoProjects = Workspace.CurrentSolution
|
||||
.AddProject(ProjectInfo.Create(
|
||||
projectId1,
|
||||
VersionStamp.Default,
|
||||
"One",
|
||||
"One",
|
||||
LanguageNames.CSharp,
|
||||
filePath: "One.csproj"))
|
||||
.AddProject(ProjectInfo.Create(
|
||||
projectId2,
|
||||
VersionStamp.Default,
|
||||
"Two",
|
||||
"Two",
|
||||
LanguageNames.CSharp,
|
||||
filePath: "Two.csproj"));
|
||||
|
||||
EmptySolution = emptySolution;
|
||||
ProjectNumberOne = project1;
|
||||
ProjectNumberTwo = project2;
|
||||
ProjectNumberThree = project3;
|
||||
SolutionWithTwoProjects = solutionWithTwoProjects;
|
||||
SolutionWithOneProject = solutionWithOneProject;
|
||||
SolutionWithOneProject = EmptySolution.GetIsolatedSolution()
|
||||
.AddProject(ProjectInfo.Create(
|
||||
projectId3,
|
||||
VersionStamp.Default,
|
||||
"Three",
|
||||
"Three",
|
||||
LanguageNames.CSharp,
|
||||
filePath: "Three.csproj"));
|
||||
|
||||
ProjectNumberOne = SolutionWithTwoProjects.GetProject(projectId1);
|
||||
ProjectNumberTwo = SolutionWithTwoProjects.GetProject(projectId2);
|
||||
ProjectNumberThree = SolutionWithOneProject.GetProject(projectId3);
|
||||
|
||||
HostProjectOne = new HostProject("One.csproj", FallbackRazorConfiguration.MVC_1_1);
|
||||
HostProjectTwo = new HostProject("Two.csproj", FallbackRazorConfiguration.MVC_1_1);
|
||||
HostProjectThree = new HostProject("Three.csproj", FallbackRazorConfiguration.MVC_1_1);
|
||||
}
|
||||
|
||||
private HostProject HostProjectOne { get; }
|
||||
|
||||
private HostProject HostProjectTwo { get; }
|
||||
|
||||
private HostProject HostProjectThree { get; }
|
||||
|
||||
private Solution EmptySolution { get; }
|
||||
|
||||
private Solution SolutionWithOneProject { get; }
|
||||
|
|
@ -66,7 +86,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Arrange
|
||||
var trigger = new WorkspaceProjectSnapshotChangeTrigger();
|
||||
var projectManager = new TestProjectSnapshotManager(new[] { trigger }, Workspace);
|
||||
|
||||
projectManager.HostProjectAdded(HostProjectOne);
|
||||
projectManager.HostProjectAdded(HostProjectTwo);
|
||||
|
||||
var e = new WorkspaceChangeEventArgs(kind, oldSolution: EmptySolution, newSolution: SolutionWithTwoProjects);
|
||||
|
||||
// Act
|
||||
|
|
@ -74,9 +96,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
projectManager.Projects.OrderBy(p => p.UnderlyingProject.Name),
|
||||
p => Assert.Equal(ProjectNumberOne.Id, p.UnderlyingProject.Id),
|
||||
p => Assert.Equal(ProjectNumberTwo.Id, p.UnderlyingProject.Id));
|
||||
projectManager.Projects.OrderBy(p => p.WorkspaceProject.Name),
|
||||
p => Assert.Equal(ProjectNumberOne.Id, p.WorkspaceProject.Id),
|
||||
p => Assert.Equal(ProjectNumberTwo.Id, p.WorkspaceProject.Id));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -90,21 +112,25 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Arrange
|
||||
var trigger = new WorkspaceProjectSnapshotChangeTrigger();
|
||||
var projectManager = new TestProjectSnapshotManager(new[] { trigger }, Workspace);
|
||||
projectManager.HostProjectAdded(HostProjectOne);
|
||||
projectManager.HostProjectAdded(HostProjectTwo);
|
||||
projectManager.HostProjectAdded(HostProjectThree);
|
||||
|
||||
// Initialize with a project. This will get removed.
|
||||
var e = new WorkspaceChangeEventArgs(WorkspaceChangeKind.SolutionAdded, oldSolution: EmptySolution, newSolution: SolutionWithOneProject);
|
||||
trigger.Workspace_WorkspaceChanged(Workspace, e);
|
||||
|
||||
e = new WorkspaceChangeEventArgs(kind, oldSolution: EmptySolution, newSolution: SolutionWithTwoProjects);
|
||||
e = new WorkspaceChangeEventArgs(kind, oldSolution: SolutionWithOneProject, newSolution: SolutionWithTwoProjects);
|
||||
|
||||
// Act
|
||||
trigger.Workspace_WorkspaceChanged(Workspace, e);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
projectManager.Projects.OrderBy(p => p.UnderlyingProject.Name),
|
||||
p => Assert.Equal(ProjectNumberOne.Id, p.UnderlyingProject.Id),
|
||||
p => Assert.Equal(ProjectNumberTwo.Id, p.UnderlyingProject.Id));
|
||||
projectManager.Projects.OrderBy(p => p.WorkspaceProject?.Name),
|
||||
p => Assert.Null(p.WorkspaceProject),
|
||||
p => Assert.Equal(ProjectNumberOne.Id, p.WorkspaceProject.Id),
|
||||
p => Assert.Equal(ProjectNumberTwo.Id, p.WorkspaceProject.Id));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -115,6 +141,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Arrange
|
||||
var trigger = new WorkspaceProjectSnapshotChangeTrigger();
|
||||
var projectManager = new TestProjectSnapshotManager(new[] { trigger }, Workspace);
|
||||
projectManager.HostProjectAdded(HostProjectOne);
|
||||
projectManager.HostProjectAdded(HostProjectTwo);
|
||||
|
||||
// Initialize with some projects.
|
||||
var e = new WorkspaceChangeEventArgs(WorkspaceChangeKind.SolutionAdded, oldSolution: EmptySolution, newSolution: SolutionWithTwoProjects);
|
||||
|
|
@ -128,13 +156,13 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
projectManager.Projects.OrderBy(p => p.UnderlyingProject.Name),
|
||||
projectManager.Projects.OrderBy(p => p.WorkspaceProject.Name),
|
||||
p =>
|
||||
{
|
||||
Assert.Equal(ProjectNumberOne.Id, p.UnderlyingProject.Id);
|
||||
Assert.Equal("Changed", p.UnderlyingProject.AssemblyName);
|
||||
Assert.Equal(ProjectNumberOne.Id, p.WorkspaceProject.Id);
|
||||
Assert.Equal("Changed", p.WorkspaceProject.AssemblyName);
|
||||
},
|
||||
p => Assert.Equal(ProjectNumberTwo.Id, p.UnderlyingProject.Id));
|
||||
p => Assert.Equal(ProjectNumberTwo.Id, p.WorkspaceProject.Id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -143,6 +171,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Arrange
|
||||
var trigger = new WorkspaceProjectSnapshotChangeTrigger();
|
||||
var projectManager = new TestProjectSnapshotManager(new[] { trigger }, Workspace);
|
||||
projectManager.HostProjectAdded(HostProjectOne);
|
||||
projectManager.HostProjectAdded(HostProjectTwo);
|
||||
|
||||
// Initialize with some projects project.
|
||||
var e = new WorkspaceChangeEventArgs(WorkspaceChangeKind.SolutionAdded, oldSolution: EmptySolution, newSolution: SolutionWithTwoProjects);
|
||||
|
|
@ -156,8 +186,9 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
projectManager.Projects.OrderBy(p => p.UnderlyingProject.Name),
|
||||
p => Assert.Equal(ProjectNumberTwo.Id, p.UnderlyingProject.Id));
|
||||
projectManager.Projects.OrderBy(p => p.WorkspaceProject?.Name),
|
||||
p => Assert.Null(p.WorkspaceProject),
|
||||
p => Assert.Equal(ProjectNumberTwo.Id, p.WorkspaceProject.Id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -166,6 +197,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Arrange
|
||||
var trigger = new WorkspaceProjectSnapshotChangeTrigger();
|
||||
var projectManager = new TestProjectSnapshotManager(new[] { trigger }, Workspace);
|
||||
projectManager.HostProjectAdded(HostProjectThree);
|
||||
|
||||
var solution = SolutionWithOneProject;
|
||||
var e = new WorkspaceChangeEventArgs(WorkspaceChangeKind.ProjectAdded, oldSolution: EmptySolution, newSolution: solution, projectId: ProjectNumberThree.Id);
|
||||
|
|
@ -175,19 +207,21 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
projectManager.Projects.OrderBy(p => p.UnderlyingProject.Name),
|
||||
p => Assert.Equal(ProjectNumberThree.Id, p.UnderlyingProject.Id));
|
||||
projectManager.Projects.OrderBy(p => p.WorkspaceProject.Name),
|
||||
p => Assert.Equal(ProjectNumberThree.Id, p.WorkspaceProject.Id));
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(IEnumerable<ProjectSnapshotChangeTrigger> triggers, Workspace workspace)
|
||||
public TestProjectSnapshotManager(IEnumerable<ProjectSnapshotChangeTrigger> triggers, Workspace workspace)
|
||||
: base(Mock.Of<ForegroundDispatcher>(), Mock.Of<ErrorReporter>(), new TestProjectSnapshotWorker(), triggers, workspace)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(Project project)
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
Assert.NotNull(context.HostProject);
|
||||
Assert.NotNull(context.WorkspaceProject);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Moq;
|
||||
|
|
@ -21,7 +23,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var testImportsPath = "C:\\path\\to\\project\\_ViewImports.cshtml";
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
||||
var anotherTracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == anotherFilePath && t.ProjectPath == projectPath);
|
||||
var templateEngineFactoryService = GetProjectEngineFactoryService();
|
||||
var projectEngineFactoryService = GetProjectEngineFactoryService();
|
||||
var fileChangeTracker = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker.Setup(f => f.FilePath).Returns(testImportsPath);
|
||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
||||
|
|
@ -36,7 +38,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
.Returns(Mock.Of<FileChangeTracker>());
|
||||
|
||||
var called = false;
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, templateEngineFactoryService);
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, projectEngineFactoryService);
|
||||
manager.OnSubscribed(tracker);
|
||||
manager.OnSubscribed(anotherTracker);
|
||||
manager.Changed += (sender, args) =>
|
||||
|
|
@ -63,7 +65,18 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var projectManager = new Mock<ProjectSnapshotManager>();
|
||||
projectManager.Setup(p => p.Projects).Returns(Array.Empty<ProjectSnapshot>());
|
||||
|
||||
var service = new DefaultProjectEngineFactoryService(projectManager.Object);
|
||||
var projectEngineFactory = new Mock<IFallbackProjectEngineFactory>();
|
||||
projectEngineFactory.Setup(s => s.Create(It.IsAny<RazorConfiguration>(), It.IsAny<RazorProjectFileSystem>(), It.IsAny<Action<RazorProjectEngineBuilder>>()))
|
||||
.Returns<RazorConfiguration, RazorProjectFileSystem, Action<RazorProjectEngineBuilder>>(
|
||||
(c, fs, b) => RazorProjectEngine.Create(
|
||||
RazorConfiguration.Default,
|
||||
fs,
|
||||
builder => RazorExtensions.Register(builder)));
|
||||
|
||||
var service = new DefaultProjectEngineFactoryService(
|
||||
projectManager.Object,
|
||||
projectEngineFactory.Object,
|
||||
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[0]);
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Moq;
|
||||
|
|
@ -18,7 +20,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var filePath = "C:\\path\\to\\project\\Views\\Home\\file.cshtml";
|
||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
||||
var templateEngineFactoryService = GetTemplateEngineFactoryService();
|
||||
var projectEngineService = GetProjectEngineFactoryService();
|
||||
var fileChangeTracker1 = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker1.Setup(f => f.StartListening()).Verifiable();
|
||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
||||
|
|
@ -39,7 +41,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
.Returns(fileChangeTracker3.Object)
|
||||
.Verifiable();
|
||||
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, templateEngineFactoryService);
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, projectEngineService);
|
||||
|
||||
// Act
|
||||
manager.OnSubscribed(tracker);
|
||||
|
|
@ -58,7 +60,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var filePath = "C:\\path\\to\\project\\file.cshtml";
|
||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
||||
var templateEngineFactoryService = GetTemplateEngineFactoryService();
|
||||
var projectEngineService = GetProjectEngineFactoryService();
|
||||
|
||||
var callCount = 0;
|
||||
var fileChangeTrackerFactory = new Mock<FileChangeTrackerFactory>();
|
||||
|
|
@ -67,7 +69,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
.Returns(Mock.Of<FileChangeTracker>())
|
||||
.Callback(() => callCount++);
|
||||
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, templateEngineFactoryService);
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, projectEngineService);
|
||||
manager.OnSubscribed(tracker); // Start tracking the import.
|
||||
|
||||
var anotherFilePath = "C:\\path\\to\\project\\anotherFile.cshtml";
|
||||
|
|
@ -87,7 +89,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var filePath = "C:\\path\\to\\project\\file.cshtml";
|
||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
||||
var templateEngineFactoryService = GetTemplateEngineFactoryService();
|
||||
var projectEngineService = GetProjectEngineFactoryService();
|
||||
|
||||
var fileChangeTracker = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker.Setup(f => f.StopListening()).Verifiable();
|
||||
|
|
@ -97,7 +99,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
.Returns(fileChangeTracker.Object)
|
||||
.Verifiable();
|
||||
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, templateEngineFactoryService);
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, projectEngineService);
|
||||
manager.OnSubscribed(tracker); // Start tracking the import.
|
||||
|
||||
// Act
|
||||
|
|
@ -115,7 +117,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
var filePath = "C:\\path\\to\\project\\file.cshtml";
|
||||
var projectPath = "C:\\path\\to\\project\\project.csproj";
|
||||
var tracker = Mock.Of<VisualStudioDocumentTracker>(t => t.FilePath == filePath && t.ProjectPath == projectPath);
|
||||
var templateEngineFactoryService = GetTemplateEngineFactoryService();
|
||||
var projectEngineService = GetProjectEngineFactoryService();
|
||||
|
||||
var fileChangeTracker = new Mock<FileChangeTracker>();
|
||||
fileChangeTracker
|
||||
|
|
@ -126,7 +128,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
.Setup(f => f.Create(It.IsAny<string>()))
|
||||
.Returns(fileChangeTracker.Object);
|
||||
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, templateEngineFactoryService);
|
||||
var manager = new DefaultImportDocumentManager(Dispatcher, new DefaultErrorReporter(), fileChangeTrackerFactory.Object, projectEngineService);
|
||||
manager.OnSubscribed(tracker); // Starts tracking import for the first document.
|
||||
|
||||
var anotherFilePath = "C:\\path\\to\\project\\anotherFile.cshtml";
|
||||
|
|
@ -137,12 +139,23 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
manager.OnUnsubscribed(tracker);
|
||||
}
|
||||
|
||||
private RazorProjectEngineFactoryService GetTemplateEngineFactoryService()
|
||||
private RazorProjectEngineFactoryService GetProjectEngineFactoryService()
|
||||
{
|
||||
var projectManager = new Mock<ProjectSnapshotManager>();
|
||||
projectManager.Setup(p => p.Projects).Returns(Array.Empty<ProjectSnapshot>());
|
||||
|
||||
var service = new DefaultProjectEngineFactoryService(projectManager.Object);
|
||||
var projectEngineFactory = new Mock<IFallbackProjectEngineFactory>();
|
||||
projectEngineFactory.Setup(s => s.Create(It.IsAny<RazorConfiguration>(), It.IsAny<RazorProjectFileSystem>(), It.IsAny<Action<RazorProjectEngineBuilder>>()))
|
||||
.Returns<RazorConfiguration, RazorProjectFileSystem, Action<RazorProjectEngineBuilder>>(
|
||||
(c, fs, b) => RazorProjectEngine.Create(
|
||||
RazorConfiguration.Default,
|
||||
fs,
|
||||
builder => RazorExtensions.Register(builder)));
|
||||
|
||||
var service = new DefaultProjectEngineFactoryService(
|
||||
projectManager.Object,
|
||||
projectEngineFactory.Object,
|
||||
new Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[0]);
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
|
|
@ -26,30 +27,67 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
project = workspace.CurrentSolution.AddProject(info).GetProject(info.Id);
|
||||
});
|
||||
|
||||
Project = project;
|
||||
WorkspaceProject = project;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// We don't actually look at the project, we rely on the ProjectStateManager
|
||||
public Project Project { get; }
|
||||
private Lazy<IProjectEngineFactory, ICustomProjectEngineFactoryMetadata>[] CustomFactories { get; }
|
||||
|
||||
public Workspace Workspace { 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.ProjectAdded(Project);
|
||||
projectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(Project)
|
||||
{
|
||||
Configuration = new MvcExtensibilityConfiguration(
|
||||
RazorLanguageVersion.Version_2_0,
|
||||
ProjectExtensibilityConfigurationKind.ApproximateMatch,
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("2.0.0.0"))),
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("2.0.0.0")))),
|
||||
});
|
||||
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 =>
|
||||
|
|
@ -59,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>());
|
||||
}
|
||||
|
|
@ -68,17 +130,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.ProjectAdded(Project);
|
||||
projectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(Project)
|
||||
{
|
||||
Configuration = new MvcExtensibilityConfiguration(
|
||||
RazorLanguageVersion.Version_1_1,
|
||||
ProjectExtensibilityConfigurationKind.ApproximateMatch,
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("1.1.3.0"))),
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("1.1.3.0")))),
|
||||
});
|
||||
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 =>
|
||||
|
|
@ -88,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>());
|
||||
}
|
||||
|
|
@ -97,17 +153,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.ProjectAdded(Project);
|
||||
projectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(Project)
|
||||
{
|
||||
Configuration = new MvcExtensibilityConfiguration(
|
||||
RazorLanguageVersion.Version_1_0,
|
||||
ProjectExtensibilityConfigurationKind.ApproximateMatch,
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("1.0.0.0"))),
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("1.0.0.0")))),
|
||||
});
|
||||
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 =>
|
||||
|
|
@ -117,26 +166,41 @@ 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_HigherMvcVersion_UsesLatest()
|
||||
public void Create_UnknownProject_UsesVersion2_0()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.ProjectAdded(Project);
|
||||
projectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(Project)
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/DifferentPath/", b =>
|
||||
{
|
||||
Configuration = new MvcExtensibilityConfiguration(
|
||||
RazorLanguageVersion.Latest,
|
||||
ProjectExtensibilityConfigurationKind.ApproximateMatch,
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Mvc.Razor", new Version("3.0.0.0"))),
|
||||
new ProjectExtensibilityAssembly(new AssemblyIdentity("Microsoft.AspNetCore.Razor", new Version("3.0.0.0")))),
|
||||
b.Features.Add(new MyCoolNewFeature());
|
||||
});
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager);
|
||||
// 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_ForUnknownConfiguration_UsesFallbackFactory()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.HostProjectAdded(HostProject_For_UnknownConfiguration);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager, FallbackFactory, CustomFactories);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/SomePath/", b =>
|
||||
|
|
@ -146,49 +210,10 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
// Assert
|
||||
Assert.Single(engine.Engine.Features.OfType<MyCoolNewFeature>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.MvcViewDocumentClassifierPass>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_UnknownProjectPath_UsesLatest()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/DifferentPath/", 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>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_MvcReferenceNotFound_UsesLatest()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.ProjectAdded(Project);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager);
|
||||
|
||||
// Act
|
||||
var engine = factoryService.Create("/TestPath/DifferentPath/", 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
|
||||
|
|
|
|||
|
|
@ -1,63 +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.Reflection;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
public class DefaultTagHelperResolverTest
|
||||
{
|
||||
private static readonly Assembly Assembly = typeof(DefaultTagHelperResolverTest).GetTypeInfo().Assembly;
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelpers_DiscoversViewComponentTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var code = @"
|
||||
public class TestViewComponent
|
||||
{
|
||||
public string Invoke(string foo, string bar) => null;
|
||||
}";
|
||||
var syntaxTree = CSharpSyntaxTree.ParseText(code);
|
||||
var compilation = TestCompilation.Create(Assembly, syntaxTree);
|
||||
var tagHelperResolver = new DefaultTagHelperResolver()
|
||||
{
|
||||
ForceEnableViewComponentDiscovery = true
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = tagHelperResolver.GetTagHelpers(compilation);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.Diagnostics);
|
||||
Assert.Equal(1, result.Descriptors.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTagHelpers_DiscoversTagHelpers()
|
||||
{
|
||||
// Arrange
|
||||
var code = $@"
|
||||
public class TestTagHelper : {typeof(TagHelper).FullName}
|
||||
{{
|
||||
}}";
|
||||
var syntaxTree = CSharpSyntaxTree.ParseText(code);
|
||||
var compilation = TestCompilation.Create(Assembly, syntaxTree);
|
||||
var tagHelperResolver = new DefaultTagHelperResolver()
|
||||
{
|
||||
ForceEnableViewComponentDiscovery = true
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = tagHelperResolver.GetTagHelpers(compilation);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.Diagnostics);
|
||||
Assert.Equal(1, result.Descriptors.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.Editor;
|
||||
|
|
@ -64,7 +65,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
});
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, workspace, TextBuffer, ImportDocumentManager);
|
||||
|
||||
var projectSnapshot = new DefaultProjectSnapshot(project);
|
||||
var projectSnapshot = new DefaultProjectSnapshot(new HostProject(project.FilePath, RazorConfiguration.Default), project);
|
||||
var projectChangedArgs = new ProjectChangeEventArgs(projectSnapshot, ProjectChangeKind.Changed);
|
||||
|
||||
var called = false;
|
||||
|
|
@ -92,7 +93,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
});
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, workspace, TextBuffer, ImportDocumentManager);
|
||||
|
||||
var projectSnapshot = new DefaultProjectSnapshot(project);
|
||||
var projectSnapshot = new DefaultProjectSnapshot(new HostProject(project.FilePath, RazorConfiguration.Default), project);
|
||||
var projectChangedArgs = new ProjectChangeEventArgs(projectSnapshot, ProjectChangeKind.TagHelpersChanged);
|
||||
|
||||
var called = false;
|
||||
|
|
@ -120,7 +121,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
});
|
||||
var documentTracker = new DefaultVisualStudioDocumentTracker(Dispatcher, FilePath, ProjectPath, ProjectManager, WorkspaceEditorSettings, workspace, TextBuffer, ImportDocumentManager);
|
||||
|
||||
var projectSnapshot = new DefaultProjectSnapshot(project);
|
||||
var projectSnapshot = new DefaultProjectSnapshot(new HostProject(project.FilePath, RazorConfiguration.Default), project);
|
||||
var projectChangedArgs = new ProjectChangeEventArgs(projectSnapshot, ProjectChangeKind.Changed);
|
||||
|
||||
var called = false;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Runtime\Microsoft.AspNetCore.Razor.Runtime.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" />
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue