From 0ef223669784022cb1a33853c417dcf2cc7a2d5e Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 5 Nov 2024 09:13:06 +0100 Subject: [PATCH] Revert "Update project configuration from Roslyn info (#11092)" This reverts commit a37ee126f87e6f5f0a9c0e51f085f8f097875315, reversing changes made to ad744393845dd7c55b94ad6097041c6658d8c877. --- .../src/CSharp/ParseOptionsExtensions.cs | 13 -- .../src/Language/RazorConfiguration.cs | 14 +-- .../RazorSourceGenerator.RazorProviders.cs | 3 +- .../RazorLanguageServerBenchmarkBase.cs | 2 +- .../Kendo.Mvc.Examples.project.razor.json | 7 +- .../Resources/project.razor.json | 7 +- .../ProjectSystem/RazorProjectService.cs | 16 ++- .../ProjectSystem/ProjectWorkspaceState.cs | 24 +++- .../ProjectWorkspaceStateFormatter.cs | 9 +- .../Formatters/RazorConfigurationFormatter.cs | 21 +--- .../MessagePack/SerializationFormat.cs | 2 +- .../Utilities/RazorProjectInfoFactory.cs | 8 +- .../Utilities/StreamExtensions.NetCore.cs | 2 +- .../ProjectSystem/IProjectSnapshot.cs | 2 + .../ProjectSystem/ProjectSnapshot.cs | 2 + .../ProjectSystem/ProjectState.cs | 14 ++- .../ProjectSystem/RemoteProjectSnapshot.cs | 3 - ....cs => IProjectWorkspaceStateGenerator.cs} | 2 +- .../Host/ProjectSnapshotManagerProxy.cs | 2 +- ...ctWorkspaceStateGenerator.TestAccessor.cs} | 4 +- ...jectWorkspaceStateGenerator.UpdateItem.cs} | 2 +- ...r.cs => ProjectWorkspaceStateGenerator.cs} | 44 +++---- ...tionUpdatesProjectSnapshotChangeTrigger.cs | 10 +- ...aceProjectStateChangeDetector.Comparer.cs} | 7 +- ...=> WorkspaceProjectStateChangeDetector.cs} | 20 +-- .../EphemeralProjectSnapshot.cs | 3 + .../Parsing/VisualStudioRazorParser.cs | 2 +- .../OpenDocumentGeneratorTest.cs | 21 +--- .../IProjectSnapshotManagerExtensionsTest.cs | 2 +- .../RazorProjectServiceTest.cs | 3 +- .../Serialization/SerializerValidationTest.cs | 3 + .../SerializationTest.cs | 4 +- .../StreamExtensionTests.NetCore.cs | 2 +- .../ProjectSystem/TestProjectSnapshot.cs | 1 + .../TestMocks.cs | 2 + .../ProjectStateGeneratedOutputTest.cs | 25 ++++ .../ProjectSystem/ProjectStateTest.cs | 42 ++++++- ... => TestProjectWorkspaceStateGenerator.cs} | 2 +- ...orkspaceProjectStateChangeDetectorTest.cs} | 118 +++++++++--------- ... => ProjectWorkspaceStateGeneratorTest.cs} | 58 ++++----- ...UpdatesProjectSnapshotChangeTriggerTest.cs | 28 ++--- .../ObjectReaders.cs | 7 +- .../ObjectWriters.cs | 3 +- .../SerializationFormat.cs | 2 +- 44 files changed, 308 insertions(+), 260 deletions(-) delete mode 100644 src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ParseOptionsExtensions.cs rename src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/{IRoslynProjectChangeProcessor.cs => IProjectWorkspaceStateGenerator.cs} (88%) rename src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/{RoslynProjectChangeProcessor.TestAccessor.cs => ProjectWorkspaceStateGenerator.TestAccessor.cs} (93%) rename src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/{RoslynProjectChangeProcessor.UpdateItem.cs => ProjectWorkspaceStateGenerator.UpdateItem.cs} (95%) rename src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/{RoslynProjectChangeProcessor.cs => ProjectWorkspaceStateGenerator.cs} (84%) rename src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/{RoslynProjectChangeDetector.Comparer.cs => WorkspaceProjectStateChangeDetector.Comparer.cs} (76%) rename src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/{RoslynProjectChangeDetector.cs => WorkspaceProjectStateChangeDetector.cs} (96%) rename src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/{TestRoslynProjectChangeProcessor.cs => TestProjectWorkspaceStateGenerator.cs} (95%) rename src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/{RoslynProjectChangeDetectorTest.cs => WorkspaceProjectStateChangeDetectorTest.cs} (88%) rename src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/{RoslynProjectChangeProcessorTest.cs => ProjectWorkspaceStateGeneratorTest.cs} (71%) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ParseOptionsExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ParseOptionsExtensions.cs deleted file mode 100644 index 1a8df97be5f..00000000000 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ParseOptionsExtensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Microsoft.CodeAnalysis.Razor.Compiler.CSharp; - -internal static class ParseOptionsExtensions -{ - public static bool UseRoslynTokenizer(this ParseOptions parseOptions) - => parseOptions.Features.TryGetValue("use-roslyn-tokenizer", out var useRoslynTokenizerValue) - && string.Equals(useRoslynTokenizerValue, "true", StringComparison.OrdinalIgnoreCase); -} diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorConfiguration.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorConfiguration.cs index e49485e47f7..83cd64698d1 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorConfiguration.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorConfiguration.cs @@ -3,7 +3,6 @@ using System.Collections.Immutable; using System.Linq; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Razor.Language; @@ -14,14 +13,15 @@ public sealed record class RazorConfiguration( ImmutableArray Extensions, bool UseConsolidatedMvcViews = true, bool SuppressAddComponentParameter = false, - LanguageServerFlags? LanguageServerFlags = null, - bool UseRoslynTokenizer = false, - LanguageVersion CSharpLanguageVersion = LanguageVersion.Default) + LanguageServerFlags? LanguageServerFlags = null) { public static readonly RazorConfiguration Default = new( RazorLanguageVersion.Latest, ConfigurationName: "unnamed", - Extensions: []); + Extensions: [], + UseConsolidatedMvcViews: true, + SuppressAddComponentParameter: false, + LanguageServerFlags: null); public bool Equals(RazorConfiguration? other) => other is not null && @@ -30,8 +30,6 @@ public bool Equals(RazorConfiguration? other) SuppressAddComponentParameter == other.SuppressAddComponentParameter && LanguageServerFlags == other.LanguageServerFlags && UseConsolidatedMvcViews == other.UseConsolidatedMvcViews && - UseRoslynTokenizer == other.UseRoslynTokenizer && - CSharpLanguageVersion == other.CSharpLanguageVersion && Extensions.SequenceEqual(other.Extensions); public override int GetHashCode() @@ -43,8 +41,6 @@ public override int GetHashCode() hash.Add(SuppressAddComponentParameter); hash.Add(UseConsolidatedMvcViews); hash.Add(LanguageServerFlags); - hash.Add(UseRoslynTokenizer); - hash.Add(CSharpLanguageVersion); return hash; } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs index 9cf4e699a3b..09a02e57bd5 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.RazorProviders.cs @@ -56,7 +56,8 @@ public partial class RazorSourceGenerator var razorConfiguration = new RazorConfiguration(razorLanguageVersion, configurationName ?? "default", Extensions: [], UseConsolidatedMvcViews: true, SuppressAddComponentParameter: !isComponentParameterSupported); // We use the new tokenizer only when requested for now. - var useRoslynTokenizer = parseOptions.UseRoslynTokenizer(); + var useRoslynTokenizer = parseOptions.Features.TryGetValue("use-roslyn-tokenizer", out var useRoslynTokenizerValue) + && string.Equals(useRoslynTokenizerValue, "true", StringComparison.OrdinalIgnoreCase); var razorSourceGenerationOptions = new RazorSourceGenerationOptions() { diff --git a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs index 5af8926bdc3..f4c9db8e9af 100644 --- a/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs +++ b/src/Razor/benchmarks/Microsoft.AspNetCore.Razor.Microbenchmarks/LanguageServer/RazorLanguageServerBenchmarkBase.cs @@ -75,7 +75,7 @@ await projectManager.UpdateAsync( { updater.ProjectAdded(hostProject); var tagHelpers = CommonResources.LegacyTagHelpers; - var projectWorkspaceState = ProjectWorkspaceState.Create(tagHelpers); + var projectWorkspaceState = ProjectWorkspaceState.Create(tagHelpers, CodeAnalysis.CSharp.LanguageVersion.CSharp11); updater.ProjectWorkspaceStateChanged(hostProject.Key, projectWorkspaceState); updater.DocumentAdded(hostProject.Key, hostDocument, textLoader); }, 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 1bf7c4e050d..9104b499f89 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,12 +1,10 @@ { - "__Version": 7, + "__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", - "UseRoslynTokenizer": true, - "CSharpLanguageVersion": 1100, "Extensions": [ "MVC-3.0" ] @@ -159528,7 +159526,8 @@ "Runtime.Name": "Components.None" } } - ] + ], + "CSharpLanguageVersion": 1100 }, "RootNamespace": "Kendo.Mvc.Examples", "Documents": [ 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 e00a7130e2f..d1525d22f3a 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,12 +1,10 @@ { - "__Version": 7, + "__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", - "UseRoslynTokenizer": true, - "CSharpLanguageVersion": 800, "Extensions": [ "MVC-3.0" ] }, "ProjectWorkspaceState": { @@ -16127,7 +16125,8 @@ "Runtime.Name": "Components.None" } } - ] + ], + "CSharpLanguageVersion": 800 }, "RootNamespace": "blazorserver", "Documents": [ 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 52dc25f7a83..25f498d7213 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs @@ -366,16 +366,17 @@ private Task AddOrUpdateProjectCoreAsync( if (!projectWorkspaceState.Equals(ProjectWorkspaceState.Default)) { - _logger.LogInformation($"Updating project '{project.Key}' TagHelpers ({projectWorkspaceState.TagHelpers.Length})."); + _logger.LogInformation($"Updating project '{project.Key}' TagHelpers ({projectWorkspaceState.TagHelpers.Length}) and C# Language Version ({projectWorkspaceState.CSharpLanguageVersion})."); } updater.ProjectWorkspaceStateChanged(project.Key, projectWorkspaceState); + var currentConfiguration = project.Configuration; var currentRootNamespace = project.RootNamespace; - if (project.Configuration == configuration && + if (currentConfiguration.ConfigurationName == configuration?.ConfigurationName && currentRootNamespace == rootNamespace) { - _logger.LogTrace($"Skipping configuration update for '{project.Key}' as the configuration and root namespace are unchanged."); + _logger.LogTrace($"Updating project '{project.Key}'. The project is already using configuration '{configuration.ConfigurationName}' and root namespace '{rootNamespace}'."); return; } @@ -384,9 +385,14 @@ private Task AddOrUpdateProjectCoreAsync( configuration = FallbackRazorConfiguration.Latest; _logger.LogInformation($"Updating project '{project.Key}' to use the latest configuration ('{configuration.ConfigurationName}')'."); } - else + else if (currentConfiguration.ConfigurationName != configuration.ConfigurationName) { - _logger.LogInformation($"Updating project '{project.Key}' to Razor configuration '{configuration.ConfigurationName}', namespace '{rootNamespace}', with language version '{configuration.LanguageVersion}'."); + _logger.LogInformation($"Updating project '{project.Key}' to Razor configuration '{configuration.ConfigurationName}' with language version '{configuration.LanguageVersion}'."); + } + + if (currentRootNamespace != rootNamespace) + { + _logger.LogInformation($"Updating project '{project.Key}''s root namespace to '{rootNamespace}'."); } var hostProject = new HostProject(project.FilePath, project.IntermediateOutputPath, configuration, rootNamespace, displayName); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/ProjectWorkspaceState.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/ProjectWorkspaceState.cs index f8eb552ce80..b4c2c9548fb 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/ProjectWorkspaceState.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/ProjectSystem/ProjectWorkspaceState.cs @@ -5,40 +5,52 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.AspNetCore.Razor.Language; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Razor.ProjectSystem; internal sealed class ProjectWorkspaceState : IEquatable { - public static readonly ProjectWorkspaceState Default = new(ImmutableArray.Empty); + public static readonly ProjectWorkspaceState Default = new(ImmutableArray.Empty, LanguageVersion.Default); public ImmutableArray TagHelpers { get; } + public LanguageVersion CSharpLanguageVersion { get; } private ProjectWorkspaceState( - ImmutableArray tagHelpers) + ImmutableArray tagHelpers, + LanguageVersion csharpLanguageVersion) { TagHelpers = tagHelpers; + CSharpLanguageVersion = csharpLanguageVersion; } public static ProjectWorkspaceState Create( - ImmutableArray tagHelpers) - => tagHelpers.IsEmpty + ImmutableArray tagHelpers, + LanguageVersion csharpLanguageVersion = LanguageVersion.Default) + => tagHelpers.IsEmpty && csharpLanguageVersion == LanguageVersion.Default ? Default - : new(tagHelpers); + : new(tagHelpers, csharpLanguageVersion); + + public static ProjectWorkspaceState Create(LanguageVersion csharpLanguageVersion) + => csharpLanguageVersion == LanguageVersion.Default + ? Default + : new(ImmutableArray.Empty, csharpLanguageVersion); public override bool Equals(object? obj) => obj is ProjectWorkspaceState other && Equals(other); public bool Equals(ProjectWorkspaceState? other) => other is not null && - TagHelpers.SequenceEqual(other.TagHelpers); + TagHelpers.SequenceEqual(other.TagHelpers) && + CSharpLanguageVersion == other.CSharpLanguageVersion; public override int GetHashCode() { var hash = HashCodeCombiner.Start(); hash.Add(TagHelpers); + hash.Add(CSharpLanguageVersion); return hash.CombinedHash; } diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/ProjectWorkspaceStateFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/ProjectWorkspaceStateFormatter.cs index 156c443958e..60a9894b1da 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/ProjectWorkspaceStateFormatter.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/ProjectWorkspaceStateFormatter.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Razor.ProjectSystem; using Microsoft.AspNetCore.Razor.Serialization.MessagePack.Formatters.TagHelpers; using Microsoft.AspNetCore.Razor.Utilities; +using Microsoft.CodeAnalysis.CSharp; namespace Microsoft.AspNetCore.Razor.Serialization.MessagePack.Formatters; @@ -21,7 +22,7 @@ private ProjectWorkspaceStateFormatter() public override ProjectWorkspaceState Deserialize(ref MessagePackReader reader, SerializerCachingOptions options) { - reader.ReadArrayHeaderAndVerify(2); + reader.ReadArrayHeaderAndVerify(3); var checksums = reader.Deserialize>(options); @@ -46,17 +47,19 @@ public override ProjectWorkspaceState Deserialize(ref MessagePackReader reader, } var tagHelpers = builder.DrainToImmutable(); + var csharpLanguageVersion = (LanguageVersion)reader.ReadInt32(); - return ProjectWorkspaceState.Create(tagHelpers); + return ProjectWorkspaceState.Create(tagHelpers, csharpLanguageVersion); } public override void Serialize(ref MessagePackWriter writer, ProjectWorkspaceState value, SerializerCachingOptions options) { - writer.WriteArrayHeader(2); + writer.WriteArrayHeader(3); var checksums = value.TagHelpers.SelectAsArray(x => x.Checksum); writer.Serialize(checksums, options); writer.Serialize(value.TagHelpers, options); + writer.Write((int)value.CSharpLanguageVersion); } } diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/RazorConfigurationFormatter.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/RazorConfigurationFormatter.cs index 4bb6b57f8a1..6232c1fabdb 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/RazorConfigurationFormatter.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Serialization/MessagePack/Formatters/RazorConfigurationFormatter.cs @@ -4,7 +4,6 @@ using MessagePack; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.PooledObjects; -using Microsoft.CodeAnalysis.CSharp; namespace Microsoft.AspNetCore.Razor.Serialization.MessagePack.Formatters; @@ -12,10 +11,6 @@ internal sealed class RazorConfigurationFormatter : ValueFormatter Instance = new RazorConfigurationFormatter(); - // The count of properties in RazorConfiguration that are serialized. The number of Extensions will be added - // to this, for the final serialized value count. - private const int SerializedPropertyCount = 6; - private RazorConfigurationFormatter() { } @@ -29,10 +24,8 @@ public override RazorConfiguration Deserialize(ref MessagePackReader reader, Ser var languageVersionText = CachedStringFormatter.Instance.Deserialize(ref reader, options) ?? string.Empty; var suppressAddComponentParameter = reader.ReadBoolean(); var useConsolidatedMvcViews = reader.ReadBoolean(); - var useRoslynTokenizer = reader.ReadBoolean(); - var csharpLanguageVersion = (LanguageVersion)reader.ReadInt32(); - count -= SerializedPropertyCount; + count -= 4; using var builder = new PooledArrayBuilder(); @@ -53,16 +46,14 @@ public override RazorConfiguration Deserialize(ref MessagePackReader reader, Ser configurationName, extensions, UseConsolidatedMvcViews: useConsolidatedMvcViews, - SuppressAddComponentParameter: suppressAddComponentParameter, - UseRoslynTokenizer: useRoslynTokenizer, - CSharpLanguageVersion: csharpLanguageVersion); + SuppressAddComponentParameter: suppressAddComponentParameter); } public override void Serialize(ref MessagePackWriter writer, RazorConfiguration value, SerializerCachingOptions options) { - // Write SerializedPropertyCount values + 1 value per extension. + // Write 4 values + 1 value per extension. var extensions = value.Extensions; - var count = extensions.Length + SerializedPropertyCount; + var count = extensions.Length + 4; writer.WriteArrayHeader(count); @@ -79,10 +70,8 @@ public override void Serialize(ref MessagePackWriter writer, RazorConfiguration writer.Write(value.SuppressAddComponentParameter); writer.Write(value.UseConsolidatedMvcViews); - writer.Write(value.UseRoslynTokenizer); - writer.Write((int)value.CSharpLanguageVersion); - count -= SerializedPropertyCount; + count -= 4; for (var i = 0; i < count; i++) { 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 bb7d6524f7b..0fc5c52667c 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 = 7; + public const int Version = 6; } 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 a5649c9405c..7eeb86d7ac1 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/RazorProjectInfoFactory.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/RazorProjectInfoFactory.cs @@ -18,7 +18,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.Compiler.CSharp; -using Microsoft.NET.Sdk.Razor.SourceGenerators; namespace Microsoft.AspNetCore.Razor.Utilities; @@ -56,9 +55,7 @@ static RazorProjectInfoFactory() return null; } - var csharpParseOptions = project.ParseOptions as CSharpParseOptions ?? CSharpParseOptions.Default; - var csharpLanguageVersion = csharpParseOptions.LanguageVersion; - var useRoslynTokenizer = csharpParseOptions.UseRoslynTokenizer(); + var csharpLanguageVersion = (project.ParseOptions as CSharpParseOptions)?.LanguageVersion ?? LanguageVersion.Default; var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); if (compilation is null) @@ -79,7 +76,6 @@ static RazorProjectInfoFactory() builder.SetCSharpLanguageVersion(csharpLanguageVersion); builder.SetSupportLocalizedComponentNames(); // ProjectState in MS.CA.Razor.Workspaces does this, so I'm doing it too! - builder.Features.Add(new ConfigureRazorParserOptions(useRoslynTokenizer, csharpParseOptions)); }; var engineFactory = ProjectEngineFactories.DefaultProvider.GetFactory(configuration); @@ -91,7 +87,7 @@ static RazorProjectInfoFactory() var tagHelpers = await project.GetTagHelpersAsync(engine, NoOpTelemetryReporter.Instance, cancellationToken).ConfigureAwait(false); - var projectWorkspaceState = ProjectWorkspaceState.Create(tagHelpers); + var projectWorkspaceState = ProjectWorkspaceState.Create(tagHelpers, csharpLanguageVersion); return new RazorProjectInfo( projectKey: new ProjectKey(intermediateOutputPath), diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/StreamExtensions.NetCore.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/StreamExtensions.NetCore.cs index 45562ebbd82..81abc7a2a16 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/StreamExtensions.NetCore.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/StreamExtensions.NetCore.cs @@ -1,6 +1,7 @@ // 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.ProjectSystem; using System; using System.Buffers; using System.Diagnostics; @@ -8,7 +9,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.ProjectSystem; namespace Microsoft.AspNetCore.Razor.Utilities; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/IProjectSnapshot.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/IProjectSnapshot.cs index ea4d2ea12de..3494b7c108d 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/IProjectSnapshot.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/IProjectSnapshot.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.ProjectSystem; +using Microsoft.CodeAnalysis.CSharp; namespace Microsoft.CodeAnalysis.Razor.ProjectSystem; @@ -31,6 +32,7 @@ internal interface IProjectSnapshot string? RootNamespace { get; } string DisplayName { get; } VersionStamp Version { get; } + LanguageVersion CSharpLanguageVersion { get; } ProjectWorkspaceState ProjectWorkspaceState { get; } RazorProjectEngine GetProjectEngine(); diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectSnapshot.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectSnapshot.cs index e2fe87b71a8..8f844402e2e 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectSnapshot.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectSnapshot.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.AspNetCore.Razor.ProjectSystem; using Microsoft.AspNetCore.Razor.Utilities; +using Microsoft.CodeAnalysis.CSharp; namespace Microsoft.CodeAnalysis.Razor.ProjectSystem; @@ -31,6 +32,7 @@ internal sealed class ProjectSnapshot(ProjectState state) : IProjectSnapshot public string? RootNamespace => _state.HostProject.RootNamespace; public string DisplayName => _state.HostProject.DisplayName; public VersionStamp Version => _state.Version; + public LanguageVersion CSharpLanguageVersion => _state.CSharpLanguageVersion; public ProjectWorkspaceState ProjectWorkspaceState => _state.ProjectWorkspaceState; public int DocumentCount => _state.Documents.Count; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectState.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectState.cs index 1fe54321749..9e753414420 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectState.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectState.cs @@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Razor.Utilities; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; -using Microsoft.NET.Sdk.Razor.SourceGenerators; namespace Microsoft.CodeAnalysis.Razor.ProjectSystem; @@ -152,6 +151,14 @@ private ProjectState( { ProjectWorkspaceStateVersion = Version; } + + if ((difference & ClearProjectWorkspaceStateVersionMask) != 0 && + CSharpLanguageVersion != older.CSharpLanguageVersion) + { + // C# language version changed. This impacts the ProjectEngine, reset it. + _projectEngine = null; + ConfigurationVersion = Version; + } } // Internal set for testing. @@ -166,6 +173,8 @@ private ProjectState( public ImmutableArray TagHelpers => ProjectWorkspaceState.TagHelpers; + public LanguageVersion CSharpLanguageVersion => ProjectWorkspaceState.CSharpLanguageVersion; + /// /// Gets the version of this project, INCLUDING content changes. The is /// incremented for each new instance created. @@ -198,9 +207,8 @@ RazorProjectEngine CreateProjectEngine() return _projectEngineFactoryProvider.Create(configuration, rootDirectoryPath, builder => { builder.SetRootNamespace(HostProject.RootNamespace); - builder.SetCSharpLanguageVersion(configuration.CSharpLanguageVersion); + builder.SetCSharpLanguageVersion(CSharpLanguageVersion); builder.SetSupportLocalizedComponentNames(); - builder.Features.Add(new ConfigureRazorParserOptions(useRoslynTokenizer: configuration.UseRoslynTokenizer, CSharpParseOptions.Default)); }); } } diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/RemoteProjectSnapshot.cs b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/RemoteProjectSnapshot.cs index 1720c1a09be..e970f1067dc 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/RemoteProjectSnapshot.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/ProjectSystem/RemoteProjectSnapshot.cs @@ -19,7 +19,6 @@ using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.Compiler.CSharp; using Microsoft.CodeAnalysis.Razor.ProjectSystem; -using Microsoft.NET.Sdk.Razor.SourceGenerators; using Microsoft.VisualStudio.Threading; namespace Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; @@ -52,7 +51,6 @@ public RemoteProjectSnapshot(Project project, RemoteSolutionSnapshot solutionSna _lazyProjectEngine = new AsyncLazy(async () => { var configuration = await _lazyConfiguration.GetValueAsync(); - var csharpParseOptions = project.ParseOptions as CSharpParseOptions ?? CSharpParseOptions.Default; return ProjectEngineFactories.DefaultProvider.Create( configuration, rootDirectoryPath: Path.GetDirectoryName(FilePath).AssumeNotNull(), @@ -61,7 +59,6 @@ public RemoteProjectSnapshot(Project project, RemoteSolutionSnapshot solutionSna builder.SetRootNamespace(RootNamespace); builder.SetCSharpLanguageVersion(CSharpLanguageVersion); builder.SetSupportLocalizedComponentNames(); - builder.Features.Add(new ConfigureRazorParserOptions(useRoslynTokenizer: csharpParseOptions.UseRoslynTokenizer(), csharpParseOptions)); }); }, joinableTaskFactory: null); diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/IRoslynProjectChangeProcessor.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/IProjectWorkspaceStateGenerator.cs similarity index 88% rename from src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/IRoslynProjectChangeProcessor.cs rename to src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/IProjectWorkspaceStateGenerator.cs index b94fceb6597..8bf2287cc10 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/IRoslynProjectChangeProcessor.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/IProjectWorkspaceStateGenerator.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualStudio.Razor; -internal interface IRoslynProjectChangeProcessor +internal interface IProjectWorkspaceStateGenerator { void EnqueueUpdate(Project? workspaceProject, IProjectSnapshot projectSnapshot); diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LiveShare/Host/ProjectSnapshotManagerProxy.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LiveShare/Host/ProjectSnapshotManagerProxy.cs index b38129fa399..7ae74da1e67 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LiveShare/Host/ProjectSnapshotManagerProxy.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/LiveShare/Host/ProjectSnapshotManagerProxy.cs @@ -101,7 +101,7 @@ internal async Task CalculateUpdatedStateAsync } var tagHelpers = await project.GetTagHelpersAsync(CancellationToken.None).ConfigureAwait(false); - var projectWorkspaceState = ProjectWorkspaceState.Create(tagHelpers); + var projectWorkspaceState = ProjectWorkspaceState.Create(tagHelpers, project.CSharpLanguageVersion); var projectFilePath = _session.ConvertLocalPathToSharedUri(project.FilePath); var intermediateOutputPath = _session.ConvertLocalPathToSharedUri(project.IntermediateOutputPath); var projectHandleProxy = new ProjectSnapshotHandleProxy(projectFilePath, intermediateOutputPath, project.Configuration, project.RootNamespace, projectWorkspaceState); diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeProcessor.TestAccessor.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectWorkspaceStateGenerator.TestAccessor.cs similarity index 93% rename from src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeProcessor.TestAccessor.cs rename to src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectWorkspaceStateGenerator.TestAccessor.cs index c57b68d32a9..089fb498809 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeProcessor.TestAccessor.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectWorkspaceStateGenerator.TestAccessor.cs @@ -8,11 +8,11 @@ namespace Microsoft.VisualStudio.Razor; -internal sealed partial class RoslynProjectChangeProcessor +internal sealed partial class ProjectWorkspaceStateGenerator { internal TestAccessor GetTestAccessor() => new(this); - internal sealed class TestAccessor(RoslynProjectChangeProcessor instance) + internal sealed class TestAccessor(ProjectWorkspaceStateGenerator instance) { public interface IUpdateItem { diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeProcessor.UpdateItem.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectWorkspaceStateGenerator.UpdateItem.cs similarity index 95% rename from src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeProcessor.UpdateItem.cs rename to src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectWorkspaceStateGenerator.UpdateItem.cs index 10e1afbeb49..63ff4cc45e9 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeProcessor.UpdateItem.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectWorkspaceStateGenerator.UpdateItem.cs @@ -7,7 +7,7 @@ namespace Microsoft.VisualStudio.Razor; -internal sealed partial class RoslynProjectChangeProcessor +internal sealed partial class ProjectWorkspaceStateGenerator { private sealed class UpdateItem { diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeProcessor.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectWorkspaceStateGenerator.cs similarity index 84% rename from src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeProcessor.cs rename to src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectWorkspaceStateGenerator.cs index 4d8f9a62f47..e6b29390c64 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeProcessor.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/ProjectWorkspaceStateGenerator.cs @@ -6,34 +6,32 @@ using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.AspNetCore.Razor.ProjectSystem; using Microsoft.AspNetCore.Razor.Telemetry; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Razor.Compiler.CSharp; using Microsoft.CodeAnalysis.Razor.Logging; using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Razor.Workspaces; namespace Microsoft.VisualStudio.Razor; -[Export(typeof(IRoslynProjectChangeProcessor))] +[Export(typeof(IProjectWorkspaceStateGenerator))] [method: ImportingConstructor] -internal sealed partial class RoslynProjectChangeProcessor( +internal sealed partial class ProjectWorkspaceStateGenerator( IProjectSnapshotManager projectManager, ITagHelperResolver tagHelperResolver, ILoggerFactory loggerFactory, ITelemetryReporter telemetryReporter) - : IRoslynProjectChangeProcessor, IDisposable + : IProjectWorkspaceStateGenerator, IDisposable { // SemaphoreSlim is banned. See https://github.com/dotnet/razor/issues/10390 for more info. #pragma warning disable RS0030 // Do not use banned APIs private readonly IProjectSnapshotManager _projectManager = projectManager; private readonly ITagHelperResolver _tagHelperResolver = tagHelperResolver; - private readonly ILogger _logger = loggerFactory.GetOrCreateLogger(); + private readonly ILogger _logger = loggerFactory.GetOrCreateLogger(); private readonly ITelemetryReporter _telemetryReporter = telemetryReporter; private readonly SemaphoreSlim _semaphore = new(initialCount: 1); @@ -140,7 +138,9 @@ private async Task UpdateWorkspaceStateAsync(Project? workspaceProject, IProject return; } - if (await GetProjectWorkspaceStateAndConfigurationAsync(workspaceProject, projectSnapshot, cancellationToken) is not var (workspaceState, configuration)) + var workspaceState = await GetProjectWorkspaceStateAsync(workspaceProject, projectSnapshot, cancellationToken); + + if (workspaceState is null) { _logger.LogTrace($"Didn't receive {nameof(ProjectWorkspaceState)} for '{projectKey}'"); return; @@ -153,15 +153,11 @@ private async Task UpdateWorkspaceStateAsync(Project? workspaceProject, IProject _logger.LogTrace($"Received {nameof(ProjectWorkspaceState)} with {workspaceState.TagHelpers.Length} tag helper(s) for '{projectKey}'"); - // We got a possibly changed configuration, but need a host project to update. Fortunately everything will no-op if this - // is unchanged - var hostProject = new HostProject(projectSnapshot.FilePath, projectSnapshot.IntermediateOutputPath, configuration, projectSnapshot.RootNamespace, projectSnapshot.DisplayName); - await _projectManager .UpdateAsync( static (updater, state) => { - var (projectKey, workspaceState, hostProject, logger, cancellationToken) = state; + var (projectKey, workspaceState, logger, cancellationToken) = state; if (cancellationToken.IsCancellationRequested) { @@ -169,10 +165,9 @@ await _projectManager } logger.LogTrace($"Updating project with {workspaceState.TagHelpers.Length} tag helper(s) for '{projectKey}'"); - updater.ProjectConfigurationChanged(hostProject); updater.ProjectWorkspaceStateChanged(projectKey, workspaceState); }, - state: (projectKey, workspaceState, hostProject, _logger, cancellationToken), + state: (projectKey, workspaceState, _logger, cancellationToken), cancellationToken) .ConfigureAwait(false); } @@ -261,7 +256,7 @@ private void ReleaseSemaphore(ProjectKey projectKey) /// Attempts to produce a from the provide and . /// Returns if an error is encountered. /// - private async Task<(ProjectWorkspaceState, RazorConfiguration)?> GetProjectWorkspaceStateAndConfigurationAsync( + private async Task GetProjectWorkspaceStateAsync( Project? workspaceProject, IProjectSnapshot projectSnapshot, CancellationToken cancellationToken) @@ -270,7 +265,7 @@ private void ReleaseSemaphore(ProjectKey projectKey) // we just a default ProjectWorkspaceState. if (workspaceProject is null) { - return (ProjectWorkspaceState.Default, RazorConfiguration.Default); + return ProjectWorkspaceState.Default; } _logger.LogTrace($"Starting tag helper discovery for {projectSnapshot.FilePath}"); @@ -284,18 +279,9 @@ private void ReleaseSemaphore(ProjectKey projectKey) try { - var csharpParseOptions = workspaceProject.ParseOptions as CSharpParseOptions ?? CSharpParseOptions.Default; - var csharpLanguageVersion = csharpParseOptions.LanguageVersion; - var useRoslynTokenizer = csharpParseOptions.UseRoslynTokenizer(); - - var compilation = await workspaceProject.GetCompilationAsync(cancellationToken).ConfigureAwait(false); - var suppressAddComponentParameter = compilation is not null && !compilation.HasAddComponentParameter(); - var configuration = projectSnapshot.Configuration with - { - SuppressAddComponentParameter = suppressAddComponentParameter, - UseRoslynTokenizer = useRoslynTokenizer, - CSharpLanguageVersion = csharpLanguageVersion - }; + var csharpLanguageVersion = workspaceProject.ParseOptions is CSharpParseOptions csharpParseOptions + ? csharpParseOptions.LanguageVersion + : LanguageVersion.Default; using var _ = StopwatchPool.GetPooledObject(out var watch); @@ -319,7 +305,7 @@ private void ReleaseSemaphore(ProjectKey projectKey) Project: {projectSnapshot.FilePath} """); - return (ProjectWorkspaceState.Create(tagHelpers), configuration); + return ProjectWorkspaceState.Create(tagHelpers, csharpLanguageVersion); } catch (OperationCanceledException) { diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/VsSolutionUpdatesProjectSnapshotChangeTrigger.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/VsSolutionUpdatesProjectSnapshotChangeTrigger.cs index 7286defb341..c1e9de5988f 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/VsSolutionUpdatesProjectSnapshotChangeTrigger.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/VsSolutionUpdatesProjectSnapshotChangeTrigger.cs @@ -21,7 +21,7 @@ internal class VsSolutionUpdatesProjectSnapshotChangeTrigger : IRazorStartupServ { private readonly IServiceProvider _serviceProvider; private readonly IProjectSnapshotManager _projectManager; - private readonly IRoslynProjectChangeProcessor _projectChangeProcessor; + private readonly IProjectWorkspaceStateGenerator _workspaceStateGenerator; private readonly IWorkspaceProvider _workspaceProvider; private readonly JoinableTaskFactory _jtf; private readonly CancellationTokenSource _disposeTokenSource; @@ -36,13 +36,13 @@ internal class VsSolutionUpdatesProjectSnapshotChangeTrigger : IRazorStartupServ public VsSolutionUpdatesProjectSnapshotChangeTrigger( [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, IProjectSnapshotManager projectManager, - IRoslynProjectChangeProcessor projectChangeProcessor, + IProjectWorkspaceStateGenerator workspaceStateGenerator, IWorkspaceProvider workspaceProvider, JoinableTaskContext joinableTaskContext) { _serviceProvider = serviceProvider; _projectManager = projectManager; - _projectChangeProcessor = projectChangeProcessor; + _workspaceStateGenerator = workspaceStateGenerator; _workspaceProvider = workspaceProvider; _jtf = joinableTaskContext.Factory; @@ -110,7 +110,7 @@ private void ProjectManager_Changed(object sender, ProjectChangeEventArgs args) if (args.SolutionIsClosing) { // If the solution is closing, cancel all existing updates. - _projectChangeProcessor.CancelUpdates(); + _workspaceStateGenerator.CancelUpdates(); } } @@ -133,7 +133,7 @@ private async Task OnProjectBuiltAsync(IVsHierarchy projectHierarchy, Cancellati { // Trigger a tag helper update by forcing the project manager to see the workspace Project // from the current solution. - _projectChangeProcessor.EnqueueUpdate(workspaceProject, projectSnapshot); + _workspaceStateGenerator.EnqueueUpdate(workspaceProject, projectSnapshot); } } } diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeDetector.Comparer.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/WorkspaceProjectStateChangeDetector.Comparer.cs similarity index 76% rename from src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeDetector.Comparer.cs rename to src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/WorkspaceProjectStateChangeDetector.Comparer.cs index d4b9d5c64d8..a99454a01e3 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeDetector.Comparer.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/WorkspaceProjectStateChangeDetector.Comparer.cs @@ -3,11 +3,12 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.ProjectSystem; namespace Microsoft.VisualStudio.Razor; -internal partial class RoslynProjectChangeDetector +internal partial class WorkspaceProjectStateChangeDetector { private sealed class Comparer : IEqualityComparer<(Project?, IProjectSnapshot)> { @@ -22,14 +23,14 @@ public bool Equals((Project?, IProjectSnapshot) x, (Project?, IProjectSnapshot) var (_, snapshotX) = x; var (_, snapshotY) = y; - return snapshotX.Key.Equals(snapshotY.Key); + return FilePathComparer.Instance.Equals(snapshotX.Key.Id, snapshotY.Key.Id); } public int GetHashCode((Project?, IProjectSnapshot) obj) { var (_, snapshot) = obj; - return snapshot.Key.GetHashCode(); + return FilePathComparer.Instance.GetHashCode(snapshot.Key.Id); } } } diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeDetector.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/WorkspaceProjectStateChangeDetector.cs similarity index 96% rename from src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeDetector.cs rename to src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/WorkspaceProjectStateChangeDetector.cs index 33b2c551211..0d45fd4a82a 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RoslynProjectChangeDetector.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/WorkspaceProjectStateChangeDetector.cs @@ -20,11 +20,11 @@ namespace Microsoft.VisualStudio.Razor; [Export(typeof(IRazorStartupService))] -internal partial class RoslynProjectChangeDetector : IRazorStartupService, IDisposable +internal partial class WorkspaceProjectStateChangeDetector : IRazorStartupService, IDisposable { private static readonly TimeSpan s_delay = TimeSpan.FromSeconds(1); - private readonly IRoslynProjectChangeProcessor _processor; + private readonly IProjectWorkspaceStateGenerator _generator; private readonly IProjectSnapshotManager _projectManager; private readonly LanguageServerFeatureOptions _options; private readonly CodeAnalysis.Workspace _workspace; @@ -35,23 +35,23 @@ internal partial class RoslynProjectChangeDetector : IRazorStartupService, IDisp private WorkspaceChangedListener? _workspaceChangedListener; [ImportingConstructor] - public RoslynProjectChangeDetector( - IRoslynProjectChangeProcessor processor, + public WorkspaceProjectStateChangeDetector( + IProjectWorkspaceStateGenerator generator, IProjectSnapshotManager projectManager, LanguageServerFeatureOptions options, IWorkspaceProvider workspaceProvider) - : this(processor, projectManager, options, workspaceProvider, s_delay) + : this(generator, projectManager, options, workspaceProvider, s_delay) { } - public RoslynProjectChangeDetector( - IRoslynProjectChangeProcessor processor, + public WorkspaceProjectStateChangeDetector( + IProjectWorkspaceStateGenerator generator, IProjectSnapshotManager projectManager, LanguageServerFeatureOptions options, IWorkspaceProvider workspaceProvider, TimeSpan delay) { - _processor = processor; + _generator = generator; _projectManager = projectManager; _options = options; @@ -94,7 +94,7 @@ private ValueTask ProcessBatchAsync(ImmutableArray<(Project? Project, IProjectSn return default; } - _processor.EnqueueUpdate(project, projectSnapshot); + _generator.EnqueueUpdate(project, projectSnapshot); } return default; @@ -414,7 +414,7 @@ private bool TryGetProjectSnapshot(Project? project, [NotNullWhen(true)] out IPr internal TestAccessor GetTestAccessor() => new(this); - internal sealed class TestAccessor(RoslynProjectChangeDetector instance) + internal sealed class TestAccessor(WorkspaceProjectStateChangeDetector instance) { public void CancelExistingWork() { diff --git a/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/EphemeralProjectSnapshot.cs b/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/EphemeralProjectSnapshot.cs index 8257de4f3f9..48ec5599ffb 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/EphemeralProjectSnapshot.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/EphemeralProjectSnapshot.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Razor.ProjectEngineHost; using Microsoft.AspNetCore.Razor.ProjectSystem; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Razor.ProjectSystem; namespace Microsoft.VisualStudio.LegacyEditor.Razor; @@ -49,6 +50,8 @@ public EphemeralProjectSnapshot(IProjectEngineFactoryProvider projectEngineFacto public VersionStamp Version => VersionStamp.Default; + public LanguageVersion CSharpLanguageVersion => ProjectWorkspaceState.CSharpLanguageVersion; + public ValueTask> GetTagHelpersAsync(CancellationToken cancellationToken) => new(ProjectWorkspaceState.TagHelpers); public ProjectWorkspaceState ProjectWorkspaceState => ProjectWorkspaceState.Default; diff --git a/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/VisualStudioRazorParser.cs b/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/VisualStudioRazorParser.cs index 67d2ab55309..ebc69bd57fa 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/VisualStudioRazorParser.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LegacyEditor.Razor/Parsing/VisualStudioRazorParser.cs @@ -494,7 +494,7 @@ private void ConfigureProjectEngine(RazorProjectEngineBuilder builder) var projectSnapshot = _documentTracker.ProjectSnapshot; if (projectSnapshot != null) { - builder.SetCSharpLanguageVersion(projectSnapshot.Configuration.CSharpLanguageVersion); + builder.SetCSharpLanguageVersion(projectSnapshot.CSharpLanguageVersion); } builder.SetRootNamespace(projectSnapshot?.RootNamespace); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/OpenDocumentGeneratorTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/OpenDocumentGeneratorTest.cs index f56b61fddd3..7c05c89c158 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/OpenDocumentGeneratorTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/OpenDocumentGeneratorTest.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer; using Microsoft.AspNetCore.Razor.Test.Common.ProjectSystem; using Microsoft.AspNetCore.Razor.Test.Common.Workspaces; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Text; using Xunit; @@ -141,14 +142,8 @@ await projectManager.UpdateAsync(updater => updater.DocumentAdded(_hostProject1.Key, _documents[0], _documents[0].CreateEmptyTextLoader()); // Act - var changed = _hostProject1 with - { - Configuration = _hostProject1.Configuration with - { - CSharpLanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp8 - } - }; - updater.ProjectConfigurationChanged(changed); + updater.ProjectWorkspaceStateChanged(_hostProject1.Key, + ProjectWorkspaceState.Create(LanguageVersion.CSharp8)); }); // Assert @@ -171,14 +166,8 @@ await projectManager.UpdateAsync(updater => updater.DocumentOpened(_hostProject1.Key, _documents[0].FilePath, SourceText.From(string.Empty)); // Act - var changed = _hostProject1 with - { - Configuration = _hostProject1.Configuration with - { - CSharpLanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp8 - } - }; - updater.ProjectConfigurationChanged(changed); + updater.ProjectWorkspaceStateChanged(_hostProject1.Key, + ProjectWorkspaceState.Create(LanguageVersion.CSharp8)); }); // Assert diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/ProjectSystem/IProjectSnapshotManagerExtensionsTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/ProjectSystem/IProjectSnapshotManagerExtensionsTest.cs index e9402ab71b6..e8335d1056d 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/ProjectSystem/IProjectSnapshotManagerExtensionsTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/ProjectSystem/IProjectSnapshotManagerExtensionsTest.cs @@ -266,7 +266,7 @@ await projectManager.UpdateAsync(updater => private static void AssertSnapshotsEqual(IProjectSnapshot first, IProjectSnapshot second) { Assert.Equal(first.FilePath, second.FilePath); - Assert.Equal(first.Configuration, second.Configuration); + Assert.Equal(first.CSharpLanguageVersion, second.CSharpLanguageVersion); Assert.Equal(first.RootNamespace, second.RootNamespace); } } 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 46a9609ffa8..a12f848949f 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs @@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer; using Microsoft.AspNetCore.Razor.Test.Common.ProjectSystem; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Text; using Moq; @@ -65,7 +66,7 @@ await _projectManager.UpdateAsync(updater => updater.ProjectAdded(hostProject); }); - var projectWorkspaceState = ProjectWorkspaceState.Create([new TagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "TagHelper", "TagHelperAssembly").Build()]); + var projectWorkspaceState = ProjectWorkspaceState.Create(LanguageVersion.LatestMajor); // Act await _projectInfoListener.UpdatedAsync(new RazorProjectInfo( 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 97a8f86c57a..5cd9e7e4863 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,6 +3,7 @@ using System.Collections.Immutable; using System.IO; +using System.Reflection; using MessagePack; using MessagePack.Resolvers; using Microsoft.AspNetCore.Razor.Language; @@ -11,8 +12,10 @@ 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 Xunit; using Xunit.Abstractions; +using MessagePackSerializationFormat = Microsoft.AspNetCore.Razor.Serialization.MessagePack.SerializationFormat; namespace Microsoft.AspNetCore.Razor.ProjectEngineHost.Test.Serialization; 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 50ca8dd6db9..c910ea76606 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/SerializationTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/SerializationTest.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Razor.Serialization; using Microsoft.AspNetCore.Razor.Serialization.Json; using Microsoft.AspNetCore.Razor.Test.Common; +using Microsoft.CodeAnalysis.CSharp; using Newtonsoft.Json.Linq; using Xunit; using Xunit.Abstractions; @@ -25,7 +26,8 @@ public SerializationTest(ITestOutputHelper testOutput) _configuration = new(languageVersion, "Custom", [new("TestExtension")]); _projectWorkspaceState = ProjectWorkspaceState.Create( - tagHelpers: [TagHelperDescriptorBuilder.Create("Test", "TestAssembly").Build()]); + tagHelpers: [TagHelperDescriptorBuilder.Create("Test", "TestAssembly").Build()], + csharpLanguageVersion: LanguageVersion.LatestMajor); } [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 965779a4bd8..8895137d798 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 @@ -77,7 +77,7 @@ public async Task SerializeProjectInfo() .TagMatchingRuleDescriptor(rule => rule.RequireTagName("tag-name")) .Build(); - var projectWorkspaceState = ProjectWorkspaceState.Create([tagHelper]); + var projectWorkspaceState = ProjectWorkspaceState.Create([tagHelper], CodeAnalysis.CSharp.LanguageVersion.Latest); var projectInfo = new RazorProjectInfo( new ProjectKey("TestProject"), diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/ProjectSystem/TestProjectSnapshot.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/ProjectSystem/TestProjectSnapshot.cs index c094f233b34..731bf4a98dd 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/ProjectSystem/TestProjectSnapshot.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/ProjectSystem/TestProjectSnapshot.cs @@ -44,6 +44,7 @@ public static TestProjectSnapshot Create(string filePath, ProjectWorkspaceState? public string IntermediateOutputPath => RealSnapshot.IntermediateOutputPath; public string? RootNamespace => RealSnapshot.RootNamespace; public string DisplayName => RealSnapshot.DisplayName; + public LanguageVersion CSharpLanguageVersion => RealSnapshot.CSharpLanguageVersion; public ProjectWorkspaceState ProjectWorkspaceState => RealSnapshot.ProjectWorkspaceState; public VersionStamp Version => RealSnapshot.Version; diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/TestMocks.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/TestMocks.cs index cdbc6c655d3..9abf55ceff3 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/TestMocks.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/TestMocks.cs @@ -121,6 +121,8 @@ public static IProjectSnapshot CreateProjectSnapshot(HostProject hostProject, Pr { mock.SetupGet(x => x.ProjectWorkspaceState) .Returns(projectWorkspaceState); + mock.SetupGet(x => x.CSharpLanguageVersion) + .Returns(projectWorkspaceState.CSharpLanguageVersion); mock.Setup(x => x.GetTagHelpersAsync(It.IsAny())) .ReturnsAsync(projectWorkspaceState.TagHelpers); } diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/ProjectSystem/ProjectStateGeneratedOutputTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/ProjectSystem/ProjectStateGeneratedOutputTest.cs index 64dbd8b65b8..7e80b2537b7 100644 --- a/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/ProjectSystem/ProjectStateGeneratedOutputTest.cs +++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/ProjectSystem/ProjectStateGeneratedOutputTest.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Razor.ProjectSystem; using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.AspNetCore.Razor.Test.Common.Workspaces; +using Microsoft.CodeAnalysis.CSharp; using Xunit; using Xunit.Abstractions; @@ -183,6 +184,30 @@ public async Task ProjectWorkspaceStateChange_WithTagHelperChange_DoesNotCacheOu Assert.Equal(state.ProjectWorkspaceStateVersion, actualInputVersion); } + [Fact] + public async Task ProjectWorkspaceStateChange_WithProjectWorkspaceState_CSharpLanguageVersionChange_DoesNotCacheOutput() + { + // Arrange + var csharp8ValidConfiguration = new RazorConfiguration(RazorLanguageVersion.Version_3_0, _hostProject.Configuration.ConfigurationName, _hostProject.Configuration.Extensions); + var hostProject = TestProjectData.SomeProject with { Configuration = csharp8ValidConfiguration }; + var originalWorkspaceState = ProjectWorkspaceState.Create(_someTagHelpers, LanguageVersion.CSharp7); + var original = + ProjectState.Create(ProjectEngineFactoryProvider, hostProject, originalWorkspaceState) + .WithAddedHostDocument(_hostDocument, TestMocks.CreateTextLoader("@DateTime.Now", VersionStamp.Default)); + var changedWorkspaceState = ProjectWorkspaceState.Create(_someTagHelpers, LanguageVersion.CSharp8); + + var (originalOutput, originalInputVersion) = await GetOutputAsync(original, _hostDocument, DisposalToken); + + // Act + var state = original.WithProjectWorkspaceState(changedWorkspaceState); + + // Assert + var (actualOutput, actualInputVersion) = await GetOutputAsync(state, _hostDocument, DisposalToken); + Assert.NotSame(originalOutput, actualOutput); + Assert.NotEqual(originalInputVersion, actualInputVersion); + Assert.Equal(state.ProjectWorkspaceStateVersion, actualInputVersion); + } + [Fact] public async Task ConfigurationChange_DoesNotCacheOutput() { diff --git a/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/ProjectSystem/ProjectStateTest.cs b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/ProjectSystem/ProjectStateTest.cs index 334a5c016c5..2658d893683 100644 --- a/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/ProjectSystem/ProjectStateTest.cs +++ b/src/Razor/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/ProjectSystem/ProjectStateTest.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Razor.ProjectSystem; using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.AspNetCore.Razor.Test.Common.Workspaces; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; using Xunit; using Xunit.Abstractions; @@ -641,6 +642,45 @@ public void ProjectState_WithHostProject_ResetsImportedDocuments() Assert.Equal(TestProjectData.SomeProjectFile1.FilePath, documentFilePath); } + [Fact] + public void ProjectState_WithProjectWorkspaceState_Changed() + { + // Arrange + var original = ProjectState.Create(ProjectEngineFactoryProvider, _hostProject, _projectWorkspaceState) + .WithAddedHostDocument(_documents[2], DocumentState.EmptyLoader) + .WithAddedHostDocument(_documents[1], DocumentState.EmptyLoader); + + // Force init + var originalTagHelpers = original.TagHelpers; + var originalProjectWorkspaceStateVersion = original.ProjectWorkspaceStateVersion; + + var changed = ProjectWorkspaceState.Create(_projectWorkspaceState.TagHelpers, LanguageVersion.CSharp6); + + // Act + var state = original.WithProjectWorkspaceState(changed); + + // Assert + Assert.NotEqual(original.Version, state.Version); + Assert.Same(changed, state.ProjectWorkspaceState); + + var actualTagHelpers = state.TagHelpers; + var actualProjectWorkspaceStateVersion = state.ProjectWorkspaceStateVersion; + + // The C# language version changed, and the tag helpers didn't change + Assert.NotSame(original.ProjectEngine, state.ProjectEngine); + + Assert.Equal(originalTagHelpers.Length, actualTagHelpers.Length); + for (var i = 0; i < originalTagHelpers.Length; i++) + { + Assert.Same(originalTagHelpers[i], actualTagHelpers[i]); + } + + Assert.NotEqual(originalProjectWorkspaceStateVersion, actualProjectWorkspaceStateVersion); + + Assert.NotSame(original.Documents[_documents[1].FilePath], state.Documents[_documents[1].FilePath]); + Assert.NotSame(original.Documents[_documents[2].FilePath], state.Documents[_documents[2].FilePath]); + } + [Fact] public void ProjectState_WithProjectWorkspaceState_Changed_TagHelpersChanged() { @@ -687,7 +727,7 @@ public void ProjectState_WithProjectWorkspaceState_IdenticalState_Caches() _ = original.TagHelpers; _ = original.ProjectWorkspaceStateVersion; - var changed = ProjectWorkspaceState.Create(original.TagHelpers); + var changed = ProjectWorkspaceState.Create(original.TagHelpers, original.CSharpLanguageVersion); // Act var state = original.WithProjectWorkspaceState(changed); diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/TestRoslynProjectChangeProcessor.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/TestProjectWorkspaceStateGenerator.cs similarity index 95% rename from src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/TestRoslynProjectChangeProcessor.cs rename to src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/TestProjectWorkspaceStateGenerator.cs index 7dd0f677c14..2cabe615d39 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/TestRoslynProjectChangeProcessor.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/TestProjectWorkspaceStateGenerator.cs @@ -8,7 +8,7 @@ namespace Microsoft.VisualStudio.Razor.ProjectSystem; -internal class TestRoslynProjectChangeProcessor : IRoslynProjectChangeProcessor +internal class TestProjectWorkspaceStateGenerator : IProjectWorkspaceStateGenerator { private readonly List _updates = []; diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/RoslynProjectChangeDetectorTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/WorkspaceProjectStateChangeDetectorTest.cs similarity index 88% rename from src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/RoslynProjectChangeDetectorTest.cs rename to src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/WorkspaceProjectStateChangeDetectorTest.cs index 9c7421c2d62..586c6ba2455 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/RoslynProjectChangeDetectorTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectSystem/WorkspaceProjectStateChangeDetectorTest.cs @@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.Razor.ProjectSystem; -public class RoslynProjectChangeDetectorTest : VisualStudioWorkspaceTestBase +public class WorkspaceProjectStateChangeDetectorTest : VisualStudioWorkspaceTestBase { private readonly HostProject _hostProjectOne; private readonly HostProject _hostProjectTwo; @@ -36,7 +36,7 @@ public class RoslynProjectChangeDetectorTest : VisualStudioWorkspaceTestBase private readonly DocumentId _backgroundVirtualCSharpDocumentId; private readonly DocumentId _partialComponentClassDocumentId; - public RoslynProjectChangeDetectorTest(ITestOutputHelper testOutput) + public WorkspaceProjectStateChangeDetectorTest(ITestOutputHelper testOutput) : base(testOutput) { _emptySolution = Workspace.CurrentSolution; @@ -122,9 +122,9 @@ public RoslynProjectChangeDetectorTest(ITestOutputHelper testOutput) public async Task SolutionClosing_StopsActiveWork() { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); var workspaceChangedTask = detectorAccessor.ListenForWorkspaceChangesAsync( @@ -141,7 +141,7 @@ await projectManager.UpdateAsync(updater => await workspaceChangedTask; await detectorAccessor.WaitUntilCurrentBatchCompletesAsync(); - processor.Clear(); + generator.Clear(); // Act await projectManager.UpdateAsync(updater => @@ -154,7 +154,7 @@ await projectManager.UpdateAsync(updater => // Assert - Assert.Empty(processor.Updates); + Assert.Empty(generator.Updates); } [UITheory] @@ -164,9 +164,9 @@ await projectManager.UpdateAsync(updater => public async Task WorkspaceChanged_DocumentEvents_EnqueuesUpdatesForDependentProjects(WorkspaceChangeKind kind) { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); await projectManager.UpdateAsync(updater => @@ -192,10 +192,10 @@ await projectManager.UpdateAsync(updater => await detectorAccessor.WaitUntilCurrentBatchCompletesAsync(); // Assert - Assert.Equal(3, processor.Updates.Count); - Assert.Contains(processor.Updates, u => u.ProjectSnapshot.Key == _projectNumberOne.ToProjectKey()); - Assert.Contains(processor.Updates, u => u.ProjectSnapshot.Key == _projectNumberTwo.ToProjectKey()); - Assert.Contains(processor.Updates, u => u.ProjectSnapshot.Key == _projectNumberThree.ToProjectKey()); + Assert.Equal(3, generator.Updates.Count); + Assert.Contains(generator.Updates, u => u.ProjectSnapshot.Key == _projectNumberOne.ToProjectKey()); + Assert.Contains(generator.Updates, u => u.ProjectSnapshot.Key == _projectNumberTwo.ToProjectKey()); + Assert.Contains(generator.Updates, u => u.ProjectSnapshot.Key == _projectNumberThree.ToProjectKey()); } [UITheory] @@ -205,9 +205,9 @@ await projectManager.UpdateAsync(updater => public async Task WorkspaceChanged_ProjectEvents_EnqueuesUpdatesForDependentProjects(WorkspaceChangeKind kind) { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); await projectManager.UpdateAsync(updater => @@ -233,10 +233,10 @@ await projectManager.UpdateAsync(updater => await detectorAccessor.WaitUntilCurrentBatchCompletesAsync(); // Assert - Assert.Equal(3, processor.Updates.Count); - Assert.Contains(processor.Updates, u => u.ProjectSnapshot.Key == _projectNumberOne.ToProjectKey()); - Assert.Contains(processor.Updates, u => u.ProjectSnapshot.Key == _projectNumberTwo.ToProjectKey()); - Assert.Contains(processor.Updates, u => u.ProjectSnapshot.Key == _projectNumberThree.ToProjectKey()); + Assert.Equal(3, generator.Updates.Count); + Assert.Contains(generator.Updates, u => u.ProjectSnapshot.Key == _projectNumberOne.ToProjectKey()); + Assert.Contains(generator.Updates, u => u.ProjectSnapshot.Key == _projectNumberTwo.ToProjectKey()); + Assert.Contains(generator.Updates, u => u.ProjectSnapshot.Key == _projectNumberThree.ToProjectKey()); } [UITheory] @@ -248,9 +248,9 @@ await projectManager.UpdateAsync(updater => public async Task WorkspaceChanged_SolutionEvents_EnqueuesUpdatesForProjectsInSolution(WorkspaceChangeKind kind) { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); await projectManager.UpdateAsync(updater => @@ -268,7 +268,7 @@ await projectManager.UpdateAsync(updater => // Assert Assert.Collection( - processor.Updates, + generator.Updates, p => Assert.Equal(_projectNumberOne.Id, p.WorkspaceProject?.Id), p => Assert.Equal(_projectNumberTwo.Id, p.WorkspaceProject?.Id)); } @@ -282,9 +282,9 @@ await projectManager.UpdateAsync(updater => public async Task WorkspaceChanged_SolutionEvents_EnqueuesStateClear_EnqueuesSolutionProjectUpdates(WorkspaceChangeKind kind) { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); await projectManager.UpdateAsync(updater => @@ -309,7 +309,7 @@ await projectManager.UpdateAsync(updater => // Assert Assert.Collection( - processor.Updates, + generator.Updates, p => Assert.Equal(_projectNumberThree.Id, p.WorkspaceProject?.Id), p => Assert.Null(p.WorkspaceProject), p => Assert.Equal(_projectNumberOne.Id, p.WorkspaceProject?.Id), @@ -322,9 +322,9 @@ await projectManager.UpdateAsync(updater => public async Task WorkspaceChanged_ProjectChangeEvents_UpdatesProjectState_AfterDelay(WorkspaceChangeKind kind) { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); await projectManager.UpdateAsync(updater => @@ -334,7 +334,7 @@ await projectManager.UpdateAsync(updater => // Stop any existing work and clear out any updates that we might have received. detectorAccessor.CancelExistingWork(); - processor.Clear(); + generator.Clear(); // Create a listener for the workspace change we're about to send. var listenerTask = detectorAccessor.ListenForWorkspaceChangesAsync(kind); @@ -349,7 +349,7 @@ await projectManager.UpdateAsync(updater => await detectorAccessor.WaitUntilCurrentBatchCompletesAsync(); // Assert - var update = Assert.Single(processor.Updates); + var update = Assert.Single(generator.Updates); Assert.Equal(_projectNumberOne.Id, update.WorkspaceProject?.Id); Assert.Equal(_hostProjectOne.FilePath, update.ProjectSnapshot.FilePath); } @@ -358,9 +358,9 @@ await projectManager.UpdateAsync(updater => public async Task WorkspaceChanged_DocumentChanged_BackgroundVirtualCS_UpdatesProjectState_AfterDelay() { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); Workspace.TryApplyChanges(_solutionWithTwoProjects); @@ -370,7 +370,7 @@ await projectManager.UpdateAsync(updater => updater.ProjectAdded(_hostProjectOne); }); - processor.Clear(); + generator.Clear(); var solution = _solutionWithTwoProjects.WithDocumentText(_backgroundVirtualCSharpDocumentId, SourceText.From("public class Foo{}")); var e = new WorkspaceChangeEventArgs(WorkspaceChangeKind.DocumentChanged, oldSolution: _solutionWithTwoProjects, newSolution: solution, projectId: _projectNumberOne.Id, _backgroundVirtualCSharpDocumentId); @@ -381,7 +381,7 @@ await projectManager.UpdateAsync(updater => await detectorAccessor.WaitUntilCurrentBatchCompletesAsync(); // Assert - var update = Assert.Single(processor.Updates); + var update = Assert.Single(generator.Updates); Assert.Equal(_projectNumberOne.Id, update.WorkspaceProject?.Id); Assert.Equal(_hostProjectOne.FilePath, update.ProjectSnapshot.FilePath); } @@ -390,9 +390,9 @@ await projectManager.UpdateAsync(updater => public async Task WorkspaceChanged_DocumentChanged_CSHTML_UpdatesProjectState_AfterDelay() { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); Workspace.TryApplyChanges(_solutionWithTwoProjects); @@ -402,7 +402,7 @@ await projectManager.UpdateAsync(updater => updater.ProjectAdded(_hostProjectOne); }); - processor.Clear(); + generator.Clear(); var solution = _solutionWithTwoProjects.WithDocumentText(_cshtmlDocumentId, SourceText.From("Hello World")); var e = new WorkspaceChangeEventArgs(WorkspaceChangeKind.DocumentChanged, oldSolution: _solutionWithTwoProjects, newSolution: solution, projectId: _projectNumberOne.Id, _cshtmlDocumentId); @@ -413,7 +413,7 @@ await projectManager.UpdateAsync(updater => await detectorAccessor.WaitUntilCurrentBatchCompletesAsync(); // Assert - var update = Assert.Single(processor.Updates); + var update = Assert.Single(generator.Updates); Assert.Equal(_projectNumberOne.Id, update.WorkspaceProject?.Id); Assert.Equal(_hostProjectOne.FilePath, update.ProjectSnapshot.FilePath); } @@ -422,9 +422,9 @@ await projectManager.UpdateAsync(updater => public async Task WorkspaceChanged_DocumentChanged_Razor_UpdatesProjectState_AfterDelay() { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); Workspace.TryApplyChanges(_solutionWithTwoProjects); @@ -434,7 +434,7 @@ await projectManager.UpdateAsync(updater => updater.ProjectAdded(_hostProjectOne); }); - processor.Clear(); + generator.Clear(); var solution = _solutionWithTwoProjects.WithDocumentText(_razorDocumentId, SourceText.From("Hello World")); var e = new WorkspaceChangeEventArgs(WorkspaceChangeKind.DocumentChanged, oldSolution: _solutionWithTwoProjects, newSolution: solution, projectId: _projectNumberOne.Id, _razorDocumentId); @@ -445,7 +445,7 @@ await projectManager.UpdateAsync(updater => await detectorAccessor.WaitUntilCurrentBatchCompletesAsync(); // Assert - var update = Assert.Single(processor.Updates); + var update = Assert.Single(generator.Updates); Assert.Equal(_projectNumberOne.Id, update.WorkspaceProject?.Id); Assert.Equal(_hostProjectOne.FilePath, update.ProjectSnapshot.FilePath); } @@ -454,9 +454,9 @@ await projectManager.UpdateAsync(updater => public async Task WorkspaceChanged_DocumentChanged_PartialComponent_UpdatesProjectState_AfterDelay() { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); Workspace.TryApplyChanges(_solutionWithTwoProjects); @@ -467,7 +467,7 @@ await projectManager.UpdateAsync(updater => }); await detectorAccessor.WaitUntilCurrentBatchCompletesAsync(); - processor.Clear(); + generator.Clear(); var sourceText = SourceText.From($$""" public partial class TestComponent : {{ComponentsApi.IComponent.MetadataName}} {} @@ -495,7 +495,7 @@ public interface IComponent {} await detectorAccessor.WaitUntilCurrentBatchCompletesAsync(); // Assert - var update = Assert.Single(processor.Updates); + var update = Assert.Single(generator.Updates); Assert.Equal(_projectNumberOne.Id, update.WorkspaceProject?.Id); Assert.Equal(_hostProjectOne.FilePath, update.ProjectSnapshot.FilePath); } @@ -504,9 +504,9 @@ public interface IComponent {} public async Task WorkspaceChanged_ProjectRemovedEvent_QueuesProjectStateRemoval() { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); await projectManager.UpdateAsync(updater => @@ -524,7 +524,7 @@ await projectManager.UpdateAsync(updater => // Assert Assert.Single( - processor.Updates, + generator.Updates, p => p.WorkspaceProject is null); } @@ -532,9 +532,9 @@ await projectManager.UpdateAsync(updater => public async Task WorkspaceChanged_ProjectAddedEvent_AddsProject() { // Arrange - var processor = new TestRoslynProjectChangeProcessor(); + var generator = new TestProjectWorkspaceStateGenerator(); var projectManager = CreateProjectSnapshotManager(); - using var detector = CreateDetector(processor, projectManager); + using var detector = CreateDetector(generator, projectManager); var detectorAccessor = detector.GetTestAccessor(); await projectManager.UpdateAsync(updater => @@ -553,7 +553,7 @@ await projectManager.UpdateAsync(updater => // Assert Assert.Single( - processor.Updates, + generator.Updates, p => p.WorkspaceProject?.Id == _projectNumberThree.Id); } @@ -576,7 +576,7 @@ public partial class TestComponent{} await document.GetSemanticModelAsync(); // Act - var result = RoslynProjectChangeDetector.IsPartialComponentClass(document); + var result = WorkspaceProjectStateChangeDetector.IsPartialComponentClass(document); // Assert Assert.False(result); @@ -605,7 +605,7 @@ public interface IComponent {} await document.GetSemanticModelAsync(); // Act - var result = RoslynProjectChangeDetector.IsPartialComponentClass(document); + var result = WorkspaceProjectStateChangeDetector.IsPartialComponentClass(document); // Assert Assert.True(result); @@ -630,7 +630,7 @@ public interface IComponent {} Assert.NotNull(document); // Act - var result = RoslynProjectChangeDetector.IsPartialComponentClass(document); + var result = WorkspaceProjectStateChangeDetector.IsPartialComponentClass(document); // Assert Assert.False(result); @@ -657,7 +657,7 @@ public interface IComponent {} await document.GetSyntaxRootAsync(); // Act - var result = RoslynProjectChangeDetector.IsPartialComponentClass(document); + var result = WorkspaceProjectStateChangeDetector.IsPartialComponentClass(document); // Assert Assert.False(result); @@ -680,7 +680,7 @@ public async Task IsPartialComponentClass_NonClass_ReturnsFalse() await document.GetSemanticModelAsync(); // Act - var result = RoslynProjectChangeDetector.IsPartialComponentClass(document); + var result = WorkspaceProjectStateChangeDetector.IsPartialComponentClass(document); // Assert Assert.False(result); @@ -713,7 +713,7 @@ public interface IComponent {} await document.GetSemanticModelAsync(); // Act - var result = RoslynProjectChangeDetector.IsPartialComponentClass(document); + var result = WorkspaceProjectStateChangeDetector.IsPartialComponentClass(document); // Assert Assert.True(result); @@ -745,12 +745,12 @@ public interface IComponent {} await document.GetSemanticModelAsync(); // Act - var result = RoslynProjectChangeDetector.IsPartialComponentClass(document); + var result = WorkspaceProjectStateChangeDetector.IsPartialComponentClass(document); // Assert Assert.False(result); } - private RoslynProjectChangeDetector CreateDetector(IRoslynProjectChangeProcessor processor, IProjectSnapshotManager projectManager) - => new(processor, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, TimeSpan.FromMilliseconds(10)); + private WorkspaceProjectStateChangeDetector CreateDetector(IProjectWorkspaceStateGenerator generator, IProjectSnapshotManager projectManager) + => new(generator, projectManager, TestLanguageServerFeatureOptions.Instance, WorkspaceProvider, TimeSpan.FromMilliseconds(10)); } diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/RoslynProjectChangeProcessorTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectWorkspaceStateGeneratorTest.cs similarity index 71% rename from src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/RoslynProjectChangeProcessorTest.cs rename to src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectWorkspaceStateGeneratorTest.cs index 315c2a5dc42..02bfb9efc75 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/RoslynProjectChangeProcessorTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/ProjectWorkspaceStateGeneratorTest.cs @@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.Razor; -public class RoslynProjectChangeProcessorTest : VisualStudioWorkspaceTestBase +public class ProjectWorkspaceStateGeneratorTest : VisualStudioWorkspaceTestBase { private readonly TestTagHelperResolver _tagHelperResolver; private readonly Project _workspaceProject; @@ -26,7 +26,7 @@ public class RoslynProjectChangeProcessorTest : VisualStudioWorkspaceTestBase private readonly ProjectWorkspaceState _projectWorkspaceStateWithTagHelpers; private readonly TestProjectSnapshotManager _projectManager; - public RoslynProjectChangeProcessorTest(ITestOutputHelper testOutput) + public ProjectWorkspaceStateGeneratorTest(ITestOutputHelper testOutput) : base(testOutput) { _tagHelperResolver = new TestTagHelperResolver( @@ -53,36 +53,36 @@ public RoslynProjectChangeProcessorTest(ITestOutputHelper testOutput) public void Dispose_MakesUpdateNoop() { // Arrange - using var processor = new RoslynProjectChangeProcessor( + using var generator = new ProjectWorkspaceStateGenerator( _projectManager, _tagHelperResolver, LoggerFactory, NoOpTelemetryReporter.Instance); - var processorAccessor = processor.GetTestAccessor(); - processorAccessor.BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false); + var generatorAccessor = generator.GetTestAccessor(); + generatorAccessor.BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false); // Act - processor.Dispose(); + generator.Dispose(); - processor.EnqueueUpdate(_workspaceProject, _projectSnapshot); + generator.EnqueueUpdate(_workspaceProject, _projectSnapshot); // Assert - Assert.Empty(processorAccessor.GetUpdates()); + Assert.Empty(generatorAccessor.GetUpdates()); } [UIFact] public void Update_StartsUpdateTask() { // Arrange - using var processor = new RoslynProjectChangeProcessor( + using var generator = new ProjectWorkspaceStateGenerator( _projectManager, _tagHelperResolver, LoggerFactory, NoOpTelemetryReporter.Instance); - var processorAccessor = processor.GetTestAccessor(); - processorAccessor.BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false); + var generatorAccessor = generator.GetTestAccessor(); + generatorAccessor.BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false); // Act - processor.EnqueueUpdate(_workspaceProject, _projectSnapshot); + generator.EnqueueUpdate(_workspaceProject, _projectSnapshot); // Assert - var update = Assert.Single(processorAccessor.GetUpdates()); + var update = Assert.Single(generatorAccessor.GetUpdates()); Assert.False(update.IsCompleted); } @@ -90,18 +90,18 @@ public void Update_StartsUpdateTask() public void Update_SoftCancelsIncompleteTaskForSameProject() { // Arrange - using var processor = new RoslynProjectChangeProcessor( + using var generator = new ProjectWorkspaceStateGenerator( _projectManager, _tagHelperResolver, LoggerFactory, NoOpTelemetryReporter.Instance); - var processorAccessor = processor.GetTestAccessor(); - processorAccessor.BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false); + var generatorAccessor = generator.GetTestAccessor(); + generatorAccessor.BlockBackgroundWorkStart = new ManualResetEventSlim(initialState: false); - processor.EnqueueUpdate(_workspaceProject, _projectSnapshot); + generator.EnqueueUpdate(_workspaceProject, _projectSnapshot); - var initialUpdate = Assert.Single(processorAccessor.GetUpdates()); + var initialUpdate = Assert.Single(generatorAccessor.GetUpdates()); // Act - processor.EnqueueUpdate(_workspaceProject, _projectSnapshot); + generator.EnqueueUpdate(_workspaceProject, _projectSnapshot); // Assert Assert.True(initialUpdate.IsCancellationRequested); @@ -111,11 +111,11 @@ public void Update_SoftCancelsIncompleteTaskForSameProject() public async Task Update_NullWorkspaceProject_ClearsProjectWorkspaceState() { // Arrange - using var processor = new RoslynProjectChangeProcessor( + using var generator = new ProjectWorkspaceStateGenerator( _projectManager, _tagHelperResolver, LoggerFactory, NoOpTelemetryReporter.Instance); - var processorAccessor = processor.GetTestAccessor(); - processorAccessor.NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false); + var generatorAccessor = generator.GetTestAccessor(); + generatorAccessor.NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false); await _projectManager.UpdateAsync(updater => { @@ -124,10 +124,10 @@ await _projectManager.UpdateAsync(updater => }); // Act - processor.EnqueueUpdate(workspaceProject: null, _projectSnapshot); + generator.EnqueueUpdate(workspaceProject: null, _projectSnapshot); // Jump off the UI thread so the background work can complete. - await Task.Run(() => processorAccessor.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(3))); + await Task.Run(() => generatorAccessor.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(3))); // Assert var newProjectSnapshot = _projectManager.GetLoadedProject(_projectSnapshot.Key); @@ -139,11 +139,11 @@ await _projectManager.UpdateAsync(updater => public async Task Update_ResolvesTagHelpersAndUpdatesWorkspaceState() { // Arrange - using var processor = new RoslynProjectChangeProcessor( + using var generator = new ProjectWorkspaceStateGenerator( _projectManager, _tagHelperResolver, LoggerFactory, NoOpTelemetryReporter.Instance); - var processorAccessor = processor.GetTestAccessor(); - processorAccessor.NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false); + var generatorAccessor = generator.GetTestAccessor(); + generatorAccessor.NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false); await _projectManager.UpdateAsync(updater => { @@ -151,10 +151,10 @@ await _projectManager.UpdateAsync(updater => }); // Act - processor.EnqueueUpdate(_workspaceProject, _projectSnapshot); + generator.EnqueueUpdate(_workspaceProject, _projectSnapshot); // Jump off the UI thread so the background work can complete. - await Task.Run(() => processorAccessor.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(3))); + await Task.Run(() => generatorAccessor.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(3))); // Assert var newProjectSnapshot = _projectManager.GetLoadedProject(_projectSnapshot.Key); diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/VsSolutionUpdatesProjectSnapshotChangeTriggerTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/VsSolutionUpdatesProjectSnapshotChangeTriggerTest.cs index d8a42d8829c..b9158404c92 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/VsSolutionUpdatesProjectSnapshotChangeTriggerTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/VsSolutionUpdatesProjectSnapshotChangeTriggerTest.cs @@ -89,7 +89,7 @@ public async Task Initialize_AttachesEventSink() using (var trigger = new VsSolutionUpdatesProjectSnapshotChangeTrigger( serviceProvider, projectManager, - StrictMock.Of(), + StrictMock.Of(), _workspaceProvider, JoinableTaskContext)) { @@ -128,7 +128,7 @@ public async Task Initialize_SwitchesToMainThread() using var trigger = new VsSolutionUpdatesProjectSnapshotChangeTrigger( serviceProvider, projectManager, - StrictMock.Of(), + StrictMock.Of(), _workspaceProvider, JoinableTaskContext); @@ -158,7 +158,7 @@ public async Task SolutionClosing_CancelsActiveWork() }); var serviceProvider = VsMocks.CreateServiceProvider(); - var roslynProjectChangeProcessor = new TestRoslynProjectChangeProcessor(); + var workspaceStateGenerator = new TestProjectWorkspaceStateGenerator(); var vsHierarchyMock = new StrictMock(); var vsProjectMock = vsHierarchyMock.As(); @@ -169,7 +169,7 @@ public async Task SolutionClosing_CancelsActiveWork() using var trigger = new VsSolutionUpdatesProjectSnapshotChangeTrigger( serviceProvider, projectManager, - roslynProjectChangeProcessor, + workspaceStateGenerator, _workspaceProvider, JoinableTaskContext); @@ -187,7 +187,7 @@ await projectManager.UpdateAsync(updater => updater.ProjectRemoved(s_someProject.Key); }); - var update = Assert.Single(roslynProjectChangeProcessor.Updates); + var update = Assert.Single(workspaceStateGenerator.Updates); Assert.NotNull(update.WorkspaceProject); Assert.Equal(update.WorkspaceProject.Id, _someWorkspaceProject.Id); Assert.Same(expectedProjectSnapshot, update.ProjectSnapshot); @@ -211,12 +211,12 @@ public async Task OnProjectBuiltAsync_KnownProject_EnqueuesProjectStateUpdate() }); var serviceProvider = VsMocks.CreateServiceProvider(); - var roslynProjectChangeProcessor = new TestRoslynProjectChangeProcessor(); + var workspaceStateGenerator = new TestProjectWorkspaceStateGenerator(); using var trigger = new VsSolutionUpdatesProjectSnapshotChangeTrigger( serviceProvider, projectManager, - roslynProjectChangeProcessor, + workspaceStateGenerator, _workspaceProvider, JoinableTaskContext); @@ -232,7 +232,7 @@ public async Task OnProjectBuiltAsync_KnownProject_EnqueuesProjectStateUpdate() await testAccessor.OnProjectBuiltAsync(vsHierarchyMock.Object, DisposalToken); // Assert - var update = Assert.Single(roslynProjectChangeProcessor.Updates); + var update = Assert.Single(workspaceStateGenerator.Updates); Assert.NotNull(update.WorkspaceProject); Assert.Equal(update.WorkspaceProject.Id, _someWorkspaceProject.Id); Assert.Same(expectedProjectSnapshot, update.ProjectSnapshot); @@ -262,12 +262,12 @@ await projectManager.UpdateAsync(updater => new HostProject("/Some/Unknown/Path.csproj", "/Some/Unknown/obj", RazorConfiguration.Default, "Path")); }); - var roslynProjectChangeProcessor = new TestRoslynProjectChangeProcessor(); + var workspaceStateGenerator = new TestProjectWorkspaceStateGenerator(); using var trigger = new VsSolutionUpdatesProjectSnapshotChangeTrigger( serviceProvider, projectManager, - roslynProjectChangeProcessor, + workspaceStateGenerator, _workspaceProvider, JoinableTaskContext); @@ -277,7 +277,7 @@ await projectManager.UpdateAsync(updater => await testAccessor.OnProjectBuiltAsync(StrictMock.Of(), DisposalToken); // Assert - Assert.Empty(roslynProjectChangeProcessor.Updates); + Assert.Empty(workspaceStateGenerator.Updates); } [UIFact] @@ -298,12 +298,12 @@ public async Task OnProjectBuiltAsync_UnknownProject_DoesNotEnqueueUpdate() var projectManager = CreateProjectSnapshotManager(); - var roslynProjectChangeProcessor = new TestRoslynProjectChangeProcessor(); + var workspaceStateGenerator = new TestProjectWorkspaceStateGenerator(); using var trigger = new VsSolutionUpdatesProjectSnapshotChangeTrigger( serviceProvider, projectManager, - roslynProjectChangeProcessor, + workspaceStateGenerator, _workspaceProvider, JoinableTaskContext); @@ -313,6 +313,6 @@ public async Task OnProjectBuiltAsync_UnknownProject_DoesNotEnqueueUpdate() await testAccessor.OnProjectBuiltAsync(StrictMock.Of(), DisposalToken); // Assert - Assert.Empty(roslynProjectChangeProcessor.Updates); + Assert.Empty(workspaceStateGenerator.Updates); } } diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectReaders.cs b/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectReaders.cs index ed7f469a31b..91ae5629e82 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectReaders.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectReaders.cs @@ -44,8 +44,6 @@ public static RazorConfiguration ReadConfigurationFromProperties(JsonDataReader var languageVersionText = reader.ReadNonNullString(nameof(RazorConfiguration.LanguageVersion)); var suppressAddComponentParameter = reader.ReadBooleanOrFalse(nameof(RazorConfiguration.SuppressAddComponentParameter)); var useConsolidatedMvcViews = reader.ReadBooleanOrTrue(nameof(RazorConfiguration.UseConsolidatedMvcViews)); - var useRoslynTokenizer = reader.ReadBooleanOrFalse(nameof(RazorConfiguration.UseRoslynTokenizer)); - var csharpLanguageVersion = (LanguageVersion)reader.ReadInt32OrZero(nameof(RazorConfiguration.CSharpLanguageVersion)); var extensions = reader.ReadImmutableArrayOrEmpty(nameof(RazorConfiguration.Extensions), static r => { @@ -57,7 +55,7 @@ public static RazorConfiguration ReadConfigurationFromProperties(JsonDataReader ? version : RazorLanguageVersion.Version_2_1; - return new(languageVersion, configurationName, extensions, SuppressAddComponentParameter: suppressAddComponentParameter, UseRoslynTokenizer: useRoslynTokenizer); + return new(languageVersion, configurationName, extensions, SuppressAddComponentParameter: suppressAddComponentParameter); } public static RazorDiagnostic ReadDiagnostic(JsonDataReader reader) @@ -104,8 +102,9 @@ public static DocumentSnapshotHandle ReadDocumentSnapshotHandleFromProperties(Js public static ProjectWorkspaceState ReadProjectWorkspaceStateFromProperties(JsonDataReader reader) { var tagHelpers = reader.ReadImmutableArrayOrEmpty(nameof(ProjectWorkspaceState.TagHelpers), static r => ReadTagHelper(r, useCache: true)); + var csharpLanguageVersion = (LanguageVersion)reader.ReadInt32OrZero(nameof(ProjectWorkspaceState.CSharpLanguageVersion)); - return ProjectWorkspaceState.Create(tagHelpers); + return ProjectWorkspaceState.Create(tagHelpers, csharpLanguageVersion); } public static TagHelperDescriptor ReadTagHelper(JsonDataReader reader, bool useCache) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectWriters.cs b/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectWriters.cs index c03051e73c9..aad91449d2b 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectWriters.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/ObjectWriters.cs @@ -37,8 +37,6 @@ public static void WriteProperties(JsonDataWriter writer, RazorConfiguration val writer.WriteIfNotFalse(nameof(value.SuppressAddComponentParameter), value.SuppressAddComponentParameter); writer.WriteIfNotTrue(nameof(value.UseConsolidatedMvcViews), value.UseConsolidatedMvcViews); - writer.WriteIfNotFalse(nameof(value.UseRoslynTokenizer), value.UseRoslynTokenizer); - writer.WriteIfNotZero(nameof(value.CSharpLanguageVersion), (int)value.CSharpLanguageVersion); writer.WriteArrayIfNotNullOrEmpty(nameof(value.Extensions), value.Extensions, static (w, v) => w.Write(v.ExtensionName)); } @@ -86,6 +84,7 @@ public static void Write(JsonDataWriter writer, ProjectWorkspaceState? value) public static void WriteProperties(JsonDataWriter writer, ProjectWorkspaceState value) { writer.WriteArrayIfNotDefaultOrEmpty(nameof(value.TagHelpers), value.TagHelpers, Write); + writer.WriteIfNotZero(nameof(value.CSharpLanguageVersion), (int)value.CSharpLanguageVersion); } public static void Write(JsonDataWriter writer, TagHelperDescriptor? value) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/SerializationFormat.cs b/src/Shared/Microsoft.AspNetCore.Razor.Serialization.Json/SerializationFormat.cs index f0941e5b38b..c6a8db23f4a 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 = 7; + public const int Version = 6; }