diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/CSSErrorCodes.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/CSSErrorCodes.cs index 658f918272f..31f568a5f5a 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/CSSErrorCodes.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/CSSErrorCodes.cs @@ -6,6 +6,7 @@ namespace Microsoft.CodeAnalysis.Razor.Diagnostics; // Note: This type should be kept in sync with WTE's ErrorCodes.cs internal static class CSSErrorCodes { + public const string UnrecognizedBlockType = "CSS002"; public const string MissingOpeningBrace = "CSS023"; public const string MissingSelectorAfterCombinator = "CSS029"; public const string MissingSelectorBeforeCombinatorCode = "CSS031"; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs index 23e1adc835a..08879012b37 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs @@ -234,6 +234,7 @@ private static bool ShouldFilterHtmlDiagnosticBasedOnErrorCode(LspDiagnostic dia return str switch { + CSSErrorCodes.UnrecognizedBlockType => IsEscapedAtSign(diagnostic, sourceText), CSSErrorCodes.MissingOpeningBrace => IsCSharpInStyleBlock(diagnostic, sourceText, syntaxTree), CSSErrorCodes.MissingSelectorAfterCombinator => IsCSharpInStyleBlock(diagnostic, sourceText, syntaxTree), CSSErrorCodes.MissingSelectorBeforeCombinatorCode => IsCSharpInStyleBlock(diagnostic, sourceText, syntaxTree), @@ -244,6 +245,34 @@ private static bool ShouldFilterHtmlDiagnosticBasedOnErrorCode(LspDiagnostic dia _ => false, }; + static bool IsEscapedAtSign(LspDiagnostic diagnostic, SourceText sourceText) + { + // Filters out "Unrecognized block type" errors in CSS, which occur with something like this: + // + // + // + // The "@@" tells Razor that the user wants an "@" in the final Html, but the design time document + // for the Html has to line up with the source Razor file, so that doesn't happen in the IDE. When + // CSS gets the two "@"s, it raises the "Unrecognized block type" error. + + if (!sourceText.TryGetAbsoluteIndex(diagnostic.Range.Start, out var absoluteIndex)) + { + return false; + } + + // It's much easier to just check the source text directly, rather than try to understand all of the + // possible shapes of the syntax tree here. We assume that since the diagnostics we're filtering out + // came from the CSS server, it's a CSS block. + return absoluteIndex > 0 && + sourceText[absoluteIndex] == '@' && + sourceText[absoluteIndex - 1] == '@'; + } + static bool IsCSharpInStyleBlock(LspDiagnostic diagnostic, SourceText sourceText, RazorSyntaxTree syntaxTree) { // C# in a style block causes diagnostics because the HTML background document replaces C# with "~" diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentPullDiagnosticsTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentPullDiagnosticsTest.cs index 1bc8fd332cc..335a7e96ecd 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentPullDiagnosticsTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentPullDiagnosticsTest.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Razor; using Microsoft.AspNetCore.Razor.Test.Common; +using Microsoft.CodeAnalysis.Razor.Diagnostics; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Test.Utilities; @@ -65,6 +66,47 @@ public Task Html() }]); } + [Fact] + public Task FilterEscapedAtFromCss() + { + TestCode input = """ +
+ + + +
+ """; + + return VerifyDiagnosticsAsync(input, + htmlResponse: [new VSInternalDiagnosticReport + { + Diagnostics = + [ + new Diagnostic + { + Code = CSSErrorCodes.UnrecognizedBlockType, + Range = SourceText.From(input.Text).GetRange(new TextSpan(input.Text.IndexOf("@@") + 1, 1)) + }, + new Diagnostic + { + Code = CSSErrorCodes.UnrecognizedBlockType, + Range = SourceText.From(input.Text).GetRange(new TextSpan(input.Text.IndexOf("f"), 1)) + } + ] + }]); + } + [Fact] public Task CombinedAndNestedDiagnostics() => VerifyDiagnosticsAsync("""