Add host project system for VS4Mac.
- Tied into VS4Macs ProjectExtensions in order to bootstrap our Razor world. - We currently watch all DotNet projects with the expectation that they're the only ones that can potentially turn into Razor compatible projects. - Added a fallback Razor project host which is used for pre-Razor SDK Razor versions (< 2.1). - Added a default Razor project host which consumes all MSBuild data from the users packages and sets up the Razor world accordingly. - Had to modify some existing contracts to work better with new expectations. one of these was the VS4Mac specific Workspace accessor; essentially we needed to be able to lookup a workspace from a solution. - Some of our previous expectations about addins were wrong (not being able to directly reference your libraries). To avoid using reflection to bootstrap our types I tried out directly referencing our libraries and all worked fine. - Refactored the DefaultRazorProjectHost in windows (since we had to in Mac) for testing purposes. #2081
This commit is contained in:
parent
492e958114
commit
1d602d1205
|
|
@ -36,7 +36,7 @@
|
|||
<MicrosoftVisualStudioShellInterop90PackageVersion>9.0.30729</MicrosoftVisualStudioShellInterop90PackageVersion>
|
||||
<MicrosoftVisualStudioShellInteropPackageVersion>7.10.6071</MicrosoftVisualStudioShellInteropPackageVersion>
|
||||
<MicrosoftVisualStudioTextUIPackageVersion>15.6.161-preview</MicrosoftVisualStudioTextUIPackageVersion>
|
||||
<MonoAddinsPackageVersion>1.3.7</MonoAddinsPackageVersion>
|
||||
<MonoAddinsPackageVersion>1.3.8</MonoAddinsPackageVersion>
|
||||
<MonoDevelopSdkPackageVersion>1.0.1</MonoDevelopSdkPackageVersion>
|
||||
<MoqPackageVersion>4.7.49</MoqPackageVersion>
|
||||
<NETStandardLibrary20PackageVersion>2.0.1</NETStandardLibrary20PackageVersion>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Mac.RazorAddin, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Remote.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Editor.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
|
@ -10,6 +12,9 @@ using System.Threading.Tasks.Dataflow;
|
|||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.VisualStudio.LanguageServices;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
using ProjectState = System.Collections.Immutable.IImmutableDictionary<string, Microsoft.VisualStudio.ProjectSystem.Properties.IProjectRuleSnapshot>;
|
||||
using ProjectStateItem = System.Collections.Generic.KeyValuePair<string, System.Collections.Immutable.IImmutableDictionary<string, string>>;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -82,47 +87,180 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
{
|
||||
await ExecuteWithLock(async () =>
|
||||
{
|
||||
var languageVersion = update.Value.CurrentState[Rules.RazorGeneral.SchemaName].Properties[Rules.RazorGeneral.RazorLangVersionProperty];
|
||||
var defaultConfiguration = update.Value.CurrentState[Rules.RazorGeneral.SchemaName].Properties[Rules.RazorGeneral.RazorDefaultConfigurationProperty];
|
||||
|
||||
RazorConfiguration configuration = null;
|
||||
if (!string.IsNullOrEmpty(languageVersion) && !string.IsNullOrEmpty(defaultConfiguration))
|
||||
if (TryGetConfiguration(update.Value.CurrentState, out var configuration))
|
||||
{
|
||||
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();
|
||||
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
|
||||
await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (configuration == null)
|
||||
else
|
||||
{
|
||||
// Ok we can't find a language version. Let's assume this project isn't using Razor then.
|
||||
// Ok we can't find a configuration. Let's assume this project isn't using Razor then.
|
||||
await UpdateProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration);
|
||||
await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false);
|
||||
});
|
||||
}, registerFaultHandler: true);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetConfiguration(
|
||||
ProjectState projectState,
|
||||
out RazorConfiguration configuration)
|
||||
{
|
||||
if (!TryGetDefaultConfiguration(projectState, out var defaultConfiguration))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetLanguageVersion(projectState, out var languageVersion))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetConfigurationItem(defaultConfiguration, projectState, out var configurationItem))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetExtensions(configuredExtensionNames, projectState, out var extensions))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
configuration = new ProjectSystemRazorConfiguration(languageVersion, configurationItem.Key, extensions);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetDefaultConfiguration(ProjectState projectState, out string defaultConfiguration)
|
||||
{
|
||||
if (!projectState.TryGetValue(Rules.RazorGeneral.SchemaName, out var rule))
|
||||
{
|
||||
defaultConfiguration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rule.Properties.TryGetValue(Rules.RazorGeneral.RazorDefaultConfigurationProperty, out defaultConfiguration))
|
||||
{
|
||||
defaultConfiguration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(defaultConfiguration))
|
||||
{
|
||||
defaultConfiguration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetLanguageVersion(ProjectState projectState, out RazorLanguageVersion languageVersion)
|
||||
{
|
||||
if (!projectState.TryGetValue(Rules.RazorGeneral.SchemaName, out var rule))
|
||||
{
|
||||
languageVersion = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rule.Properties.TryGetValue(Rules.RazorGeneral.RazorLangVersionProperty, out var languageVersionValue))
|
||||
{
|
||||
languageVersion = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(languageVersionValue))
|
||||
{
|
||||
languageVersion = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RazorLanguageVersion.TryParse(languageVersionValue, out languageVersion))
|
||||
{
|
||||
languageVersion = RazorLanguageVersion.Latest;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetConfigurationItem(
|
||||
string configuration,
|
||||
ProjectState projectState,
|
||||
out ProjectStateItem configurationItem)
|
||||
{
|
||||
if (!projectState.TryGetValue(Rules.RazorConfiguration.PrimaryDataSourceItemType, out var configurationState))
|
||||
{
|
||||
configurationItem = default(ProjectStateItem);
|
||||
return false;
|
||||
}
|
||||
|
||||
var razorConfigurationItems = configurationState.Items;
|
||||
foreach (var item in razorConfigurationItems)
|
||||
{
|
||||
if (item.Key == configuration)
|
||||
{
|
||||
configurationItem = item;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
configurationItem = default(ProjectStateItem);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetConfiguredExtensionNames(ProjectStateItem configurationItem, out string[] configuredExtensionNames)
|
||||
{
|
||||
if (!configurationItem.Value.TryGetValue(Rules.RazorConfiguration.ExtensionsProperty, out var extensionNamesValue))
|
||||
{
|
||||
configuredExtensionNames = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(extensionNamesValue))
|
||||
{
|
||||
configuredExtensionNames = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
configuredExtensionNames = extensionNamesValue.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetExtensions(string[] configuredExtensionNames, ProjectState projectState, out ProjectSystemRazorExtension[] extensions)
|
||||
{
|
||||
if (!projectState.TryGetValue(Rules.RazorExtension.PrimaryDataSourceItemType, out var extensionState))
|
||||
{
|
||||
extensions = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var extensionItems = extensionState.Items;
|
||||
var extensionList = new List<ProjectSystemRazorExtension>();
|
||||
foreach (var item in extensionItems)
|
||||
{
|
||||
var extensionName = item.Key;
|
||||
if (configuredExtensionNames.Contains(extensionName))
|
||||
{
|
||||
extensionList.Add(new ProjectSystemRazorExtension(extensionName));
|
||||
}
|
||||
}
|
||||
|
||||
extensions = extensionList.ToArray();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,12 +13,13 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
|||
{
|
||||
[System.Composition.Shared]
|
||||
[Export(typeof(VisualStudioWorkspaceAccessor))]
|
||||
internal class DefaultVisualStudioWorkspaceAccessor : VisualStudioWorkspaceAccessor
|
||||
[Export(typeof(VisualStudioMacWorkspaceAccessor))]
|
||||
internal class DefaultVisualStudioMacWorkspaceAccessor : VisualStudioMacWorkspaceAccessor
|
||||
{
|
||||
private readonly TextBufferProjectService _projectService;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultVisualStudioWorkspaceAccessor(TextBufferProjectService projectService)
|
||||
public DefaultVisualStudioMacWorkspaceAccessor(TextBufferProjectService projectService)
|
||||
{
|
||||
if (projectService == null)
|
||||
{
|
||||
|
|
@ -56,7 +57,17 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
|||
return false;
|
||||
}
|
||||
|
||||
workspace = TypeSystemService.GetWorkspace(hostSolution);
|
||||
return TryGetWorkspace(hostSolution, out workspace);
|
||||
}
|
||||
|
||||
public override bool TryGetWorkspace(Solution solution, out Workspace workspace)
|
||||
{
|
||||
if (solution == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(solution));
|
||||
}
|
||||
|
||||
workspace = TypeSystemService.GetWorkspace(solution);
|
||||
|
||||
// Workspace cannot be null at this point. If TypeSystemService.GetWorkspace isn't able to find a corresponding
|
||||
// workspace it returns an empty workspace. Therefore, in order to see if we have a valid workspace we need to
|
||||
|
|
@ -17,6 +17,7 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.Editor
|
|||
[Export(typeof(TextBufferProjectService))]
|
||||
internal class DefaultTextBufferProjectService : TextBufferProjectService
|
||||
{
|
||||
private const string DotNetCoreRazorCapability = "DotNetCoreRazor | AspNetCore";
|
||||
private readonly ITextDocumentFactoryService _documentFactory;
|
||||
|
||||
[ImportingConstructor]
|
||||
|
|
@ -73,7 +74,20 @@ namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.Editor
|
|||
}
|
||||
|
||||
// VisualStudio for Mac only supports ASP.NET Core Razor.
|
||||
public override bool IsSupportedProject(object project) => project is DotNetProject;
|
||||
public override bool IsSupportedProject(object project)
|
||||
{
|
||||
if (!(project is DotNetProject dotNetProject))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dotNetProject.IsCapabilityMatch(DotNetCoreRazorCapability))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string GetProjectName(object project)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
using MonoDevelop.Projects;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.ProjectSystem
|
||||
{
|
||||
internal class DefaultDotNetProjectHost : DotNetProjectHost
|
||||
{
|
||||
private const string ExplicitRazorConfigurationCapability = "DotNetCoreRazorConfiguration";
|
||||
|
||||
private readonly DotNetProject _project;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly VisualStudioMacWorkspaceAccessor _workspaceAccessor;
|
||||
private readonly TextBufferProjectService _projectService;
|
||||
private RazorProjectHostBase _razorProjectHost;
|
||||
|
||||
public DefaultDotNetProjectHost(
|
||||
DotNetProject project,
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
VisualStudioMacWorkspaceAccessor workspaceAccessor,
|
||||
TextBufferProjectService projectService)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (workspaceAccessor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceAccessor));
|
||||
}
|
||||
|
||||
if (projectService == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectService));
|
||||
}
|
||||
|
||||
_project = project;
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_workspaceAccessor = workspaceAccessor;
|
||||
_projectService = projectService;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal DefaultDotNetProjectHost(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
VisualStudioMacWorkspaceAccessor workspaceAccessor,
|
||||
TextBufferProjectService projectService)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (workspaceAccessor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceAccessor));
|
||||
}
|
||||
|
||||
if (projectService == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectService));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_workspaceAccessor = workspaceAccessor;
|
||||
_projectService = projectService;
|
||||
}
|
||||
|
||||
public override DotNetProject Project => _project;
|
||||
|
||||
public override void Subscribe()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
UpdateRazorHostProject();
|
||||
|
||||
_project.ProjectCapabilitiesChanged += Project_ProjectCapabilitiesChanged;
|
||||
_project.Disposing += Project_Disposing;
|
||||
}
|
||||
|
||||
private void Project_Disposing(object sender, EventArgs e)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
_project.ProjectCapabilitiesChanged -= Project_ProjectCapabilitiesChanged;
|
||||
_project.Disposing -= Project_Disposing;
|
||||
|
||||
DetatchCurrentRazorProjectHost();
|
||||
}
|
||||
|
||||
private void Project_ProjectCapabilitiesChanged(object sender, EventArgs e) => UpdateRazorHostProject();
|
||||
|
||||
// Internal for testing
|
||||
internal void UpdateRazorHostProject()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
DetatchCurrentRazorProjectHost();
|
||||
|
||||
if (!_projectService.IsSupportedProject(_project))
|
||||
{
|
||||
// Not a Razor compatible project.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryGetProjectSnapshotManager(out var projectSnapshotManager))
|
||||
{
|
||||
// Could not get a ProjectSnapshotManager for the current project.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_project.IsCapabilityMatch(ExplicitRazorConfigurationCapability))
|
||||
{
|
||||
// SDK >= 2.1
|
||||
_razorProjectHost = new DefaultRazorProjectHost(_project, _foregroundDispatcher, projectSnapshotManager);
|
||||
return;
|
||||
}
|
||||
|
||||
// We're an older version of Razor at this point, SDK < 2.1
|
||||
_razorProjectHost = new FallbackRazorProjectHost(_project, _foregroundDispatcher, projectSnapshotManager);
|
||||
}
|
||||
|
||||
private bool TryGetProjectSnapshotManager(out ProjectSnapshotManagerBase projectSnapshotManagerBase)
|
||||
{
|
||||
if (!_workspaceAccessor.TryGetWorkspace(_project.ParentSolution, out var workspace))
|
||||
{
|
||||
// Could not locate workspace for razor project. Project is most likely tearing down.
|
||||
projectSnapshotManagerBase = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var languageService = workspace.Services.GetLanguageServices(RazorLanguage.Name);
|
||||
projectSnapshotManagerBase = (ProjectSnapshotManagerBase)languageService.GetRequiredService<ProjectSnapshotManager>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void DetatchCurrentRazorProjectHost()
|
||||
{
|
||||
_razorProjectHost?.Detatch();
|
||||
_razorProjectHost = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using MonoDevelop.Projects;
|
||||
using MonoDevelop.Projects.MSBuild;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.ProjectSystem
|
||||
{
|
||||
internal class DefaultRazorProjectHost : RazorProjectHostBase
|
||||
{
|
||||
private const string RazorLangVersionProperty = "RazorLangVersion";
|
||||
private const string RazorDefaultConfigurationProperty = "RazorDefaultConfiguration";
|
||||
private const string RazorExtensionItemType = "RazorExtension";
|
||||
private const string RazorConfigurationItemType = "RazorConfiguration";
|
||||
private const string RazorConfigurationItemTypeExtensionsProperty = "Extensions";
|
||||
|
||||
public DefaultRazorProjectHost(
|
||||
DotNetProject project,
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
ProjectSnapshotManagerBase projectSnapshotManager)
|
||||
: base(project, foregroundDispatcher, projectSnapshotManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task OnProjectChangedAsync()
|
||||
{
|
||||
ForegroundDispatcher.AssertBackgroundThread();
|
||||
|
||||
await ExecuteWithLockAsync(async () =>
|
||||
{
|
||||
var projectProperties = DotNetProject.MSBuildProject.EvaluatedProperties;
|
||||
var projectItems = DotNetProject.MSBuildProject.EvaluatedItems;
|
||||
|
||||
if (TryGetConfiguration(projectProperties, projectItems, out var configuration))
|
||||
{
|
||||
var hostProject = new HostProject(DotNetProject.FileName.FullPath, configuration);
|
||||
await UpdateHostProjectUnsafeAsync(hostProject).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ok we can't find a configuration. Let's assume this project isn't using Razor then.
|
||||
await UpdateHostProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetConfiguration(
|
||||
IMSBuildEvaluatedPropertyCollection projectProperties,
|
||||
IEnumerable<IMSBuildItemEvaluated> projectItems,
|
||||
out RazorConfiguration configuration)
|
||||
{
|
||||
if (!TryGetDefaultConfiguration(projectProperties, out var defaultConfiguration))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetLanguageVersion(projectProperties, out var languageVersion))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetConfigurationItem(defaultConfiguration, projectItems, out var configurationItem))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames))
|
||||
{
|
||||
configuration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var extensions = GetExtensions(configuredExtensionNames, projectItems);
|
||||
configuration = new ProjectSystemRazorConfiguration(languageVersion, configurationItem.Include, extensions);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetDefaultConfiguration(IMSBuildEvaluatedPropertyCollection projectProperties, out string defaultConfiguration)
|
||||
{
|
||||
defaultConfiguration = projectProperties.GetValue(RazorDefaultConfigurationProperty);
|
||||
if (string.IsNullOrEmpty(defaultConfiguration))
|
||||
{
|
||||
defaultConfiguration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetLanguageVersion(IMSBuildEvaluatedPropertyCollection projectProperties, out RazorLanguageVersion languageVersion)
|
||||
{
|
||||
var languageVersionValue = projectProperties.GetValue(RazorLangVersionProperty);
|
||||
if (string.IsNullOrEmpty(languageVersionValue))
|
||||
{
|
||||
languageVersion = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RazorLanguageVersion.TryParse(languageVersionValue, out languageVersion))
|
||||
{
|
||||
languageVersion = RazorLanguageVersion.Latest;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetConfigurationItem(
|
||||
string configuration,
|
||||
IEnumerable<IMSBuildItemEvaluated> projectItems,
|
||||
out IMSBuildItemEvaluated configurationItem)
|
||||
{
|
||||
foreach (var item in projectItems)
|
||||
{
|
||||
if (item.Name == RazorConfigurationItemType && item.Include == configuration)
|
||||
{
|
||||
configurationItem = item;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
configurationItem = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool TryGetConfiguredExtensionNames(IMSBuildItemEvaluated configurationItem, out string[] configuredExtensionNames)
|
||||
{
|
||||
var extensionNamesValue = configurationItem.Metadata.GetValue(RazorConfigurationItemTypeExtensionsProperty);
|
||||
|
||||
if (string.IsNullOrEmpty(extensionNamesValue))
|
||||
{
|
||||
configuredExtensionNames = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
configuredExtensionNames = extensionNamesValue.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static ProjectSystemRazorExtension[] GetExtensions(
|
||||
string[] configuredExtensionNames,
|
||||
IEnumerable<IMSBuildItemEvaluated> projectItems)
|
||||
{
|
||||
var extensions = new List<ProjectSystemRazorExtension>();
|
||||
|
||||
foreach (var item in projectItems)
|
||||
{
|
||||
if (item.Name != RazorExtensionItemType)
|
||||
{
|
||||
// Not a RazorExtension
|
||||
continue;
|
||||
}
|
||||
|
||||
var extensionName = item.Include;
|
||||
if (configuredExtensionNames.Contains(extensionName))
|
||||
{
|
||||
extensions.Add(new ProjectSystemRazorExtension(extensionName));
|
||||
}
|
||||
}
|
||||
|
||||
return extensions.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// 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 MonoDevelop.Projects;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.ProjectSystem
|
||||
{
|
||||
internal abstract class DotNetProjectHost
|
||||
{
|
||||
public abstract DotNetProject Project { get; }
|
||||
|
||||
public abstract void Subscribe();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Editor.Razor;
|
||||
using MonoDevelop.Projects;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.ProjectSystem
|
||||
{
|
||||
[System.Composition.Shared]
|
||||
[Export(typeof(DotNetProjectHostFactory))]
|
||||
internal class DotNetProjectHostFactory
|
||||
{
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly VisualStudioMacWorkspaceAccessor _workspaceAccessor;
|
||||
private readonly TextBufferProjectService _projectService;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DotNetProjectHostFactory(
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
VisualStudioMacWorkspaceAccessor workspaceAccessor,
|
||||
TextBufferProjectService projectService)
|
||||
{
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (workspaceAccessor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(workspaceAccessor));
|
||||
}
|
||||
|
||||
if (projectService == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectService));
|
||||
}
|
||||
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_workspaceAccessor = workspaceAccessor;
|
||||
_projectService = projectService;
|
||||
}
|
||||
|
||||
public DotNetProjectHost Create(DotNetProject project)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
var projectHost = new DefaultDotNetProjectHost(project, _foregroundDispatcher, _workspaceAccessor, _projectService);
|
||||
return projectHost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using MonoDevelop.Projects;
|
||||
using AssemblyReference = MonoDevelop.Projects.AssemblyReference;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.ProjectSystem
|
||||
{
|
||||
internal class FallbackRazorProjectHost : RazorProjectHostBase
|
||||
{
|
||||
private const string MvcAssemblyFileName = "Microsoft.AspNetCore.Mvc.Razor.dll";
|
||||
|
||||
public FallbackRazorProjectHost(
|
||||
DotNetProject project,
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
ProjectSnapshotManagerBase projectSnapshotManager)
|
||||
: base(project, foregroundDispatcher, projectSnapshotManager)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task OnProjectChangedAsync()
|
||||
{
|
||||
ForegroundDispatcher.AssertBackgroundThread();
|
||||
|
||||
await ExecuteWithLockAsync(async () =>
|
||||
{
|
||||
var referencedAssemblies = await DotNetProject.GetReferencedAssemblies(ConfigurationSelector.Default);
|
||||
var mvcReference = referencedAssemblies.FirstOrDefault(IsMvcAssembly);
|
||||
|
||||
if (mvcReference == null)
|
||||
{
|
||||
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
|
||||
await UpdateHostProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var version = GetAssemblyVersion(mvcReference.FilePath);
|
||||
if (version == null)
|
||||
{
|
||||
// Ok we can't find an MVC version. Let's assume this project isn't using Razor then.
|
||||
await UpdateHostProjectUnsafeAsync(null).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var configuration = FallbackRazorConfiguration.SelectConfiguration(version);
|
||||
var hostProject = new HostProject(DotNetProject.FileName.FullPath, configuration);
|
||||
await UpdateHostProjectUnsafeAsync(hostProject).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static bool IsMvcAssembly(AssemblyReference reference)
|
||||
{
|
||||
var fileName = reference?.FilePath.FileName;
|
||||
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.Equals(reference.FilePath.FileName, MvcAssemblyFileName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Mvc assembly
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Version GetAssemblyVersion(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,169 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using MonoDevelop.Projects;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.ProjectSystem
|
||||
{
|
||||
internal abstract class RazorProjectHostBase
|
||||
{
|
||||
// References changes are always triggered when project changes happen.
|
||||
private const string ProjectChangedHint = "References";
|
||||
|
||||
private bool _batchingProjectChanges;
|
||||
private readonly DotNetProject _dotNetProject;
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
private readonly ProjectSnapshotManagerBase _projectSnapshotManager;
|
||||
private readonly AsyncSemaphore _onProjectChangedInnerSemaphore;
|
||||
private readonly AsyncSemaphore _projectChangedSemaphore;
|
||||
private HostProject _currentHostProject;
|
||||
|
||||
public RazorProjectHostBase(
|
||||
DotNetProject project,
|
||||
ForegroundDispatcher foregroundDispatcher,
|
||||
ProjectSnapshotManagerBase projectSnapshotManager)
|
||||
{
|
||||
if (project == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(project));
|
||||
}
|
||||
|
||||
if (foregroundDispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foregroundDispatcher));
|
||||
}
|
||||
|
||||
if (projectSnapshotManager == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectSnapshotManager));
|
||||
}
|
||||
|
||||
_dotNetProject = project;
|
||||
_foregroundDispatcher = foregroundDispatcher;
|
||||
_projectSnapshotManager = projectSnapshotManager;
|
||||
_onProjectChangedInnerSemaphore = new AsyncSemaphore(initialCount: 1);
|
||||
_projectChangedSemaphore = new AsyncSemaphore(initialCount: 1);
|
||||
|
||||
AttachToProject();
|
||||
}
|
||||
|
||||
public DotNetProject DotNetProject => _dotNetProject;
|
||||
|
||||
public HostProject HostProject => _currentHostProject;
|
||||
|
||||
protected ForegroundDispatcher ForegroundDispatcher => _foregroundDispatcher;
|
||||
|
||||
public void Detatch()
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
DotNetProject.Modified -= DotNetProject_Modified;
|
||||
|
||||
UpdateHostProjectForeground(null);
|
||||
}
|
||||
|
||||
protected abstract Task OnProjectChangedAsync();
|
||||
|
||||
// Protected virtual for testing
|
||||
protected virtual void AttachToProject()
|
||||
{
|
||||
ForegroundDispatcher.AssertForegroundThread();
|
||||
|
||||
DotNetProject.Modified += DotNetProject_Modified;
|
||||
|
||||
// Trigger the initial update to the project.
|
||||
_batchingProjectChanges = true;
|
||||
Task.Factory.StartNew(ProjectChangedBackgroundAsync, null, CancellationToken.None, TaskCreationOptions.None, ForegroundDispatcher.BackgroundScheduler);
|
||||
}
|
||||
|
||||
// Must be called inside the lock.
|
||||
protected async Task UpdateHostProjectUnsafeAsync(HostProject newHostProject)
|
||||
{
|
||||
_foregroundDispatcher.AssertBackgroundThread();
|
||||
|
||||
await Task.Factory.StartNew(UpdateHostProjectForeground, newHostProject, CancellationToken.None, TaskCreationOptions.None, ForegroundDispatcher.ForegroundScheduler);
|
||||
}
|
||||
|
||||
protected async Task ExecuteWithLockAsync(Func<Task> func)
|
||||
{
|
||||
using (await _projectChangedSemaphore.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
await func().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProjectChangedBackgroundAsync(object state)
|
||||
{
|
||||
ForegroundDispatcher.AssertBackgroundThread();
|
||||
|
||||
_batchingProjectChanges = false;
|
||||
|
||||
// Ensure ordering, typically we'll only have 1 background thread in flight at a time. However,
|
||||
// between this line and the one prior another background thread could have also entered this
|
||||
// method. This is here to protect against us changing the order of project changed events.
|
||||
using (await _onProjectChangedInnerSemaphore.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
await OnProjectChangedAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void DotNetProject_Modified(object sender, SolutionItemModifiedEventArgs args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
ForegroundDispatcher.AssertForegroundThread();
|
||||
|
||||
if (_batchingProjectChanges)
|
||||
{
|
||||
// Already waiting to recompute host project, no need to do any more work to determine if we're dirty.
|
||||
return;
|
||||
}
|
||||
|
||||
var projectChanged = args.Any(arg => string.Equals(arg.Hint, ProjectChangedHint, StringComparison.Ordinal));
|
||||
if (projectChanged)
|
||||
{
|
||||
// This method can be spammed for tons of project change events but all we really care about is "are we dirty?".
|
||||
// Therefore, we re-dispatch here to allow any remaining project change events to fire and to then only have 1 host
|
||||
// project change trigger; this way we don't spam our own system with re-configure calls.
|
||||
_batchingProjectChanges = true;
|
||||
Task.Factory.StartNew(ProjectChangedBackgroundAsync, null, CancellationToken.None, TaskCreationOptions.None, ForegroundDispatcher.BackgroundScheduler);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateHostProjectForeground(object state)
|
||||
{
|
||||
_foregroundDispatcher.AssertForegroundThread();
|
||||
|
||||
var newHostProject = (HostProject)state;
|
||||
|
||||
if (_currentHostProject == null && newHostProject == null)
|
||||
{
|
||||
// This is a no-op. This project isn't using Razor.
|
||||
}
|
||||
else if (_currentHostProject == null && newHostProject != null)
|
||||
{
|
||||
_projectSnapshotManager.HostProjectAdded(newHostProject);
|
||||
}
|
||||
else if (_currentHostProject != null && newHostProject == null)
|
||||
{
|
||||
_projectSnapshotManager.HostProjectRemoved(HostProject);
|
||||
}
|
||||
else
|
||||
{
|
||||
_projectSnapshotManager.HostProjectChanged(newHostProject);
|
||||
}
|
||||
|
||||
_currentHostProject = newHostProject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,5 +3,6 @@
|
|||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Mac.RazorAddin, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Mac.LanguageServices.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
// 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.Editor.Razor;
|
||||
using MonoDevelop.Projects;
|
||||
using Workspace = Microsoft.CodeAnalysis.Workspace;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
||||
{
|
||||
internal abstract class VisualStudioMacWorkspaceAccessor : VisualStudioWorkspaceAccessor
|
||||
{
|
||||
public abstract bool TryGetWorkspace(Solution solution, out Workspace workspace);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,16 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.VisualStudio.LanguageServices.Razor;
|
||||
using Microsoft.VisualStudio.ProjectSystem;
|
||||
using Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using ProjectStateItem = System.Collections.Generic.KeyValuePair<string, System.Collections.Immutable.IImmutableDictionary<string, string>>;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
||||
{
|
||||
|
|
@ -24,6 +28,571 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
private Workspace Workspace { get; }
|
||||
|
||||
[Fact]
|
||||
public void TryGetDefaultConfiguration_FailsIfNoRule()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>().ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetDefaultConfiguration(projectState, out var defaultConfiguration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(defaultConfiguration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetDefaultConfiguration_FailsIfNoConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>())
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetDefaultConfiguration(projectState, out var defaultConfiguration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(defaultConfiguration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetDefaultConfiguration_FailsIfEmptyConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(
|
||||
Rules.RazorGeneral.SchemaName,
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorGeneral.RazorDefaultConfigurationProperty] = string.Empty
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetDefaultConfiguration(projectState, out var defaultConfiguration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(defaultConfiguration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetDefaultConfiguration_SucceedsWithValidConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var expectedConfiguration = "Razor-13.37";
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(
|
||||
Rules.RazorGeneral.SchemaName,
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorGeneral.RazorDefaultConfigurationProperty] = expectedConfiguration
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetDefaultConfiguration(projectState, out var defaultConfiguration);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Equal(expectedConfiguration, defaultConfiguration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetLanguageVersion_FailsIfNoRule()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>().ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetLanguageVersion(projectState, out var languageVersion);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(languageVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetLanguageVersion_FailsIfNoLanguageVersion()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(Rules.RazorGeneral.SchemaName, new Dictionary<string, string>())
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetLanguageVersion(projectState, out var languageVersion);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(languageVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetLanguageVersion_FailsIfEmptyLanguageVersion()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(
|
||||
Rules.RazorGeneral.SchemaName,
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorGeneral.RazorLangVersionProperty] = string.Empty
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetLanguageVersion(projectState, out var languageVersion);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(languageVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetLanguageVersion_SucceedsWithValidLanguageVersion()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(
|
||||
Rules.RazorGeneral.SchemaName,
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorGeneral.RazorLangVersionProperty] = "1.0"
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetLanguageVersion(projectState, out var languageVersion);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Same(RazorLanguageVersion.Version_1_0, languageVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetLanguageVersion_SucceedsWithUnknownLanguageVersion_DefaultsToLatest()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(
|
||||
Rules.RazorGeneral.SchemaName,
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorGeneral.RazorLangVersionProperty] = "13.37"
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetLanguageVersion(projectState, out var languageVersion);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Same(RazorLanguageVersion.Latest, languageVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfigurationItem_FailsNoRazorConfigurationRule()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>().ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfigurationItem("Razor-13.37", projectState, out var configurationItem);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfigurationItem_FailsNoRazorConfigurationItems()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorConfiguration.SchemaName] = TestProjectRuleSnapshot.CreateItems(
|
||||
Rules.RazorConfiguration.SchemaName,
|
||||
new Dictionary<string, Dictionary<string, string>>())
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfigurationItem("Razor-13.37", projectState, out var configurationItem);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfigurationItem_FailsNoMatchingRazorConfigurationItems()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorConfiguration.SchemaName] = TestProjectRuleSnapshot.CreateItems(
|
||||
Rules.RazorConfiguration.SchemaName,
|
||||
new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
["Razor-10.0"] = new Dictionary<string, string>(),
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfigurationItem("Razor-13.37", projectState, out var configurationItem);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfigurationItem_SucceedsForMatchingConfigurationItem()
|
||||
{
|
||||
// Arrange
|
||||
var expectedConfiguration = "Razor-13.37";
|
||||
var expectedConfigurationValue = new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorConfiguration.ExtensionsProperty] = "SomeExtension"
|
||||
};
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorConfiguration.SchemaName] = TestProjectRuleSnapshot.CreateItems(
|
||||
Rules.RazorConfiguration.SchemaName,
|
||||
new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
[expectedConfiguration] = expectedConfigurationValue
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfigurationItem(expectedConfiguration, projectState, out var configurationItem);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Equal(expectedConfiguration, configurationItem.Key);
|
||||
Assert.True(Enumerable.SequenceEqual(expectedConfigurationValue, configurationItem.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguredExtensionNames_FailsIfNoExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var extensions = new Dictionary<string, string>().ToImmutableDictionary();
|
||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionnames);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuredExtensionnames);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguredExtensionNames_FailsIfEmptyExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var extensions = new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorConfiguration.ExtensionsProperty] = string.Empty
|
||||
}.ToImmutableDictionary();
|
||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuredExtensionNames);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguredExtensionNames_SucceedsIfSingleExtension()
|
||||
{
|
||||
// Arrange
|
||||
var expectedExtensionName = "SomeExtensionName";
|
||||
var extensions = new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorConfiguration.ExtensionsProperty] = expectedExtensionName
|
||||
}.ToImmutableDictionary();
|
||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
var extensionName = Assert.Single(configuredExtensionNames);
|
||||
Assert.Equal(expectedExtensionName, extensionName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguredExtensionNames_SucceedsIfMultipleExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var extensions = new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorConfiguration.ExtensionsProperty] = "SomeExtensionName;SomeOtherExtensionName"
|
||||
}.ToImmutableDictionary();
|
||||
var configurationItem = new ProjectStateItem(Rules.RazorConfiguration.SchemaName, extensions);
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Collection(
|
||||
configuredExtensionNames,
|
||||
name => Assert.Equal("SomeExtensionName", name),
|
||||
name => Assert.Equal("SomeOtherExtensionName", name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetExtensions_NoExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>().ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetExtensions(new[] { "Extension1", "Extension2" }, projectState, out var extensions);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(extensions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetExtensions_SucceedsWithUnConfiguredExtensionTypes()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorExtension.PrimaryDataSourceItemType] = TestProjectRuleSnapshot.CreateItems(
|
||||
Rules.RazorExtension.PrimaryDataSourceItemType,
|
||||
new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
["UnconfiguredExtensionName"] = new Dictionary<string, string>()
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetExtensions(new[] { "Extension1", "Extension2" }, projectState, out var extensions);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Empty(extensions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetExtensions_SucceedsWithSomeConfiguredExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var expectedExtension1Name = "Extension1";
|
||||
var expectedExtension2Name = "Extension2";
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorExtension.PrimaryDataSourceItemType] = TestProjectRuleSnapshot.CreateItems(
|
||||
Rules.RazorExtension.PrimaryDataSourceItemType,
|
||||
new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
["UnconfiguredExtensionName"] = new Dictionary<string, string>(),
|
||||
[expectedExtension1Name] = new Dictionary<string, string>(),
|
||||
[expectedExtension2Name] = new Dictionary<string, string>(),
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetExtensions(new[] { expectedExtension1Name, expectedExtension2Name }, projectState, out var extensions);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Collection(
|
||||
extensions,
|
||||
extension => Assert.Equal(expectedExtension2Name, extension.ExtensionName),
|
||||
extension => Assert.Equal(expectedExtension1Name, extension.ExtensionName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguration_FailsIfNoDefaultConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>().ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguration(projectState, out var configuration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguration_FailsIfNoLanguageVersion()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(
|
||||
Rules.RazorGeneral.SchemaName,
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorGeneral.RazorDefaultConfigurationProperty] = "13.37"
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguration(projectState, out var configuration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguration_FailsIfNoConfigurationItems()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(
|
||||
Rules.RazorGeneral.SchemaName,
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorGeneral.RazorDefaultConfigurationProperty] = "13.37",
|
||||
[Rules.RazorGeneral.RazorLangVersionProperty] = "1.0",
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguration(projectState, out var configuration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguration_FailsIfNoConfiguredExtensionNames()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(
|
||||
Rules.RazorGeneral.SchemaName,
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorGeneral.RazorDefaultConfigurationProperty] = "13.37",
|
||||
[Rules.RazorGeneral.RazorLangVersionProperty] = "1.0",
|
||||
}),
|
||||
[Rules.RazorConfiguration.SchemaName] = TestProjectRuleSnapshot.CreateItems(
|
||||
Rules.RazorConfiguration.SchemaName,
|
||||
new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
["Razor-13.37"] = new Dictionary<string, string>()
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguration(projectState, out var configuration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguration_FailsIfNoExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(
|
||||
Rules.RazorGeneral.SchemaName,
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorGeneral.RazorDefaultConfigurationProperty] = "13.37",
|
||||
[Rules.RazorGeneral.RazorLangVersionProperty] = "1.0",
|
||||
}),
|
||||
[Rules.RazorConfiguration.SchemaName] = TestProjectRuleSnapshot.CreateItems(
|
||||
Rules.RazorConfiguration.SchemaName,
|
||||
new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
["SomeExtension"] = new Dictionary<string, string>()
|
||||
{
|
||||
["Extensions"] = "Razor-13.37"
|
||||
}
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguration(projectState, out var configuration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuration);
|
||||
}
|
||||
|
||||
// This is more of an integration test but is here to test the overall flow/functionality
|
||||
[Fact]
|
||||
public void TryGetConfiguration_SucceedsWithAllPreRequisites()
|
||||
{
|
||||
// Arrange
|
||||
var expectedLanguageVersion = RazorLanguageVersion.Version_1_0;
|
||||
var expectedConfigurationName = "Razor-Test";
|
||||
var expectedExtension1Name = "Extension1";
|
||||
var expectedExtension2Name = "Extension2";
|
||||
var projectState = new Dictionary<string, IProjectRuleSnapshot>()
|
||||
{
|
||||
[Rules.RazorGeneral.SchemaName] = TestProjectRuleSnapshot.CreateProperties(
|
||||
Rules.RazorGeneral.SchemaName,
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
[Rules.RazorGeneral.RazorDefaultConfigurationProperty] = expectedConfigurationName,
|
||||
[Rules.RazorGeneral.RazorLangVersionProperty] = "1.0",
|
||||
}),
|
||||
[Rules.RazorConfiguration.SchemaName] = TestProjectRuleSnapshot.CreateItems(
|
||||
Rules.RazorConfiguration.SchemaName,
|
||||
new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
["UnconfiguredRazorConfiguration"] = new Dictionary<string, string>()
|
||||
{
|
||||
["Extensions"] = "Razor-9.0"
|
||||
},
|
||||
[expectedConfigurationName] = new Dictionary<string, string>()
|
||||
{
|
||||
["Extensions"] = expectedExtension1Name + ";" + expectedExtension2Name
|
||||
}
|
||||
}),
|
||||
[Rules.RazorExtension.PrimaryDataSourceItemType] = TestProjectRuleSnapshot.CreateItems(
|
||||
Rules.RazorExtension.PrimaryDataSourceItemType,
|
||||
new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
[expectedExtension1Name] = new Dictionary<string, string>(),
|
||||
[expectedExtension2Name] = new Dictionary<string, string>(),
|
||||
})
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguration(projectState, out var configuration);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Equal(expectedLanguageVersion, configuration.LanguageVersion);
|
||||
Assert.Equal(expectedConfigurationName, configuration.ConfigurationName);
|
||||
Assert.Collection(
|
||||
configuration.Extensions,
|
||||
extension => Assert.Equal(expectedExtension2Name, extension.ExtensionName),
|
||||
extension => Assert.Equal(expectedExtension1Name, extension.ExtensionName));
|
||||
}
|
||||
|
||||
[ForegroundFact]
|
||||
public async Task DefaultRazorProjectHost_ForegroundThread_CreateAndDispose_Succeeds()
|
||||
{
|
||||
|
|
@ -232,8 +801,8 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
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));
|
||||
e => Assert.Equal("Another-Thing", e.ExtensionName),
|
||||
e => Assert.Equal("MVC-2.0", e.ExtensionName));
|
||||
|
||||
await Task.Run(async () => await host.DisposeAsync());
|
||||
Assert.Empty(ProjectManager.Projects);
|
||||
|
|
@ -443,7 +1012,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
|
|||
|
||||
private class TestProjectSnapshotManager : DefaultProjectSnapshotManager
|
||||
{
|
||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, Workspace workspace)
|
||||
public TestProjectSnapshotManager(ForegroundDispatcher dispatcher, Workspace workspace)
|
||||
: base(dispatcher, Mock.Of<ErrorReporter>(), Mock.Of<ProjectSnapshotWorker>(), Array.Empty<ProjectSnapshotChangeTrigger>(), workspace)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 Microsoft.VisualStudio.Editor.Razor;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.ProjectSystem
|
||||
{
|
||||
public class DefaultDotNetProjectHostTest : ForegroundDispatcherTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void UpdateRazorHostProject_UnsupportedProjectNoops()
|
||||
{
|
||||
// Arrange
|
||||
var projectService = new Mock<TextBufferProjectService>();
|
||||
projectService.Setup(p => p.IsSupportedProject(It.IsAny<object>()))
|
||||
.Returns(false);
|
||||
var dotNetProjectHost = new DefaultDotNetProjectHost(
|
||||
Dispatcher,
|
||||
Mock.Of<VisualStudioMacWorkspaceAccessor>(),
|
||||
projectService.Object);
|
||||
|
||||
// Act & Assert
|
||||
dotNetProjectHost.UpdateRazorHostProject();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// Purposefully do not have any more tests here because that would involve mocking MonoDevelop
|
||||
// types. The default constructors for the Solution / DotNetProject MonoDevelop types change
|
||||
// static classes (they assume they're being created in an IDE).
|
||||
// -------------------------------------------------------------------------------------------
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,470 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Language;
|
||||
using MonoDevelop.Projects.MSBuild;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.ProjectSystem
|
||||
{
|
||||
public class DefaultRazorProjectHostTest
|
||||
{
|
||||
[Fact]
|
||||
public void TryGetDefaultConfiguration_FailsIfNoConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetDefaultConfiguration(projectProperties, out var defaultConfiguration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(defaultConfiguration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetDefaultConfiguration_FailsIfEmptyConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
projectProperties.SetValue("RazorDefaultConfiguration", string.Empty);
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetDefaultConfiguration(projectProperties, out var defaultConfiguration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(defaultConfiguration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetDefaultConfiguration_SucceedsWithValidConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var expectedConfiguration = "Razor-13.37";
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
projectProperties.SetValue("RazorDefaultConfiguration", expectedConfiguration);
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetDefaultConfiguration(projectProperties, out var defaultConfiguration);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Equal(expectedConfiguration, defaultConfiguration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetLanguageVersion_FailsIfNoLanguageVersion()
|
||||
{
|
||||
// Arrange
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetLanguageVersion(projectProperties, out var languageVersion);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(languageVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetLanguageVersion_FailsIfEmptyLanguageVersion()
|
||||
{
|
||||
// Arrange
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
projectProperties.SetValue("RazorLangVersion", string.Empty);
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetLanguageVersion(projectProperties, out var languageVersion);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(languageVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetLanguageVersion_SucceedsWithValidLanguageVersion()
|
||||
{
|
||||
// Arrange
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
projectProperties.SetValue("RazorLangVersion", "1.0");
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetLanguageVersion(projectProperties, out var languageVersion);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Same(RazorLanguageVersion.Version_1_0, languageVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetLanguageVersion_SucceedsWithUnknownLanguageVersion_DefaultsToLatest()
|
||||
{
|
||||
// Arrange
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
projectProperties.SetValue("RazorLangVersion", "13.37");
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetLanguageVersion(projectProperties, out var languageVersion);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Same(RazorLanguageVersion.Latest, languageVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfigurationItem_FailsNoRazorConfigurationItems()
|
||||
{
|
||||
// Arrange
|
||||
var projectItems = Enumerable.Empty<IMSBuildItemEvaluated>();
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfigurationItem("Razor-13.37", projectItems, out var configurationItem);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configurationItem);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfigurationItem_FailsNoMatchingRazorConfigurationItems()
|
||||
{
|
||||
// Arrange
|
||||
var projectItems = new IMSBuildItemEvaluated[]
|
||||
{
|
||||
new TestMSBuildItem("RazorConfiguration")
|
||||
{
|
||||
Include = "Razor-10.0",
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfigurationItem("Razor-13.37", projectItems, out var configurationItem);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configurationItem);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfigurationItem_SucceedsForMatchingConfigurationItem()
|
||||
{
|
||||
// Arrange
|
||||
var expectedConfiguration = "Razor-13.37";
|
||||
var expectedConfigurationItem = new TestMSBuildItem("RazorConfiguration")
|
||||
{
|
||||
Include = expectedConfiguration,
|
||||
};
|
||||
var projectItems = new IMSBuildItemEvaluated[]
|
||||
{
|
||||
new TestMSBuildItem("RazorConfiguration")
|
||||
{
|
||||
Include = "Razor-10.0-DoesNotMatch",
|
||||
},
|
||||
expectedConfigurationItem
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfigurationItem(expectedConfiguration, projectItems, out var configurationItem);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Same(expectedConfigurationItem, configurationItem);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguredExtensionNames_FailsIfNoExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var configurationItem = new TestMSBuildItem("RazorConfiguration");
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionnames);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuredExtensionnames);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguredExtensionNames_FailsIfEmptyExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var configurationItem = new TestMSBuildItem("RazorConfiguration");
|
||||
configurationItem.TestMetadata.SetValue("Extensions", string.Empty);
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuredExtensionNames);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguredExtensionNames_SucceedsIfSingleExtension()
|
||||
{
|
||||
// Arrange
|
||||
var expectedExtensionName = "SomeExtensionName";
|
||||
var configurationItem = new TestMSBuildItem("RazorConfiguration");
|
||||
configurationItem.TestMetadata.SetValue("Extensions", expectedExtensionName);
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
var extensionName = Assert.Single(configuredExtensionNames);
|
||||
Assert.Equal(expectedExtensionName, extensionName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguredExtensionNames_SucceedsIfMultipleExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var configurationItem = new TestMSBuildItem("RazorConfiguration");
|
||||
configurationItem.TestMetadata.SetValue("Extensions", "SomeExtensionName;SomeOtherExtensionName");
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguredExtensionNames(configurationItem, out var configuredExtensionNames);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Collection(
|
||||
configuredExtensionNames,
|
||||
name => Assert.Equal("SomeExtensionName", name),
|
||||
name => Assert.Equal("SomeOtherExtensionName", name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExtensions_NoExtensionTypes_ReturnsEmptyArray()
|
||||
{
|
||||
// Arrange
|
||||
var projectItems = new IMSBuildItemEvaluated[]
|
||||
{
|
||||
new TestMSBuildItem("NotAnExtension")
|
||||
{
|
||||
Include = "Extension1",
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
var extensions = DefaultRazorProjectHost.GetExtensions(new[] { "Extension1", "Extension2" }, projectItems);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(extensions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExtensions_UnConfiguredExtensionTypes_ReturnsEmptyArray()
|
||||
{
|
||||
// Arrange
|
||||
var projectItems = new IMSBuildItemEvaluated[]
|
||||
{
|
||||
new TestMSBuildItem("RazorExtension")
|
||||
{
|
||||
Include = "UnconfiguredExtensionName",
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
var extensions = DefaultRazorProjectHost.GetExtensions(new[] { "Extension1", "Extension2" }, projectItems);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(extensions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExtensions_SomeConfiguredExtensions_ReturnsConfiguredExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var expectedExtension1Name = "Extension1";
|
||||
var expectedExtension2Name = "Extension2";
|
||||
var projectItems = new IMSBuildItemEvaluated[]
|
||||
{
|
||||
new TestMSBuildItem("RazorExtension")
|
||||
{
|
||||
Include = "UnconfiguredExtensionName",
|
||||
},
|
||||
new TestMSBuildItem("RazorExtension")
|
||||
{
|
||||
Include = expectedExtension1Name,
|
||||
},
|
||||
new TestMSBuildItem("RazorExtension")
|
||||
{
|
||||
Include = expectedExtension2Name,
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
var extensions = DefaultRazorProjectHost.GetExtensions(new[] { expectedExtension1Name, expectedExtension2Name }, projectItems);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
extensions,
|
||||
extension => Assert.Equal(expectedExtension1Name, extension.ExtensionName),
|
||||
extension => Assert.Equal(expectedExtension2Name, extension.ExtensionName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguration_FailsIfNoDefaultConfiguration()
|
||||
{
|
||||
// Arrange
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
var projectItems = new IMSBuildItemEvaluated[0];
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguration(projectProperties, projectItems, out var configuration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguration_FailsIfNoLanguageVersion()
|
||||
{
|
||||
// Arrange
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
projectProperties.SetValue("RazorDefaultConfiguration", "Razor-13.37");
|
||||
var projectItems = new IMSBuildItemEvaluated[0];
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguration(projectProperties, projectItems, out var configuration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguration_FailsIfNoConfigurationItems()
|
||||
{
|
||||
// Arrange
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
projectProperties.SetValue("RazorDefaultConfiguration", "Razor-13.37");
|
||||
projectProperties.SetValue("RazorLangVersion", "1.0");
|
||||
var projectItems = new IMSBuildItemEvaluated[0];
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguration(projectProperties, projectItems, out var configuration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetConfiguration_FailsIfNoConfiguredExtensionNames()
|
||||
{
|
||||
// Arrange
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
projectProperties.SetValue("RazorDefaultConfiguration", "Razor-13.37");
|
||||
projectProperties.SetValue("RazorLangVersion", "1.0");
|
||||
var projectItems = new IMSBuildItemEvaluated[]
|
||||
{
|
||||
new TestMSBuildItem("RazorConfiguration")
|
||||
{
|
||||
Include = "Razor-13.37",
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguration(projectProperties, projectItems, out var configuration);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.Null(configuration);
|
||||
}
|
||||
|
||||
// This is more of an integration test but is here to test the overall flow/functionality
|
||||
[Fact]
|
||||
public void TryGetConfiguration_SucceedsWithAllPreRequisites()
|
||||
{
|
||||
// Arrange
|
||||
var expectedLanguageVersion = RazorLanguageVersion.Version_1_0;
|
||||
var expectedConfigurationName = "Razor-Test";
|
||||
var expectedExtension1Name = "Extension1";
|
||||
var expectedExtension2Name = "Extension2";
|
||||
var expectedRazorConfigurationItem = new TestMSBuildItem("RazorConfiguration")
|
||||
{
|
||||
Include = expectedConfigurationName,
|
||||
};
|
||||
expectedRazorConfigurationItem.TestMetadata.SetValue("Extensions", "Extension1;Extension2");
|
||||
var projectItems = new IMSBuildItemEvaluated[]
|
||||
{
|
||||
new TestMSBuildItem("RazorConfiguration")
|
||||
{
|
||||
Include = "UnconfiguredRazorConfiguration",
|
||||
},
|
||||
new TestMSBuildItem("RazorExtension")
|
||||
{
|
||||
Include = "UnconfiguredExtensionName",
|
||||
},
|
||||
new TestMSBuildItem("RazorExtension")
|
||||
{
|
||||
Include = expectedExtension1Name,
|
||||
},
|
||||
new TestMSBuildItem("RazorExtension")
|
||||
{
|
||||
Include = expectedExtension2Name,
|
||||
},
|
||||
expectedRazorConfigurationItem,
|
||||
};
|
||||
var projectProperties = new MSBuildPropertyGroup();
|
||||
projectProperties.SetValue("RazorDefaultConfiguration", expectedConfigurationName);
|
||||
projectProperties.SetValue("RazorLangVersion", "1.0");
|
||||
|
||||
// Act
|
||||
var result = DefaultRazorProjectHost.TryGetConfiguration(projectProperties, projectItems, out var configuration);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
Assert.Equal(expectedLanguageVersion, configuration.LanguageVersion);
|
||||
Assert.Equal(expectedConfigurationName, configuration.ConfigurationName);
|
||||
Assert.Collection(
|
||||
configuration.Extensions,
|
||||
extension => Assert.Equal(expectedExtension1Name, extension.ExtensionName),
|
||||
extension => Assert.Equal(expectedExtension2Name, extension.ExtensionName));
|
||||
}
|
||||
|
||||
private class TestMSBuildItem : IMSBuildItemEvaluated
|
||||
{
|
||||
private readonly MSBuildPropertyGroup _metadata;
|
||||
private readonly string _name;
|
||||
private string _include;
|
||||
|
||||
public TestMSBuildItem(string name)
|
||||
{
|
||||
_name = name;
|
||||
_metadata = new MSBuildPropertyGroup();
|
||||
}
|
||||
|
||||
public string Name => _name;
|
||||
|
||||
public string Include
|
||||
{
|
||||
get => _include;
|
||||
set => _include = value;
|
||||
}
|
||||
|
||||
public MSBuildPropertyGroup TestMetadata => _metadata;
|
||||
|
||||
public IMSBuildPropertyGroupEvaluated Metadata => _metadata;
|
||||
|
||||
public string Condition => throw new System.NotImplementedException();
|
||||
|
||||
public bool IsImported => throw new System.NotImplementedException();
|
||||
|
||||
|
||||
public string UnevaluatedInclude => throw new System.NotImplementedException();
|
||||
|
||||
public MSBuildItem SourceItem => throw new System.NotImplementedException();
|
||||
|
||||
public IEnumerable<MSBuildItem> SourceItems => throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,13 +8,13 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor
|
||||
{
|
||||
public class DefaultVisualStudioWorkspaceAccessorTest
|
||||
public class DefaultVisualStudioMacWorkspaceAccessorTest
|
||||
{
|
||||
[Fact]
|
||||
public void TryGetWorkspace_NoHostProject_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var workspaceAccessor = new DefaultVisualStudioWorkspaceAccessor(Mock.Of<TextBufferProjectService>());
|
||||
var workspaceAccessor = new DefaultVisualStudioMacWorkspaceAccessor(Mock.Of<TextBufferProjectService>());
|
||||
var textBuffer = Mock.Of<ITextBuffer>();
|
||||
|
||||
// Act
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// 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 MonoDevelop.Core;
|
||||
using MonoDevelop.Projects;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.LanguageServices.Razor.ProjectSystem
|
||||
{
|
||||
public class FallbackRazorProjectHostTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
public void IsMvcAssembly_FailsIfNullOrEmptyFilePath(string filePath)
|
||||
{
|
||||
// Arrange
|
||||
var assemblyFilePath = new FilePath(filePath);
|
||||
var assemblyReference = new AssemblyReference(assemblyFilePath);
|
||||
|
||||
// Act
|
||||
var result = FallbackRazorProjectHost.IsMvcAssembly(assemblyReference);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsMvcAssembly_FailsIfNotMvc()
|
||||
{
|
||||
// Arrange
|
||||
var assemblyFilePath = new FilePath("C:/Path/To/Assembly.dll");
|
||||
var assemblyReference = new AssemblyReference(assemblyFilePath);
|
||||
|
||||
// Act
|
||||
var result = FallbackRazorProjectHost.IsMvcAssembly(assemblyReference);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsMvcAssembly_SucceedsIfMvc()
|
||||
{
|
||||
// Arrange
|
||||
var assemblyFilePath = new FilePath("C:/Path/To/Microsoft.AspNetCore.Mvc.Razor.dll");
|
||||
var assemblyReference = new AssemblyReference(assemblyFilePath);
|
||||
|
||||
// Act
|
||||
var result = FallbackRazorProjectHost.IsMvcAssembly(assemblyReference);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// Purposefully do not have any more tests here because that would involve mocking MonoDevelop
|
||||
// types. The default constructors for the Solution / DotNetProject MonoDevelop types change
|
||||
// static classes (they assume they're being created in an IDE).
|
||||
// -------------------------------------------------------------------------------------------
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,10 @@
|
|||
<TargetFramework>net461</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.VisualStudio.Mac.LanguageServices.Razor\Microsoft.VisualStudio.Mac.LanguageServices.Razor.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.VisualStudio.Editor.Razor.Test.Common\Microsoft.VisualStudio.Editor.Razor.Test.Common.csproj" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"methodDisplay": "method",
|
||||
"shadowCopy": false
|
||||
}
|
||||
|
|
@ -42,15 +42,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--
|
||||
The extension project can not have a direct reference to its language service pieces. They are included via the manifest above.
|
||||
This piece ensures that transitive builds/restores occurr for this project.
|
||||
-->
|
||||
<ProjectReference Include="..\..\src\Microsoft.VisualStudio.Mac.LanguageServices.Razor\Microsoft.VisualStudio.Mac.LanguageServices.Razor.csproj">
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
<PrivateAssets>true</PrivateAssets>
|
||||
<OutputItemType>Content</OutputItemType>
|
||||
<Targets>Build</Targets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\src\Microsoft.VisualStudio.Mac.LanguageServices.Razor\Microsoft.VisualStudio.Mac.LanguageServices.Razor.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -19,5 +19,10 @@
|
|||
<Assembly file="Microsoft.VisualStudio.Editor.Razor.dll" />
|
||||
<Assembly file="Microsoft.CodeAnalysis.Razor.Workspaces.dll" />
|
||||
<Assembly file="Microsoft.VisualStudio.Mac.LanguageServices.Razor.dll" />
|
||||
</Extension>
|
||||
</Extension>
|
||||
|
||||
<!-- Project Extensions -->
|
||||
<Extension path = "/MonoDevelop/ProjectModel/ProjectModelExtensions">
|
||||
<Class class="Microsoft.VisualStudio.Mac.RazorAddin.RazorProjectExtension" insertafter="FinalStep" />
|
||||
</Extension>
|
||||
</ExtensionModel>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.VisualStudio.Mac.LanguageServices.Razor.ProjectSystem;
|
||||
using MonoDevelop.Ide.Composition;
|
||||
using MonoDevelop.Ide.TypeSystem;
|
||||
using MonoDevelop.Projects;
|
||||
|
||||
namespace Microsoft.VisualStudio.Mac.RazorAddin
|
||||
{
|
||||
internal class RazorProjectExtension : ProjectExtension
|
||||
{
|
||||
private readonly object _lock = new object();
|
||||
private readonly ForegroundDispatcher _foregroundDispatcher;
|
||||
|
||||
public RazorProjectExtension()
|
||||
{
|
||||
_foregroundDispatcher = CompositionManager.GetExportedValue<ForegroundDispatcher>();
|
||||
}
|
||||
|
||||
protected override void OnBoundToSolution()
|
||||
{
|
||||
if (!(Project is DotNetProject dotNetProject))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DotNetProjectHost projectHost;
|
||||
lock (_lock)
|
||||
{
|
||||
if (Project.ExtendedProperties.Contains(typeof(DotNetProjectHost)))
|
||||
{
|
||||
// Already have a project host.
|
||||
return;
|
||||
}
|
||||
|
||||
var projectHostFactory = CompositionManager.GetExportedValue<DotNetProjectHostFactory>();
|
||||
projectHost = projectHostFactory.Create(dotNetProject);
|
||||
Project.ExtendedProperties[typeof(DotNetProjectHost)] = projectHost;
|
||||
}
|
||||
|
||||
// Once a workspace is created for the solution we'll setup our project host for the current project. The Razor world
|
||||
// shares a lifetime with the workspace (as Roslyn services) so we need to ensure it exists prior to wiring the host
|
||||
// world to the Roslyn world.
|
||||
TypeSystemService.GetWorkspaceAsync(Project.ParentSolution).ContinueWith(task =>
|
||||
{
|
||||
if (task.IsFaulted || task.IsCanceled)
|
||||
{
|
||||
// We only want to act if we could properly retrieve the workspace.
|
||||
return;
|
||||
}
|
||||
|
||||
projectHost.Subscribe();
|
||||
},
|
||||
_foregroundDispatcher.ForegroundScheduler);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue