diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/Razor/GenerateMethodCodeActionResolver.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/Razor/GenerateMethodCodeActionResolver.cs index 026cf9c36ec..8df517e9037 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/Razor/GenerateMethodCodeActionResolver.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/CodeActions/Razor/GenerateMethodCodeActionResolver.cs @@ -215,7 +215,7 @@ private async Task GenerateMethodInCodeBlockAsync( var formattedChange = await _razorFormattingService.TryGetCSharpCodeActionEditAsync( documentContext, - result.SelectAsArray(code.Source.Text.GetTextChange), + result.SelectAsArray(code.GetCSharpSourceText().GetTextChange), formattingOptions, cancellationToken).ConfigureAwait(false); diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/RazorFormattingService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/RazorFormattingService.cs index 23110dab756..838dd78cab3 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/RazorFormattingService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/RazorFormattingService.cs @@ -126,7 +126,7 @@ public Task> GetCSharpOnTypeFormattingChangesAsync(Do triggerCharacter, [_csharpOnTypeFormattingPass, .. _validationPasses], collapseChanges: false, - automaticallyAddUsings: false, + isCodeActionFormattingRequest: false, cancellationToken: cancellationToken); public Task> GetHtmlOnTypeFormattingChangesAsync(DocumentContext documentContext, ImmutableArray htmlChanges, RazorFormattingOptions options, int hostDocumentIndex, char triggerCharacter, CancellationToken cancellationToken) @@ -138,7 +138,7 @@ public Task> GetHtmlOnTypeFormattingChangesAsync(Docu triggerCharacter, [_htmlOnTypeFormattingPass, .. _validationPasses], collapseChanges: false, - automaticallyAddUsings: false, + isCodeActionFormattingRequest: false, cancellationToken: cancellationToken); public async Task TryGetSingleCSharpEditAsync(DocumentContext documentContext, TextChange csharpEdit, RazorFormattingOptions options, CancellationToken cancellationToken) @@ -151,7 +151,7 @@ public Task> GetHtmlOnTypeFormattingChangesAsync(Docu triggerCharacter: '\0', [_csharpOnTypeFormattingPass, .. _validationPasses], collapseChanges: false, - automaticallyAddUsings: false, + isCodeActionFormattingRequest: false, cancellationToken: cancellationToken).ConfigureAwait(false); return razorChanges.SingleOrDefault(); } @@ -166,7 +166,7 @@ public Task> GetHtmlOnTypeFormattingChangesAsync(Docu triggerCharacter: '\0', [_csharpOnTypeFormattingPass], collapseChanges: true, - automaticallyAddUsings: true, + isCodeActionFormattingRequest: true, cancellationToken: cancellationToken).ConfigureAwait(false); return razorChanges.SingleOrDefault(); } @@ -183,7 +183,7 @@ public Task> GetHtmlOnTypeFormattingChangesAsync(Docu triggerCharacter: '\0', [_csharpOnTypeFormattingPass], collapseChanges: true, - automaticallyAddUsings: false, + isCodeActionFormattingRequest: false, cancellationToken: cancellationToken).ConfigureAwait(false); razorChanges = UnwrapCSharpSnippets(razorChanges); @@ -211,7 +211,7 @@ private async Task> ApplyFormattedChangesAsync( char triggerCharacter, ImmutableArray formattingPasses, bool collapseChanges, - bool automaticallyAddUsings, + bool isCodeActionFormattingRequest, CancellationToken cancellationToken) { // If we only received a single edit, let's always return a single edit back. @@ -219,13 +219,19 @@ private async Task> ApplyFormattedChangesAsync( collapseChanges |= generatedDocumentChanges.Length == 1; var documentSnapshot = documentContext.Snapshot; - var codeDocument = await _codeDocumentProvider.GetCodeDocumentAsync(documentSnapshot).ConfigureAwait(false); + + // Code actions were computed on the regular document, which with FUSE could be a runtime document. We have to make + // sure for code actions specifically we are formatting that same document, or TextChange spans may not line up + var codeDocument = isCodeActionFormattingRequest + ? await documentSnapshot.GetGeneratedOutputAsync(forceDesignTimeGeneratedOutput: false).ConfigureAwait(false) + : await _codeDocumentProvider.GetCodeDocumentAsync(documentSnapshot).ConfigureAwait(false); + var context = FormattingContext.CreateForOnTypeFormatting( documentSnapshot, codeDocument, options, _codeDocumentProvider, - automaticallyAddUsings, + automaticallyAddUsings: isCodeActionFormattingRequest, hostDocumentIndex, triggerCharacter); var result = generatedDocumentChanges; diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/TelemetryReporter.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/TelemetryReporter.cs index 8b268f4b9ec..c9cee632176 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/TelemetryReporter.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/Telemetry/TelemetryReporter.cs @@ -10,8 +10,6 @@ using Microsoft.AspNetCore.Razor.Telemetry; using Microsoft.AspNetCore.Razor; using System.IO; - - #if DEBUG using System.Linq; #endif @@ -268,7 +266,6 @@ public TelemetryScope TrackLspRequest(string lspMethodName, string languageServe new("eventscope.correlationid", correlationId)); } - /// /// Returns values that should be set to (failureParameter1, failureParameter2) when reporting a fault. /// Those values represent the blamed stackframe module and method name. diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperServiceTestBase.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperServiceTestBase.cs index 7a2dbb635fc..de6cbf14e92 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperServiceTestBase.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Completion/TagHelperServiceTestBase.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Immutable; +using Microsoft.AspNetCore.Mvc.Razor.Extensions; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Components; using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer; @@ -266,7 +267,7 @@ internal static RazorCodeDocument CreateCodeDocument(string text, string filePat tagHelpers = tagHelpers.NullToEmpty(); var sourceDocument = TestRazorSourceDocument.Create(text, filePath: filePath, relativePath: filePath); - var projectEngine = RazorProjectEngine.Create(builder => { }); + var projectEngine = RazorProjectEngine.Create(RazorExtensions.Register); var fileKind = filePath.EndsWith(".razor", StringComparison.Ordinal) ? FileKinds.Component : FileKinds.Legacy; var codeDocument = projectEngine.ProcessDesignTime(sourceDocument, fileKind, importSources: default, tagHelpers); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs index 3f0365de187..da02e54cda3 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs @@ -48,13 +48,13 @@ public partial class SemanticTokensTest(ITestOutputHelper testOutput) : TagHelpe } }; - private static readonly Regex s_matchNewLines = MyRegex(); + private static readonly Regex s_matchNewLines = NewLineRegex(); #if NET [GeneratedRegex("\r\n")] - private static partial Regex MyRegex(); + private static partial Regex NewLineRegex(); #else - private static Regex MyRegex() => new Regex("\r\n|\r|\n"); + private static Regex NewLineRegex() => new Regex("\r\n|\r|\n"); #endif [Theory] @@ -654,6 +654,26 @@ public void M() await VerifySemanticTokensAsync(documentText, precise, isRazorFile: true); } + [Theory] + [CombinatorialData] + public async Task GetSemanticTokens_Legacy_Model(bool precise) + { + var documentText = """ + @using System + @model SampleApp.Pages.ErrorModel + +
+ + @{ + @Model.ToString(); + } + +
+ """; + + await VerifySemanticTokensAsync(documentText, precise, isRazorFile: false); + } + [Theory] [CombinatorialData] public async Task GetSemanticTokens_CSharp_LargeFile(bool precise) diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/TestFiles/GetSemanticTokens_Legacy_Model.semantic.txt b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/TestFiles/GetSemanticTokens_Legacy_Model.semantic.txt new file mode 100644 index 00000000000..96a45b243e5 --- /dev/null +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/TestFiles/GetSemanticTokens_Legacy_Model.semantic.txt @@ -0,0 +1,28 @@ +//line,characterPos,length,tokenType,modifier,text +0 0 1 razorTransition 0 [@] +0 1 5 keyword 0 [using] +0 6 6 namespace name 0 [System] +1 0 1 razorTransition 0 [@] +0 1 5 razorDirective 0 [model] +0 6 9 variable 0 [SampleApp] +0 9 1 operator 0 [.] +0 1 5 variable 0 [Pages] +0 5 1 operator 0 [.] +0 1 10 variable 0 [ErrorModel] +2 0 1 markupTagDelimiter 0 [<] +0 1 3 markupElement 0 [div] +0 3 1 markupTagDelimiter 0 [>] +2 4 1 razorTransition 0 [@] +0 1 1 razorTransition 0 [{] +1 8 1 razorTransition 0 [@] +0 1 5 variable 0 [Model] +0 5 1 operator 0 [.] +0 1 8 variable 0 [ToString] +0 8 1 punctuation 0 [(] +0 1 1 punctuation 0 [)] +0 1 1 punctuation 0 [;] +1 4 1 razorTransition 0 [}] +2 0 1 markupTagDelimiter 0 [<] +0 1 1 markupTagDelimiter 0 [/] +0 1 3 markupElement 0 [div] +0 3 1 markupTagDelimiter 0 [>] diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostSemanticTokensRangeEndpointTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostSemanticTokensRangeEndpointTest.cs index 7f841c9b12d..794cfc67ff1 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostSemanticTokensRangeEndpointTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostSemanticTokensRangeEndpointTest.cs @@ -67,6 +67,7 @@ public async Task Legacy(bool colorBackground, bool precise) { var input = """ @page "/" + @model AppThing.Model @using System
This is some HTML
diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TestFiles/SemanticTokens/Legacy.txt b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TestFiles/SemanticTokens/Legacy.txt index 99325b3275a..f1f0cdb3e74 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TestFiles/SemanticTokens/Legacy.txt +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TestFiles/SemanticTokens/Legacy.txt @@ -3,6 +3,11 @@ Line Δ, Char Δ, Length, Type, Modifier(s), Text 0 1 4 razorDirective [] [page] 0 5 3 string [] ["/"] 1 0 1 razorTransition [] [@] +0 1 5 razorDirective [] [model] +0 6 8 variable [] [AppThing] +0 8 1 operator [] [.] +0 1 5 variable [] [Model] +1 0 1 razorTransition [] [@] 0 1 5 keyword [] [using] 0 6 6 namespace name [] [System] 2 0 1 markupTagDelimiter [] [<] diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TestFiles/SemanticTokens/Legacy_with_background.txt b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TestFiles/SemanticTokens/Legacy_with_background.txt index c2a10eb327f..77227b0c3b6 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TestFiles/SemanticTokens/Legacy_with_background.txt +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/TestFiles/SemanticTokens/Legacy_with_background.txt @@ -3,6 +3,11 @@ Line Δ, Char Δ, Length, Type, Modifier(s), Text 0 1 4 razorDirective [] [page] 0 5 3 string [razorCode] ["/"] 1 0 1 razorTransition [] [@] +0 1 5 razorDirective [] [model] +0 6 8 variable [razorCode] [AppThing] +0 8 1 operator [razorCode] [.] +0 1 5 variable [razorCode] [Model] +1 0 1 razorTransition [] [@] 0 1 5 keyword [razorCode] [using] 0 5 1 markupTextLiteral [razorCode] [ ] 0 1 6 namespace name [razorCode] [System] diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs index 993f4ee213e..69aff9a6bbb 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs @@ -178,7 +178,7 @@ private void IncrementCount() """); } - [IdeFact] + [IdeFact(Skip = "https://github.com/dotnet/razor/issues/10860")] // FUSE changes whitespace on folding ranges public async Task CodeFolding_IfBlock() { await TestServices.SolutionExplorer.AddFileAsync( diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs index 4fa2dbdf4fc..e735c892d97 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs @@ -367,8 +367,8 @@ public string? MyProperty { set { } } [IdeTheory] [InlineData("MyProperty:get")] - [InlineData("MyProperty:set")] - [InlineData("MyProperty:after")] + [InlineData("MyProperty:set", Skip = "https://github.com/dotnet/razor/issues/10966")] + [InlineData("MyProperty:after", Skip = "https://github.com/dotnet/razor/issues/10966")] public async Task GoToDefinition_ComponentAttribute_BindSet(string attribute) { // Create the file diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/MultiTargetProjectTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/MultiTargetProjectTests.cs index 006bccb5f92..d44b45ca01f 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/MultiTargetProjectTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/MultiTargetProjectTests.cs @@ -64,7 +64,7 @@ public async Task OpenExistingProject_WithReopenedFile() // Open SurveyPrompt and make sure its all up and running await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.ErrorCshtmlFile, ControlledHangMitigatingCancellationToken); - await TestServices.Editor.WaitForSemanticClassificationAsync("class name", ControlledHangMitigatingCancellationToken, count: 1); + await TestServices.Editor.WaitForSemanticClassificationAsync("RazorTagHelperElement", ControlledHangMitigatingCancellationToken, count: 1); await TestServices.SolutionExplorer.CloseSolutionAsync(ControlledHangMitigatingCancellationToken); @@ -73,7 +73,7 @@ public async Task OpenExistingProject_WithReopenedFile() await TestServices.Workspace.WaitForProjectSystemAsync(ControlledHangMitigatingCancellationToken); - await TestServices.Editor.WaitForSemanticClassificationAsync("class name", ControlledHangMitigatingCancellationToken, count: 1); + await TestServices.Editor.WaitForSemanticClassificationAsync("RazorTagHelperElement", ControlledHangMitigatingCancellationToken, count: 1); TestServices.Input.Send("1"); diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/NonRazorSdkTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/NonRazorSdkTests.cs index 209864d375a..726d8151f71 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/NonRazorSdkTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/NonRazorSdkTests.cs @@ -35,7 +35,7 @@ protected override void PrepareProjectForFirstOpen(string projectFileName) base.PrepareProjectForFirstOpen(projectFileName); } - [IdeFact] + [IdeFact(Skip = "https://github.com/dotnet/razor/issues/10965")] // Completion is broken in FUSE for sole `@` characters public async Task Completion_DateTime() { // We open the Index.razor file, and wait for 3 RazorComponentElement's to be classified, as that diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/ProjectTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/ProjectTests.cs index ad744cb6260..8e4de9be7e5 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/ProjectTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/ProjectTests.cs @@ -95,7 +95,7 @@ public async Task OpenExistingProject_WithReopenedFile() // Open SurveyPrompt and make sure its all up and running await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.ErrorCshtmlFile, ControlledHangMitigatingCancellationToken); - await TestServices.Editor.WaitForSemanticClassificationAsync("class name", ControlledHangMitigatingCancellationToken, count: 1); + await TestServices.Editor.WaitForSemanticClassificationAsync("RazorTagHelperElement", ControlledHangMitigatingCancellationToken, count: 1); await TestServices.SolutionExplorer.CloseSolutionAsync(ControlledHangMitigatingCancellationToken); @@ -104,7 +104,7 @@ public async Task OpenExistingProject_WithReopenedFile() await TestServices.Workspace.WaitForProjectSystemAsync(ControlledHangMitigatingCancellationToken); - await TestServices.Editor.WaitForSemanticClassificationAsync("class name", ControlledHangMitigatingCancellationToken, count: 1); + await TestServices.Editor.WaitForSemanticClassificationAsync("RazorTagHelperElement", ControlledHangMitigatingCancellationToken, count: 1); TestServices.Input.Send("1"); diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Semantic/RazorSemanticTokensTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Semantic/RazorSemanticTokensTests.cs index 9b9d8dcf6dc..fcf08a26472 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Semantic/RazorSemanticTokensTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Semantic/RazorSemanticTokensTests.cs @@ -54,7 +54,7 @@ @using Microsoft.AspNetCore.Components.Forms await TestServices.Editor.VerifyGetClassificationsAsync(expectedClassifications, ControlledHangMitigatingCancellationToken); } - [IdeFact] + [IdeFact(Skip = "https://github.com/dotnet/razor/issues/5595")] // Broken in FUSE due to @inherits bug public async Task Components_AreColored() { // Arrange