Add a project system
Step 1: Add HostProject This is a somewhat complex addition to the ProjectSnapshotManager. Now that we accept updates from the underlying IDE project system we need to coordinate those with the Workspace. This means that ProjectSnapshot itself now also has a version concept. Step 2: Introduce a new project system based on CPS We use project capabilities defined by the Razor SDK to determine whether to rely on MSBuild evaluation to detect the configuration or whether to fallback to assembly-based detection. Step 3: Flow RazorConfiguration everywhere We use now expose the RazorConfiguration to the language service and editor. This means that we no longer need to detect the project's configuration asynchronously, it happens much faster now.
This commit is contained in:
parent
59a1cf9293
commit
5008c7803c
|
|
@ -8,6 +8,8 @@
|
|||
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.1.0-preview2-30077</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
|
||||
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.1.0-preview2-30106</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
|
||||
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview2-30106</MicrosoftAspNetCoreTestingPackageVersion>
|
||||
<MicrosoftBuildFrameworkPackageVersion>15.3.409</MicrosoftBuildFrameworkPackageVersion>
|
||||
<MicrosoftBuildPackageVersion>15.3.409</MicrosoftBuildPackageVersion>
|
||||
<MicrosoftCodeAnalysisCommonPackageVersion>2.4.0</MicrosoftCodeAnalysisCommonPackageVersion>
|
||||
<MicrosoftCodeAnalysisCSharpPackageVersion>2.4.0</MicrosoftCodeAnalysisCSharpPackageVersion>
|
||||
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.1.0-preview2-30106</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
|
||||
|
|
@ -46,6 +48,7 @@
|
|||
<VSIX_MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>2.6.0-beta1-62023-02</VSIX_MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
|
||||
<VSIX_MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>2.6.0-beta1-62023-02</VSIX_MicrosoftCodeAnalysisEditorFeaturesTextPackageVersion>
|
||||
<VSIX_MicrosoftCodeAnalysisRemoteRazorServiceHubPackageVersion>2.6.0-beta1-62023-02</VSIX_MicrosoftCodeAnalysisRemoteRazorServiceHubPackageVersion>
|
||||
<VSIX_MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>2.6.0-beta1-62023-02</VSIX_MicrosoftCodeAnalysisVisualBasicWorkspacesPackageVersion>
|
||||
<VSIX_MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>2.6.0-beta1-62023-02</VSIX_MicrosoftCodeAnalysisWorkspacesCommonPackageVersion>
|
||||
<VSIX_MicrosoftVisualStudioLanguageServicesPackageVersion>2.6.0-beta1-62023-02</VSIX_MicrosoftVisualStudioLanguageServicesPackageVersion>
|
||||
<VSIX_MicrosoftVisualStudioLanguageServicesRazorRemoteClientPackageVersion>2.6.0-beta1-62023-02</VSIX_MicrosoftVisualStudioLanguageServicesRazorRemoteClientPackageVersion>
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,6 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
|
|
@ -18,32 +16,60 @@ 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;
|
||||
|
||||
HostProject = hostProject;
|
||||
|
||||
ComputedVersion = other.ComputedVersion;
|
||||
Configuration = other.Configuration;
|
||||
FilePath = other.FilePath;
|
||||
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));
|
||||
}
|
||||
|
||||
WorkspaceProject = workspaceProject;
|
||||
|
||||
ComputedVersion = other.ComputedVersion;
|
||||
FilePath = other.FilePath;
|
||||
HostProject = other.HostProject;
|
||||
|
||||
Version = other.Version.GetNewerVersion();
|
||||
}
|
||||
|
||||
private DefaultProjectSnapshot(ProjectSnapshotUpdateContext update, DefaultProjectSnapshot other)
|
||||
|
|
@ -58,34 +84,67 @@ 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;
|
||||
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 HostProject HostProject { get; }
|
||||
|
||||
public override bool IsInitialized => WorkspaceProject != null;
|
||||
|
||||
public override VersionStamp Version { get; }
|
||||
|
||||
public override Project WorkspaceProject { get; }
|
||||
|
||||
// This is the version that the computed state is based on.
|
||||
public VersionStamp? ComputedVersion { get; set; }
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
|
@ -95,14 +154,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return new DefaultProjectSnapshot(update, this);
|
||||
}
|
||||
|
||||
public bool HasConfigurationChanged(ProjectSnapshot original)
|
||||
public bool HasConfigurationChanged(DefaultProjectSnapshot original)
|
||||
{
|
||||
if (original == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(original));
|
||||
}
|
||||
|
||||
return !object.Equals(Configuration, original.Configuration);
|
||||
// We don't have any computed state right now, so treat all background updates as
|
||||
// significant.
|
||||
return !object.Equals(ComputedVersion, original.ComputedVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,27 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
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 +34,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 +74,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 +88,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,17 +102,25 @@ 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());
|
||||
}
|
||||
|
||||
// Now we need to know if the changes that we applied are significant. If that's the case then
|
||||
|
|
@ -156,58 +132,288 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
}
|
||||
}
|
||||
|
||||
public override void ProjectRemoved(Project underlyingProject)
|
||||
public override void HostProjectAdded(HostProject hostProject)
|
||||
{
|
||||
if (underlyingProject == null)
|
||||
if (hostProject == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(underlyingProject));
|
||||
throw new ArgumentNullException(nameof(hostProject));
|
||||
}
|
||||
|
||||
if (_projects.TryGetValue(underlyingProject.Id, out var snapshot))
|
||||
|
||||
_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))
|
||||
{
|
||||
_projects.Remove(underlyingProject.Id);
|
||||
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());
|
||||
}
|
||||
|
||||
// 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 ProjectBuildComplete(Project underlyingProject)
|
||||
public override void WorkspaceProjectAdded(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 original))
|
||||
_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.WithProjectChange(underlyingProject);
|
||||
_projects[underlyingProject.Id] = snapshot;
|
||||
var snapshot = original.WithWorkspaceProject(workspaceProject);
|
||||
_projects[workspaceProject.FilePath] = snapshot;
|
||||
|
||||
// Notify the background worker so it can trigger tag helper discovery.
|
||||
NotifyBackgroundWorker(underlyingProject);
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ProjectsCleared()
|
||||
public override void WorkspaceProjectRemoved(Project workspaceProject)
|
||||
{
|
||||
foreach (var kvp in _projects.ToArray())
|
||||
if (workspaceProject == 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(workspaceProject));
|
||||
}
|
||||
|
||||
_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 ReportError(Exception exception)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
_errorReporter.ReportError(exception);
|
||||
}
|
||||
|
||||
public override void ReportError(Exception exception, ProjectSnapshot project)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
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
|
||||
|
|
@ -221,15 +427,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,25 +9,16 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
internal class DefaultProjectSnapshotWorker : ProjectSnapshotWorker
|
||||
{
|
||||
private readonly ProjectExtensibilityConfigurationFactory _configurationFactory;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
|
||||
public DefaultProjectSnapshotWorker(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
ProjectExtensibilityConfigurationFactory configurationFactory)
|
||||
public DefaultProjectSnapshotWorker(ForegroundDispatcher foregroundDispatcher)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (configurationFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurationFactory));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_configurationFactory = configurationFactory;
|
||||
}
|
||||
|
||||
public override Task ProcessUpdateAsync(ProjectSnapshotUpdateContext update, CancellationToken cancellationToken = default(CancellationToken))
|
||||
|
|
@ -46,14 +37,15 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
return ProjectUpdatesCoreAsync(update);
|
||||
}
|
||||
|
||||
private async Task ProjectUpdatesCoreAsync(object state)
|
||||
protected virtual void OnProcessingUpdate()
|
||||
{
|
||||
var update = (ProjectSnapshotUpdateContext)state;
|
||||
}
|
||||
|
||||
// We'll have more things to process here, but for now we're just hardcoding the configuration.
|
||||
private Task ProjectUpdatesCoreAsync(object state)
|
||||
{
|
||||
OnProcessingUpdate();
|
||||
|
||||
var configuration = await _configurationFactory.GetConfigurationAsync(update.UnderlyingProject);
|
||||
update.Configuration = configuration;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultProjectSnapshotWorker(
|
||||
_foregroundDispatcher,
|
||||
languageServices.GetRequiredService<ProjectExtensibilityConfigurationFactory>());
|
||||
return new DefaultProjectSnapshotWorker(_foregroundDispatcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,20 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
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 VersionStamp Version { get; }
|
||||
|
||||
public abstract Project WorkspaceProject { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,20 +9,26 @@ 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 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, ProjectSnapshot project);
|
||||
|
||||
public abstract void ReportError(Exception exception, Project 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 (string.Equals(filePath, project.WorkspaceProject.FilePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return project;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,18 +9,35 @@ 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 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
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;
|
||||
|
|
@ -14,11 +13,7 @@ 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;
|
||||
|
||||
|
|
@ -41,22 +36,19 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
|
||||
// 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 configuration = project?.Configuration ?? DefaultConfiguration;
|
||||
var fileSystem = RazorProjectFileSystem.Create(projectPath);
|
||||
|
||||
RazorProjectEngine projectEngine;
|
||||
if (razorLanguageVersion.Major == 1)
|
||||
if (configuration.LanguageVersion.Major == 1)
|
||||
{
|
||||
projectEngine = RazorProjectEngine.Create(razorConfiguration, fileSystem, b =>
|
||||
projectEngine = RazorProjectEngine.Create(configuration, fileSystem, b =>
|
||||
{
|
||||
configure?.Invoke(b);
|
||||
|
||||
Mvc1_X.RazorExtensions.Register(b);
|
||||
|
||||
if (configuration.MvcAssembly.Identity.Version.Minor >= 1)
|
||||
if (configuration.LanguageVersion.Minor >= 1)
|
||||
{
|
||||
Mvc1_X.RazorExtensions.RegisterViewComponentTagHelpers(b);
|
||||
}
|
||||
|
|
@ -64,7 +56,7 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
}
|
||||
else
|
||||
{
|
||||
projectEngine = RazorProjectEngine.Create(razorConfiguration, fileSystem, b =>
|
||||
projectEngine = RazorProjectEngine.Create(configuration, fileSystem, b =>
|
||||
{
|
||||
configure?.Invoke(b);
|
||||
|
||||
|
|
@ -83,9 +75,9 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
for (var i = 0; i < projects.Count; i++)
|
||||
{
|
||||
var project = projects[i];
|
||||
if (project.UnderlyingProject.FilePath != null)
|
||||
if (project.WorkspaceProject?.FilePath != null)
|
||||
{
|
||||
if (string.Equals(directory, NormalizeDirectoryPath(Path.GetDirectoryName(project.UnderlyingProject.FilePath)), StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(directory, NormalizeDirectoryPath(Path.GetDirectoryName(project.WorkspaceProject.FilePath)), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return project;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
_textViews = new List<ITextView>();
|
||||
}
|
||||
|
||||
internal override ProjectExtensibilityConfiguration Configuration => _project.Configuration;
|
||||
public override RazorConfiguration Configuration => _project.Configuration;
|
||||
|
||||
public override EditorSettings EditorSettings => _editorSettingsManager.Current;
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
|
||||
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;
|
||||
|
||||
|
|
@ -206,7 +206,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor.Editor
|
|||
private 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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="$(MicrosoftBuildPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Build.Framework" Version="$(MicrosoftBuildFrameworkPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.ComponentModelHost" Version="$(MicrosoftVisualStudioComponentModelHostPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Editor" Version="$(MicrosoftVisualStudioEditorPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.LanguageServices.Razor.RemoteClient" Version="$(VSIX_MicrosoftVisualStudioLanguageServicesRazorRemoteClientPackageVersion)" />
|
||||
|
|
@ -26,7 +28,7 @@
|
|||
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.8.0" Version="$(MicrosoftVisualStudioShellInterop80PackageVersion)" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.9.0" Version="$(MicrosoftVisualStudioShellInterop90PackageVersion)" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop" Version="$(MicrosoftVisualStudioShellInteropPackageVersion)" />
|
||||
|
||||
|
||||
<!-- We need to use this version of Json.Net to maintain consistency with Visual Studio. -->
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(VisualStudio_NewtonsoftJsonPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
|
@ -35,4 +37,82 @@
|
|||
<ProjectReference Include="..\..\src\Microsoft.VisualStudio.Editor.Razor\Microsoft.VisualStudio.Editor.Razor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
The ProjectSystem.SDK tasks that handle XamlPropertyRule don't work on the dotnet core version
|
||||
of MSBuild. The workaround here is to only hardcode the generated code location such that it gets
|
||||
checked in. Then we don't need to generate it at build time.
|
||||
|
||||
If you make changes to the rule files, then you need to update them using Desktop MSBuild :(
|
||||
-->
|
||||
<ItemGroup Condition="'$(MSBuildRuntimeType)'=='Core'">
|
||||
<None Include="ProjectSystem\Rules\RazorConfiguration.xaml" />
|
||||
<None Include="ProjectSystem\Rules\RazorExtension.xaml" />
|
||||
<None Include="ProjectSystem\Rules\RazorGeneral.xaml" />
|
||||
<Compile Update="ProjectSystem\Rules\RazorConfiguration.cs">
|
||||
<DependentUpon>RazorConfiguration.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="ProjectSystem\Rules\RazorExtension.cs">
|
||||
<DependentUpon>RazorExtension.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="ProjectSystem\Rules\RazorGeneral.cs">
|
||||
<DependentUpon>RazorGeneral.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="ProjectSystem\Rules\RazorConfiguration.xaml">
|
||||
<LogicalName>XamlRuleToCode:RazorConfiguration.xaml</LogicalName>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="ProjectSystem\Rules\RazorExtension.xaml">
|
||||
<LogicalName>XamlRuleToCode:RazorExtension.xaml</LogicalName>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="ProjectSystem\Rules\RazorGeneral.xaml">
|
||||
<LogicalName>XamlRuleToCode:RazorGeneral.xaml</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(MSBuildRuntimeType)'!='Core'">
|
||||
<XamlPropertyRule Include="ProjectSystem\Rules\RazorConfiguration.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:GenerateRuleSourceFromXaml</Generator>
|
||||
<Namespace>Microsoft.CodeAnalysis.Razor.ProjectSystem.Rules</Namespace>
|
||||
<RuleInjectionClassName>RazorProjectProperties</RuleInjectionClassName>
|
||||
<Context></Context>
|
||||
<OutputPath>ProjectSystem\Rules\</OutputPath>
|
||||
</XamlPropertyRule>
|
||||
<XamlPropertyRule Include="ProjectSystem\Rules\RazorExtension.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:GenerateRuleSourceFromXaml</Generator>
|
||||
<Namespace>Microsoft.CodeAnalysis.Razor.ProjectSystem.Rules</Namespace>
|
||||
<RuleInjectionClassName>RazorProjectProperties</RuleInjectionClassName>
|
||||
<OutputPath>ProjectSystem\Rules\</OutputPath>
|
||||
</XamlPropertyRule>
|
||||
<XamlPropertyRule Include="ProjectSystem\Rules\RazorGeneral.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:GenerateRuleSourceFromXaml</Generator>
|
||||
<Namespace>Microsoft.CodeAnalysis.Razor.ProjectSystem.Rules</Namespace>
|
||||
<RuleInjectionClassName>RazorProjectProperties</RuleInjectionClassName>
|
||||
<OutputPath>ProjectSystem\Rules\</OutputPath>
|
||||
</XamlPropertyRule>
|
||||
<Compile Update="ProjectSystem\Rules\RazorConfiguration.cs">
|
||||
<DependentUpon>RazorGeneral.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="ProjectSystem\Rules\RazorExtension.cs">
|
||||
<DependentUpon>RazorGeneral.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="ProjectSystem\Rules\RazorGeneral.cs">
|
||||
<DependentUpon>RazorGeneral.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Despite us specifying %(XamlPropertyRule.OutputPath), the ProjectSystem.SDK targets still add files in the
|
||||
obj folder to the Compile group. This is a workaround to remove them.
|
||||
-->
|
||||
<Target Name="GrossProjectSystemSDKWorkaround" AfterTargets="GenerateRulePrep">
|
||||
<ItemGroup>
|
||||
<Compile Remove="@(XamlPropertyRule->'$(IntermediateOutputPath)%(FileName)$(DefaultLanguageSourceExtension)')" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
// 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)
|
||||
{
|
||||
await ExecuteWithLock(async () =>
|
||||
{
|
||||
if (IsDisposing || IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
// 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 });
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
await ExecuteWithLock(async () =>
|
||||
{
|
||||
if (IsDisposing || IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
// 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,88 @@
|
|||
// 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(
|
||||
IProjectThreadingService threadingService,
|
||||
UnconfiguredProject unconfiguredProject,
|
||||
IActiveConfiguredProjectSubscriptionService activeConfiguredProjectSubscription,
|
||||
ActiveConfiguredProject<ConfiguredProject> activeConfiguredProject,
|
||||
ActiveConfiguredProject<IAssemblyReferencesService> activeConfiguredProjectAssemblyReferences,
|
||||
ActiveConfiguredProject<IPackageReferencesService> activeConfiguredProjectPackageReferences,
|
||||
ActiveConfiguredProject<Rules.RazorProjectProperties> activeConfiguredProjectRazorProperties)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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 IProjectThreadingService ThreadingService { get; }
|
||||
|
||||
public 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,212 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem.Rules {
|
||||
|
||||
|
||||
internal partial class RazorConfiguration {
|
||||
|
||||
/// <summary>Backing field for deserialized rule.<see cref='Microsoft.Build.Framework.XamlTypes.Rule'/>.</summary>
|
||||
private static Microsoft.Build.Framework.XamlTypes.Rule deserializedFallbackRule;
|
||||
|
||||
/// <summary>The name of the schema to look for at runtime to fulfill property access.</summary>
|
||||
internal const string SchemaName = "RazorConfiguration";
|
||||
|
||||
/// <summary>The ItemType given in the Rule.DataSource property. May not apply to every Property's individual DataSource.</summary>
|
||||
internal const string PrimaryDataSourceItemType = "RazorConfiguration";
|
||||
|
||||
/// <summary>The Label given in the Rule.DataSource property. May not apply to every Property's individual DataSource.</summary>
|
||||
internal const string PrimaryDataSourceLabel = "";
|
||||
|
||||
/// <summary>Razor Extensions (The "Extensions" property).</summary>
|
||||
internal const string ExtensionsProperty = "Extensions";
|
||||
|
||||
/// <summary>Backing field for the <see cref='Microsoft.Build.Framework.XamlTypes.Rule'/> property.</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule;
|
||||
|
||||
/// <summary>Backing field for the file name of the rule property.</summary>
|
||||
private string file;
|
||||
|
||||
/// <summary>Backing field for the ItemType property.</summary>
|
||||
private string itemType;
|
||||
|
||||
/// <summary>Backing field for the ItemName property.</summary>
|
||||
private string itemName;
|
||||
|
||||
/// <summary>Configured Project</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject;
|
||||
|
||||
/// <summary>The dictionary of named catalogs.</summary>
|
||||
private System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs;
|
||||
|
||||
/// <summary>Backing field for the <see cref='Microsoft.VisualStudio.ProjectSystem.Properties.IRule'/> property.</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule fallbackRule;
|
||||
|
||||
/// <summary>Thread locking object</summary>
|
||||
private object locker = new object();
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorConfiguration class.</summary>
|
||||
internal RazorConfiguration(Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule) {
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorConfiguration class.</summary>
|
||||
internal RazorConfiguration(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs, string context, string file, string itemType, string itemName) :
|
||||
this(GetRule(System.Collections.Immutable.ImmutableDictionary.GetValueOrDefault(catalogs, context), file, itemType, itemName)) {
|
||||
if ((configuredProject == null)) {
|
||||
throw new System.ArgumentNullException("configuredProject");
|
||||
}
|
||||
this.configuredProject = configuredProject;
|
||||
this.catalogs = catalogs;
|
||||
this.file = file;
|
||||
this.itemType = itemType;
|
||||
this.itemName = itemName;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorConfiguration class.</summary>
|
||||
internal RazorConfiguration(Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule, Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject) :
|
||||
this(rule) {
|
||||
if ((rule == null)) {
|
||||
throw new System.ArgumentNullException("rule");
|
||||
}
|
||||
if ((configuredProject == null)) {
|
||||
throw new System.ArgumentNullException("configuredProject");
|
||||
}
|
||||
this.configuredProject = configuredProject;
|
||||
this.rule = rule;
|
||||
this.file = this.rule.File;
|
||||
this.itemType = this.rule.ItemType;
|
||||
this.itemName = this.rule.ItemName;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorConfiguration class.</summary>
|
||||
internal RazorConfiguration(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs, string context, Microsoft.VisualStudio.ProjectSystem.Properties.IProjectPropertiesContext propertyContext) :
|
||||
this(configuredProject, catalogs, context, GetContextFile(propertyContext), propertyContext.ItemType, propertyContext.ItemName) {
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorConfiguration class that assumes a project context (neither property sheet nor items).</summary>
|
||||
internal RazorConfiguration(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs) :
|
||||
this(configuredProject, catalogs, "Project", null, null, null) {
|
||||
}
|
||||
|
||||
/// <summary>Gets the IRule used to get and set properties.</summary>
|
||||
public Microsoft.VisualStudio.ProjectSystem.Properties.IRule Rule {
|
||||
get {
|
||||
return this.rule;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Razor Extensions</summary>
|
||||
internal Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty Extensions {
|
||||
get {
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IRule localRule = this.rule;
|
||||
if ((localRule == null)) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
}
|
||||
if ((localRule == null)) {
|
||||
return null;
|
||||
}
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(ExtensionsProperty)));
|
||||
if (((property == null)
|
||||
&& (this.GeneratedFallbackRule != null))) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(ExtensionsProperty)));
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Get the fallback rule if the current rule on disk is missing or a property in the rule on disk is missing</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule GeneratedFallbackRule {
|
||||
get {
|
||||
if (((this.fallbackRule == null)
|
||||
&& (this.configuredProject != null))) {
|
||||
System.Threading.Monitor.Enter(this.locker);
|
||||
try {
|
||||
if ((this.fallbackRule == null)) {
|
||||
this.InitializeFallbackRule();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
System.Threading.Monitor.Exit(this.locker);
|
||||
}
|
||||
}
|
||||
return this.fallbackRule;
|
||||
}
|
||||
}
|
||||
|
||||
private static Microsoft.VisualStudio.ProjectSystem.Properties.IRule GetRule(Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog catalog, string file, string itemType, string itemName) {
|
||||
if ((catalog == null)) {
|
||||
return null;
|
||||
}
|
||||
return catalog.BindToContext(SchemaName, file, itemType, itemName);
|
||||
}
|
||||
|
||||
private static string GetContextFile(Microsoft.VisualStudio.ProjectSystem.Properties.IProjectPropertiesContext propertiesContext) {
|
||||
if ((propertiesContext.IsProjectFile == true)) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return propertiesContext.File;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeFallbackRule() {
|
||||
if ((this.configuredProject == null)) {
|
||||
return;
|
||||
}
|
||||
Microsoft.Build.Framework.XamlTypes.Rule unboundRule = RazorConfiguration.deserializedFallbackRule;
|
||||
if ((unboundRule == null)) {
|
||||
System.IO.Stream xamlStream = null;
|
||||
System.Reflection.Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
try {
|
||||
xamlStream = thisAssembly.GetManifestResourceStream("XamlRuleToCode:RazorConfiguration.xaml");
|
||||
Microsoft.Build.Framework.XamlTypes.IProjectSchemaNode root = ((Microsoft.Build.Framework.XamlTypes.IProjectSchemaNode)(System.Xaml.XamlServices.Load(xamlStream)));
|
||||
System.Collections.Generic.IEnumerator<System.Object> ruleEnumerator = root.GetSchemaObjects(typeof(Microsoft.Build.Framework.XamlTypes.Rule)).GetEnumerator();
|
||||
for (
|
||||
; ((unboundRule == null)
|
||||
&& ruleEnumerator.MoveNext());
|
||||
) {
|
||||
Microsoft.Build.Framework.XamlTypes.Rule t = ((Microsoft.Build.Framework.XamlTypes.Rule)(ruleEnumerator.Current));
|
||||
if (System.StringComparer.OrdinalIgnoreCase.Equals(t.Name, SchemaName)) {
|
||||
unboundRule = t;
|
||||
unboundRule.Name = "843bc0bc-5265-4864-9b06-f5d5503b0484";
|
||||
RazorConfiguration.deserializedFallbackRule = unboundRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if ((xamlStream != null)) {
|
||||
((System.IDisposable)(xamlStream)).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.configuredProject.Services.AdditionalRuleDefinitions.AddRuleDefinition(unboundRule, "FallbackRuleCodeGenerationContext");
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog catalog = this.configuredProject.Services.PropertyPagesCatalog.GetMemoryOnlyCatalog("FallbackRuleCodeGenerationContext");
|
||||
this.fallbackRule = catalog.BindToContext(unboundRule.Name, this.file, this.itemType, this.itemName);
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class RazorProjectProperties {
|
||||
|
||||
private static System.Func<System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>>, object, RazorConfiguration> CreateRazorConfigurationPropertiesDelegate = new System.Func<System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>>, object, RazorConfiguration>(CreateRazorConfigurationProperties);
|
||||
|
||||
private static RazorConfiguration CreateRazorConfigurationProperties(System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>> namedCatalogs, object state) {
|
||||
RazorProjectProperties that = ((RazorProjectProperties)(state));
|
||||
return new RazorConfiguration(that.ConfiguredProject, namedCatalogs.Result, "Project", that.File, that.ItemType, that.ItemName);
|
||||
}
|
||||
|
||||
/// <summary>Gets the strongly-typed property accessor used to get and set Configuration Properties properties.</summary>
|
||||
internal System.Threading.Tasks.Task<RazorConfiguration> GetRazorConfigurationPropertiesAsync() {
|
||||
System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>> namedCatalogsTask = this.GetNamedCatalogsAsync();
|
||||
return namedCatalogsTask.ContinueWith(CreateRazorConfigurationPropertiesDelegate, this, System.Threading.CancellationToken.None, System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously, System.Threading.Tasks.TaskScheduler.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,235 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem.Rules {
|
||||
|
||||
|
||||
internal partial class RazorExtension {
|
||||
|
||||
/// <summary>Backing field for deserialized rule.<see cref='Microsoft.Build.Framework.XamlTypes.Rule'/>.</summary>
|
||||
private static Microsoft.Build.Framework.XamlTypes.Rule deserializedFallbackRule;
|
||||
|
||||
/// <summary>The name of the schema to look for at runtime to fulfill property access.</summary>
|
||||
internal const string SchemaName = "RazorExtension";
|
||||
|
||||
/// <summary>The ItemType given in the Rule.DataSource property. May not apply to every Property's individual DataSource.</summary>
|
||||
internal const string PrimaryDataSourceItemType = "RazorExtension";
|
||||
|
||||
/// <summary>The Label given in the Rule.DataSource property. May not apply to every Property's individual DataSource.</summary>
|
||||
internal const string PrimaryDataSourceLabel = "";
|
||||
|
||||
/// <summary>Razor Extension Assembly Name (The "AssemblyName" property).</summary>
|
||||
internal const string AssemblyNameProperty = "AssemblyName";
|
||||
|
||||
/// <summary>Razor Extension Assembly File Path (The "AssemblyFilePath" property).</summary>
|
||||
internal const string AssemblyFilePathProperty = "AssemblyFilePath";
|
||||
|
||||
/// <summary>Backing field for the <see cref='Microsoft.Build.Framework.XamlTypes.Rule'/> property.</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule;
|
||||
|
||||
/// <summary>Backing field for the file name of the rule property.</summary>
|
||||
private string file;
|
||||
|
||||
/// <summary>Backing field for the ItemType property.</summary>
|
||||
private string itemType;
|
||||
|
||||
/// <summary>Backing field for the ItemName property.</summary>
|
||||
private string itemName;
|
||||
|
||||
/// <summary>Configured Project</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject;
|
||||
|
||||
/// <summary>The dictionary of named catalogs.</summary>
|
||||
private System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs;
|
||||
|
||||
/// <summary>Backing field for the <see cref='Microsoft.VisualStudio.ProjectSystem.Properties.IRule'/> property.</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule fallbackRule;
|
||||
|
||||
/// <summary>Thread locking object</summary>
|
||||
private object locker = new object();
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorExtension class.</summary>
|
||||
internal RazorExtension(Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule) {
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorExtension class.</summary>
|
||||
internal RazorExtension(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs, string context, string file, string itemType, string itemName) :
|
||||
this(GetRule(System.Collections.Immutable.ImmutableDictionary.GetValueOrDefault(catalogs, context), file, itemType, itemName)) {
|
||||
if ((configuredProject == null)) {
|
||||
throw new System.ArgumentNullException("configuredProject");
|
||||
}
|
||||
this.configuredProject = configuredProject;
|
||||
this.catalogs = catalogs;
|
||||
this.file = file;
|
||||
this.itemType = itemType;
|
||||
this.itemName = itemName;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorExtension class.</summary>
|
||||
internal RazorExtension(Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule, Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject) :
|
||||
this(rule) {
|
||||
if ((rule == null)) {
|
||||
throw new System.ArgumentNullException("rule");
|
||||
}
|
||||
if ((configuredProject == null)) {
|
||||
throw new System.ArgumentNullException("configuredProject");
|
||||
}
|
||||
this.configuredProject = configuredProject;
|
||||
this.rule = rule;
|
||||
this.file = this.rule.File;
|
||||
this.itemType = this.rule.ItemType;
|
||||
this.itemName = this.rule.ItemName;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorExtension class.</summary>
|
||||
internal RazorExtension(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs, string context, Microsoft.VisualStudio.ProjectSystem.Properties.IProjectPropertiesContext propertyContext) :
|
||||
this(configuredProject, catalogs, context, GetContextFile(propertyContext), propertyContext.ItemType, propertyContext.ItemName) {
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorExtension class that assumes a project context (neither property sheet nor items).</summary>
|
||||
internal RazorExtension(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs) :
|
||||
this(configuredProject, catalogs, "Project", null, null, null) {
|
||||
}
|
||||
|
||||
/// <summary>Gets the IRule used to get and set properties.</summary>
|
||||
public Microsoft.VisualStudio.ProjectSystem.Properties.IRule Rule {
|
||||
get {
|
||||
return this.rule;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Razor Extension Assembly Name</summary>
|
||||
internal Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty AssemblyName {
|
||||
get {
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IRule localRule = this.rule;
|
||||
if ((localRule == null)) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
}
|
||||
if ((localRule == null)) {
|
||||
return null;
|
||||
}
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(AssemblyNameProperty)));
|
||||
if (((property == null)
|
||||
&& (this.GeneratedFallbackRule != null))) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(AssemblyNameProperty)));
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Razor Extension Assembly File Path</summary>
|
||||
internal Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty AssemblyFilePath {
|
||||
get {
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IRule localRule = this.rule;
|
||||
if ((localRule == null)) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
}
|
||||
if ((localRule == null)) {
|
||||
return null;
|
||||
}
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(AssemblyFilePathProperty)));
|
||||
if (((property == null)
|
||||
&& (this.GeneratedFallbackRule != null))) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(AssemblyFilePathProperty)));
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Get the fallback rule if the current rule on disk is missing or a property in the rule on disk is missing</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule GeneratedFallbackRule {
|
||||
get {
|
||||
if (((this.fallbackRule == null)
|
||||
&& (this.configuredProject != null))) {
|
||||
System.Threading.Monitor.Enter(this.locker);
|
||||
try {
|
||||
if ((this.fallbackRule == null)) {
|
||||
this.InitializeFallbackRule();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
System.Threading.Monitor.Exit(this.locker);
|
||||
}
|
||||
}
|
||||
return this.fallbackRule;
|
||||
}
|
||||
}
|
||||
|
||||
private static Microsoft.VisualStudio.ProjectSystem.Properties.IRule GetRule(Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog catalog, string file, string itemType, string itemName) {
|
||||
if ((catalog == null)) {
|
||||
return null;
|
||||
}
|
||||
return catalog.BindToContext(SchemaName, file, itemType, itemName);
|
||||
}
|
||||
|
||||
private static string GetContextFile(Microsoft.VisualStudio.ProjectSystem.Properties.IProjectPropertiesContext propertiesContext) {
|
||||
if ((propertiesContext.IsProjectFile == true)) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return propertiesContext.File;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeFallbackRule() {
|
||||
if ((this.configuredProject == null)) {
|
||||
return;
|
||||
}
|
||||
Microsoft.Build.Framework.XamlTypes.Rule unboundRule = RazorExtension.deserializedFallbackRule;
|
||||
if ((unboundRule == null)) {
|
||||
System.IO.Stream xamlStream = null;
|
||||
System.Reflection.Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
try {
|
||||
xamlStream = thisAssembly.GetManifestResourceStream("XamlRuleToCode:RazorExtension.xaml");
|
||||
Microsoft.Build.Framework.XamlTypes.IProjectSchemaNode root = ((Microsoft.Build.Framework.XamlTypes.IProjectSchemaNode)(System.Xaml.XamlServices.Load(xamlStream)));
|
||||
System.Collections.Generic.IEnumerator<System.Object> ruleEnumerator = root.GetSchemaObjects(typeof(Microsoft.Build.Framework.XamlTypes.Rule)).GetEnumerator();
|
||||
for (
|
||||
; ((unboundRule == null)
|
||||
&& ruleEnumerator.MoveNext());
|
||||
) {
|
||||
Microsoft.Build.Framework.XamlTypes.Rule t = ((Microsoft.Build.Framework.XamlTypes.Rule)(ruleEnumerator.Current));
|
||||
if (System.StringComparer.OrdinalIgnoreCase.Equals(t.Name, SchemaName)) {
|
||||
unboundRule = t;
|
||||
unboundRule.Name = "10b26d1c-5ab7-4ca2-ab64-182fe18d53dd";
|
||||
RazorExtension.deserializedFallbackRule = unboundRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if ((xamlStream != null)) {
|
||||
((System.IDisposable)(xamlStream)).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.configuredProject.Services.AdditionalRuleDefinitions.AddRuleDefinition(unboundRule, "FallbackRuleCodeGenerationContext");
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog catalog = this.configuredProject.Services.PropertyPagesCatalog.GetMemoryOnlyCatalog("FallbackRuleCodeGenerationContext");
|
||||
this.fallbackRule = catalog.BindToContext(unboundRule.Name, this.file, this.itemType, this.itemName);
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class RazorProjectProperties {
|
||||
|
||||
private static System.Func<System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>>, object, RazorExtension> CreateRazorExtensionPropertiesDelegate = new System.Func<System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>>, object, RazorExtension>(CreateRazorExtensionProperties);
|
||||
|
||||
private static RazorExtension CreateRazorExtensionProperties(System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>> namedCatalogs, object state) {
|
||||
RazorProjectProperties that = ((RazorProjectProperties)(state));
|
||||
return new RazorExtension(that.ConfiguredProject, namedCatalogs.Result, "Project", that.File, that.ItemType, that.ItemName);
|
||||
}
|
||||
|
||||
/// <summary>Gets the strongly-typed property accessor used to get and set Extension Properties properties.</summary>
|
||||
internal System.Threading.Tasks.Task<RazorExtension> GetRazorExtensionPropertiesAsync() {
|
||||
System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>> namedCatalogsTask = this.GetNamedCatalogsAsync();
|
||||
return namedCatalogsTask.ContinueWith(CreateRazorExtensionPropertiesDelegate, this, System.Threading.CancellationToken.None, System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously, System.Threading.Tasks.TaskScheduler.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,235 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem.Rules {
|
||||
|
||||
|
||||
internal partial class RazorGeneral {
|
||||
|
||||
/// <summary>Backing field for deserialized rule.<see cref='Microsoft.Build.Framework.XamlTypes.Rule'/>.</summary>
|
||||
private static Microsoft.Build.Framework.XamlTypes.Rule deserializedFallbackRule;
|
||||
|
||||
/// <summary>The name of the schema to look for at runtime to fulfill property access.</summary>
|
||||
internal const string SchemaName = "RazorGeneral";
|
||||
|
||||
/// <summary>The ItemType given in the Rule.DataSource property. May not apply to every Property's individual DataSource.</summary>
|
||||
internal const string PrimaryDataSourceItemType = null;
|
||||
|
||||
/// <summary>The Label given in the Rule.DataSource property. May not apply to every Property's individual DataSource.</summary>
|
||||
internal const string PrimaryDataSourceLabel = "";
|
||||
|
||||
/// <summary>Razor Language Version (The "RazorLangVersion" property).</summary>
|
||||
internal const string RazorLangVersionProperty = "RazorLangVersion";
|
||||
|
||||
/// <summary>Razor Configuration Name (The "RazorDefaultConfiguration" property).</summary>
|
||||
internal const string RazorDefaultConfigurationProperty = "RazorDefaultConfiguration";
|
||||
|
||||
/// <summary>Backing field for the <see cref='Microsoft.Build.Framework.XamlTypes.Rule'/> property.</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule;
|
||||
|
||||
/// <summary>Backing field for the file name of the rule property.</summary>
|
||||
private string file;
|
||||
|
||||
/// <summary>Backing field for the ItemType property.</summary>
|
||||
private string itemType;
|
||||
|
||||
/// <summary>Backing field for the ItemName property.</summary>
|
||||
private string itemName;
|
||||
|
||||
/// <summary>Configured Project</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject;
|
||||
|
||||
/// <summary>The dictionary of named catalogs.</summary>
|
||||
private System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs;
|
||||
|
||||
/// <summary>Backing field for the <see cref='Microsoft.VisualStudio.ProjectSystem.Properties.IRule'/> property.</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule fallbackRule;
|
||||
|
||||
/// <summary>Thread locking object</summary>
|
||||
private object locker = new object();
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorGeneral class.</summary>
|
||||
internal RazorGeneral(Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule) {
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorGeneral class.</summary>
|
||||
internal RazorGeneral(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs, string context, string file, string itemType, string itemName) :
|
||||
this(GetRule(System.Collections.Immutable.ImmutableDictionary.GetValueOrDefault(catalogs, context), file, itemType, itemName)) {
|
||||
if ((configuredProject == null)) {
|
||||
throw new System.ArgumentNullException("configuredProject");
|
||||
}
|
||||
this.configuredProject = configuredProject;
|
||||
this.catalogs = catalogs;
|
||||
this.file = file;
|
||||
this.itemType = itemType;
|
||||
this.itemName = itemName;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorGeneral class.</summary>
|
||||
internal RazorGeneral(Microsoft.VisualStudio.ProjectSystem.Properties.IRule rule, Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject) :
|
||||
this(rule) {
|
||||
if ((rule == null)) {
|
||||
throw new System.ArgumentNullException("rule");
|
||||
}
|
||||
if ((configuredProject == null)) {
|
||||
throw new System.ArgumentNullException("configuredProject");
|
||||
}
|
||||
this.configuredProject = configuredProject;
|
||||
this.rule = rule;
|
||||
this.file = this.rule.File;
|
||||
this.itemType = this.rule.ItemType;
|
||||
this.itemName = this.rule.ItemName;
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorGeneral class.</summary>
|
||||
internal RazorGeneral(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs, string context, Microsoft.VisualStudio.ProjectSystem.Properties.IProjectPropertiesContext propertyContext) :
|
||||
this(configuredProject, catalogs, context, GetContextFile(propertyContext), propertyContext.ItemType, propertyContext.ItemName) {
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the RazorGeneral class that assumes a project context (neither property sheet nor items).</summary>
|
||||
internal RazorGeneral(Microsoft.VisualStudio.ProjectSystem.ConfiguredProject configuredProject, System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog> catalogs) :
|
||||
this(configuredProject, catalogs, "Project", null, null, null) {
|
||||
}
|
||||
|
||||
/// <summary>Gets the IRule used to get and set properties.</summary>
|
||||
public Microsoft.VisualStudio.ProjectSystem.Properties.IRule Rule {
|
||||
get {
|
||||
return this.rule;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Razor Language Version</summary>
|
||||
internal Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty RazorLangVersion {
|
||||
get {
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IRule localRule = this.rule;
|
||||
if ((localRule == null)) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
}
|
||||
if ((localRule == null)) {
|
||||
return null;
|
||||
}
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(RazorLangVersionProperty)));
|
||||
if (((property == null)
|
||||
&& (this.GeneratedFallbackRule != null))) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(RazorLangVersionProperty)));
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Razor Configuration Name</summary>
|
||||
internal Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty RazorDefaultConfiguration {
|
||||
get {
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IRule localRule = this.rule;
|
||||
if ((localRule == null)) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
}
|
||||
if ((localRule == null)) {
|
||||
return null;
|
||||
}
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(RazorDefaultConfigurationProperty)));
|
||||
if (((property == null)
|
||||
&& (this.GeneratedFallbackRule != null))) {
|
||||
localRule = this.GeneratedFallbackRule;
|
||||
property = ((Microsoft.VisualStudio.ProjectSystem.Properties.IEvaluatedProperty)(localRule.GetProperty(RazorDefaultConfigurationProperty)));
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Get the fallback rule if the current rule on disk is missing or a property in the rule on disk is missing</summary>
|
||||
private Microsoft.VisualStudio.ProjectSystem.Properties.IRule GeneratedFallbackRule {
|
||||
get {
|
||||
if (((this.fallbackRule == null)
|
||||
&& (this.configuredProject != null))) {
|
||||
System.Threading.Monitor.Enter(this.locker);
|
||||
try {
|
||||
if ((this.fallbackRule == null)) {
|
||||
this.InitializeFallbackRule();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
System.Threading.Monitor.Exit(this.locker);
|
||||
}
|
||||
}
|
||||
return this.fallbackRule;
|
||||
}
|
||||
}
|
||||
|
||||
private static Microsoft.VisualStudio.ProjectSystem.Properties.IRule GetRule(Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog catalog, string file, string itemType, string itemName) {
|
||||
if ((catalog == null)) {
|
||||
return null;
|
||||
}
|
||||
return catalog.BindToContext(SchemaName, file, itemType, itemName);
|
||||
}
|
||||
|
||||
private static string GetContextFile(Microsoft.VisualStudio.ProjectSystem.Properties.IProjectPropertiesContext propertiesContext) {
|
||||
if ((propertiesContext.IsProjectFile == true)) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return propertiesContext.File;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeFallbackRule() {
|
||||
if ((this.configuredProject == null)) {
|
||||
return;
|
||||
}
|
||||
Microsoft.Build.Framework.XamlTypes.Rule unboundRule = RazorGeneral.deserializedFallbackRule;
|
||||
if ((unboundRule == null)) {
|
||||
System.IO.Stream xamlStream = null;
|
||||
System.Reflection.Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
try {
|
||||
xamlStream = thisAssembly.GetManifestResourceStream("XamlRuleToCode:RazorGeneral.xaml");
|
||||
Microsoft.Build.Framework.XamlTypes.IProjectSchemaNode root = ((Microsoft.Build.Framework.XamlTypes.IProjectSchemaNode)(System.Xaml.XamlServices.Load(xamlStream)));
|
||||
System.Collections.Generic.IEnumerator<System.Object> ruleEnumerator = root.GetSchemaObjects(typeof(Microsoft.Build.Framework.XamlTypes.Rule)).GetEnumerator();
|
||||
for (
|
||||
; ((unboundRule == null)
|
||||
&& ruleEnumerator.MoveNext());
|
||||
) {
|
||||
Microsoft.Build.Framework.XamlTypes.Rule t = ((Microsoft.Build.Framework.XamlTypes.Rule)(ruleEnumerator.Current));
|
||||
if (System.StringComparer.OrdinalIgnoreCase.Equals(t.Name, SchemaName)) {
|
||||
unboundRule = t;
|
||||
unboundRule.Name = "15acc140-184e-44be-a4d3-62505276a0bb";
|
||||
RazorGeneral.deserializedFallbackRule = unboundRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if ((xamlStream != null)) {
|
||||
((System.IDisposable)(xamlStream)).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.configuredProject.Services.AdditionalRuleDefinitions.AddRuleDefinition(unboundRule, "FallbackRuleCodeGenerationContext");
|
||||
Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog catalog = this.configuredProject.Services.PropertyPagesCatalog.GetMemoryOnlyCatalog("FallbackRuleCodeGenerationContext");
|
||||
this.fallbackRule = catalog.BindToContext(unboundRule.Name, this.file, this.itemType, this.itemName);
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class RazorProjectProperties {
|
||||
|
||||
private static System.Func<System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>>, object, RazorGeneral> CreateRazorGeneralPropertiesDelegate = new System.Func<System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>>, object, RazorGeneral>(CreateRazorGeneralProperties);
|
||||
|
||||
private static RazorGeneral CreateRazorGeneralProperties(System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>> namedCatalogs, object state) {
|
||||
RazorProjectProperties that = ((RazorProjectProperties)(state));
|
||||
return new RazorGeneral(that.ConfiguredProject, namedCatalogs.Result, "Project", that.File, that.ItemType, that.ItemName);
|
||||
}
|
||||
|
||||
/// <summary>Gets the strongly-typed property accessor used to get and set Razor Properties properties.</summary>
|
||||
internal System.Threading.Tasks.Task<RazorGeneral> GetRazorGeneralPropertiesAsync() {
|
||||
System.Threading.Tasks.Task<System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IPropertyPagesCatalog>> namedCatalogsTask = this.GetNamedCatalogsAsync();
|
||||
return namedCatalogsTask.ContinueWith(CreateRazorGeneralPropertiesDelegate, this, System.Threading.CancellationToken.None, System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously, System.Threading.Tasks.TaskScheduler.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// 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;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem.Rules
|
||||
{
|
||||
[Export]
|
||||
internal partial class RazorProjectProperties : StronglyTypedPropertyAccess
|
||||
{
|
||||
[ImportingConstructor]
|
||||
public RazorProjectProperties(ConfiguredProject configuredProject)
|
||||
: base(configuredProject)
|
||||
{
|
||||
}
|
||||
|
||||
public RazorProjectProperties(ConfiguredProject configuredProject, UnconfiguredProject unconfiguredProject)
|
||||
: base(configuredProject, unconfiguredProject)
|
||||
{
|
||||
}
|
||||
|
||||
public RazorProjectProperties(ConfiguredProject configuredProject, IProjectPropertiesContext projectPropertiesContext)
|
||||
: base(configuredProject, projectPropertiesContext)
|
||||
{
|
||||
}
|
||||
|
||||
public RazorProjectProperties(ConfiguredProject configuredProject, string file, string itemType, string itemName)
|
||||
: base(configuredProject, file, itemType, itemName)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 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; }
|
||||
|
||||
IProjectThreadingService ThreadingService { get; }
|
||||
|
||||
UnconfiguredProject UnconfiguredProject { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,6 @@
|
|||
// 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.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
|
|
@ -15,19 +8,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);
|
||||
}
|
||||
|
|
@ -36,25 +30,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)
|
||||
{
|
||||
Configuration = Mock.Of<ProjectExtensibilityConfiguration>(),
|
||||
};
|
||||
var anotherProject = GetWorkspaceProject("Test1");
|
||||
var update = new ProjectSnapshotUpdateContext(original.FilePath, hostProject, anotherProject, original.Version);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,28 +26,31 @@ 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);
|
||||
}
|
||||
|
||||
// We don't actually look at the project, we rely on the ProjectStateManager
|
||||
public Project Project { get; }
|
||||
private HostProject HostProject_For_1_0 { get; }
|
||||
|
||||
public Workspace Workspace { get; }
|
||||
private HostProject HostProject_For_1_1 { get; }
|
||||
|
||||
private HostProject HostProject_For_2_0 { 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()
|
||||
{
|
||||
// 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_0);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager);
|
||||
|
||||
|
|
@ -68,15 +71,8 @@ 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);
|
||||
|
||||
|
|
@ -97,15 +93,8 @@ 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);
|
||||
|
||||
|
|
@ -121,35 +110,6 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
Assert.Empty(engine.Engine.Features.OfType<Mvc1_X.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_HigherMvcVersion_UsesLatest()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.ProjectAdded(Project);
|
||||
projectManager.ProjectUpdated(new ProjectSnapshotUpdateContext(Project)
|
||||
{
|
||||
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")))),
|
||||
});
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager);
|
||||
|
||||
// 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.MvcViewDocumentClassifierPass>());
|
||||
Assert.Single(engine.Engine.Features.OfType<MvcLatest.ViewComponentTagHelperPass>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_UnknownProjectPath_UsesLatest()
|
||||
{
|
||||
|
|
@ -175,7 +135,8 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
|||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Workspace);
|
||||
projectManager.ProjectAdded(Project);
|
||||
projectManager.HostProjectAdded(HostProject_For_2_0);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
var factoryService = new DefaultProjectEngineFactoryService(projectManager);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,815 @@
|
|||
// 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 : ForegroundDispatcherTestBase
|
||||
{
|
||||
public DefaultProjectSnapshotManagerTest()
|
||||
{
|
||||
HostProject = new HostProject("Test.csproj", FallbackRazorConfiguration.MVC_2_0);
|
||||
|
||||
Workspace = TestWorkspace.Create();
|
||||
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Enumerable.Empty<ProjectSnapshotChangeTrigger>(), Workspace);
|
||||
|
||||
var projectId = ProjectId.CreateNewId("Test");
|
||||
var solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create(
|
||||
projectId,
|
||||
VersionStamp.Default,
|
||||
"Test",
|
||||
"Test",
|
||||
LanguageNames.CSharp,
|
||||
"Test.csproj"));
|
||||
WorkspaceProject = solution.GetProject(projectId);
|
||||
|
||||
var vbProjectId = ProjectId.CreateNewId("VB");
|
||||
solution = solution.AddProject(ProjectInfo.Create(
|
||||
vbProjectId,
|
||||
VersionStamp.Default,
|
||||
"VB",
|
||||
"VB",
|
||||
LanguageNames.VisualBasic,
|
||||
"VB.vbproj"));
|
||||
VBWorkspaceProject = solution.GetProject(vbProjectId);
|
||||
|
||||
var projectWithoutFilePathId = ProjectId.CreateNewId("NoFile");
|
||||
solution = solution.AddProject(ProjectInfo.Create(
|
||||
projectWithoutFilePathId,
|
||||
VersionStamp.Default,
|
||||
"NoFile",
|
||||
"NoFile",
|
||||
LanguageNames.CSharp));
|
||||
WorkspaceProjectWithoutFilePath = solution.GetProject(projectWithoutFilePathId);
|
||||
|
||||
// Approximates a project with multi-targeting
|
||||
var projectIdWithDifferentTfm = ProjectId.CreateNewId("TestWithDifferentTfm");
|
||||
solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create(
|
||||
projectIdWithDifferentTfm,
|
||||
VersionStamp.Default,
|
||||
"Test (Different TFM)",
|
||||
"Test",
|
||||
LanguageNames.CSharp,
|
||||
"Test.csproj"));
|
||||
WorkspaceProjectWithDifferentTfm = solution.GetProject(projectIdWithDifferentTfm);
|
||||
}
|
||||
|
||||
private HostProject HostProject { get; }
|
||||
|
||||
private Project WorkspaceProject { get; }
|
||||
|
||||
private Project WorkspaceProjectWithDifferentTfm { get; }
|
||||
|
||||
private Project WorkspaceProjectWithoutFilePath { get; }
|
||||
|
||||
private Project VBWorkspaceProject { get; }
|
||||
|
||||
private TestProjectSnapshotManager ProjectManager { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectAdded_WithoutWorkspaceProject_NotifiesListeners()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectAdded_FindsWorkspaceProject_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
Assert.True(Workspace.TryApplyChanges(WorkspaceProject.Solution));
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.True(snapshot.IsInitialized);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectChanged_WithoutWorkspaceProject_NotifiesListeners_AndDoesNotStartBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_0); // Simulate a project change
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectChanged_WithWorkspaceProject_RetainsComputedState_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Adding some computed state
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_0); // Simulate a project change
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.True(snapshot.IsInitialized);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectChanged_IgnoresUnknownProject()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectChanged(HostProject);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void HostProjectRemoved_RemovesProject_NotifiesListeners()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.HostProjectRemoved(HostProject);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WithComputedState_IgnoresUnknownProject()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(new ProjectSnapshotUpdateContext("Test", HostProject, WorkspaceProject, VersionStamp.Default));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WhenHostProjectChanged_MadeClean_NotifiesListeners_AndDoesNotStartBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_0); // Simulate a project change
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.False(snapshot.IsDirty);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WhenWorkspaceProjectChanged_MadeClean_NotifiesListeners_AndDoesNotStartBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.False(snapshot.IsDirty);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WhenHostProjectChanged_StillDirty_WithSignificantChanges_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
var project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_0); // Simulate a project change
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectChanged_BackgroundUpdate_StillDirty_WithSignificantChanges_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact(Skip = "We no longer have any background-computed state")]
|
||||
public void ProjectUpdated_WhenHostProjectChanged_StillDirty_WithoutSignificantChanges_DoesNotNotifyListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate an update based on the original state
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_0); // Simulate a project change
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Now start computing another update
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
project = new HostProject(HostProject.FilePath, FallbackRazorConfiguration.MVC_1_1); // Simulate a project change
|
||||
ProjectManager.HostProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext); // Still dirty because the project changed while computing the update
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[Fact(Skip = "We no longer have any background-computed state")]
|
||||
public void ProjectUpdated_WhenWorkspaceProjectChanged_StillDirty_WithoutSignificantChanges_DoesNotNotifyListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate an update based on the original state
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Now start computing another update
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
project = project.WithAssemblyName("Test2"); // Simulate a project change
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext); // Still dirty because the project changed while computing the update
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WhenHostProjectRemoved_DiscardsUpdate()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
ProjectManager.HostProjectRemoved(HostProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
Assert.Null(snapshot);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_WhenWorkspaceProjectRemoved_DiscardsUpdate()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
ProjectManager.WorkspaceProjectRemoved(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void ProjectUpdated_BackgroundUpdate_MadeClean_WithSignificantChanges_NotifiesListeners_AndDoesNotStartBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
|
||||
// Act
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsDirty);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectAdded_WithoutHostProject_IgnoresWorkspaceProject()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectAdded_IgnoresNonCSharpProject()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectAdded(VBWorkspaceProject);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectAdded_IgnoresSecondProjectWithSameFilePath()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProjectWithDifferentTfm);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectAdded_IgnoresProjectWithoutFilePath()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProjectWithoutFilePath);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectAdded_WithHostProject_NotifiesListenters_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.True(snapshot.IsInitialized);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectChanged_WithoutHostProject_IgnoresWorkspaceProject()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectChanged_IgnoresNonCSharpProject()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(VBWorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = VBWorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectChanged_IgnoresProjectWithoutFilePath()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProjectWithoutFilePath);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = WorkspaceProjectWithoutFilePath.WithAssemblyName("Test1"); // Simulate a project change
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectChanged_IgnoresSecondProjectWithSameFilePath()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectChanged(WorkspaceProjectWithDifferentTfm);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectChanged_MadeDirty_RetainsComputedState_NotifiesListeners_AndStartsBackgroundWorker()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Generate the update
|
||||
var snapshot = ProjectManager.GetSnapshot(HostProject);
|
||||
var updateContext = snapshot.CreateUpdateContext();
|
||||
ProjectManager.ProjectUpdated(updateContext);
|
||||
ProjectManager.Reset();
|
||||
|
||||
var project = WorkspaceProject.WithAssemblyName("Test1"); // Simulate a project change
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectChanged(project);
|
||||
|
||||
// Assert
|
||||
snapshot = ProjectManager.GetSnapshot(project);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectRemoved_WithHostProject_DoesNotRemoveProject()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectRemoved(WorkspaceProject);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectRemoved_WithHostProject_FallsBackToSecondProject()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Sets up a solution where the which has WorkspaceProjectWithDifferentTfm but not WorkspaceProject
|
||||
// This will enable us to fall back and find the WorkspaceProjectWithDifferentTfm
|
||||
Assert.True(Workspace.TryApplyChanges(WorkspaceProjectWithDifferentTfm.Solution));
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectRemoved(WorkspaceProject);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.True(snapshot.IsDirty);
|
||||
Assert.True(snapshot.IsInitialized);
|
||||
Assert.Equal(WorkspaceProjectWithDifferentTfm.Id, snapshot.WorkspaceProject.Id);
|
||||
|
||||
Assert.True(ProjectManager.ListenersNotified);
|
||||
Assert.True(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectRemoved_IgnoresSecondProjectWithSameFilePath()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectRemoved(WorkspaceProjectWithDifferentTfm);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.Same(WorkspaceProject, snapshot.WorkspaceProject);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectRemoved_IgnoresNonCSharpProject()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(VBWorkspaceProject);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectRemoved(VBWorkspaceProject);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectRemoved_IgnoresProjectWithoutFilePath()
|
||||
{
|
||||
// Arrange
|
||||
ProjectManager.HostProjectAdded(HostProject);
|
||||
ProjectManager.WorkspaceProjectAdded(WorkspaceProjectWithoutFilePath);
|
||||
ProjectManager.Reset();
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectRemoved(WorkspaceProjectWithoutFilePath);
|
||||
|
||||
// Assert
|
||||
var snapshot = ProjectManager.GetSnapshot(WorkspaceProject);
|
||||
Assert.False(snapshot.IsInitialized);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public void WorkspaceProjectRemoved_IgnoresUnknownProject()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
ProjectManager.WorkspaceProjectRemoved(WorkspaceProject);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
Assert.False(ProjectManager.ListenersNotified);
|
||||
Assert.False(ProjectManager.WorkerStarted);
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, IEnumerable<ProjectSnapshotChangeTrigger> triggers, Workspace workspace)
|
||||
: base(dispatcher, Mock.Of<ErrorReporter>(), Mock.Of<ProjectSnapshotWorker>(), triggers, workspace)
|
||||
{
|
||||
}
|
||||
|
||||
public bool ListenersNotified { get; private set; }
|
||||
|
||||
public bool WorkerStarted { get; private set; }
|
||||
|
||||
public DefaultProjectSnapshot GetSnapshot(HostProject hostProject)
|
||||
{
|
||||
return Projects.Cast<DefaultProjectSnapshot>().FirstOrDefault(s => s.FilePath == hostProject.FilePath);
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot GetSnapshot(Project workspaceProject)
|
||||
{
|
||||
return Projects.Cast<DefaultProjectSnapshot>().FirstOrDefault(s => s.FilePath == workspaceProject.FilePath);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ListenersNotified = false;
|
||||
WorkerStarted = false;
|
||||
}
|
||||
|
||||
protected override void NotifyListeners(ProjectChangeEventArgs e)
|
||||
{
|
||||
ListenersNotified = true;
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
Assert.NotNull(context.HostProject);
|
||||
Assert.NotNull(context.WorkspaceProject);
|
||||
|
||||
WorkerStarted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,456 @@
|
|||
// 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.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
public class DefaultRazorProjectHostTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public DefaultRazorProjectHostTest()
|
||||
{
|
||||
Workspace = new AdhocWorkspace();
|
||||
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);
|
||||
}
|
||||
|
||||
private TestProjectSnapshotManager ProjectManager { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DefaultRazorProjectHost_ForegroundThread_CreateAndDispose_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
// Act & Assert
|
||||
await host.LoadAsync();
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
await host.DisposeAsync();
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DefaultRazorProjectHost_BackgroundThread_CreateAndDispose_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
// Act & Assert
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_ReadsProperties_InitializesProject()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "2.1" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>() { { "Extensions", "MVC-2.1;Another-Thing" }, } },
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>(){ } },
|
||||
{ "Another-Thing", new Dictionary<string, string>(){ } },
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
|
||||
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
Assert.Collection(
|
||||
snapshot.Configuration.Extensions,
|
||||
e => Assert.Equal("MVC-2.1", e.ExtensionName),
|
||||
e => Assert.Equal("Another-Thing", e.ExtensionName));
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_NoVersionFound_DoesNotIniatializeProject()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_UpdateProject_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "2.1" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>() { { "Extensions", "MVC-2.1;Another-Thing" }, } },
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>(){ } },
|
||||
{ "Another-Thing", new Dictionary<string, string>(){ } },
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act - 1
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
|
||||
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
Assert.Collection(
|
||||
snapshot.Configuration.Extensions,
|
||||
e => Assert.Equal("MVC-2.1", e.ExtensionName),
|
||||
e => Assert.Equal("Another-Thing", e.ExtensionName));
|
||||
|
||||
// Act - 2
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorLangVersionProperty, "2.0");
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.0");
|
||||
changes[1].After.SetItem("MVC-2.0", new Dictionary<string, string>() { { "Extensions", "MVC-2.0;Another-Thing" }, });
|
||||
changes[2].After.SetItem("MVC-2.0", new Dictionary<string, string>());
|
||||
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 2
|
||||
snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
|
||||
Assert.Equal(RazorLanguageVersion.Version_2_0, snapshot.Configuration.LanguageVersion);
|
||||
Assert.Equal("MVC-2.0", snapshot.Configuration.ConfigurationName);
|
||||
Assert.Collection(
|
||||
snapshot.Configuration.Extensions,
|
||||
e => Assert.Equal("MVC-2.0", e.ExtensionName),
|
||||
e => Assert.Equal("Another-Thing", e.ExtensionName));
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_VersionRemoved_DeinitializesProject()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "2.1" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>() { { "Extensions", "MVC-2.1;Another-Thing" }, } },
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>(){ } },
|
||||
{ "Another-Thing", new Dictionary<string, string>(){ } },
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act - 1
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
|
||||
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
Assert.Collection(
|
||||
snapshot.Configuration.Extensions,
|
||||
e => Assert.Equal("MVC-2.1", e.ExtensionName),
|
||||
e => Assert.Equal("Another-Thing", e.ExtensionName));
|
||||
|
||||
// Act - 2
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorLangVersionProperty, "");
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "");
|
||||
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 2
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_AfterDispose_IgnoresUpdate()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "2.1" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>() { { "Extensions", "MVC-2.1;Another-Thing" }, } },
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>(){ } },
|
||||
{ "Another-Thing", new Dictionary<string, string>(){ } },
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act - 1
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
|
||||
Assert.Equal(RazorLanguageVersion.Version_2_1, snapshot.Configuration.LanguageVersion);
|
||||
Assert.Equal("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
Assert.Collection(
|
||||
snapshot.Configuration.Extensions,
|
||||
e => Assert.Equal("MVC-2.1", e.ExtensionName),
|
||||
e => Assert.Equal("Another-Thing", e.ExtensionName));
|
||||
|
||||
// Act - 2
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
|
||||
// Assert - 2
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act - 3
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorLangVersionProperty, "2.0");
|
||||
changes[0].After.SetProperty(Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.0");
|
||||
changes[1].After.SetItem("MVC-2.0", new Dictionary<string, string>() { { "Extensions", "MVC-2.0;Another-Thing" }, });
|
||||
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 3
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectRenamed_RemovesHostProject_CopiesConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorGeneral.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>()
|
||||
{
|
||||
{ Rules.RazorGeneral.RazorLangVersionProperty, "2.1" },
|
||||
{ Rules.RazorGeneral.RazorDefaultConfigurationProperty, "MVC-2.1" },
|
||||
}),
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorConfiguration.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorConfiguration.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>() { { "Extensions", "MVC-2.1;Another-Thing" }, } },
|
||||
})
|
||||
},
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = Rules.RazorExtension.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(Rules.RazorExtension.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "MVC-2.1", new Dictionary<string, string>(){ } },
|
||||
{ "Another-Thing", new Dictionary<string, string>(){ } },
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new DefaultRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act - 1
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Same("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
|
||||
// Act - 2
|
||||
services.UnconfiguredProject.FullPath = "Test2.csproj";
|
||||
await Task.Run(async () => await host.OnProjectRenamingAsync());
|
||||
|
||||
// Assert - 1
|
||||
snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test2.csproj", snapshot.FilePath);
|
||||
Assert.Same("MVC-2.1", snapshot.Configuration.ConfigurationName);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, Workspace workspace)
|
||||
: base(dispatcher, Mock.Of<ErrorReporter>(), Mock.Of<ProjectSnapshotWorker>(), Array.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
// 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.Tasks;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
public class FallbackRazorProjectHostTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
public FallbackRazorProjectHostTest()
|
||||
{
|
||||
Workspace = new AdhocWorkspace();
|
||||
ProjectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);
|
||||
}
|
||||
|
||||
private TestProjectSnapshotManager ProjectManager { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task FallbackRazorProjectHost_ForegroundThread_CreateAndDispose_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var host = new TestFallbackRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
// Act & Assert
|
||||
await host.LoadAsync();
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
await host.DisposeAsync();
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task FallbackRazorProjectHost_BackgroundThread_CreateAndDispose_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
var host = new TestFallbackRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
// Act & Assert
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_ReadsProperties_InitializesProject()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new TestFallbackRazorProjectHost(services, Workspace, ProjectManager)
|
||||
{
|
||||
AssemblyVersion = new Version(2, 0), // Mock for reading the assembly's version
|
||||
};
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Same(FallbackRazorConfiguration.MVC_2_0, snapshot.Configuration);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_NoAssemblyFound_DoesNotIniatializeProject()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
}),
|
||||
},
|
||||
|
||||
};
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new TestFallbackRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_AssemblyFoundButCannotReadVersion_DoesNotIniatializeProject()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new TestFallbackRazorProjectHost(services, Workspace, ProjectManager);
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_UpdateProject_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new TestFallbackRazorProjectHost(services, Workspace, ProjectManager)
|
||||
{
|
||||
AssemblyVersion = new Version(2, 0),
|
||||
};
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act - 1
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Same(FallbackRazorConfiguration.MVC_2_0, snapshot.Configuration);
|
||||
|
||||
// Act - 2
|
||||
host.AssemblyVersion = new Version(1, 0);
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 2
|
||||
snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Same(FallbackRazorConfiguration.MVC_1_0, snapshot.Configuration);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_VersionRemoved_DeinitializesProject()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new TestFallbackRazorProjectHost(services, Workspace, ProjectManager)
|
||||
{
|
||||
AssemblyVersion = new Version(2, 0),
|
||||
};
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act - 1
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Same(FallbackRazorConfiguration.MVC_2_0, snapshot.Configuration);
|
||||
|
||||
// Act - 2
|
||||
host.AssemblyVersion= null;
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 2
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectChanged_AfterDispose_IgnoresUpdate()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new TestFallbackRazorProjectHost(services, Workspace, ProjectManager)
|
||||
{
|
||||
AssemblyVersion = new Version(2, 0),
|
||||
};
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act - 1
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Same(FallbackRazorConfiguration.MVC_2_0, snapshot.Configuration);
|
||||
|
||||
// Act - 2
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
|
||||
// Assert - 2
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act - 3
|
||||
host.AssemblyVersion = new Version(1, 1);
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 3
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task OnProjectRenamed_RemovesHostProject_CopiesConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var changes = new TestProjectChangeDescription[]
|
||||
{
|
||||
new TestProjectChangeDescription()
|
||||
{
|
||||
RuleName = ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName,
|
||||
After = TestProjectRuleSnapshot.CreateItems(ManageProjectSystemSchema.ResolvedCompilationReference.SchemaName, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ "c:\\nuget\\Microsoft.AspNetCore.Mvc.razor.dll", new Dictionary<string, string>() },
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
var services = new TestProjectSystemServices("Test.csproj");
|
||||
|
||||
var host = new TestFallbackRazorProjectHost(services, Workspace, ProjectManager)
|
||||
{
|
||||
AssemblyVersion = new Version(2, 0), // Mock for reading the assembly's version
|
||||
};
|
||||
|
||||
await Task.Run(async () => await host.LoadAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
||||
// Act - 1
|
||||
await Task.Run(async () => await host.OnProjectChanged(services.CreateUpdate(changes)));
|
||||
|
||||
// Assert - 1
|
||||
var snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test.csproj", snapshot.FilePath);
|
||||
Assert.Same(FallbackRazorConfiguration.MVC_2_0, snapshot.Configuration);
|
||||
|
||||
// Act - 2
|
||||
services.UnconfiguredProject.FullPath = "Test2.csproj";
|
||||
await Task.Run(async () => await host.OnProjectRenamingAsync());
|
||||
|
||||
// Assert - 1
|
||||
snapshot = Assert.Single(ProjectManager.Projects);
|
||||
Assert.Equal("Test2.csproj", snapshot.FilePath);
|
||||
Assert.Same(FallbackRazorConfiguration.MVC_2_0, snapshot.Configuration);
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
}
|
||||
|
||||
private class TestFallbackRazorProjectHost : FallbackRazorProjectHost
|
||||
{
|
||||
internal TestFallbackRazorProjectHost(IUnconfiguredProjectCommonServices commonServices, Workspace workspace, ProjectSnapshotManagerBase projectManager)
|
||||
: base(commonServices, workspace, projectManager)
|
||||
{
|
||||
}
|
||||
|
||||
public Version AssemblyVersion { get; set; }
|
||||
|
||||
protected override Version GetAssemblyVersion(string filePath)
|
||||
{
|
||||
return AssemblyVersion;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, Workspace workspace)
|
||||
: base(dispatcher, Mock.Of<ErrorReporter>(), Mock.Of<ProjectSnapshotWorker>(), Array.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,30 +16,54 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
public ProjectSnapshotWorkerQueueTest()
|
||||
{
|
||||
Project project1 = null;
|
||||
Project project2 = null;
|
||||
HostProject1 = new HostProject("Test1.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
HostProject2 = new HostProject("Test2.csproj", FallbackRazorConfiguration.MVC_1_0);
|
||||
|
||||
Workspace = TestWorkspace.Create(workspace =>
|
||||
{
|
||||
project1 = workspace.CurrentSolution.AddProject("Test1", "Test1", LanguageNames.CSharp);
|
||||
project2 = workspace.CurrentSolution.AddProject("Test2", "Test2", LanguageNames.CSharp);
|
||||
});
|
||||
Workspace = TestWorkspace.Create();
|
||||
|
||||
Project1 = project1;
|
||||
Project2 = project2;
|
||||
var projectId1 = ProjectId.CreateNewId("Test1");
|
||||
var projectId2 = ProjectId.CreateNewId("Test2");
|
||||
|
||||
var solution = Workspace.CurrentSolution
|
||||
.AddProject(ProjectInfo.Create(
|
||||
projectId1,
|
||||
VersionStamp.Default,
|
||||
"Test1",
|
||||
"Test1",
|
||||
LanguageNames.CSharp,
|
||||
"Test1.csproj"))
|
||||
.AddProject(ProjectInfo.Create(
|
||||
projectId2,
|
||||
VersionStamp.Default,
|
||||
"Test2",
|
||||
"Test2",
|
||||
LanguageNames.CSharp,
|
||||
"Test2.csproj")); ;
|
||||
|
||||
WorkspaceProject1 = solution.GetProject(projectId1);
|
||||
WorkspaceProject2 = solution.GetProject(projectId2);
|
||||
}
|
||||
|
||||
public Project Project1 { get; }
|
||||
private HostProject HostProject1 { get; }
|
||||
|
||||
public Project Project2 { get; }
|
||||
private HostProject HostProject2 { get; }
|
||||
|
||||
public Workspace Workspace { get; }
|
||||
private Project WorkspaceProject1 { get; }
|
||||
|
||||
private Project WorkspaceProject2 { get; }
|
||||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task Queue_ProcessesNotifications_AndGoesBackToSleep()
|
||||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);
|
||||
projectManager.HostProjectAdded(HostProject1);
|
||||
projectManager.HostProjectAdded(HostProject2);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject1);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject2);
|
||||
|
||||
var projectWorker = new TestProjectSnapshotWorker();
|
||||
|
||||
var queue = new ProjectSnapshotWorkerQueue(Dispatcher, projectManager, projectWorker)
|
||||
|
|
@ -51,10 +75,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
};
|
||||
|
||||
// Act & Assert
|
||||
queue.Enqueue(Project1);
|
||||
queue.Enqueue(projectManager.GetSnapshot(HostProject1).CreateUpdateContext());
|
||||
|
||||
Assert.True(queue.IsScheduledOrRunning);
|
||||
Assert.True(queue.HasPendingNotifications);
|
||||
Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
|
||||
Assert.True(queue.HasPendingNotifications, "Queue should have a notification created during Enqueue");
|
||||
|
||||
// Allow the background work to proceed.
|
||||
queue.BlockBackgroundWorkStart.Set();
|
||||
|
|
@ -62,8 +86,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
// Get off the foreground thread and allow the updates to flow through.
|
||||
await Task.Run(() => queue.NotifyForegroundWorkFinish.Wait(TimeSpan.FromSeconds(1)));
|
||||
|
||||
Assert.False(queue.IsScheduledOrRunning);
|
||||
Assert.False(queue.HasPendingNotifications);
|
||||
Assert.False(queue.IsScheduledOrRunning, "Queue should not have restarted");
|
||||
Assert.False(queue.HasPendingNotifications, "Queue should have processed all notifications");
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
|
|
@ -71,6 +95,11 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
// Arrange
|
||||
var projectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);
|
||||
projectManager.HostProjectAdded(HostProject1);
|
||||
projectManager.HostProjectAdded(HostProject2);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject1);
|
||||
projectManager.WorkspaceProjectAdded(WorkspaceProject2);
|
||||
|
||||
var projectWorker = new TestProjectSnapshotWorker();
|
||||
|
||||
var queue = new ProjectSnapshotWorkerQueue(Dispatcher, projectManager, projectWorker)
|
||||
|
|
@ -82,20 +111,20 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
};
|
||||
|
||||
// Act & Assert
|
||||
queue.Enqueue(Project1);
|
||||
queue.Enqueue(projectManager.GetSnapshot(HostProject1).CreateUpdateContext());
|
||||
|
||||
Assert.True(queue.IsScheduledOrRunning);
|
||||
Assert.True(queue.HasPendingNotifications);
|
||||
Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
|
||||
Assert.True(queue.HasPendingNotifications, "Queue should have a notification created during Enqueue");
|
||||
|
||||
// Allow the background work to proceed.
|
||||
queue.BlockBackgroundWorkStart.Set();
|
||||
|
||||
queue.NotifyBackgroundWorkFinish.Wait(); // Block the foreground thread so we can queue another notification.
|
||||
|
||||
Assert.True(queue.IsScheduledOrRunning);
|
||||
Assert.False(queue.HasPendingNotifications);
|
||||
Assert.True(queue.IsScheduledOrRunning, "Worker should be processing now");
|
||||
Assert.False(queue.HasPendingNotifications, "Worker should have taken all notifications");
|
||||
|
||||
queue.Enqueue(Project2);
|
||||
queue.Enqueue(projectManager.GetSnapshot(HostProject2).CreateUpdateContext());
|
||||
|
||||
Assert.True(queue.HasPendingNotifications); // Now we should see the worker restart when it finishes.
|
||||
|
||||
|
|
@ -106,17 +135,17 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
queue.NotifyForegroundWorkFinish.Reset();
|
||||
|
||||
// It should start running again right away.
|
||||
Assert.True(queue.IsScheduledOrRunning);
|
||||
Assert.True(queue.HasPendingNotifications);
|
||||
Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
|
||||
Assert.True(queue.HasPendingNotifications, "Queue should have a notification created during Enqueue");
|
||||
|
||||
// Allow the background work to proceed.
|
||||
queue.BlockBackgroundWorkStart.Set();
|
||||
|
||||
// Get off the foreground thread and allow the updates to flow through.
|
||||
await Task.Run(() => queue.NotifyForegroundWorkFinish.Wait(TimeSpan.FromSeconds(1)));
|
||||
|
||||
Assert.False(queue.IsScheduledOrRunning);
|
||||
Assert.False(queue.HasPendingNotifications);
|
||||
|
||||
Assert.False(queue.IsScheduledOrRunning, "Queue should not have restarted");
|
||||
Assert.False(queue.HasPendingNotifications, "Queue should have processed all notifications");
|
||||
}
|
||||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
|
|
@ -126,17 +155,24 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot GetSnapshot(ProjectId id)
|
||||
public DefaultProjectSnapshot GetSnapshot(HostProject hostProject)
|
||||
{
|
||||
return Projects.Cast<DefaultProjectSnapshot>().FirstOrDefault(s => s.UnderlyingProject.Id == id);
|
||||
return Projects.Cast<DefaultProjectSnapshot>().FirstOrDefault(s => s.FilePath == hostProject.FilePath);
|
||||
}
|
||||
|
||||
public DefaultProjectSnapshot GetSnapshot(Project workspaceProject)
|
||||
{
|
||||
return Projects.Cast<DefaultProjectSnapshot>().FirstOrDefault(s => s.FilePath == workspaceProject.FilePath);
|
||||
}
|
||||
|
||||
protected override void NotifyListeners(ProjectChangeEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void NotifyBackgroundWorker(Project project)
|
||||
protected override void NotifyBackgroundWorker(ProjectSnapshotUpdateContext context)
|
||||
{
|
||||
Assert.NotNull(context.HostProject);
|
||||
Assert.NotNull(context.WorkspaceProject);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
// 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 System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
|
||||
namespace Microsoft.VisualStudio.ProjectSystem.References
|
||||
{
|
||||
internal class TestAssemblyReference : IAssemblyReference
|
||||
{
|
||||
public AssemblyName AssemblyName { get; set; }
|
||||
|
||||
public string FullPath { get; set; }
|
||||
|
||||
public IProjectProperties Metadata => throw new System.NotImplementedException();
|
||||
|
||||
public Task<AssemblyName> GetAssemblyNameAsync()
|
||||
{
|
||||
return Task.FromResult(AssemblyName);
|
||||
}
|
||||
|
||||
public Task<bool> GetCopyLocalAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<bool> GetCopyLocalSatelliteAssembliesAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetDescriptionAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetFullPathAsync()
|
||||
{
|
||||
return Task.FromResult(FullPath);
|
||||
}
|
||||
|
||||
public Task<string> GetNameAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<bool> GetReferenceOutputAssemblyAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<string> GetRequiredTargetFrameworkAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<bool> GetSpecificVersionAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<bool> IsWinMDFileAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// 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.Properties;
|
||||
|
||||
namespace Microsoft.VisualStudio.ProjectSystem
|
||||
{
|
||||
internal class TestProjectChangeDescription : IProjectChangeDescription
|
||||
{
|
||||
public string RuleName { get; set; }
|
||||
|
||||
public TestProjectRuleSnapshot Before { get; set; }
|
||||
|
||||
public IProjectChangeDiff Difference { get; set; }
|
||||
|
||||
public TestProjectRuleSnapshot After { get; set; }
|
||||
|
||||
IProjectRuleSnapshot IProjectChangeDescription.Before => Before;
|
||||
|
||||
IProjectChangeDiff IProjectChangeDescription.Difference => Difference;
|
||||
|
||||
IProjectRuleSnapshot IProjectChangeDescription.After => After;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// 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.Collections.Immutable;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
|
||||
namespace Microsoft.VisualStudio.ProjectSystem
|
||||
{
|
||||
internal class TestProjectRuleSnapshot : IProjectRuleSnapshot
|
||||
{
|
||||
public static TestProjectRuleSnapshot CreateProperties(string ruleName, Dictionary<string, string> properties)
|
||||
{
|
||||
return new TestProjectRuleSnapshot(
|
||||
ruleName,
|
||||
items: ImmutableDictionary<string, IImmutableDictionary<string, string>>.Empty,
|
||||
properties: properties.ToImmutableDictionary(),
|
||||
dataSourceVersions: ImmutableDictionary<NamedIdentity, IComparable>.Empty);
|
||||
}
|
||||
|
||||
public static TestProjectRuleSnapshot CreateItems(string ruleName, Dictionary<string, Dictionary<string, string>> items)
|
||||
{
|
||||
return new TestProjectRuleSnapshot(
|
||||
ruleName,
|
||||
items: items.ToImmutableDictionary(kvp => kvp.Key, kvp => (IImmutableDictionary<string, string>)kvp.Value.ToImmutableDictionary()),
|
||||
properties: ImmutableDictionary<string, string>.Empty,
|
||||
dataSourceVersions: ImmutableDictionary<NamedIdentity, IComparable>.Empty);
|
||||
}
|
||||
|
||||
public TestProjectRuleSnapshot(
|
||||
string ruleName,
|
||||
IImmutableDictionary<string, IImmutableDictionary<string, string>> items,
|
||||
IImmutableDictionary<string, string> properties,
|
||||
IImmutableDictionary<NamedIdentity, IComparable> dataSourceVersions)
|
||||
{
|
||||
RuleName = ruleName;
|
||||
Items = items;
|
||||
Properties = properties;
|
||||
DataSourceVersions = dataSourceVersions;
|
||||
}
|
||||
|
||||
public void SetProperty(string key, string value)
|
||||
{
|
||||
Properties = Properties.SetItem(key, value);
|
||||
}
|
||||
|
||||
public void SetItem(string key, Dictionary<string, string> values)
|
||||
{
|
||||
Items = Items.SetItem(key, values.ToImmutableDictionary());
|
||||
}
|
||||
|
||||
public string RuleName { get; }
|
||||
|
||||
public IImmutableDictionary<string, IImmutableDictionary<string, string>> Items { get; set; }
|
||||
|
||||
public IImmutableDictionary<string, string> Properties { get; set; }
|
||||
|
||||
public IImmutableDictionary<NamedIdentity, IComparable> DataSourceVersions { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,799 @@
|
|||
// 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.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Dataflow;
|
||||
using System.Xml;
|
||||
using Microsoft.Build.Execution;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Framework.XamlTypes;
|
||||
using Microsoft.VisualStudio.Composition;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Build;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
using Microsoft.VisualStudio.ProjectSystem.References;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using Moq;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
internal class TestProjectSystemServices : IUnconfiguredProjectCommonServices
|
||||
{
|
||||
public TestProjectSystemServices(string fullPath, params TestPropertyData[] data)
|
||||
{
|
||||
ProjectService = new TestProjectService();
|
||||
ThreadingService = ProjectService.Services.ThreadingPolicy;
|
||||
|
||||
UnconfiguredProject = new TestUnconfiguredProject(ProjectService, fullPath);
|
||||
ProjectService.LoadedUnconfiguredProjects.Add(UnconfiguredProject);
|
||||
|
||||
ActiveConfiguredProject = new TestConfiguredProject(UnconfiguredProject, data);
|
||||
UnconfiguredProject.LoadedConfiguredProjects.Add(ActiveConfiguredProject);
|
||||
|
||||
ActiveConfiguredProjectAssemblyReferences = new TestAssemblyReferencesService();
|
||||
ActiveConfiguredProjectRazorProperties = new Rules.RazorProjectProperties(ActiveConfiguredProject, UnconfiguredProject);
|
||||
ActiveConfiguredProjectSubscription = new TestActiveConfiguredProjectSubscriptionService();
|
||||
}
|
||||
|
||||
public TestProjectServices Services { get; }
|
||||
|
||||
public TestProjectService ProjectService { get; }
|
||||
|
||||
public TestUnconfiguredProject UnconfiguredProject { get; }
|
||||
|
||||
public TestConfiguredProject ActiveConfiguredProject { get; }
|
||||
|
||||
public TestAssemblyReferencesService ActiveConfiguredProjectAssemblyReferences { get; }
|
||||
|
||||
public Rules.RazorProjectProperties ActiveConfiguredProjectRazorProperties { get; }
|
||||
|
||||
public TestActiveConfiguredProjectSubscriptionService ActiveConfiguredProjectSubscription { get; }
|
||||
|
||||
public TestThreadingService ThreadingService { get; }
|
||||
|
||||
ConfiguredProject IUnconfiguredProjectCommonServices.ActiveConfiguredProject => ActiveConfiguredProject;
|
||||
|
||||
IAssemblyReferencesService IUnconfiguredProjectCommonServices.ActiveConfiguredProjectAssemblyReferences => ActiveConfiguredProjectAssemblyReferences;
|
||||
|
||||
IPackageReferencesService IUnconfiguredProjectCommonServices.ActiveConfiguredProjectPackageReferences => throw new NotImplementedException();
|
||||
|
||||
Rules.RazorProjectProperties IUnconfiguredProjectCommonServices.ActiveConfiguredProjectRazorProperties => ActiveConfiguredProjectRazorProperties;
|
||||
|
||||
IActiveConfiguredProjectSubscriptionService IUnconfiguredProjectCommonServices.ActiveConfiguredProjectSubscription => ActiveConfiguredProjectSubscription;
|
||||
|
||||
IProjectThreadingService IUnconfiguredProjectCommonServices.ThreadingService => ThreadingService;
|
||||
|
||||
UnconfiguredProject IUnconfiguredProjectCommonServices.UnconfiguredProject => UnconfiguredProject;
|
||||
|
||||
public IProjectVersionedValue<IProjectSubscriptionUpdate> CreateUpdate(params TestProjectChangeDescription[] descriptions)
|
||||
{
|
||||
return new ProjectVersionedValue<IProjectSubscriptionUpdate>(
|
||||
value: new ProjectSubscriptionUpdate(
|
||||
projectChanges: descriptions.ToImmutableDictionary(d => d.RuleName, d => (IProjectChangeDescription)d),
|
||||
projectConfiguration: ActiveConfiguredProject.ProjectConfiguration),
|
||||
dataSourceVersions: ImmutableDictionary<NamedIdentity, IComparable>.Empty);
|
||||
}
|
||||
|
||||
public class TestProjectServices : IProjectServices
|
||||
{
|
||||
public TestProjectServices(TestProjectService projectService)
|
||||
{
|
||||
ProjectService = projectService;
|
||||
ThreadingPolicy = new TestThreadingService();
|
||||
}
|
||||
|
||||
public TestProjectService ProjectService { get; }
|
||||
|
||||
public TestThreadingService ThreadingPolicy { get; }
|
||||
|
||||
IProjectLockService IProjectServices.ProjectLockService => throw new NotImplementedException();
|
||||
|
||||
IProjectThreadingService IProjectServices.ThreadingPolicy => ThreadingPolicy;
|
||||
|
||||
IProjectFaultHandlerService IProjectServices.FaultHandler => throw new NotImplementedException();
|
||||
|
||||
IProjectReloader IProjectServices.ProjectReloader => throw new NotImplementedException();
|
||||
|
||||
ExportProvider IProjectCommonServices.ExportProvider => throw new NotImplementedException();
|
||||
|
||||
IProjectDataSourceRegistry IProjectCommonServices.DataSourceRegistry => throw new NotImplementedException();
|
||||
|
||||
IProjectService IProjectCommonServices.ProjectService => ProjectService;
|
||||
|
||||
IProjectCapabilitiesScope IProjectCommonServices.Capabilities => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public class TestProjectService : IProjectService
|
||||
{
|
||||
public TestProjectService()
|
||||
{
|
||||
LoadedUnconfiguredProjects = new List<TestUnconfiguredProject>();
|
||||
Services = new TestProjectServices(this);
|
||||
}
|
||||
|
||||
public List<TestUnconfiguredProject> LoadedUnconfiguredProjects { get; }
|
||||
|
||||
public TestProjectServices Services { get; }
|
||||
|
||||
IEnumerable<UnconfiguredProject> IProjectService.LoadedUnconfiguredProjects => throw new NotImplementedException();
|
||||
|
||||
IProjectServices IProjectService.Services => Services;
|
||||
|
||||
IProjectCapabilitiesScope IProjectService.Capabilities => throw new NotImplementedException();
|
||||
|
||||
Task<UnconfiguredProject> IProjectService.LoadProjectAsync(string projectLocation, IImmutableSet<string> projectCapabilities)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<UnconfiguredProject> IProjectService.LoadProjectAsync(XmlReader reader, IImmutableSet<string> projectCapabilities)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<UnconfiguredProject> IProjectService.LoadProjectAsync(string projectLocation, bool delayAutoLoad, IImmutableSet<string> projectCapabilities)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task IProjectService.UnloadProjectAsync(UnconfiguredProject project)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestUnconfiguredProject : UnconfiguredProject
|
||||
{
|
||||
public TestUnconfiguredProject(TestProjectService projectService, string fullPath)
|
||||
{
|
||||
ProjectService = projectService;
|
||||
FullPath = fullPath;
|
||||
|
||||
LoadedConfiguredProjects = new List<TestConfiguredProject>();
|
||||
}
|
||||
|
||||
public TestProjectService ProjectService { get; }
|
||||
|
||||
public string FullPath { get; set; }
|
||||
|
||||
public List<TestConfiguredProject> LoadedConfiguredProjects { get; }
|
||||
|
||||
string UnconfiguredProject.FullPath => FullPath;
|
||||
bool UnconfiguredProject.RequiresReloadForExternalFileChange => throw new NotImplementedException();
|
||||
|
||||
IProjectCapabilitiesScope UnconfiguredProject.Capabilities => throw new NotImplementedException();
|
||||
|
||||
IProjectService UnconfiguredProject.ProjectService => ProjectService;
|
||||
|
||||
IUnconfiguredProjectServices UnconfiguredProject.Services => throw new NotImplementedException();
|
||||
|
||||
IEnumerable<ConfiguredProject> UnconfiguredProject.LoadedConfiguredProjects => LoadedConfiguredProjects;
|
||||
|
||||
bool UnconfiguredProject.IsLoading => throw new NotImplementedException();
|
||||
|
||||
event AsyncEventHandler UnconfiguredProject.ProjectUnloading
|
||||
{
|
||||
add
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
event AsyncEventHandler<ProjectRenamedEventArgs> UnconfiguredProject.ProjectRenaming
|
||||
{
|
||||
add
|
||||
{
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
event AsyncEventHandler<ProjectRenamedEventArgs> UnconfiguredProject.ProjectRenamedOnWriter
|
||||
{
|
||||
add
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
event AsyncEventHandler<ProjectRenamedEventArgs> UnconfiguredProject.ProjectRenamed
|
||||
{
|
||||
add
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
Task<bool> UnconfiguredProject.CanRenameAsync(string newFilePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<Encoding> UnconfiguredProject.GetFileEncodingAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<bool> UnconfiguredProject.GetIsDirtyAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<ConfiguredProject> UnconfiguredProject.GetSuggestedConfiguredProjectAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<ConfiguredProject> UnconfiguredProject.LoadConfiguredProjectAsync(string name, IImmutableDictionary<string, string> configurationProperties)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<ConfiguredProject> UnconfiguredProject.LoadConfiguredProjectAsync(ProjectConfiguration projectConfiguration)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task UnconfiguredProject.ReloadAsync(bool immediately)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task UnconfiguredProject.RenameAsync(string newFilePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task UnconfiguredProject.SaveAsync(string filePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task UnconfiguredProject.SaveCopyAsync(string filePath, Encoding fileEncoding)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task UnconfiguredProject.SaveUserFileAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task UnconfiguredProject.SetFileEncodingAsync(Encoding value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestConfiguredProject : ConfiguredProject
|
||||
{
|
||||
public TestConfiguredProject(TestUnconfiguredProject unconfiguredProject, TestPropertyData[] data)
|
||||
{
|
||||
UnconfiguredProject = unconfiguredProject;
|
||||
Services = new TestConfiguredProjectServices(this, data);
|
||||
|
||||
ProjectConfiguration = new StandardProjectConfiguration(
|
||||
"Debug|AnyCPU",
|
||||
ImmutableDictionary<string, string>.Empty.Add("Configuration", "Debug").Add("Platform", "AnyCPU"));
|
||||
}
|
||||
|
||||
public TestUnconfiguredProject UnconfiguredProject { get; }
|
||||
|
||||
public ProjectConfiguration ProjectConfiguration { get; }
|
||||
|
||||
public TestConfiguredProjectServices Services { get; }
|
||||
|
||||
IComparable ConfiguredProject.ProjectVersion => throw new NotImplementedException();
|
||||
|
||||
IReceivableSourceBlock<IComparable> ConfiguredProject.ProjectVersionBlock => throw new NotImplementedException();
|
||||
|
||||
ProjectConfiguration ConfiguredProject.ProjectConfiguration => ProjectConfiguration;
|
||||
|
||||
IProjectCapabilitiesScope ConfiguredProject.Capabilities => throw new NotImplementedException();
|
||||
|
||||
UnconfiguredProject ConfiguredProject.UnconfiguredProject => UnconfiguredProject;
|
||||
|
||||
IConfiguredProjectServices ConfiguredProject.Services => Services;
|
||||
|
||||
event AsyncEventHandler ConfiguredProject.ProjectUnloading
|
||||
{
|
||||
add
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
event EventHandler ConfiguredProject.ProjectChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
event EventHandler ConfiguredProject.ProjectChangedSynchronous
|
||||
{
|
||||
add
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfiguredProject.NotifyProjectChange()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestConfiguredProjectServices : IConfiguredProjectServices
|
||||
{
|
||||
public TestConfiguredProjectServices(TestConfiguredProject configuredProject, TestPropertyData[] data)
|
||||
{
|
||||
ConfiguredProject = configuredProject;
|
||||
|
||||
AdditionalRuleDefinitions = new TestAdditionalRuleDefinitionsService();
|
||||
PropertyPagesCatalog = new TestPropertyPagesCatalogProvider(new TestPropertyPagesCatalog(data));
|
||||
}
|
||||
|
||||
public TestConfiguredProject ConfiguredProject { get; }
|
||||
|
||||
public TestAdditionalRuleDefinitionsService AdditionalRuleDefinitions { get; }
|
||||
|
||||
public TestPropertyPagesCatalogProvider PropertyPagesCatalog { get; }
|
||||
|
||||
IOutputGroupsService IConfiguredProjectServices.OutputGroups => throw new NotImplementedException();
|
||||
|
||||
IBuildProject IConfiguredProjectServices.Build => throw new NotImplementedException();
|
||||
|
||||
IBuildSupport IConfiguredProjectServices.BuildSupport => throw new NotImplementedException();
|
||||
|
||||
IAssemblyReferencesService IConfiguredProjectServices.AssemblyReferences => throw new NotImplementedException();
|
||||
|
||||
IComReferencesService IConfiguredProjectServices.ComReferences => throw new NotImplementedException();
|
||||
|
||||
ISdkReferencesService IConfiguredProjectServices.SdkReferences => throw new NotImplementedException();
|
||||
|
||||
IPackageReferencesService IConfiguredProjectServices.PackageReferences => throw new NotImplementedException();
|
||||
|
||||
IWinRTReferencesService IConfiguredProjectServices.WinRTReferences => throw new NotImplementedException();
|
||||
|
||||
IBuildDependencyProjectReferencesService IConfiguredProjectServices.ProjectReferences => throw new NotImplementedException();
|
||||
|
||||
IProjectItemProvider IConfiguredProjectServices.SourceItems => throw new NotImplementedException();
|
||||
|
||||
IProjectPropertiesProvider IConfiguredProjectServices.ProjectPropertiesProvider => throw new NotImplementedException();
|
||||
|
||||
IProjectPropertiesProvider IConfiguredProjectServices.UserPropertiesProvider => throw new NotImplementedException();
|
||||
|
||||
IProjectAsynchronousTasksService IConfiguredProjectServices.ProjectAsynchronousTasks => throw new NotImplementedException();
|
||||
|
||||
IAdditionalRuleDefinitionsService IConfiguredProjectServices.AdditionalRuleDefinitions => AdditionalRuleDefinitions;
|
||||
|
||||
IPropertyPagesCatalogProvider IConfiguredProjectServices.PropertyPagesCatalog => PropertyPagesCatalog;
|
||||
|
||||
IProjectSubscriptionService IConfiguredProjectServices.ProjectSubscription => throw new NotImplementedException();
|
||||
|
||||
IProjectSnapshotService IConfiguredProjectServices.ProjectSnapshotService => throw new NotImplementedException();
|
||||
|
||||
object IConfiguredProjectServices.HostObject => throw new NotImplementedException();
|
||||
|
||||
ExportProvider IProjectCommonServices.ExportProvider => throw new NotImplementedException();
|
||||
|
||||
IProjectDataSourceRegistry IProjectCommonServices.DataSourceRegistry => throw new NotImplementedException();
|
||||
|
||||
IProjectService IProjectCommonServices.ProjectService => ConfiguredProject.UnconfiguredProject.ProjectService;
|
||||
|
||||
IProjectCapabilitiesScope IProjectCommonServices.Capabilities => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public class TestAdditionalRuleDefinitionsService : IAdditionalRuleDefinitionsService
|
||||
{
|
||||
IProjectVersionedValue<IAdditionalRuleDefinitions> IAdditionalRuleDefinitionsService.AdditionalRuleDefinitions => throw new NotImplementedException();
|
||||
|
||||
IReceivableSourceBlock<IProjectVersionedValue<IAdditionalRuleDefinitions>> IProjectValueDataSource<IAdditionalRuleDefinitions>.SourceBlock => throw new NotImplementedException();
|
||||
|
||||
ISourceBlock<IProjectVersionedValue<object>> IProjectValueDataSource.SourceBlock => throw new NotImplementedException();
|
||||
|
||||
NamedIdentity IProjectValueDataSource.DataSourceKey => throw new NotImplementedException();
|
||||
|
||||
IComparable IProjectValueDataSource.DataSourceVersion => throw new NotImplementedException();
|
||||
|
||||
bool IAdditionalRuleDefinitionsService.AddRuleDefinition(string path, string context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IAdditionalRuleDefinitionsService.AddRuleDefinition(Rule rule, string context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IDisposable IJoinableProjectValueDataSource.Join()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
bool IAdditionalRuleDefinitionsService.RemoveRuleDefinition(string path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IAdditionalRuleDefinitionsService.RemoveRuleDefinition(Rule rule)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class TestPropertyPagesCatalogProvider : IPropertyPagesCatalogProvider
|
||||
{
|
||||
public TestPropertyPagesCatalogProvider(TestPropertyPagesCatalog catalog)
|
||||
{
|
||||
Catalog = catalog;
|
||||
CatalogsByContext = new Dictionary<string, IPropertyPagesCatalog>()
|
||||
{
|
||||
{ "Project", catalog },
|
||||
};
|
||||
}
|
||||
|
||||
public TestPropertyPagesCatalog Catalog { get; }
|
||||
|
||||
public Dictionary<string, IPropertyPagesCatalog> CatalogsByContext { get; }
|
||||
|
||||
public IReceivableSourceBlock<IProjectVersionedValue<IProjectCatalogSnapshot>> SourceBlock => throw new NotImplementedException();
|
||||
|
||||
public NamedIdentity DataSourceKey => throw new NotImplementedException();
|
||||
|
||||
public IComparable DataSourceVersion => throw new NotImplementedException();
|
||||
|
||||
ISourceBlock<IProjectVersionedValue<object>> IProjectValueDataSource.SourceBlock => throw new NotImplementedException();
|
||||
|
||||
public Task<IPropertyPagesCatalog> GetCatalogAsync(string name, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(CatalogsByContext[name]);
|
||||
}
|
||||
|
||||
public Task<IImmutableDictionary<string, IPropertyPagesCatalog>> GetCatalogsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult<IImmutableDictionary<string, IPropertyPagesCatalog>>(CatalogsByContext.ToImmutableDictionary());
|
||||
}
|
||||
|
||||
public IPropertyPagesCatalog GetMemoryOnlyCatalog(string context)
|
||||
{
|
||||
return Catalog;
|
||||
}
|
||||
|
||||
public IDisposable Join()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestActiveConfiguredProjectSubscriptionService : IActiveConfiguredProjectSubscriptionService
|
||||
{
|
||||
public TestActiveConfiguredProjectSubscriptionService()
|
||||
{
|
||||
JointRuleBlock = new BufferBlock<IProjectVersionedValue<IProjectSubscriptionUpdate>>();
|
||||
JointRuleSource = new TestProjectValueDataSource<IProjectSubscriptionUpdate>(JointRuleBlock);
|
||||
}
|
||||
|
||||
public BufferBlock<IProjectVersionedValue<IProjectSubscriptionUpdate>> JointRuleBlock { get; }
|
||||
|
||||
public TestProjectValueDataSource<IProjectSubscriptionUpdate> JointRuleSource { get; }
|
||||
|
||||
IReceivableSourceBlock<IProjectVersionedValue<IProjectSnapshot>> IProjectSubscriptionService.ProjectBlock => throw new NotImplementedException();
|
||||
|
||||
IProjectValueDataSource<IProjectSnapshot> IProjectSubscriptionService.ProjectSource => throw new NotImplementedException();
|
||||
|
||||
IProjectValueDataSource<IProjectImportTreeSnapshot> IProjectSubscriptionService.ImportTreeSource => throw new NotImplementedException();
|
||||
|
||||
IProjectValueDataSource<IProjectSharedFoldersSnapshot> IProjectSubscriptionService.SharedFoldersSource => throw new NotImplementedException();
|
||||
|
||||
IProjectValueDataSource<IImmutableDictionary<string, IOutputGroup>> IProjectSubscriptionService.OutputGroupsSource => throw new NotImplementedException();
|
||||
|
||||
IReceivableSourceBlock<IProjectVersionedValue<IProjectCatalogSnapshot>> IProjectSubscriptionService.ProjectCatalogBlock => throw new NotImplementedException();
|
||||
|
||||
IProjectValueDataSource<IProjectCatalogSnapshot> IProjectSubscriptionService.ProjectCatalogSource => throw new NotImplementedException();
|
||||
|
||||
IReceivableSourceBlock<IProjectVersionedValue<IProjectSubscriptionUpdate>> IProjectSubscriptionService.ProjectRuleBlock => throw new NotImplementedException();
|
||||
|
||||
IProjectValueDataSource<IProjectSubscriptionUpdate> IProjectSubscriptionService.ProjectRuleSource => throw new NotImplementedException();
|
||||
|
||||
IReceivableSourceBlock<IProjectVersionedValue<IProjectSubscriptionUpdate>> IProjectSubscriptionService.ProjectBuildRuleBlock => throw new NotImplementedException();
|
||||
|
||||
IProjectValueDataSource<IProjectSubscriptionUpdate> IProjectSubscriptionService.ProjectBuildRuleSource => throw new NotImplementedException();
|
||||
|
||||
ISourceBlock<IProjectVersionedValue<IProjectSubscriptionUpdate>> IProjectSubscriptionService.JointRuleBlock => JointRuleBlock;
|
||||
|
||||
IProjectValueDataSource<IProjectSubscriptionUpdate> IProjectSubscriptionService.JointRuleSource => JointRuleSource;
|
||||
|
||||
IReceivableSourceBlock<IProjectVersionedValue<IProjectSubscriptionUpdate>> IProjectSubscriptionService.SourceItemsRuleBlock => throw new NotImplementedException();
|
||||
|
||||
IProjectValueDataSource<IProjectSubscriptionUpdate> IProjectSubscriptionService.SourceItemsRuleSource => throw new NotImplementedException();
|
||||
|
||||
IReceivableSourceBlock<IProjectVersionedValue<IImmutableSet<string>>> IProjectSubscriptionService.SourceItemRuleNamesBlock => throw new NotImplementedException();
|
||||
|
||||
IProjectValueDataSource<IImmutableSet<string>> IProjectSubscriptionService.SourceItemRuleNamesSource => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public class TestProjectValueDataSource<T> : IProjectValueDataSource<T>
|
||||
{
|
||||
public TestProjectValueDataSource(BufferBlock<IProjectVersionedValue<T>> sourceBlock)
|
||||
{
|
||||
SourceBlock = sourceBlock;
|
||||
}
|
||||
|
||||
public BufferBlock<IProjectVersionedValue<T>> SourceBlock { get; }
|
||||
|
||||
IReceivableSourceBlock<IProjectVersionedValue<T>> IProjectValueDataSource<T>.SourceBlock => SourceBlock;
|
||||
|
||||
ISourceBlock<IProjectVersionedValue<object>> IProjectValueDataSource.SourceBlock => throw new NotImplementedException();
|
||||
|
||||
NamedIdentity IProjectValueDataSource.DataSourceKey => throw new NotImplementedException();
|
||||
|
||||
IComparable IProjectValueDataSource.DataSourceVersion => throw new NotImplementedException();
|
||||
|
||||
IDisposable IJoinableProjectValueDataSource.Join()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestPropertyPagesCatalog : IPropertyPagesCatalog
|
||||
{
|
||||
private readonly Dictionary<string, IRule> _data;
|
||||
|
||||
public TestPropertyPagesCatalog(TestPropertyData[] data)
|
||||
{
|
||||
_data = new Dictionary<string, IRule>();
|
||||
foreach (var category in data.GroupBy(p => p.Category))
|
||||
{
|
||||
_data.Add(
|
||||
category.Key,
|
||||
CreateRule(category.Select(property => CreateProperty(property.PropertyName, property.Value, property.SetValues))));
|
||||
}
|
||||
}
|
||||
|
||||
private static IRule CreateRule(IEnumerable<IProperty> properties)
|
||||
{
|
||||
var rule = new Mock<IRule>();
|
||||
rule
|
||||
.Setup(o => o.GetProperty(It.IsAny<string>()))
|
||||
.Returns((string propertyName) =>
|
||||
{
|
||||
|
||||
return properties.FirstOrDefault(p => p.Name == propertyName);
|
||||
});
|
||||
|
||||
return rule.Object;
|
||||
}
|
||||
|
||||
private static IProperty CreateProperty(string name, object value, List<object> setValues = null)
|
||||
{
|
||||
var property = new Mock<IProperty>();
|
||||
property.SetupGet(o => o.Name)
|
||||
.Returns(name);
|
||||
|
||||
property.Setup(o => o.GetValueAsync())
|
||||
.ReturnsAsync(value);
|
||||
|
||||
property.As<IEvaluatedProperty>().Setup(p => p.GetEvaluatedValueAtEndAsync()).ReturnsAsync(value.ToString());
|
||||
property.As<IEvaluatedProperty>().Setup(p => p.GetEvaluatedValueAsync()).ReturnsAsync(value.ToString());
|
||||
|
||||
if (setValues != null)
|
||||
{
|
||||
property
|
||||
.Setup(p => p.SetValueAsync(It.IsAny<object>()))
|
||||
.Callback<object>(obj => setValues.Add(obj))
|
||||
.Returns(() => Task.CompletedTask);
|
||||
}
|
||||
|
||||
return property.Object;
|
||||
}
|
||||
|
||||
IRule IPropertyPagesCatalog.BindToContext(string schemaName, string file, string itemType, string itemName)
|
||||
{
|
||||
_data.TryGetValue(schemaName, out var value);
|
||||
return value;
|
||||
}
|
||||
|
||||
IRule IPropertyPagesCatalog.BindToContext(string schemaName, IProjectPropertiesContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IRule IPropertyPagesCatalog.BindToContext(string schemaName, ProjectInstance projectInstance, string itemType, string itemName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IRule IPropertyPagesCatalog.BindToContext(string schemaName, ProjectInstance projectInstance, ITaskItem taskItem)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IReadOnlyCollection<string> IPropertyPagesCatalog.GetProjectLevelPropertyPagesSchemas()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IReadOnlyCollection<string> IPropertyPagesCatalog.GetPropertyPagesSchemas()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IReadOnlyCollection<string> IPropertyPagesCatalog.GetPropertyPagesSchemas(string itemType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IReadOnlyCollection<string> IPropertyPagesCatalog.GetPropertyPagesSchemas(IEnumerable<string> paths)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Rule IPropertyPagesCatalog.GetSchema(string schemaName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestAssemblyReferencesService : IAssemblyReferencesService
|
||||
{
|
||||
public TestAssemblyReferencesService()
|
||||
{
|
||||
ResolvedReferences = new List<IAssemblyReference>();
|
||||
}
|
||||
|
||||
public List<IAssemblyReference> ResolvedReferences { get; }
|
||||
|
||||
Task<AddReferenceResult<IUnresolvedAssemblyReference>> IAssemblyReferencesService.AddAsync(AssemblyName assemblyName, string assemblyPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<bool> IAssemblyReferencesService.CanResolveAsync(AssemblyName assemblyName, string assemblyPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<bool> IAssemblyReferencesService.ContainsAsync(AssemblyName assemblyName, string assemblyPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<IAssemblyReference> IAssemblyReferencesService.GetResolvedReferenceAsync(AssemblyName assemblyName, string assemblyPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<IAssemblyReference> IResolvableReferencesService<IUnresolvedAssemblyReference, IAssemblyReference>.GetResolvedReferenceAsync(IUnresolvedAssemblyReference unresolvedReference)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<IImmutableSet<IAssemblyReference>> IResolvableReferencesService<IUnresolvedAssemblyReference, IAssemblyReference>.GetResolvedReferencesAsync()
|
||||
{
|
||||
return Task.FromResult<IImmutableSet<IAssemblyReference>>(ResolvedReferences.ToImmutableHashSet());
|
||||
}
|
||||
|
||||
Task<IUnresolvedAssemblyReference> IAssemblyReferencesService.GetUnresolvedReferenceAsync(AssemblyName assemblyName, string assemblyPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<IUnresolvedAssemblyReference> IResolvableReferencesService<IUnresolvedAssemblyReference, IAssemblyReference>.GetUnresolvedReferenceAsync(IAssemblyReference resolvedReference)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<IImmutableSet<IUnresolvedAssemblyReference>> IResolvableReferencesService<IUnresolvedAssemblyReference, IAssemblyReference>.GetUnresolvedReferencesAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task IAssemblyReferencesService.RemoveAsync(AssemblyName assemblyName, string assemblyPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task IResolvableReferencesService<IUnresolvedAssemblyReference, IAssemblyReference>.RemoveAsync(IUnresolvedAssemblyReference reference)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task IResolvableReferencesService<IUnresolvedAssemblyReference, IAssemblyReference>.RemoveAsync(IEnumerable<IUnresolvedAssemblyReference> references)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestThreadingService : IProjectThreadingService
|
||||
{
|
||||
public TestThreadingService()
|
||||
{
|
||||
JoinableTaskContext = new JoinableTaskContextNode(new JoinableTaskContext());
|
||||
JoinableTaskFactory = new JoinableTaskFactory(JoinableTaskContext.Context);
|
||||
}
|
||||
|
||||
public JoinableTaskContextNode JoinableTaskContext { get; }
|
||||
|
||||
public JoinableTaskFactory JoinableTaskFactory { get; }
|
||||
|
||||
public bool IsOnMainThread => throw new NotImplementedException();
|
||||
|
||||
public void ExecuteSynchronously(Func<Task> asyncAction)
|
||||
{
|
||||
asyncAction().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public T ExecuteSynchronously<T>(Func<Task<T>> asyncAction)
|
||||
{
|
||||
return asyncAction().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public void Fork(
|
||||
Func<Task> asyncAction,
|
||||
JoinableTaskFactory factory = null,
|
||||
UnconfiguredProject unconfiguredProject = null,
|
||||
ConfiguredProject configuredProject = null,
|
||||
ErrorReportSettings watsonReportSettings = null,
|
||||
ProjectFaultSeverity faultSeverity = ProjectFaultSeverity.Recoverable,
|
||||
ForkOptions options = ForkOptions.Default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IDisposable SuppressProjectExecutionContext()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void VerifyOnUIThread()
|
||||
{
|
||||
if (!JoinableTaskContext.IsOnMainThread)
|
||||
{
|
||||
throw new InvalidOperationException("This isn't the main thread.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
public class TestPropertyData
|
||||
{
|
||||
public string Category { get; set; }
|
||||
|
||||
public string PropertyName { get; set; }
|
||||
|
||||
public object Value { get; set; }
|
||||
|
||||
public List<object> SetValues { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.VisualStudio.RazorExtension.DocumentInfo
|
|||
_documentTracker = documentTracker;
|
||||
}
|
||||
|
||||
public string Configuration => _documentTracker.Configuration?.DisplayName;
|
||||
public string Configuration => _documentTracker.Configuration?.ConfigurationName;
|
||||
|
||||
public bool IsSupportedDocument => _documentTracker.IsSupportedProject;
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,9 @@
|
|||
<Compile Include="RazorInfo\DocumentInfoViewModel.cs" />
|
||||
<Compile Include="RazorInfo\DocumentViewModel.cs" />
|
||||
<Compile Include="NotifyPropertyChanged.cs" />
|
||||
<Compile Include="RazorInfo\ProjectSnapshotViewModel.cs" />
|
||||
<Compile Include="RazorInfo\ProjectViewModel.cs" />
|
||||
<Compile Include="RazorInfo\PropertyViewModel.cs" />
|
||||
<Compile Include="RazorInfo\RazorInfoToolWindow.cs" />
|
||||
<Compile Include="RazorInfo\RazorInfoToolWindowCommand.cs" />
|
||||
<Compile Include="RazorInfo\RazorInfoViewModel.cs" />
|
||||
|
|
@ -296,4 +298,4 @@
|
|||
<Target Name="GetBuildVersion" Outputs="$(VsixVersion)" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="Exists('$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets')" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
@ -8,21 +8,10 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
{
|
||||
public class ProjectInfoViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private ObservableCollection<AssemblyViewModel> _assemblies;
|
||||
private ObservableCollection<DirectiveViewModel> _directives;
|
||||
private ObservableCollection<DocumentViewModel> _documents;
|
||||
private ObservableCollection<TagHelperViewModel> _tagHelpers;
|
||||
|
||||
public ObservableCollection<AssemblyViewModel> Assemblies
|
||||
{
|
||||
get { return _assemblies; }
|
||||
set
|
||||
{
|
||||
_assemblies = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<DirectiveViewModel> Directives
|
||||
{
|
||||
get { return _directives; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
// 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.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class ProjectSnapshotViewModel : NotifyPropertyChanged
|
||||
{
|
||||
internal ProjectSnapshotViewModel(ProjectSnapshot project)
|
||||
{
|
||||
Project = project;
|
||||
|
||||
Id = project.WorkspaceProject?.Id;
|
||||
Properties = new ObservableCollection<PropertyViewModel>()
|
||||
{
|
||||
new PropertyViewModel("Razor Language Version", project.Configuration?.LanguageVersion.ToString()),
|
||||
new PropertyViewModel("Configuration Name", $"{project.Configuration?.ConfigurationName} ({project.Configuration?.GetType().Name ?? "unknown"})"),
|
||||
new PropertyViewModel("Workspace Project", project.WorkspaceProject?.Name)
|
||||
};
|
||||
}
|
||||
|
||||
internal ProjectSnapshot Project { get; }
|
||||
|
||||
public string Name => Path.GetFileNameWithoutExtension(Project.FilePath);
|
||||
|
||||
public ProjectId Id { get; }
|
||||
|
||||
public ObservableCollection<PropertyViewModel> Properties { get; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -2,21 +2,35 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class ProjectViewModel : NotifyPropertyChanged
|
||||
{
|
||||
public ProjectViewModel(Project project)
|
||||
private ProjectSnapshotViewModel _snapshot;
|
||||
|
||||
internal ProjectViewModel(string filePath)
|
||||
{
|
||||
Id = project.Id;
|
||||
Name = project.Name;
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
public string FilePath { get; }
|
||||
|
||||
public string Name { get; }
|
||||
public string Name => Path.GetFileNameWithoutExtension(FilePath);
|
||||
|
||||
public ProjectId Id { get; }
|
||||
public bool HasSnapshot => Snapshot != null;
|
||||
|
||||
public ProjectSnapshotViewModel Snapshot
|
||||
{
|
||||
get => _snapshot;
|
||||
set
|
||||
{
|
||||
_snapshot = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(HasSnapshot));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// 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.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
{
|
||||
public class PropertyViewModel : NotifyPropertyChanged
|
||||
{
|
||||
internal PropertyViewModel(string name, string value)
|
||||
{
|
||||
Name = name;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public string Value { get; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.ComponentModelHost;
|
||||
|
|
@ -18,16 +17,22 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
[Guid("079e9499-d150-40af-8876-3047f7942c2a")]
|
||||
public class RazorInfoToolWindow : ToolWindowPane
|
||||
{
|
||||
private ProjectExtensibilityConfigurationFactory _configurationFactory;
|
||||
private IRazorEngineDocumentGenerator _documentGenerator;
|
||||
private IRazorEngineDirectiveResolver _directiveResolver;
|
||||
private ProjectSnapshotManager _projectManager;
|
||||
private TagHelperResolver _tagHelperResolver;
|
||||
private VisualStudioWorkspace _workspace;
|
||||
|
||||
public RazorInfoToolWindow() : base(null)
|
||||
{
|
||||
this.Caption = "Razor Info";
|
||||
this.Content = new RazorInfoToolWindowControl();
|
||||
Caption = "Razor Info";
|
||||
Content = new RazorInfoToolWindowControl();
|
||||
}
|
||||
|
||||
private RazorInfoViewModel DataContext
|
||||
{
|
||||
get => (RazorInfoViewModel)((RazorInfoToolWindowControl)Content).DataContext;
|
||||
set => ((RazorInfoToolWindowControl)Content).DataContext = value;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
|
|
@ -35,16 +40,28 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
base.Initialize();
|
||||
|
||||
var componentModel = (IComponentModel)GetService(typeof(SComponentModel));
|
||||
_workspace = componentModel.GetService<VisualStudioWorkspace>();
|
||||
|
||||
_configurationFactory = componentModel.GetService<ProjectExtensibilityConfigurationFactory>();
|
||||
_documentGenerator = componentModel.GetService<IRazorEngineDocumentGenerator>();
|
||||
_directiveResolver = componentModel.GetService<IRazorEngineDirectiveResolver>();
|
||||
_tagHelperResolver = componentModel.GetService<TagHelperResolver>();
|
||||
_tagHelperResolver = _workspace.Services.GetLanguageServices(RazorLanguage.Name).GetRequiredService<TagHelperResolver>();
|
||||
|
||||
_workspace = componentModel.GetService<VisualStudioWorkspace>();
|
||||
_workspace.WorkspaceChanged += Workspace_WorkspaceChanged;
|
||||
_projectManager = _workspace.Services.GetLanguageServices(RazorLanguage.Name).GetRequiredService<ProjectSnapshotManager>();
|
||||
_projectManager.Changed += ProjectManager_Changed;
|
||||
|
||||
Reset(_workspace.CurrentSolution);
|
||||
DataContext = new RazorInfoViewModel(this, _workspace, _projectManager, _directiveResolver, _tagHelperResolver, _documentGenerator, OnException);
|
||||
foreach (var project in _projectManager.Projects)
|
||||
{
|
||||
DataContext.Projects.Add(new ProjectViewModel(project.FilePath)
|
||||
{
|
||||
Snapshot = new ProjectSnapshotViewModel(project),
|
||||
});
|
||||
}
|
||||
|
||||
if (DataContext.Projects.Count > 0)
|
||||
{
|
||||
DataContext.CurrentProject = DataContext.Projects[0];
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
|
@ -53,28 +70,69 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
|
||||
if (disposing)
|
||||
{
|
||||
_workspace.WorkspaceChanged -= Workspace_WorkspaceChanged;
|
||||
_projectManager.Changed -= ProjectManager_Changed;
|
||||
}
|
||||
}
|
||||
|
||||
private void Reset(Solution solution)
|
||||
private void ProjectManager_Changed(object sender, ProjectChangeEventArgs e)
|
||||
{
|
||||
if (solution == null)
|
||||
switch (e.Kind)
|
||||
{
|
||||
((RazorInfoToolWindowControl)this.Content).DataContext = null;
|
||||
return;
|
||||
}
|
||||
case ProjectChangeKind.Added:
|
||||
{
|
||||
var added = new ProjectViewModel(e.Project.FilePath)
|
||||
{
|
||||
Snapshot = new ProjectSnapshotViewModel(e.Project),
|
||||
};
|
||||
|
||||
var viewModel = new RazorInfoViewModel(this, _workspace, _configurationFactory, _directiveResolver, _tagHelperResolver, _documentGenerator, OnException);
|
||||
foreach (var project in solution.Projects)
|
||||
{
|
||||
if (project.Language == LanguageNames.CSharp)
|
||||
{
|
||||
viewModel.Projects.Add(new ProjectViewModel(project));
|
||||
}
|
||||
}
|
||||
DataContext.Projects.Add(added);
|
||||
|
||||
((RazorInfoToolWindowControl)this.Content).DataContext = viewModel;
|
||||
if (DataContext.Projects.Count == 1)
|
||||
{
|
||||
DataContext.CurrentProject = added;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.Removed:
|
||||
{
|
||||
ProjectViewModel removed = null;
|
||||
for (var i = DataContext.Projects.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var project = DataContext.Projects[i];
|
||||
if (project.FilePath == e.Project.FilePath)
|
||||
{
|
||||
removed = project;
|
||||
DataContext.Projects.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (DataContext.CurrentProject == removed)
|
||||
{
|
||||
DataContext.CurrentProject = null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ProjectChangeKind.Changed:
|
||||
{
|
||||
ProjectViewModel changed = null;
|
||||
for (var i = DataContext.Projects.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var project = DataContext.Projects[i];
|
||||
if (project.FilePath == e.Project.FilePath)
|
||||
{
|
||||
changed = project;
|
||||
changed.Snapshot = new ProjectSnapshotViewModel(e.Project);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnException(Exception ex)
|
||||
|
|
@ -87,24 +145,6 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
OLEMSGBUTTON.OLEMSGBUTTON_OK,
|
||||
OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
|
||||
}
|
||||
|
||||
private void Workspace_WorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
|
||||
{
|
||||
switch (e.Kind)
|
||||
{
|
||||
case WorkspaceChangeKind.ProjectAdded:
|
||||
case WorkspaceChangeKind.ProjectChanged:
|
||||
case WorkspaceChangeKind.ProjectReloaded:
|
||||
case WorkspaceChangeKind.ProjectRemoved:
|
||||
case WorkspaceChangeKind.SolutionAdded:
|
||||
case WorkspaceChangeKind.SolutionChanged:
|
||||
case WorkspaceChangeKind.SolutionCleared:
|
||||
case WorkspaceChangeKind.SolutionReloaded:
|
||||
case WorkspaceChangeKind.SolutionRemoved:
|
||||
Reset(e.NewSolution);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,162 +1,266 @@
|
|||
<UserControl x:Class="Microsoft.VisualStudio.RazorExtension.RazorInfo.RazorInfoToolWindowControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:behaviors="clr-namespace:Microsoft.VisualStudio.RazorExtension.Behaviors"
|
||||
Background="{DynamicResource VsBrush.Window}"
|
||||
Foreground="{DynamicResource VsBrush.WindowText}"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="300"
|
||||
Name="RazorInfoToolWindow">
|
||||
<UserControl.Resources>
|
||||
<Style x:Key="{x:Type Button}">
|
||||
<Setter Property="FrameworkElement.Margin"
|
||||
Value="5" />
|
||||
</Style>
|
||||
<Style x:Key="{x:Type GridSplitter}">
|
||||
<Setter Property="FrameworkElement.Height"
|
||||
Value="5" />
|
||||
<Setter Property="FrameworkElement.HorizontalAlignment"
|
||||
Value="Stretch" />
|
||||
<Setter Property="FrameworkElement.VerticalAlignment"
|
||||
Value="Bottom" />
|
||||
<Setter Property="GridSplitter.ShowsPreview"
|
||||
Value="True" />
|
||||
</Style>
|
||||
<Style x:Key="{x:Type ListBox}">
|
||||
<Setter Property="FrameworkElement.Margin"
|
||||
Value="5" />
|
||||
</Style>
|
||||
<Style x:Key="{x:Type ListView}">
|
||||
<Setter Property="FrameworkElement.Margin"
|
||||
Value="5" />
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Expander Grid.Row="0"
|
||||
IsExpanded="True">
|
||||
<Expander.Header>
|
||||
<Label Content="Select Project" />
|
||||
</Expander.Header>
|
||||
<StackPanel>
|
||||
<ListBox ItemsSource="{Binding Projects}"
|
||||
SelectedValue="{Binding CurrentProject}"
|
||||
IsEnabled="{Binding IsSelectionEnabled}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="{Binding Name}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListBox>
|
||||
<Button Content="Load"
|
||||
Command="{Binding LoadCommand}" />
|
||||
<UserControl
|
||||
x:Class="Microsoft.VisualStudio.RazorExtension.RazorInfo.RazorInfoToolWindowControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Microsoft.VisualStudio.RazorExtension.RazorInfo"
|
||||
xmlns:behaviors="clr-namespace:Microsoft.VisualStudio.RazorExtension.Behaviors"
|
||||
Background="{DynamicResource VsBrush.Window}"
|
||||
Foreground="{DynamicResource VsBrush.WindowText}"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="300"
|
||||
Name="RazorInfoToolWindow">
|
||||
<UserControl.Resources>
|
||||
<Style
|
||||
x:Key="{x:Type Button}">
|
||||
<Setter
|
||||
Property="FrameworkElement.Margin"
|
||||
Value="5" />
|
||||
<Setter
|
||||
Property="Control.Padding"
|
||||
Value="5 2 5 2" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="{x:Type GridSplitter}">
|
||||
<Setter
|
||||
Property="FrameworkElement.Height"
|
||||
Value="5" />
|
||||
<Setter
|
||||
Property="FrameworkElement.HorizontalAlignment"
|
||||
Value="Stretch" />
|
||||
<Setter
|
||||
Property="FrameworkElement.VerticalAlignment"
|
||||
Value="Bottom" />
|
||||
<Setter
|
||||
Property="GridSplitter.ShowsPreview"
|
||||
Value="True" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="{x:Type ComboBox}">
|
||||
<Setter
|
||||
Property="FrameworkElement.Margin"
|
||||
Value="5" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="{x:Type Label}">
|
||||
<Setter
|
||||
Property="FrameworkElement.Margin"
|
||||
Value="5" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="{x:Type ListBox}">
|
||||
<Setter
|
||||
Property="FrameworkElement.Margin"
|
||||
Value="5" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="{x:Type ListView}">
|
||||
<Setter
|
||||
Property="FrameworkElement.Margin"
|
||||
Value="5" />
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<ScrollViewer
|
||||
VerticalScrollBarVisibility="Visible">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
<RowDefinition
|
||||
Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid
|
||||
Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition
|
||||
Width="Auto" />
|
||||
<ColumnDefinition
|
||||
Width="*" />
|
||||
<ColumnDefinition
|
||||
Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label
|
||||
Grid.Column="0"
|
||||
Content="Select Project" />
|
||||
<ComboBox
|
||||
Grid.Column="1"
|
||||
ItemsSource="{Binding Projects}"
|
||||
SelectedValue="{Binding CurrentProject}"
|
||||
IsEnabled="{Binding IsSelectionEnabled}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel
|
||||
Orientation="Horizontal">
|
||||
<Label
|
||||
Margin="0"
|
||||
Content="{Binding Name}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ComboBox>
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Content="Load"
|
||||
Command="{Binding LoadCommand}" />
|
||||
</Grid>
|
||||
<GridSplitter
|
||||
Grid.Row="1"
|
||||
IsEnabled="False" />
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
IsEnabled="{Binding CurrentProject.HasSnapshot}"
|
||||
Height="150">
|
||||
<ListView
|
||||
ItemsSource="{Binding CurrentProject.Snapshot.Properties}">
|
||||
<ListView.View>
|
||||
<GridView
|
||||
AllowsColumnReorder="False">
|
||||
<GridViewColumn
|
||||
Width="150">
|
||||
<GridViewColumnHeader
|
||||
Content="Property Name" />
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
TextWrapping="NoWrap"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="{Binding Path=Name, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
<GridViewColumn
|
||||
Width="Auto">
|
||||
<GridViewColumnHeader
|
||||
Content="Property Value" />
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
TextAlignment="Right"
|
||||
Margin="0"
|
||||
TextWrapping="NoWrap"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="{Binding Path=Value, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
</Grid>
|
||||
<GridSplitter
|
||||
Grid.Row="3" />
|
||||
<Expander
|
||||
Grid.Row="4"
|
||||
IsEnabled="{Binding CurrentProjectInfo, TargetNullValue=False}">
|
||||
<Expander.Header>
|
||||
<Label
|
||||
Content="Tag Helpers" />
|
||||
</Expander.Header>
|
||||
<ListView
|
||||
ItemsSource="{Binding CurrentProjectInfo.TagHelpers}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel
|
||||
Orientation="Horizontal">
|
||||
<Label
|
||||
Content="{Binding TargetElement}" />
|
||||
<Label
|
||||
Content="{Binding TypeName}" />
|
||||
<Label
|
||||
Content="{Binding AssemblyName}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListView>
|
||||
</Expander>
|
||||
<GridSplitter
|
||||
Grid.Row="5" />
|
||||
<Expander
|
||||
Grid.Row="6"
|
||||
IsEnabled="{Binding CurrentProjectInfo, TargetNullValue=False}">
|
||||
<Expander.Header>
|
||||
<Label
|
||||
Content="Directives" />
|
||||
</Expander.Header>
|
||||
<ListView
|
||||
ItemsSource="{Binding CurrentProjectInfo.Directives}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel
|
||||
Orientation="Horizontal">
|
||||
<Label
|
||||
Content="{Binding Name}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListView>
|
||||
</Expander>
|
||||
<GridSplitter
|
||||
Grid.Row="7" />
|
||||
<Expander
|
||||
Grid.Row="8"
|
||||
IsEnabled="{Binding CurrentProjectInfo, TargetNullValue=False}">
|
||||
<Expander.Header>
|
||||
<Label
|
||||
Content="Select Document" />
|
||||
</Expander.Header>
|
||||
<StackPanel>
|
||||
<ListBox
|
||||
ItemsSource="{Binding CurrentProjectInfo.Documents}"
|
||||
SelectedValue="{Binding CurrentDocument}"
|
||||
IsEnabled="{Binding IsSelectionEnabled}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel
|
||||
Orientation="Horizontal">
|
||||
<Label
|
||||
Content="{Binding FilePath}" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<GridSplitter Grid.Row="1" />
|
||||
<Expander Grid.Row="2"
|
||||
IsEnabled="{Binding CurrentProjectInfo, TargetNullValue=False}">
|
||||
<Expander.Header>
|
||||
<Label Content="Assemblies" />
|
||||
</Expander.Header>
|
||||
<ListView ItemsSource="{Binding CurrentProjectInfo.Assemblies}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="{Binding Name}">
|
||||
<Label.ToolTip>
|
||||
<ToolTip Content="{Binding FilePath}" />
|
||||
</Label.ToolTip>
|
||||
</Label>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListView>
|
||||
</Expander>
|
||||
<GridSplitter Grid.Row="3" />
|
||||
<Expander Grid.Row="4"
|
||||
IsEnabled="{Binding CurrentProjectInfo, TargetNullValue=False}" >
|
||||
<Expander.Header>
|
||||
<Label Content="Tag Helpers" />
|
||||
</Expander.Header>
|
||||
<ListView ItemsSource="{Binding CurrentProjectInfo.TagHelpers}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="{Binding TargetElement}" />
|
||||
<Label Content="{Binding TypeName}" />
|
||||
<Label Content="{Binding AssemblyName}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListView>
|
||||
</Expander>
|
||||
<GridSplitter Grid.Row="5" />
|
||||
<Expander Grid.Row="6"
|
||||
IsEnabled="{Binding CurrentProjectInfo, TargetNullValue=False}">
|
||||
<Expander.Header>
|
||||
<Label Content="Directives" />
|
||||
</Expander.Header>
|
||||
<ListView ItemsSource="{Binding CurrentProjectInfo.Directives}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="{Binding Name}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListView>
|
||||
</Expander>
|
||||
<GridSplitter Grid.Row="7" />
|
||||
<Expander Grid.Row="8"
|
||||
IsEnabled="{Binding CurrentProjectInfo, TargetNullValue=False}">
|
||||
<Expander.Header>
|
||||
<Label Content="Select Document" />
|
||||
</Expander.Header>
|
||||
<StackPanel>
|
||||
<ListBox ItemsSource="{Binding CurrentProjectInfo.Documents}"
|
||||
SelectedValue="{Binding CurrentDocument}"
|
||||
IsEnabled="{Binding IsSelectionEnabled}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Label Content="{Binding FilePath}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListBox>
|
||||
<Button Content="Generate"
|
||||
Command="{Binding GenerateCommand}" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<GridSplitter Grid.Row="9" />
|
||||
<Expander Grid.Row="10"
|
||||
IsEnabled="{Binding CurrentDocumentInfo, TargetNullValue=False}">
|
||||
<Expander.Header>
|
||||
<Label Content="Generated Code" />
|
||||
</Expander.Header>
|
||||
<TextBlock Text="{Binding CurrentDocumentInfo.Text}"
|
||||
TextWrapping="Wrap" />
|
||||
</Expander>
|
||||
<GridSplitter Grid.Row="11" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListBox>
|
||||
<Button
|
||||
Content="Generate"
|
||||
Command="{Binding GenerateCommand}" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<GridSplitter
|
||||
Grid.Row="9" />
|
||||
<Expander
|
||||
Grid.Row="10"
|
||||
IsEnabled="{Binding CurrentDocumentInfo, TargetNullValue=False}">
|
||||
<Expander.Header>
|
||||
<Label
|
||||
Content="Generated Code" />
|
||||
</Expander.Header>
|
||||
<TextBlock
|
||||
Text="{Binding CurrentDocumentInfo.Text}"
|
||||
TextWrapping="Wrap" />
|
||||
</Expander>
|
||||
<GridSplitter
|
||||
Grid.Row="11" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
{
|
||||
internal class RazorInfoViewModel : NotifyPropertyChanged
|
||||
{
|
||||
private readonly ProjectExtensibilityConfigurationFactory _configurationFactory;
|
||||
private readonly IRazorEngineDirectiveResolver _directiveResolver;
|
||||
private readonly IRazorEngineDocumentGenerator _documentGenerator;
|
||||
private readonly TagHelperResolver _tagHelperResolver;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly Workspace _workspace;
|
||||
private readonly ProjectSnapshotManager _projectManager;
|
||||
private readonly Action<Exception> _errorHandler;
|
||||
|
||||
private DocumentViewModel _currentDocument;
|
||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
public RazorInfoViewModel(
|
||||
IServiceProvider services,
|
||||
Workspace workspace,
|
||||
ProjectExtensibilityConfigurationFactory configurationFactory,
|
||||
ProjectSnapshotManager projectManager,
|
||||
IRazorEngineDirectiveResolver directiveResolver,
|
||||
TagHelperResolver tagHelperResolver,
|
||||
IRazorEngineDocumentGenerator documentGenerator,
|
||||
|
|
@ -51,7 +51,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
{
|
||||
_services = services;
|
||||
_workspace = workspace;
|
||||
_configurationFactory = configurationFactory;
|
||||
_projectManager = projectManager;
|
||||
_directiveResolver = directiveResolver;
|
||||
_tagHelperResolver = tagHelperResolver;
|
||||
_documentGenerator = documentGenerator;
|
||||
|
|
@ -94,7 +94,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
CurrentProjectInfo = null; // Clear cached value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ProjectInfoViewModel CurrentProjectInfo
|
||||
{
|
||||
get { return _currentProjectInfo; }
|
||||
|
|
@ -142,7 +142,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
|
||||
private bool CanExecuteLoad(object state)
|
||||
{
|
||||
return !IsLoading && CurrentProject != null;
|
||||
return !IsLoading && CurrentProject?.Snapshot?.Project?.WorkspaceProject != null;
|
||||
}
|
||||
|
||||
private void ExecuteLoad(object state)
|
||||
|
|
@ -152,7 +152,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
|
||||
private bool CanExecuteGenerate(object state)
|
||||
{
|
||||
return !IsLoading && CurrentDocument != null;
|
||||
return !IsLoading && CurrentDocument != null && CurrentProject?.Snapshot?.Project?.WorkspaceProject != null;
|
||||
}
|
||||
|
||||
private void ExecuteGenerate(object state)
|
||||
|
|
@ -168,10 +168,9 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
IsLoading = true;
|
||||
|
||||
var solution = _workspace.CurrentSolution;
|
||||
var project = solution.GetProject(projectViewModel.Id);
|
||||
var project = solution.GetProject(projectViewModel.Snapshot.Project.WorkspaceProject.Id);
|
||||
|
||||
var documents = GetCshtmlDocuments(project);
|
||||
var configuration = await _configurationFactory.GetConfigurationAsync(project);
|
||||
|
||||
var directives = await _directiveResolver.GetRazorEngineDirectivesAsync(_workspace, project);
|
||||
var assemblyFilters = project.MetadataReferences
|
||||
|
|
@ -184,7 +183,6 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
|
||||
CurrentProjectInfo = new ProjectInfoViewModel()
|
||||
{
|
||||
Assemblies = new ObservableCollection<AssemblyViewModel>(configuration.Assemblies.Select(a => new AssemblyViewModel(a))),
|
||||
Directives = new ObservableCollection<DirectiveViewModel>(directives.Select(d => new DirectiveViewModel(d))),
|
||||
Documents = new ObservableCollection<DocumentViewModel>(documents.Select(d => new DocumentViewModel(d))),
|
||||
TagHelpers = new ObservableCollection<TagHelperViewModel>(resolutionResult.Descriptors.Select(t => new TagHelperViewModel(t))),
|
||||
|
|
@ -232,7 +230,7 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
|
||||
if (text != null)
|
||||
{
|
||||
var project = _workspace.CurrentSolution.GetProject(CurrentProject.Id);
|
||||
var project = _workspace.CurrentSolution.GetProject(CurrentProject.Snapshot.Project.WorkspaceProject.Id);
|
||||
var generated = await _documentGenerator.GenerateDocumentAsync(_workspace, project, documentViewModel.FilePath, text);
|
||||
|
||||
CurrentDocumentInfo = new DocumentInfoViewModel(generated);
|
||||
|
|
|
|||
Loading…
Reference in New Issue