diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/Telerik/Kendo.Mvc.Examples.project.razor.json b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/Telerik/Kendo.Mvc.Examples.project.razor.json index 9104b499f89..0383579bc53 100644 --- a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/Telerik/Kendo.Mvc.Examples.project.razor.json +++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/Telerik/Kendo.Mvc.Examples.project.razor.json @@ -1,13 +1,17 @@ { - "__Version": 6, - "ProjectKey": "C:\\Users\\admin\\location\\Kendo.Mvc.Examples\\obj\\Debug\\net7.0\\", - "FilePath": "C:\\Users\\admin\\location\\Kendo.Mvc.Examples\\Kendo.Mvc.Examples.csproj", - "Configuration": { - "ConfigurationName": "MVC-3.0", - "LanguageVersion": "7.0", - "Extensions": [ - "MVC-3.0" - ] + "__Version": 7, + "HostProject": { + "FilePath": "C:\\Users\\admin\\location\\Kendo.Mvc.Examples\\Kendo.Mvc.Examples.csproj", + "IntermediateOutputPath": "C:\\Users\\admin\\location\\Kendo.Mvc.Examples\\obj\\Debug\\net7.0\\", + "Configuration": { + "ConfigurationName": "MVC-3.0", + "LanguageVersion": "7.0", + "Extensions": [ + "MVC-3.0" + ], + }, + "RootNamespace": "Kendo.Mvc.Examples", + "DisplayName": "Kendo.Mvc.Examples" }, "ProjectWorkspaceState": { "TagHelpers": [ @@ -159529,7 +159533,6 @@ ], "CSharpLanguageVersion": 1100 }, - "RootNamespace": "Kendo.Mvc.Examples", "Documents": [ { "FilePath": "C:\\Program Files (x86)\\Progress\\Telerik UI for ASP.NET Core R1 2023\\wrappers\\aspnetcore\\Examples\\AspNet.Core\\VS2022\\Kendo.Mvc.Examples\\Views\\Linear_Gauge\\Scale_Options.cshtml", diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/project.razor.json b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/project.razor.json index d1525d22f3a..c3a5cba7e3b 100644 --- a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/project.razor.json +++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Resources/project.razor.json @@ -1,11 +1,15 @@ { - "__Version": 6, - "ProjectKey": "C:\\Users\\admin\\location\\blazorserver\\obj\\Debug\\net7.0\\", - "FilePath": "C:\\Users\\admin\\location\\blazorserver\\blazorserver.csproj", - "Configuration": { - "ConfigurationName": "MVC-3.0", - "LanguageVersion": "3.0", - "Extensions": [ "MVC-3.0" ] + "__Version": 7, + "HostProject": { + "FilePath": "C:\\Users\\admin\\location\\blazorserver\\blazorserver.csproj", + "IntermediateOutputPath": "C:\\Users\\admin\\location\\blazorserver\\obj\\Debug\\net7.0\\", + "Configuration": { + "ConfigurationName": "MVC-3.0", + "LanguageVersion": "3.0", + "Extensions": [ "MVC-3.0" ] + }, + "RootNamespace": "blazorserver", + "DisplayName": "blazorserver" }, "ProjectWorkspaceState": { "TagHelpers": [ @@ -16128,7 +16132,6 @@ ], "CSharpLanguageVersion": 800 }, - "RootNamespace": "blazorserver", "Documents": [ { "FilePath": "C:\\Users\\admin\\location\\blazorserver\\Shared\\MainLayout.razor", diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Serialization/RazorProjectInfoSerializationBenchmark.cs b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Serialization/RazorProjectInfoSerializationBenchmark.cs index b94b9e7c2e4..d3db3da6cab 100644 --- a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Serialization/RazorProjectInfoSerializationBenchmark.cs +++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/Serialization/RazorProjectInfoSerializationBenchmark.cs @@ -71,8 +71,7 @@ public void Deserialize_Json() var projectInfo = DeserializeProjectInfo_Json(reader); - if (projectInfo.ProjectWorkspaceState is null || - projectInfo.ProjectWorkspaceState.TagHelpers.Length != ProjectInfo.ProjectWorkspaceState?.TagHelpers.Length) + if (projectInfo.ProjectWorkspaceState.TagHelpers.Length != ProjectInfo.ProjectWorkspaceState.TagHelpers.Length) { throw new InvalidDataException(); } @@ -93,8 +92,7 @@ public void RoundTrip_Json() var projectInfo = DeserializeProjectInfo_Json(reader); - if (projectInfo.ProjectWorkspaceState is null || - projectInfo.ProjectWorkspaceState.TagHelpers.Length != ProjectInfo.ProjectWorkspaceState?.TagHelpers.Length) + if (projectInfo.ProjectWorkspaceState.TagHelpers.Length != ProjectInfo.ProjectWorkspaceState.TagHelpers.Length) { throw new InvalidDataException(); } @@ -132,8 +130,7 @@ public void Deserialize_MessagePack() { var projectInfo = DeserializeProjectInfo_MessagePack(_projectInfoMessagePackBytes); - if (projectInfo.ProjectWorkspaceState is null || - projectInfo.ProjectWorkspaceState.TagHelpers.Length != ProjectInfo.ProjectWorkspaceState?.TagHelpers.Length) + if (projectInfo.ProjectWorkspaceState.TagHelpers.Length != ProjectInfo.ProjectWorkspaceState.TagHelpers.Length) { throw new InvalidDataException(); } @@ -146,8 +143,7 @@ public void RoundTrip_MessagePack() var projectInfo = DeserializeProjectInfo_MessagePack(bytes); _buffer.Clear(); - if (projectInfo.ProjectWorkspaceState is null || - projectInfo.ProjectWorkspaceState.TagHelpers.Length != ProjectInfo.ProjectWorkspaceState?.TagHelpers.Length) + if (projectInfo.ProjectWorkspaceState.TagHelpers.Length != ProjectInfo.ProjectWorkspaceState.TagHelpers.Length) { throw new InvalidDataException(); } diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Common/HostDocumentComparer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Common/HostDocumentComparer.cs deleted file mode 100644 index 7a1bd5445cd..00000000000 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Common/HostDocumentComparer.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT license. See License.txt in the project root for license information. - -using System.Collections.Generic; -using Microsoft.CodeAnalysis.Razor; -using Microsoft.CodeAnalysis.Razor.ProjectSystem; -using Microsoft.Extensions.Internal; - -namespace Microsoft.AspNetCore.Razor.LanguageServer.Common; - -internal class HostDocumentComparer : IEqualityComparer -{ - public static readonly HostDocumentComparer Instance = new(); - - private HostDocumentComparer() - { - } - - public bool Equals(HostDocument? x, HostDocument? y) - { - if (x is null) - { - return y is null; - } - else if (y is null) - { - return false; - } - - return x.FileKind == y.FileKind && - FilePathComparer.Instance.Equals(x.FilePath, y.FilePath) && - FilePathComparer.Instance.Equals(x.TargetPath, y.TargetPath); - } - - public int GetHashCode(HostDocument hostDocument) - { - var combiner = HashCodeCombiner.Start(); - combiner.Add(hostDocument.FilePath, FilePathComparer.Instance); - combiner.Add(hostDocument.TargetPath, FilePathComparer.Instance); - combiner.Add(hostDocument.FileKind); - - return combiner.CombinedHash; - } -} diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.TestAccessor.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.TestAccessor.cs index a0312a283c8..2e8e87d720d 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.TestAccessor.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.TestAccessor.cs @@ -1,12 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. -using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.ProjectSystem; -using Microsoft.AspNetCore.Razor.Serialization; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem; @@ -19,21 +17,18 @@ internal readonly struct TestAccessor(RazorProjectService instance) public ValueTask WaitForInitializationAsync() => instance.WaitForInitializationAsync(); - public async Task AddProjectAsync( - string filePath, - string intermediateOutputPath, - RazorConfiguration? configuration, - string? rootNamespace, - string? displayName, - CancellationToken cancellationToken) + public async Task AddProjectAsync(HostProject hostProject, CancellationToken cancellationToken) { - var service = instance; - - await service.WaitForInitializationAsync().ConfigureAwait(false); + await instance.WaitForInitializationAsync().ConfigureAwait(false); return await instance._projectManager .UpdateAsync( - updater => service.AddProjectCore(updater, filePath, intermediateOutputPath, configuration, rootNamespace, displayName), + (updater, state) => + { + var (service, hostProject) = state; + return service.AddProjectCore(updater, hostProject); + }, + (instance, hostProject), cancellationToken) .ConfigureAwait(false); } diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs index 25f498d7213..71a8d668a54 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs @@ -2,16 +2,15 @@ // Licensed under the MIT license. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.LanguageServer.Common; using Microsoft.AspNetCore.Razor.ProjectSystem; -using Microsoft.AspNetCore.Razor.Serialization; using Microsoft.AspNetCore.Razor.Utilities; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Razor; @@ -19,8 +18,6 @@ using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Razor.Workspaces; using Microsoft.CodeAnalysis.Text; -using Microsoft.CommonLanguageServerProtocol.Framework; -using Microsoft.VisualStudio.Threading; namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem; @@ -82,11 +79,8 @@ private async Task InitializeAsync(CancellationToken cancellationToken) foreach (var projectInfo in _projectInfoDriver.GetLatestProjectInfo()) { await AddOrUpdateProjectCoreAsync( - projectInfo.ProjectKey, - projectInfo.FilePath, - projectInfo.Configuration, - projectInfo.RootNamespace, - projectInfo.DisplayName, + projectInfo.Key, + projectInfo.HostProject, projectInfo.ProjectWorkspaceState, projectInfo.Documents, cancellationToken) @@ -108,33 +102,27 @@ async Task IRazorProjectInfoListener.UpdatedAsync(RazorProjectInfo projectInfo, // Don't update a project during initialization. await WaitForInitializationAsync().ConfigureAwait(false); - _logger.LogTrace($"{nameof(IRazorProjectInfoListener)} received update for {projectInfo.ProjectKey}"); + _logger.LogTrace($"{nameof(IRazorProjectInfoListener)} received update for {projectInfo.Key}"); await AddOrUpdateProjectCoreAsync( - projectInfo.ProjectKey, - projectInfo.FilePath, - projectInfo.Configuration, - projectInfo.RootNamespace, - projectInfo.DisplayName, + projectInfo.Key, + projectInfo.HostProject, projectInfo.ProjectWorkspaceState, projectInfo.Documents, cancellationToken) .ConfigureAwait(false); } - async Task IRazorProjectInfoListener.RemovedAsync(ProjectKey projectKey, CancellationToken cancellationToken) + async Task IRazorProjectInfoListener.RemovedAsync(ProjectKey key, CancellationToken cancellationToken) { // Don't remove a project during initialization. await WaitForInitializationAsync().ConfigureAwait(false); - _logger.LogTrace($"{nameof(IRazorProjectInfoListener)} received remove for {projectKey}"); + _logger.LogTrace($"{nameof(IRazorProjectInfoListener)} received remove for {key}"); await AddOrUpdateProjectCoreAsync( - projectKey, - filePath: null, - configuration: null, - rootNamespace: null, - displayName: "", + key, + hostProject: null, ProjectWorkspaceState.Default, documents: [], cancellationToken) @@ -313,56 +301,47 @@ private void ActOnDocumentInMultipleProjects(string filePath, Action documents, + ImmutableArray documents, CancellationToken cancellationToken) { + Debug.Assert(hostProject is null || hostProject.Key == key); + // Note: We specifically don't wait for initialization here because this is called *during* initialization. // All other callers of this method must await WaitForInitializationAsync(). return _projectManager.UpdateAsync( updater => { - if (!_projectManager.TryGetLoadedProject(projectKey, out var project)) + if (!_projectManager.TryGetLoadedProject(key, out var project)) { - if (filePath is null) + if (hostProject is null) { // Never tracked the project to begin with, noop. - _logger.LogInformation($"Failed to update untracked project '{projectKey}'."); + _logger.LogInformation($"Failed to update untracked project '{key}'."); return; } - // If we've been given a project file path, then we have enough info to add the project ourselves, because we know - // the intermediate output path from the id - var intermediateOutputPath = projectKey.Id; - - var newKey = AddProjectCore(updater, filePath, intermediateOutputPath, configuration, rootNamespace, displayName); - Debug.Assert(newKey == projectKey); + var newKey = AddProjectCore(updater, hostProject); + Debug.Assert(newKey == key); - project = _projectManager.GetLoadedProject(projectKey); + project = _projectManager.GetLoadedProject(key); } - UpdateProjectDocuments(updater, documents, project.Key); + UpdateProjectDocuments(updater, project.Key, documents); if (!projectWorkspaceState.Equals(ProjectWorkspaceState.Default)) { @@ -373,10 +352,15 @@ private Task AddOrUpdateProjectCoreAsync( var currentConfiguration = project.Configuration; var currentRootNamespace = project.RootNamespace; + + var configuration = hostProject?.Configuration; + var rootNamespace = hostProject?.RootNamespace; + var displayName = hostProject?.DisplayName; + if (currentConfiguration.ConfigurationName == configuration?.ConfigurationName && currentRootNamespace == rootNamespace) { - _logger.LogTrace($"Updating project '{project.Key}'. The project is already using configuration '{configuration.ConfigurationName}' and root namespace '{rootNamespace}'."); + _logger.LogTrace($"Updating project '{project.Key}'. The project is already using configuration '{configuration?.ConfigurationName}' and root namespace '{rootNamespace}'."); return; } @@ -395,21 +379,20 @@ private Task AddOrUpdateProjectCoreAsync( _logger.LogInformation($"Updating project '{project.Key}''s root namespace to '{rootNamespace}'."); } - var hostProject = new HostProject(project.FilePath, project.IntermediateOutputPath, configuration, rootNamespace, displayName); - updater.ProjectConfigurationChanged(hostProject); + var newHostProject = new HostProject(project.FilePath, project.IntermediateOutputPath, configuration, rootNamespace, displayName); + updater.ProjectConfigurationChanged(newHostProject); }, cancellationToken); } private void UpdateProjectDocuments( ProjectSnapshotManager.Updater updater, - ImmutableArray documents, - ProjectKey projectKey) + ProjectKey key, + ImmutableArray documents) { - _logger.LogDebug($"UpdateProjectDocuments for {projectKey} with {documents.Length} documents: {string.Join(", ", documents.Select(d => d.FilePath))}"); + _logger.LogDebug($"UpdateProjectDocuments for {key} with {documents.Length} documents: {string.Join(", ", documents.Select(d => d.FilePath))}"); - var project = _projectManager.GetLoadedProject(projectKey); - var currentProjectKey = project.Key; + var project = _projectManager.GetLoadedProject(key); var projectDirectory = FilePathNormalizer.GetNormalizedDirectoryName(project.FilePath); var documentMap = documents.ToDictionary(document => EnsureFullPath(document.FilePath, projectDirectory), FilePathComparer.Instance); var miscellaneousProject = _projectManager.GetMiscellaneousProject(); @@ -423,17 +406,17 @@ private void UpdateProjectDocuments( continue; } - _logger.LogDebug($"Document '{documentFilePath}' no longer exists in project '{projectKey}'. Moving to miscellaneous project."); + _logger.LogDebug($"Document '{documentFilePath}' no longer exists in project '{key}'. Moving to miscellaneous project."); MoveDocument(updater, documentFilePath, fromProject: project, toProject: miscellaneousProject); } - project = _projectManager.GetLoadedProject(projectKey); + project = _projectManager.GetLoadedProject(key); // Update existing documents foreach (var documentFilePath in project.DocumentFilePaths) { - if (!documentMap.TryGetValue(documentFilePath, out var documentHandle)) + if (!documentMap.TryGetValue(documentFilePath, out var document)) { // Document exists in the project but not in the configured documents. Chances are the project configuration is from a fallback // configuration case (< 2.1) or the project isn't fully loaded yet. @@ -446,10 +429,10 @@ private void UpdateProjectDocuments( } var currentHostDocument = documentSnapshot.HostDocument; - var newFilePath = EnsureFullPath(documentHandle.FilePath, projectDirectory); - var newHostDocument = new HostDocument(newFilePath, documentHandle.TargetPath, documentHandle.FileKind); + var newFilePath = EnsureFullPath(document.FilePath, projectDirectory); + var newHostDocument = document with { FilePath = newFilePath }; - if (HostDocumentComparer.Instance.Equals(currentHostDocument, newHostDocument)) + if (currentHostDocument == newHostDocument) { // Current and "new" host documents are equivalent continue; @@ -464,17 +447,16 @@ private void UpdateProjectDocuments( ? new DocumentSnapshotTextLoader(documentSnapshot) : _remoteTextLoaderFactory.Create(newFilePath); - updater.DocumentRemoved(currentProjectKey, currentHostDocument); - updater.DocumentAdded(currentProjectKey, newHostDocument, textLoader); + updater.DocumentRemoved(key, currentHostDocument); + updater.DocumentAdded(key, newHostDocument, textLoader); } project = _projectManager.GetLoadedProject(project.Key); miscellaneousProject = _projectManager.GetMiscellaneousProject(); // Add (or migrate from misc) any new documents - foreach (var documentKvp in documentMap) + foreach (var (documentFilePath, document) in documentMap) { - var documentFilePath = documentKvp.Key; if (project.DocumentFilePaths.Contains(documentFilePath, FilePathComparer.Instance)) { // Already know about this document @@ -487,13 +469,12 @@ private void UpdateProjectDocuments( } else { - var documentHandle = documentKvp.Value; var remoteTextLoader = _remoteTextLoaderFactory.Create(documentFilePath); - var newHostDocument = new HostDocument(documentFilePath, documentHandle.TargetPath, documentHandle.FileKind); + var newHostDocument = document with { FilePath = documentFilePath }; - _logger.LogInformation($"Adding new document '{documentFilePath}' to project '{currentProjectKey}'."); + _logger.LogInformation($"Adding new document '{documentFilePath}' to project '{key}'."); - updater.DocumentAdded(currentProjectKey, newHostDocument, remoteTextLoader); + updater.DocumentAdded(key, newHostDocument, remoteTextLoader); } } } @@ -526,7 +507,7 @@ private void MoveDocument( newTargetPath = newTargetPath[projectDirectory.Length..]; } - var newHostDocument = new HostDocument(documentSnapshot.FilePath, newTargetPath, documentSnapshot.FileKind); + var newHostDocument = currentHostDocument with { TargetPath = newTargetPath }; _logger.LogInformation($"Moving '{documentFilePath}' from the '{fromProject.Key}' project to '{toProject.Key}' project."); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/HostDocument.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/HostDocument.cs new file mode 100644 index 00000000000..83c13aea960 --- /dev/null +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/HostDocument.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Razor.Language; +using Microsoft.Extensions.Internal; + +namespace Microsoft.CodeAnalysis.Razor.ProjectSystem; + +internal sealed record class HostDocument +{ + public string FileKind { get; init; } + public string FilePath { get; init; } + public string TargetPath { get; init; } + + public HostDocument(string filePath, string targetPath, string? fileKind = null) + { + FilePath = filePath; + TargetPath = targetPath; + FileKind = fileKind ?? FileKinds.GetFileKindFromFilePath(filePath); + } + + public bool Equals(HostDocument? other) + { + if (ReferenceEquals(this, other)) + { + return true; + } + + return other is not null && + FilePathComparer.Instance.Equals(FilePath, other.FilePath) && + FilePathComparer.Instance.Equals(TargetPath, other.TargetPath) && + FileKind == other.FileKind; + } + + public override int GetHashCode() + { + var hash = HashCodeCombiner.Start(); + + hash.Add(FilePath, FilePathComparer.Instance); + hash.Add(TargetPath, FilePathComparer.Instance); + hash.Add(FileKind); + + return hash.CombinedHash; + } +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/HostProject.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/HostProject.cs similarity index 100% rename from src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/HostProject.cs rename to src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/HostProject.cs diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/RazorProjectInfo.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/RazorProjectInfo.cs index 86158c6ceb9..09e0d5dc845 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/RazorProjectInfo.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/RazorProjectInfo.cs @@ -11,9 +11,9 @@ using MessagePack; using MessagePack.Resolvers; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.AspNetCore.Razor.Serialization; using Microsoft.AspNetCore.Razor.Serialization.MessagePack.Resolvers; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Razor.ProjectSystem; @@ -25,39 +25,30 @@ internal sealed record class RazorProjectInfo RazorProjectInfoResolver.Instance, StandardResolver.Instance)); - public ProjectKey ProjectKey { get; init; } - public string FilePath { get; init; } - public RazorConfiguration Configuration { get; init; } - public string? RootNamespace { get; init; } - public string DisplayName { get; init; } - public ProjectWorkspaceState ProjectWorkspaceState { get; init; } - public ImmutableArray Documents { get; init; } + public HostProject HostProject { get; } + + public ProjectKey Key => HostProject.Key; + public string FilePath => HostProject.FilePath; + public RazorConfiguration Configuration => HostProject.Configuration; + public string? RootNamespace => HostProject.RootNamespace; + public string DisplayName => HostProject.DisplayName; + + public ProjectWorkspaceState ProjectWorkspaceState { get; } + public ImmutableArray Documents { get; } public RazorProjectInfo( - ProjectKey projectKey, - string filePath, - RazorConfiguration configuration, - string? rootNamespace, - string displayName, + HostProject hostProject, ProjectWorkspaceState projectWorkspaceState, - ImmutableArray documents) + ImmutableArray documents) { - ProjectKey = projectKey; - FilePath = filePath; - Configuration = configuration; - RootNamespace = rootNamespace; - DisplayName = displayName; + HostProject = hostProject; ProjectWorkspaceState = projectWorkspaceState; Documents = documents.NullToEmpty(); } public bool Equals(RazorProjectInfo? other) => other is not null && - ProjectKey == other.ProjectKey && - FilePath == other.FilePath && - Configuration.Equals(other.Configuration) && - RootNamespace == other.RootNamespace && - DisplayName == other.DisplayName && + HostProject == other.HostProject && ProjectWorkspaceState.Equals(other.ProjectWorkspaceState) && Documents.SequenceEqual(other.Documents); @@ -65,11 +56,7 @@ public override int GetHashCode() { var hash = HashCodeCombiner.Start(); - hash.Add(ProjectKey); - hash.Add(FilePath); - hash.Add(Configuration); - hash.Add(RootNamespace); - hash.Add(DisplayName); + hash.Add(HostProject); hash.Add(ProjectWorkspaceState); hash.Add(Documents); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/DocumentSnapshotHandle.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/DocumentSnapshotHandle.cs deleted file mode 100644 index 9b4bafb1f27..00000000000 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/DocumentSnapshotHandle.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT license. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Razor.Serialization; - -internal record DocumentSnapshotHandle(string FilePath, string TargetPath, string FileKind); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/DocumentSnapshotFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/HostDocumentFormatter.cs similarity index 66% rename from src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/DocumentSnapshotFormatter.cs rename to src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/HostDocumentFormatter.cs index 6cf7401e9df..64ce2456dce 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/DocumentSnapshotFormatter.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/HostDocumentFormatter.cs @@ -2,18 +2,19 @@ // Licensed under the MIT license. See License.txt in the project root for license information. using MessagePack; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; namespace Microsoft.AspNetCore.Razor.Serialization.MessagePack.Formatters; -internal sealed class DocumentSnapshotHandleFormatter : ValueFormatter +internal sealed class HostDocumentFormatter : ValueFormatter { - public static readonly ValueFormatter Instance = new DocumentSnapshotHandleFormatter(); + public static readonly ValueFormatter Instance = new HostDocumentFormatter(); - private DocumentSnapshotHandleFormatter() + private HostDocumentFormatter() { } - public override DocumentSnapshotHandle Deserialize(ref MessagePackReader reader, SerializerCachingOptions options) + public override HostDocument Deserialize(ref MessagePackReader reader, SerializerCachingOptions options) { reader.ReadArrayHeaderAndVerify(3); @@ -21,10 +22,10 @@ public override DocumentSnapshotHandle Deserialize(ref MessagePackReader reader, var targetPath = CachedStringFormatter.Instance.Deserialize(ref reader, options).AssumeNotNull(); var fileKind = CachedStringFormatter.Instance.Deserialize(ref reader, options).AssumeNotNull(); - return new DocumentSnapshotHandle(filePath, targetPath, fileKind); + return new HostDocument(filePath, targetPath, fileKind); } - public override void Serialize(ref MessagePackWriter writer, DocumentSnapshotHandle value, SerializerCachingOptions options) + public override void Serialize(ref MessagePackWriter writer, HostDocument value, SerializerCachingOptions options) { writer.WriteArrayHeader(3); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/HostProjectFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/HostProjectFormatter.cs new file mode 100644 index 00000000000..8f13c9a2174 --- /dev/null +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/HostProjectFormatter.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using MessagePack; +using Microsoft.AspNetCore.Razor.Language; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; + +namespace Microsoft.AspNetCore.Razor.Serialization.MessagePack.Formatters; + +internal sealed class HostProjectFormatter : ValueFormatter +{ + public static readonly ValueFormatter Instance = new HostProjectFormatter(); + + private HostProjectFormatter() + { + } + + public override HostProject Deserialize(ref MessagePackReader reader, SerializerCachingOptions options) + { + reader.ReadArrayHeaderAndVerify(5); + + var filePath = CachedStringFormatter.Instance.Deserialize(ref reader, options).AssumeNotNull(); + var intermediateOutputPath = CachedStringFormatter.Instance.Deserialize(ref reader, options).AssumeNotNull(); + var configuration = reader.Deserialize(options); + var rootNamespace = CachedStringFormatter.Instance.Deserialize(ref reader, options); + var displayName = CachedStringFormatter.Instance.Deserialize(ref reader, options).AssumeNotNull(); + + return new HostProject(filePath, intermediateOutputPath, configuration, rootNamespace, displayName); + } + + public override void Serialize(ref MessagePackWriter writer, HostProject value, SerializerCachingOptions options) + { + writer.WriteArrayHeader(5); + + CachedStringFormatter.Instance.Serialize(ref writer, value.FilePath, options); + CachedStringFormatter.Instance.Serialize(ref writer, value.IntermediateOutputPath, options); + writer.Serialize(value.Configuration, options); + CachedStringFormatter.Instance.Serialize(ref writer, value.RootNamespace, options); + CachedStringFormatter.Instance.Serialize(ref writer, value.DisplayName, options); + } +} diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/RazorProjectInfoFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/RazorProjectInfoFormatter.cs index 6cafb011c69..87b8b8f475d 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/RazorProjectInfoFormatter.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/RazorProjectInfoFormatter.cs @@ -3,8 +3,8 @@ using System.Collections.Immutable; using MessagePack; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.ProjectSystem; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; namespace Microsoft.AspNetCore.Razor.Serialization.MessagePack.Formatters; @@ -18,37 +18,43 @@ private RazorProjectInfoFormatter() public override RazorProjectInfo Deserialize(ref MessagePackReader reader, SerializerCachingOptions options) { - reader.ReadArrayHeaderAndVerify(7); + VerifyVersionNumber(in reader); - var version = reader.ReadInt32(); + reader.ReadArrayHeaderAndVerify(4); - if (version != SerializationFormat.Version) - { - throw new RazorProjectInfoSerializationException(SR.Unsupported_razor_project_info_version_encountered); - } + // We skip the version number here, since we verified it above. + reader.Skip(); - var projectKeyId = CachedStringFormatter.Instance.Deserialize(ref reader, options).AssumeNotNull(); - var filePath = CachedStringFormatter.Instance.Deserialize(ref reader, options).AssumeNotNull(); - var configuration = reader.DeserializeOrNull(options) ?? RazorConfiguration.Default; + var hostProject = reader.Deserialize(options); var projectWorkspaceState = reader.DeserializeOrNull(options) ?? ProjectWorkspaceState.Default; - var rootNamespace = CachedStringFormatter.Instance.Deserialize(ref reader, options); - var displayName = CachedStringFormatter.Instance.Deserialize(ref reader, options).AssumeNotNull(); - var documents = reader.Deserialize>(options); + var documents = reader.Deserialize>(options); - return new RazorProjectInfo(new ProjectKey(projectKeyId), filePath, configuration, rootNamespace, displayName, projectWorkspaceState, documents); + return new RazorProjectInfo(hostProject, projectWorkspaceState, documents); } public override void Serialize(ref MessagePackWriter writer, RazorProjectInfo value, SerializerCachingOptions options) { - writer.WriteArrayHeader(7); + writer.WriteArrayHeader(4); writer.Write(SerializationFormat.Version); - CachedStringFormatter.Instance.Serialize(ref writer, value.ProjectKey.Id, options); - CachedStringFormatter.Instance.Serialize(ref writer, value.FilePath, options); - writer.Serialize(value.Configuration, options); + writer.Serialize(value.HostProject, options); writer.Serialize(value.ProjectWorkspaceState, options); - CachedStringFormatter.Instance.Serialize(ref writer, value.RootNamespace, options); - CachedStringFormatter.Instance.Serialize(ref writer, value.DisplayName, options); writer.Serialize(value.Documents, options); } + + private static void VerifyVersionNumber(ref readonly MessagePackReader reader) + { + // Peek ahead to check version number. + var peekReader = reader.CreatePeekReader(); + + if (peekReader.TryReadArrayHeader(out var count) && count > 0) + { + var version = peekReader.ReadInt32(); + + if (version != SerializationFormat.Version) + { + throw new RazorProjectInfoSerializationException(SR.Unsupported_razor_project_info_version_encountered); + } + } + } } diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Resolvers/RazorProjectInfoResolver.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Resolvers/RazorProjectInfoResolver.cs index 5eff22396b9..00cbfede1cf 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Resolvers/RazorProjectInfoResolver.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Resolvers/RazorProjectInfoResolver.cs @@ -40,7 +40,8 @@ private static class TypeToFormatterMap RazorProjectInfoFormatter.Instance, ChecksumFormatter.Instance, - DocumentSnapshotHandleFormatter.Instance, + HostDocumentFormatter.Instance, + HostProjectFormatter.Instance, ProjectWorkspaceStateFormatter.Instance, RazorConfigurationFormatter.Instance, diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/SerializationFormat.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/SerializationFormat.cs index 0fc5c52667c..bb7d6524f7b 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/SerializationFormat.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/SerializationFormat.cs @@ -9,5 +9,5 @@ internal static class SerializationFormat // or any of the types that compose it changes. This includes: RazorConfiguration, // ProjectWorkspaceState, TagHelperDescriptor, and DocumentSnapshotHandle. // NOTE: If this version is changed, a coordinated insertion is required between Roslyn and Razor for the C# extension. - public const int Version = 6; + public const int Version = 7; } diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/RazorProjectInfoFactory.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/RazorProjectInfoFactory.cs index bb518d231a5..580b228f74a 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/RazorProjectInfoFactory.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/RazorProjectInfoFactory.cs @@ -12,13 +12,13 @@ using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.AspNetCore.Razor.ProjectEngineHost; using Microsoft.AspNetCore.Razor.ProjectSystem; -using Microsoft.AspNetCore.Razor.Serialization; using Microsoft.AspNetCore.Razor.Telemetry; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.Compiler.CSharp; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; namespace Microsoft.AspNetCore.Razor.Utilities; @@ -35,6 +35,11 @@ static RazorProjectInfoFactory() public static async Task ConvertAsync(Project project, CancellationToken cancellationToken) { + if (project.FilePath is null) + { + return null; + } + var projectPath = Path.GetDirectoryName(project.FilePath); if (projectPath is null) { @@ -65,42 +70,34 @@ static RazorProjectInfoFactory() } var options = project.AnalyzerOptions.AnalyzerConfigOptionsProvider; - var configuration = ComputeRazorConfigurationOptions(options, compilation, out var defaultNamespace); + var (configuration, rootNamespace) = ComputeRazorConfigurationOptions(options, compilation); var fileSystem = RazorProjectFileSystem.Create(projectPath); - var defaultConfigure = (RazorProjectEngineBuilder builder) => - { - if (defaultNamespace is not null) - { - builder.SetRootNamespace(defaultNamespace); - } - - builder.SetCSharpLanguageVersion(csharpLanguageVersion); - builder.SetSupportLocalizedComponentNames(); // ProjectState in MS.CA.Razor.Workspaces does this, so I'm doing it too! - }; - var engineFactory = ProjectEngineFactories.DefaultProvider.GetFactory(configuration); var engine = engineFactory.Create( configuration, fileSystem, - configure: defaultConfigure); + configure: builder => + { + builder.SetRootNamespace(rootNamespace); + builder.SetCSharpLanguageVersion(csharpLanguageVersion); + builder.SetSupportLocalizedComponentNames(); // ProjectState in MS.CA.Razor.Workspaces does this, so I'm doing it too + }); var tagHelpers = await project.GetTagHelpersAsync(engine, NoOpTelemetryReporter.Instance, cancellationToken).ConfigureAwait(false); var projectWorkspaceState = ProjectWorkspaceState.Create(tagHelpers, csharpLanguageVersion); + var hostProject = new HostProject(project.FilePath, intermediateOutputPath, configuration, rootNamespace, displayName: project.Name); + return new RazorProjectInfo( - projectKey: new ProjectKey(intermediateOutputPath), - filePath: project.FilePath!, - configuration: configuration, - rootNamespace: defaultNamespace, - displayName: project.Name, + hostProject, projectWorkspaceState: projectWorkspaceState, documents: documents); } - private static RazorConfiguration ComputeRazorConfigurationOptions(AnalyzerConfigOptionsProvider options, Compilation compilation, out string defaultNamespace) + private static (RazorConfiguration configuration, string rootNamespace) ComputeRazorConfigurationOptions(AnalyzerConfigOptionsProvider options, Compilation compilation) { // See RazorSourceGenerator.RazorProviders.cs @@ -120,21 +117,19 @@ private static RazorConfiguration ComputeRazorConfigurationOptions(AnalyzerConfi var suppressAddComponentParameter = !compilation.HasAddComponentParameter(); - var razorConfiguration = new RazorConfiguration( + var configuration = new RazorConfiguration( razorLanguageVersion, configurationName, Extensions: [], UseConsolidatedMvcViews: true, suppressAddComponentParameter); - defaultNamespace = rootNamespace ?? "ASP"; // TODO: Source generator does this. Do we want it? - - return razorConfiguration; + return (configuration, rootNamespace ?? "ASP"); // TODO: Source generator does this. Do we want it? } - internal static ImmutableArray GetDocuments(Project project, string projectPath) + internal static ImmutableArray GetDocuments(Project project, string projectPath) { - using var documents = new PooledArrayBuilder(); + using var documents = new PooledArrayBuilder(); var normalizedProjectPath = FilePathNormalizer.NormalizeDirectory(projectPath); @@ -144,7 +139,7 @@ internal static ImmutableArray GetDocuments(Project proj if (document.FilePath is { } filePath && TryGetFileKind(filePath, out var kind)) { - documents.Add(new DocumentSnapshotHandle(filePath, GetTargetPath(filePath, normalizedProjectPath), kind)); + documents.Add(new HostDocument(filePath, GetTargetPath(filePath, normalizedProjectPath), kind)); } } @@ -159,7 +154,7 @@ internal static ImmutableArray GetDocuments(Project proj if (TryGetRazorFileName(document.FilePath, out var razorFilePath) && TryGetFileKind(razorFilePath, out var kind)) { - documents.Add(new DocumentSnapshotHandle(razorFilePath, GetTargetPath(razorFilePath, normalizedProjectPath), kind)); + documents.Add(new HostDocument(razorFilePath, GetTargetPath(razorFilePath, normalizedProjectPath), kind)); } } } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/AbstractRazorProjectInfoDriver.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/AbstractRazorProjectInfoDriver.cs index e9f9fb3c081..a19d5c11191 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/AbstractRazorProjectInfoDriver.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/AbstractRazorProjectInfoDriver.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem; internal abstract partial class AbstractRazorProjectInfoDriver : IRazorProjectInfoDriver, IDisposable { private abstract record Work(ProjectKey ProjectKey); - private sealed record Update(RazorProjectInfo ProjectInfo) : Work(ProjectInfo.ProjectKey); + private sealed record Update(RazorProjectInfo ProjectInfo) : Work(ProjectInfo.Key); private sealed record Remove(ProjectKey ProjectKey) : Work(ProjectKey); protected static readonly TimeSpan DefaultDelay = TimeSpan.FromMilliseconds(250); @@ -103,7 +103,7 @@ private async ValueTask ProcessBatchAsync(ImmutableArray items, Cancellati { case Update(var projectInfo): Logger?.LogTrace($"Sending update for {projectInfo.FilePath} with {projectInfo.ProjectWorkspaceState.TagHelpers.Length} TagHelpers"); - _latestProjectInfoMap[projectInfo.ProjectKey] = projectInfo; + _latestProjectInfoMap[projectInfo.Key] = projectInfo; break; case Remove(var projectKey): diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/Extensions.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/Extensions.cs index 34b050477a0..5390d914135 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/Extensions.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/Extensions.cs @@ -6,18 +6,13 @@ #endif using System.Diagnostics; -using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.ProjectSystem; -using Microsoft.AspNetCore.Razor.Serialization; using Microsoft.AspNetCore.Razor.Utilities; namespace Microsoft.CodeAnalysis.Razor.ProjectSystem; internal static class Extensions { - public static DocumentSnapshotHandle ToHandle(this IDocumentSnapshot snapshot) - => new(snapshot.FilePath.AssumeNotNull(), snapshot.TargetPath.AssumeNotNull(), snapshot.FileKind.AssumeNotNull()); - public static ProjectKey ToProjectKey(this Project project) { var intermediateOutputPath = FilePathNormalizer.GetNormalizedDirectoryName(project.CompilationOutputInfo.AssemblyPath); diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/HostDocument.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/HostDocument.cs deleted file mode 100644 index 61f0bbdccc0..00000000000 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/HostDocument.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT license. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Razor.Language; - -namespace Microsoft.CodeAnalysis.Razor.ProjectSystem; - -internal sealed record class HostDocument -{ - public string FileKind { get; init; } - public string FilePath { get; init; } - public string TargetPath { get; init; } - - public HostDocument(string filePath, string targetPath, string? fileKind = null) - { - FilePath = filePath; - TargetPath = targetPath; - FileKind = fileKind ?? FileKinds.GetFileKindFromFilePath(filePath); - } -} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/IProjectSnapshotExtensions.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/IProjectSnapshotExtensions.cs index 693b7d6265a..ddf27d004df 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/IProjectSnapshotExtensions.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/IProjectSnapshotExtensions.cs @@ -5,10 +5,10 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Threading; +using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.AspNetCore.Razor.ProjectSystem; -using Microsoft.AspNetCore.Razor.Serialization; namespace Microsoft.CodeAnalysis.Razor.ProjectSystem; @@ -16,26 +16,28 @@ internal static class IProjectSnapshotExtensions { public static RazorProjectInfo ToRazorProjectInfo(this IProjectSnapshot project) { - using var documents = new PooledArrayBuilder(); + var hostProject = project is ProjectSnapshot projectSnapshot + ? projectSnapshot.HostProject + : new(project.FilePath, project.IntermediateOutputPath, project.Configuration, project.RootNamespace, project.DisplayName); + + using var documents = new PooledArrayBuilder(); foreach (var documentFilePath in project.DocumentFilePaths) { if (project.TryGetDocument(documentFilePath, out var document)) { - var documentHandle = document.ToHandle(); + var hostDocument = document is DocumentSnapshot documentSnapshot + ? documentSnapshot.HostDocument + : new(document.FilePath.AssumeNotNull(), document.TargetPath.AssumeNotNull(), document.FileKind); - documents.Add(documentHandle); + documents.Add(hostDocument); } } return new RazorProjectInfo( - projectKey: project.Key, - filePath: project.FilePath, - configuration: project.Configuration, - rootNamespace: project.RootNamespace, - displayName: project.DisplayName, - projectWorkspaceState: project.ProjectWorkspaceState, - documents: documents.DrainToImmutable()); + hostProject, + project.ProjectWorkspaceState, + documents.DrainToImmutable()); } public static ImmutableArray GetTagHelpersSynchronously(this IProjectSnapshot projectSnapshot) diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace.Test/RazorWorkspaceListenerTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace.Test/RazorWorkspaceListenerTest.cs index 8fe48a38dce..09516b69a82 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace.Test/RazorWorkspaceListenerTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace.Test/RazorWorkspaceListenerTest.cs @@ -243,7 +243,7 @@ public async Task TestSerialization() Assert.Single(deserializedProjectInfo.Documents); Assert.Equal("TestProject", deserializedProjectInfo.DisplayName); Assert.Equal("ASP", deserializedProjectInfo.RootNamespace); - Assert.Equal(@"C:/test/out/", deserializedProjectInfo.ProjectKey.Id); + Assert.Equal(@"C:/test/out/", deserializedProjectInfo.Key.Id); Assert.Equal(@"C:\test\test.csproj", deserializedProjectInfo.FilePath); // Test remove action diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorComponentSearchEngineTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorComponentSearchEngineTest.cs index 9dfc3afdccd..69f7f33f3d1 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorComponentSearchEngineTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorComponentSearchEngineTest.cs @@ -66,13 +66,14 @@ protected override async Task InitializeAsync() LoggerFactory); AddDisposable(projectService); - await projectService.GetTestAccessor().AddProjectAsync( + var hostProject1 = new HostProject( s_projectFilePath1, s_intermediateOutputPath1, RazorConfiguration.Default, RootNamespace1, - displayName: "", - DisposalToken); + displayName: ""); + + await projectService.AddProjectAsync(hostProject1, DisposalToken); await projectService.AddDocumentToPotentialProjectsAsync(s_componentFilePath1, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath1, SourceText.From(""), DisposalToken); @@ -80,13 +81,14 @@ await projectService.GetTestAccessor().AddProjectAsync( await projectService.AddDocumentToPotentialProjectsAsync(s_componentFilePath2, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath2, SourceText.From("@namespace Test"), DisposalToken); - await projectService.GetTestAccessor().AddProjectAsync( + var hostProject2 = new HostProject( s_projectFilePath2, s_intermediateOutputPath2, RazorConfiguration.Default, RootNamespace2, - displayName: "", - DisposalToken); + displayName: ""); + + await projectService.AddProjectAsync(hostProject2, DisposalToken); await projectService.AddDocumentToPotentialProjectsAsync(s_componentFilePath3, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath3, SourceText.From(""), DisposalToken); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs index a12f848949f..d81b6c631c9 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs @@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Razor.LanguageServer.Common; using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem; using Microsoft.AspNetCore.Razor.ProjectSystem; -using Microsoft.AspNetCore.Razor.Serialization; using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer; using Microsoft.AspNetCore.Razor.Test.Common.ProjectSystem; @@ -70,11 +69,7 @@ await _projectManager.UpdateAsync(updater => // Act await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - hostProject.Key, - hostProject.FilePath, - hostProject.Configuration, - hostProject.RootNamespace, - hostProject.DisplayName, + hostProject, projectWorkspaceState, documents: []), DisposalToken); @@ -97,15 +92,11 @@ await _projectManager.UpdateAsync(updater => updater.DocumentAdded(hostProject.Key, hostDocument, StrictMock.Of()); }); - var newDocument = new DocumentSnapshotHandle("file.cshtml", "file.cshtml", FileKinds.Component); + var newDocument = new HostDocument("file.cshtml", "file.cshtml", FileKinds.Component); // Act await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - hostProject.Key, - hostProject.FilePath, - hostProject.Configuration, - hostProject.RootNamespace, - hostProject.DisplayName, + hostProject, ProjectWorkspaceState.Default, [newDocument]), DisposalToken); @@ -130,16 +121,12 @@ await _projectManager.UpdateAsync(updater => updater.DocumentAdded(hostProject.Key, hostDocument, StrictMock.Of()); }); - var oldDocument = new DocumentSnapshotHandle(hostDocument.FilePath, hostDocument.TargetPath, hostDocument.FileKind); - var newDocument = new DocumentSnapshotHandle("C:/path/to/file2.cshtml", "file2.cshtml", FileKinds.Legacy); + var oldDocument = new HostDocument(hostDocument.FilePath, hostDocument.TargetPath, hostDocument.FileKind); + var newDocument = new HostDocument("C:/path/to/file2.cshtml", "file2.cshtml", FileKinds.Legacy); // Act await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - hostProject.Key, - hostProject.FilePath, - hostProject.Configuration, - hostProject.RootNamespace, - hostProject.DisplayName, + hostProject, ProjectWorkspaceState.Default, [oldDocument, newDocument]), DisposalToken); @@ -167,15 +154,11 @@ await _projectManager.UpdateAsync(updater => var project = _projectManager.GetLoadedProject(hostProject.Key); - var addedDocument = new DocumentSnapshotHandle(hostDocument.FilePath, hostDocument.TargetPath, hostDocument.FileKind); + var addedDocument = new HostDocument(hostDocument.FilePath, hostDocument.TargetPath, hostDocument.FileKind); // Act await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - hostProject.Key, - hostProject.FilePath, - hostProject.Configuration, - hostProject.RootNamespace, - hostProject.DisplayName, + hostProject, ProjectWorkspaceState.Default, [addedDocument]), DisposalToken); @@ -197,30 +180,27 @@ public async Task IProjectInfoListener_UpdatedAsync_MovesDocumentsFromMisc_ViaSe const string IntermediateOutputPath = "C:/path/to/obj"; const string RootNamespace = "TestRootNamespace"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); var miscProject = _projectManager.GetMiscellaneousProject(); - var project = _projectManager.GetLoadedProject(ownerProjectKey); + var project = Assert.IsType(_projectManager.GetLoadedProject(ownerProjectKey)); - var addedDocument = new DocumentSnapshotHandle(DocumentFilePath, DocumentFilePath, FileKinds.Legacy); + var addedDocument = new HostDocument(DocumentFilePath, DocumentFilePath, FileKinds.Legacy); // Act await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - project.Key, - project.FilePath, - project.Configuration, - project.RootNamespace, - project.DisplayName, + project.HostProject, ProjectWorkspaceState.Default, [addedDocument]), DisposalToken); // Assert - project = _projectManager.GetLoadedProject(ownerProjectKey); + project = Assert.IsType(_projectManager.GetLoadedProject(ownerProjectKey)); var projectFilePaths = project.DocumentFilePaths.OrderBy(path => path); Assert.Equal(projectFilePaths, [addedDocument.FilePath]); miscProject = _projectManager.GetLoadedProject(miscProject.Key); @@ -246,15 +226,11 @@ public async Task IProjectInfoListener_UpdatedAsync_MovesExistingDocumentToMisc( return project; }); - var newDocument = new DocumentSnapshotHandle("C:/path/to/file2.cshtml", "file2.cshtml", FileKinds.Legacy); + var newDocument = new HostDocument("C:/path/to/file2.cshtml", "file2.cshtml", FileKinds.Legacy); // Act await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - hostProject.Key, - hostProject.FilePath, - hostProject.Configuration, - hostProject.RootNamespace, - hostProject.DisplayName, + hostProject, ProjectWorkspaceState.Default, [newDocument]), DisposalToken); @@ -280,17 +256,13 @@ await _projectManager.UpdateAsync(updater => updater.DocumentAdded(hostProject.Key, document, StrictMock.Of()); }); - var newDocument = new DocumentSnapshotHandle(document.FilePath, document.TargetPath, document.FileKind); + var newDocument = new HostDocument(document.FilePath, document.TargetPath, document.FileKind); using var listener = _projectManager.ListenToNotifications(); // Act & Assert await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - hostProject.Key, - hostProject.FilePath, - hostProject.Configuration, - hostProject.RootNamespace, - hostProject.DisplayName, + hostProject, ProjectWorkspaceState.Default, [newDocument]), DisposalToken); @@ -311,15 +283,11 @@ await _projectManager.UpdateAsync(updater => updater.DocumentAdded(hostProject.Key, legacyDocument, StrictMock.Of()); }); - var newDocument = new DocumentSnapshotHandle(legacyDocument.FilePath, legacyDocument.TargetPath, FileKinds.Component); + var newDocument = new HostDocument(legacyDocument.FilePath, legacyDocument.TargetPath, FileKinds.Component); // Act await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - hostProject.Key, - hostProject.FilePath, - hostProject.Configuration, - hostProject.RootNamespace, - hostProject.DisplayName, + hostProject, ProjectWorkspaceState.Default, [newDocument]), DisposalToken); @@ -339,20 +307,17 @@ public async Task IProjectInfoListener_UpdatedAsync_SameConfigurationDifferentRo const string IntermediateOutputPath = "C:/path/to/obj"; const string NewRootNamespace = "NewRootNamespace"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, rootNamespace: null, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, rootNamespace: null); - var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); + + var ownerProject = Assert.IsType(_projectManager.GetLoadedProject(ownerProjectKey)); using var listener = _projectManager.ListenToNotifications(); // Act await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - ownerProject.Key, - ownerProject.FilePath, - ownerProject.Configuration, - NewRootNamespace, - ownerProject.DisplayName, + ownerProject.HostProject with { RootNamespace = NewRootNamespace}, ProjectWorkspaceState.Default, documents: []), DisposalToken); @@ -372,20 +337,17 @@ public async Task IProjectInfoListener_UpdatedAsync_SameConfigurationAndRootName const string IntermediateOutputPath = "C:/path/to/obj"; const string RootNamespace = "TestRootNamespace"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); - var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); + + var ownerProject = Assert.IsType(_projectManager.GetLoadedProject(ownerProjectKey)); using var listener = _projectManager.ListenToNotifications(); // Act & Assert await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - ownerProject.Key, - ownerProject.FilePath, - ownerProject.Configuration, - ownerProject.RootNamespace, - displayName: "", + ownerProject.HostProject with { DisplayName = "" }, ProjectWorkspaceState.Default, documents: []), DisposalToken); @@ -401,20 +363,17 @@ public async Task IProjectInfoListener_UpdatedAsync_ChangesProjectToUseProvidedC const string IntermediateOutputPath = "C:/path/to/obj"; const string RootNamespace = "TestRootNamespace"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); - var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); + + var ownerProject = Assert.IsType(_projectManager.GetLoadedProject(ownerProjectKey)); using var listener = _projectManager.ListenToNotifications(); // Act await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - ownerProject.Key, - ownerProject.FilePath, - FallbackRazorConfiguration.MVC_1_1, - "TestRootNamespace", - displayName: "", + ownerProject.HostProject with { Configuration = FallbackRazorConfiguration.MVC_1_1, RootNamespace = "TestRootNamespace", DisplayName = ""}, ProjectWorkspaceState.Default, documents: []), DisposalToken); @@ -434,8 +393,9 @@ public async Task CloseDocument_ClosesDocumentInOwnerProject() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, DisposalToken); @@ -465,10 +425,11 @@ public async Task CloseDocument_ClosesDocumentInAllOwnerProjects() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey1 = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - var ownerProjectKey2 = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject1 = new HostProject(ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace); + var hostProject2 = new HostProject(ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey1 = await _projectService.AddProjectAsync(hostProject1, DisposalToken); + var ownerProjectKey2 = await _projectService.AddProjectAsync(hostProject2, DisposalToken); await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, DisposalToken); @@ -524,8 +485,9 @@ public async Task OpenDocument_OpensAlreadyAddedDocumentInOwnerProject() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -554,10 +516,11 @@ public async Task OpenDocument_OpensAlreadyAddedDocumentInAllOwnerProjects() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey1 = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - var ownerProjectKey2 = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject1 = new HostProject(ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace); + var hostProject2 = new HostProject(ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey1 = await _projectService.AddProjectAsync(hostProject1, DisposalToken); + var ownerProjectKey2 = await _projectService.AddProjectAsync(hostProject2, DisposalToken); await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); var ownerProject1 = _projectManager.GetLoadedProject(ownerProjectKey1); @@ -611,8 +574,9 @@ public async Task OpenDocument_OpensAndAddsDocumentToMiscellaneousProject() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); var miscProject = _projectManager.GetMiscellaneousProject(); @@ -655,8 +619,9 @@ public async Task AddDocument_AddsDocumentToOwnerProject() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -701,8 +666,9 @@ public async Task AddDocumentToMiscProjectAsync_IgnoresKnownDocument() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); + + await _projectService.AddProjectAsync(hostProject, DisposalToken); await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); // Act @@ -742,8 +708,9 @@ public async Task RemoveDocument_RemovesDocumentFromOwnerProject() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -770,10 +737,11 @@ public async Task RemoveDocument_RemovesDocumentFromAllOwnerProjects() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey1 = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - var ownerProjectKey2 = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject1 = new HostProject(ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace); + var hostProject2 = new HostProject(ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey1 = await _projectService.AddProjectAsync(hostProject1, DisposalToken); + var ownerProjectKey2 = await _projectService.AddProjectAsync(hostProject2, DisposalToken); await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); var ownerProject1 = _projectManager.GetLoadedProject(ownerProjectKey1); @@ -801,8 +769,9 @@ public async Task RemoveOpenDocument_RemovesDocumentFromOwnerProject_MovesToMisc const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, DisposalToken); @@ -887,8 +856,9 @@ public async Task UpdateDocument_ChangesDocumentInOwnerProject() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, DisposalToken); @@ -918,10 +888,11 @@ public async Task UpdateDocument_ChangesDocumentInAllOwnerProjects() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey1 = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - var ownerProjectKey2 = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject1 = new HostProject(ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace); + var hostProject2 = new HostProject(ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey1 = await _projectService.AddProjectAsync(hostProject1, DisposalToken); + var ownerProjectKey2 = await _projectService.AddProjectAsync(hostProject2, DisposalToken); await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, DisposalToken); @@ -977,8 +948,9 @@ public async Task UpdateDocument_DocumentVersionUpdated() const string RootNamespace = "TestRootNamespace"; const string DocumentFilePath = "C:/path/to/document.cshtml"; - var ownerProjectKey = await _projectService.GetTestAccessor().AddProjectAsync( - ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + var hostProject = new HostProject(ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace); + + var ownerProjectKey = await _projectService.AddProjectAsync(hostProject, DisposalToken); await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -1005,20 +977,22 @@ public async Task IRazorProjectInfoListener_UpdatedAsync_AddsProjectWithSpecifie const string RootNamespace = "My.Root.Namespace"; var configuration = new RazorConfiguration(RazorLanguageVersion.Version_1_0, "TestName", Extensions: []); - var projectKey = new ProjectKey(IntermediateOutputPath); - // Act - await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( - projectKey, + var hostProject = new HostProject( ProjectFilePath, + IntermediateOutputPath, configuration, RootNamespace, - "ProjectDisplayName", + displayName: "ProjectDisplayName"); + + // Act + await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( + hostProject, ProjectWorkspaceState.Default, documents: []), DisposalToken); - var project = _projectManager.GetLoadedProject(projectKey); + var project = _projectManager.GetLoadedProject(hostProject.Key); // Assert Assert.Equal(ProjectFilePath, project.FilePath); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs index 4c78d3dd3db..50cd0837e4d 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs @@ -654,8 +654,13 @@ public async Task Handle_Rename_SingleServer_DoesNotDelegateForRazor() projectManager, LoggerFactory)); - var projectKey1 = await projectService.GetTestAccessor().AddProjectAsync( - s_projectFilePath1, s_intermediateOutputPath1, RazorConfiguration.Default, RootNamespace1, displayName: null, DisposalToken); + var hostProject1 = new HostProject( + s_projectFilePath1, + s_intermediateOutputPath1, + RazorConfiguration.Default, + RootNamespace1); + + var projectKey1 = await projectService.AddProjectAsync(hostProject1, DisposalToken); await projectManager.UpdateAsync(updater => { @@ -676,8 +681,13 @@ await projectManager.UpdateAsync(updater => await projectService.UpdateDocumentAsync(s_componentFilePath1337, SourceText.From(ComponentText1337), DisposalToken); await projectService.UpdateDocumentAsync(s_indexFilePath1, SourceText.From(IndexText1), DisposalToken); - var projectKey2 = await projectService.GetTestAccessor().AddProjectAsync( - s_projectFilePath2, s_intermediateOutputPath2, RazorConfiguration.Default, RootNamespace2, displayName: null, DisposalToken); + var hostProject2 = new HostProject( + s_projectFilePath2, + s_intermediateOutputPath2, + RazorConfiguration.Default, + RootNamespace2); + + var projectKey2 = await projectService.AddProjectAsync(hostProject2, DisposalToken); await projectManager.UpdateAsync(updater => { diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TestRazorProjectService.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TestRazorProjectService.cs index 026a36c8242..f9f7b070fe5 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TestRazorProjectService.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TestRazorProjectService.cs @@ -2,15 +2,11 @@ // Licensed under the MIT license. See License.txt in the project root for license information. using System.Collections.Immutable; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.LanguageServer.Common; using Microsoft.AspNetCore.Razor.ProjectSystem; -using Microsoft.AspNetCore.Razor.Serialization; using Microsoft.AspNetCore.Razor.Test.Common; -using Microsoft.AspNetCore.Razor.Utilities; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Razor.Logging; using Microsoft.CodeAnalysis.Razor.ProjectSystem; @@ -42,19 +38,25 @@ private static IRazorProjectInfoDriver CreateProjectInfoDriver() return mock.Object; } + public Task AddProjectAsync(HostProject hostProject, CancellationToken cancellationToken) + { + return GetTestAccessor().AddProjectAsync(hostProject, cancellationToken); + } + public async Task AddDocumentToPotentialProjectsAsync(string textDocumentPath, CancellationToken cancellationToken) { foreach (var projectSnapshot in _projectManager.FindPotentialProjects(textDocumentPath)) { - var normalizedProjectPath = FilePathNormalizer.NormalizeDirectory(projectSnapshot.FilePath); + var hostProject = ((ProjectSnapshot)projectSnapshot).HostProject; + var documents = ImmutableArray .CreateRange(projectSnapshot.DocumentFilePaths) .Add(textDocumentPath) - .Select(d => new DocumentSnapshotHandle(d, d, FileKinds.GetFileKindFromFilePath(d))) - .ToImmutableArray(); + .SelectAsArray(static path => new HostDocument(filePath: path, targetPath: path)); - await ((IRazorProjectInfoListener)this).UpdatedAsync(new RazorProjectInfo(projectSnapshot.Key, projectSnapshot.FilePath, projectSnapshot.Configuration, projectSnapshot.RootNamespace, projectSnapshot.DisplayName, projectSnapshot.ProjectWorkspaceState, - documents), cancellationToken).ConfigureAwait(false); + await ((IRazorProjectInfoListener)this) + .UpdatedAsync(new RazorProjectInfo(hostProject, projectSnapshot.ProjectWorkspaceState, documents), cancellationToken) + .ConfigureAwait(false); } } } diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/Serialization/SerializerValidationTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/Serialization/SerializerValidationTest.cs index 5cd9e7e4863..b11c289abef 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/Serialization/SerializerValidationTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/Serialization/SerializerValidationTest.cs @@ -3,19 +3,16 @@ using System.Collections.Immutable; using System.IO; -using System.Reflection; using MessagePack; using MessagePack.Resolvers; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.ProjectSystem; -using Microsoft.AspNetCore.Razor.Serialization; using Microsoft.AspNetCore.Razor.Serialization.Json; using Microsoft.AspNetCore.Razor.Serialization.MessagePack.Resolvers; using Microsoft.AspNetCore.Razor.Test.Common; -using Microsoft.CodeAnalysis.Razor.Logging; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Xunit; using Xunit.Abstractions; -using MessagePackSerializationFormat = Microsoft.AspNetCore.Razor.Serialization.MessagePack.SerializationFormat; namespace Microsoft.AspNetCore.Razor.ProjectEngineHost.Test.Serialization; @@ -90,12 +87,9 @@ public void VerifyJson_RazorProjectInfo(string resourceName) Assert.NotNull(actualProjectInfo); // Assert - Assert.Equal(originalProjectInfo.ProjectKey, actualProjectInfo.ProjectKey); - Assert.Equal(originalProjectInfo.FilePath, actualProjectInfo.FilePath); - Assert.Equal(originalProjectInfo.Configuration, actualProjectInfo.Configuration); - Assert.Equal(originalProjectInfo.RootNamespace, actualProjectInfo.RootNamespace); + Assert.Equal(originalProjectInfo.HostProject, actualProjectInfo.HostProject); Assert.Equal(originalProjectInfo.ProjectWorkspaceState, actualProjectInfo.ProjectWorkspaceState); - Assert.Equal(originalProjectInfo.Documents, actualProjectInfo.Documents); + Assert.Equal(originalProjectInfo.Documents, actualProjectInfo.Documents); } [Theory] diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/SerializationTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/SerializationTest.cs index c910ea76606..ebe800f15bc 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/SerializationTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/SerializationTest.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Razor.Serialization.Json; using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Newtonsoft.Json.Linq; using Xunit; using Xunit.Abstractions; @@ -34,12 +35,15 @@ public SerializationTest(ITestOutputHelper testOutput) public void RazorProjectInfo_InvalidVersionThrows() { // Arrange - var projectInfo = new RazorProjectInfo( - new ProjectKey("/path/to/obj/"), - "/path/to/project.csproj", + var hostProject = new HostProject( + filePath: "/path/to/project.csproj", + intermediateOutputPath: "/path/to/obj/", _configuration, rootNamespace: "TestProject", - displayName: "project", + displayName: "project"); + + var projectInfo = new RazorProjectInfo( + hostProject, _projectWorkspaceState, documents: []); @@ -67,12 +71,15 @@ public void RazorProjectInfo_InvalidVersionThrows() public void RazorProjectInfo_MissingVersionThrows() { // Arrange - var projectInfo = new RazorProjectInfo( - new ProjectKey("/path/to/obj/"), - "/path/to/project.csproj", + var hostProject = new HostProject( + filePath: "/path/to/project.csproj", + intermediateOutputPath: "/path/to/obj/", _configuration, rootNamespace: "TestProject", - displayName: "project", + displayName: "project"); + + var projectInfo = new RazorProjectInfo( + hostProject, _projectWorkspaceState, documents: []); @@ -100,14 +107,17 @@ public void RazorProjectInfo_MissingVersionThrows() public void RazorProjectInfo_CanRoundTrip() { // Arrange - var legacyDocument = new DocumentSnapshotHandle("/path/to/file.cshtml", "file.cshtml", FileKinds.Legacy); - var componentDocument = new DocumentSnapshotHandle("/path/to/otherfile.razor", "otherfile.razor", FileKinds.Component); - var projectInfo = new RazorProjectInfo( - new ProjectKey("/path/to/obj/"), - "/path/to/project.csproj", + var hostProject = new HostProject( + filePath: "/path/to/project.csproj", + intermediateOutputPath: "/path/to/obj/", _configuration, rootNamespace: "TestProject", - displayName: "project", + displayName: "project"); + + var legacyDocument = new HostDocument("/path/to/file.cshtml", "file.cshtml", FileKinds.Legacy); + var componentDocument = new HostDocument("/path/to/otherfile.razor", "otherfile.razor", FileKinds.Component); + var projectInfo = new RazorProjectInfo( + hostProject, _projectWorkspaceState, documents: [legacyDocument, componentDocument]); @@ -119,23 +129,11 @@ public void RazorProjectInfo_CanRoundTrip() Assert.NotNull(deserializedProjectInfo); // Assert - Assert.Equal(projectInfo.FilePath, deserializedProjectInfo.FilePath); - Assert.Equal(projectInfo.Configuration, deserializedProjectInfo.Configuration); - Assert.Equal(projectInfo.RootNamespace, deserializedProjectInfo.RootNamespace); + Assert.Equal(projectInfo.HostProject, deserializedProjectInfo.HostProject); Assert.Equal(projectInfo.ProjectWorkspaceState, deserializedProjectInfo.ProjectWorkspaceState); - Assert.Collection(projectInfo.Documents.OrderBy(doc => doc.FilePath), - document => - { - Assert.Equal(legacyDocument.FilePath, document.FilePath); - Assert.Equal(legacyDocument.TargetPath, document.TargetPath); - Assert.Equal(legacyDocument.FileKind, document.FileKind); - }, - document => - { - Assert.Equal(componentDocument.FilePath, document.FilePath); - Assert.Equal(componentDocument.TargetPath, document.TargetPath); - Assert.Equal(componentDocument.FileKind, document.FileKind); - }); + Assert.Collection(projectInfo.Documents.OrderBy(static d => d.FilePath), + document => Assert.Equal(document, legacyDocument), + document => Assert.Equal(document, componentDocument)); } [Fact] diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/StreamExtensionTests.NetCore.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/StreamExtensionTests.NetCore.cs index 8895137d798..5dc3ec61c75 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/StreamExtensionTests.NetCore.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/StreamExtensionTests.NetCore.cs @@ -3,15 +3,14 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.ProjectSystem; -using Microsoft.AspNetCore.Razor.Serialization; using Microsoft.AspNetCore.Razor.Utilities; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Xunit; namespace Microsoft.AspNetCore.Razor.ProjectEngineHost.Test; @@ -71,7 +70,14 @@ public async Task SerializeProjectInfo() var configuration = new RazorConfiguration( RazorLanguageVersion.Latest, "TestConfiguration", - ImmutableArray.Empty); + Extensions: []); + + var hostProject = new HostProject( + filePath: @"C:\test\test.csproj", + intermediateOutputPath: "TestProject", + configuration, + rootNamespace: "TestNamespace", + displayName: "Test"); var tagHelper = TagHelperDescriptorBuilder.Create("TypeName", "AssemblyName") .TagMatchingRuleDescriptor(rule => rule.RequireTagName("tag-name")) @@ -80,13 +86,9 @@ public async Task SerializeProjectInfo() var projectWorkspaceState = ProjectWorkspaceState.Create([tagHelper], CodeAnalysis.CSharp.LanguageVersion.Latest); var projectInfo = new RazorProjectInfo( - new ProjectKey("TestProject"), - @"C:\test\test.csproj", - configuration, - "TestNamespace", - "Test", + hostProject, projectWorkspaceState, - [new DocumentSnapshotHandle(@"C:\test\document.razor", @"document.razor", FileKinds.Component)]); + [new HostDocument(@"C:\test\document.razor", @"document.razor", FileKinds.Component)]); var bytesToSerialize = projectInfo.Serialize(); diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/ProjectSystem/RazorProjectInfoDriverTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/ProjectSystem/RazorProjectInfoDriverTest.cs index 083e53b2aff..945197dd42e 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/ProjectSystem/RazorProjectInfoDriverTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/LanguageClient/ProjectSystem/RazorProjectInfoDriverTest.cs @@ -55,22 +55,22 @@ await projectManager.UpdateAsync(static updater => var latestProjects = driver.GetLatestProjectInfo(); // The misc files projects project should be present. - Assert.Contains(latestProjects, x => x.ProjectKey == MiscFilesHostProject.Instance.Key); + Assert.Contains(latestProjects, x => x.Key == MiscFilesHostProject.Instance.Key); // Sort the remaining projects by project key. var projects = latestProjects - .WhereAsArray(x => x.ProjectKey != MiscFilesHostProject.Instance.Key) - .Sort((x, y) => x.ProjectKey.Id.CompareTo(y.ProjectKey.Id)); + .WhereAsArray(x => x.Key != MiscFilesHostProject.Instance.Key) + .Sort((x, y) => x.Key.Id.CompareTo(y.Key.Id)); Assert.Equal(2, projects.Length); var projectInfo1 = projects[0]; - Assert.Equal(s_hostProject1.Key, projectInfo1.ProjectKey); + Assert.Equal(s_hostProject1.Key, projectInfo1.Key); var document1 = Assert.Single(projectInfo1.Documents); Assert.Equal(s_hostDocument1.FilePath, document1.FilePath); var projectInfo2 = projects[1]; - Assert.Equal(s_hostProject2.Key, projectInfo2.ProjectKey); + Assert.Equal(s_hostProject2.Key, projectInfo2.Key); var document2 = Assert.Single(projectInfo2.Documents); Assert.Equal(s_hostDocument2.FilePath, document2.FilePath); } @@ -88,7 +88,7 @@ public async Task ProcessesProjectsAddedAfterInitialization() var initialProjects = driver.GetLatestProjectInfo(); var miscFilesProject = Assert.Single(initialProjects); - Assert.Equal(MiscFilesHostProject.Instance.Key, miscFilesProject.ProjectKey); + Assert.Equal(MiscFilesHostProject.Instance.Key, miscFilesProject.Key); // Now add some projects await projectManager.UpdateAsync(static updater => @@ -105,18 +105,18 @@ await projectManager.UpdateAsync(static updater => // Sort the non-misc files projects by project key. var projects = driver .GetLatestProjectInfo() - .WhereAsArray(x => x.ProjectKey != MiscFilesHostProject.Instance.Key) - .Sort((x, y) => x.ProjectKey.Id.CompareTo(y.ProjectKey.Id)); + .WhereAsArray(x => x.Key != MiscFilesHostProject.Instance.Key) + .Sort((x, y) => x.Key.Id.CompareTo(y.Key.Id)); Assert.Equal(2, projects.Length); var projectInfo1 = projects[0]; - Assert.Equal(s_hostProject1.Key, projectInfo1.ProjectKey); + Assert.Equal(s_hostProject1.Key, projectInfo1.Key); var document1 = Assert.Single(projectInfo1.Documents); Assert.Equal(s_hostDocument1.FilePath, document1.FilePath); var projectInfo2 = projects[1]; - Assert.Equal(s_hostProject2.Key, projectInfo2.ProjectKey); + Assert.Equal(s_hostProject2.Key, projectInfo2.Key); var document2 = Assert.Single(projectInfo2.Documents); Assert.Equal(s_hostDocument2.FilePath, document2.FilePath); } @@ -143,11 +143,11 @@ await projectManager.UpdateAsync(static updater => // Sort the non-misc files projects by project key. var projects = driver .GetLatestProjectInfo() - .WhereAsArray(x => x.ProjectKey != MiscFilesHostProject.Instance.Key) - .Sort((x, y) => x.ProjectKey.Id.CompareTo(y.ProjectKey.Id)); + .WhereAsArray(x => x.Key != MiscFilesHostProject.Instance.Key) + .Sort((x, y) => x.Key.Id.CompareTo(y.Key.Id)); var projectInfo1 = Assert.Single(projects); - Assert.Equal(s_hostProject1.Key, projectInfo1.ProjectKey); + Assert.Equal(s_hostProject1.Key, projectInfo1.Key); var document1 = Assert.Single(projectInfo1.Documents); Assert.Equal(s_hostDocument1.FilePath, document1.FilePath); } @@ -169,11 +169,11 @@ await projectManager.UpdateAsync(static updater => // Sort the non-misc files projects by project key. var projects = driver .GetLatestProjectInfo() - .WhereAsArray(x => x.ProjectKey != MiscFilesHostProject.Instance.Key) - .Sort((x, y) => x.ProjectKey.Id.CompareTo(y.ProjectKey.Id)); + .WhereAsArray(x => x.Key != MiscFilesHostProject.Instance.Key) + .Sort((x, y) => x.Key.Id.CompareTo(y.Key.Id)); var projectInfo1 = Assert.Single(projects); - Assert.Equal(s_hostProject1.Key, projectInfo1.ProjectKey); + Assert.Equal(s_hostProject1.Key, projectInfo1.Key); await projectManager.UpdateAsync(static updater => { @@ -183,7 +183,7 @@ await projectManager.UpdateAsync(static updater => await testAccessor.WaitUntilCurrentBatchCompletesAsync(); var miscFilesProject = Assert.Single(driver.GetLatestProjectInfo()); - Assert.Equal(MiscFilesHostProject.Instance.Key, miscFilesProject.ProjectKey); + Assert.Equal(MiscFilesHostProject.Instance.Key, miscFilesProject.Key); } [UIFact] @@ -213,7 +213,7 @@ await projectManager.UpdateAsync(static updater => Assert.Empty(listener.Removes); var projectInfo1 = Assert.Single(listener.Updates); - Assert.Equal(s_hostProject1.Key, projectInfo1.ProjectKey); + Assert.Equal(s_hostProject1.Key, projectInfo1.Key); var document1 = Assert.Single(projectInfo1.Documents); Assert.Equal(s_hostDocument1.FilePath, document1.FilePath); } diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectReaders.cs b/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectReaders.cs index 91ae5629e82..0bc6abdd565 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectReaders.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectReaders.cs @@ -3,12 +3,12 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.IO; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.ProjectSystem; using Microsoft.AspNetCore.Razor.Utilities; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; using static Microsoft.AspNetCore.Razor.Language.RequiredAttributeDescriptor; using SR = Microsoft.AspNetCore.Razor.Serialization.Json.Internal.Strings; @@ -90,13 +90,24 @@ public static ProjectSnapshotHandle ReadProjectSnapshotHandleFromProperties(Json return new(projectId, configuration, rootNamespace); } - public static DocumentSnapshotHandle ReadDocumentSnapshotHandleFromProperties(JsonDataReader reader) + public static HostDocument ReadHostDocumentFromProperties(JsonDataReader reader) { - var filePath = reader.ReadNonNullString(nameof(DocumentSnapshotHandle.FilePath)); - var targetPath = reader.ReadNonNullString(nameof(DocumentSnapshotHandle.TargetPath)); - var fileKind = reader.ReadNonNullString(nameof(DocumentSnapshotHandle.FileKind)); + var filePath = reader.ReadNonNullString(nameof(HostDocument.FilePath)); + var targetPath = reader.ReadNonNullString(nameof(HostDocument.TargetPath)); + var fileKind = reader.ReadNonNullString(nameof(HostDocument.FileKind)); - return new DocumentSnapshotHandle(filePath, targetPath, fileKind); + return new HostDocument(filePath, targetPath, fileKind); + } + + public static HostProject ReadHostProjectFromProperties(JsonDataReader reader) + { + var filePath = reader.ReadNonNullString(nameof(HostProject.FilePath)); + var intermediateOutputPath = reader.ReadNonNullString(nameof(HostProject.IntermediateOutputPath)); + var configuration = reader.ReadNonNullObject(nameof(HostProject.Configuration), ReadConfigurationFromProperties); + var rootNamespace = reader.ReadStringOrNull(nameof(HostProject.RootNamespace)); + var displayName = reader.ReadNonNullString(nameof(HostProject.DisplayName)); + + return new HostProject(filePath, intermediateOutputPath, configuration, rootNamespace, displayName); } public static ProjectWorkspaceState ReadProjectWorkspaceStateFromProperties(JsonDataReader reader) @@ -344,16 +355,11 @@ public static RazorProjectInfo ReadProjectInfoFromProperties(JsonDataReader read throw new RazorProjectInfoSerializationException(SR.Unsupported_razor_project_info_version_encountered); } - var projectKeyId = reader.ReadNonNullString(nameof(RazorProjectInfo.ProjectKey)); - var filePath = reader.ReadNonNullString(nameof(RazorProjectInfo.FilePath)); - var configuration = reader.ReadObject(nameof(RazorProjectInfo.Configuration), ReadConfigurationFromProperties) ?? RazorConfiguration.Default; - var projectWorkspaceState = reader.ReadObject(nameof(RazorProjectInfo.ProjectWorkspaceState), ReadProjectWorkspaceStateFromProperties) ?? ProjectWorkspaceState.Default; - var rootNamespace = reader.ReadString(nameof(RazorProjectInfo.RootNamespace)); - var documents = reader.ReadImmutableArray(nameof(RazorProjectInfo.Documents), static r => r.ReadNonNullObject(ReadDocumentSnapshotHandleFromProperties)); - - var displayName = Path.GetFileNameWithoutExtension(filePath); + var hostProject = reader.ReadNonNullObject(nameof(RazorProjectInfo.HostProject), ReadHostProjectFromProperties); + var projectWorkspaceState = reader.ReadNonNullObject(nameof(RazorProjectInfo.ProjectWorkspaceState), ReadProjectWorkspaceStateFromProperties); + var documents = reader.ReadImmutableArray(nameof(RazorProjectInfo.Documents), static r => r.ReadNonNullObject(ReadHostDocumentFromProperties)); - return new RazorProjectInfo(new ProjectKey(projectKeyId), filePath, configuration, rootNamespace, displayName, projectWorkspaceState, documents); + return new RazorProjectInfo(hostProject, projectWorkspaceState, documents); } public static Checksum ReadChecksum(JsonDataReader reader) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectWriters.cs b/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectWriters.cs index aad91449d2b..9ea1192a123 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectWriters.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectWriters.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.ProjectSystem; using Microsoft.AspNetCore.Razor.Utilities; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; namespace Microsoft.AspNetCore.Razor.Serialization.Json; @@ -68,16 +69,28 @@ public static void WriteProperties(JsonDataWriter writer, ProjectSnapshotHandle writer.WriteIfNotNull(nameof(value.RootNamespace), value.RootNamespace); } - public static void Write(JsonDataWriter writer, DocumentSnapshotHandle? value) + public static void Write(JsonDataWriter writer, HostDocument? value) => writer.WriteObject(value, WriteProperties); - public static void WriteProperties(JsonDataWriter writer, DocumentSnapshotHandle value) + public static void WriteProperties(JsonDataWriter writer, HostDocument value) { writer.Write(nameof(value.FilePath), value.FilePath); writer.Write(nameof(value.TargetPath), value.TargetPath); writer.Write(nameof(value.FileKind), value.FileKind); } + public static void Write(JsonDataWriter writer, HostProject? value) + => writer.WriteObject(value, WriteProperties); + + public static void WriteProperties(JsonDataWriter writer, HostProject value) + { + writer.Write(nameof(value.FilePath), value.FilePath); + writer.Write(nameof(value.IntermediateOutputPath), value.IntermediateOutputPath); + writer.WriteObject(nameof(value.Configuration), value.Configuration, WriteProperties); + writer.WriteIfNotNull(nameof(value.RootNamespace), value.RootNamespace); + writer.Write(nameof(value.DisplayName), value.DisplayName); + } + public static void Write(JsonDataWriter writer, ProjectWorkspaceState? value) => writer.WriteObject(value, WriteProperties); @@ -239,11 +252,8 @@ public static void Write(JsonDataWriter writer, RazorProjectInfo value) public static void WriteProperties(JsonDataWriter writer, RazorProjectInfo value) { writer.Write(WellKnownPropertyNames.Version, SerializationFormat.Version); - writer.Write(nameof(value.ProjectKey), value.ProjectKey.Id); - writer.Write(nameof(value.FilePath), value.FilePath); - writer.WriteObject(nameof(value.Configuration), value.Configuration, WriteProperties); + writer.WriteObject(nameof(value.HostProject), value.HostProject, WriteProperties); writer.WriteObject(nameof(value.ProjectWorkspaceState), value.ProjectWorkspaceState, WriteProperties); - writer.Write(nameof(value.RootNamespace), value.RootNamespace); writer.WriteArray(nameof(value.Documents), value.Documents, Write); } diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/SerializationFormat.cs b/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/SerializationFormat.cs index c6a8db23f4a..f0941e5b38b 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/SerializationFormat.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/SerializationFormat.cs @@ -9,5 +9,5 @@ internal static class SerializationFormat // or any of the types that compose it changes. This includes: RazorConfiguration, // ProjectWorkspaceState, TagHelperDescriptor, and DocumentSnapshotHandle. // NOTE: If this version is changed, a coordinated insertion is required between Roslyn and Razor for the C# extension. - public const int Version = 6; + public const int Version = 7; }