Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ protected override void ExecuteCore(
{
child is not DirectiveTokenIntermediateNode directiveToken
? child
: IntermediateNodeFactory.CSharpToken(directiveToken.Content)
: IntermediateNodeFactory.CSharpToken(
content: directiveToken.Content,
// To avoid breaking hot reload, we don't map the content back to the source unless we're on Razor 10 or higher
source: codeDocument.ParserOptions.LanguageVersion >= RazorLanguageVersion.Version_10_0

@davidwengier davidwengier Dec 12, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than gating this on a language version, we could just add a source mapping and not a line directive for these, which would solve the problem in the IDE, but wouldn't solve CLI build diagnostics (which are presumably broken), and would mean adding a new property to IntermediateNode so wanted to see if anyone cared first.

? directiveToken.Source
: null)
}
},
IntermediateNodeFactory.CSharpToken(";")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,24 +143,26 @@ private protected void UpdateClientLSPInitializationOptions(Func<RemoteClientLSP
}
}

protected abstract TextDocument CreateProjectAndRazorDocument(
private protected abstract TextDocument CreateProjectAndRazorDocument(
string contents,
RazorFileKind? fileKind = null,
string? documentFilePath = null,
(string fileName, string contents)[]? additionalFiles = null,
bool inGlobalNamespace = false,
bool miscellaneousFile = false,
bool addDefaultImports = true);
bool addDefaultImports = true,
Action<RazorProjectBuilder>? projectConfigure = null);

protected TextDocument CreateProjectAndRazorDocument(
private protected TextDocument CreateProjectAndRazorDocument(
CodeAnalysis.Workspace remoteWorkspace,
string contents,
RazorFileKind? fileKind = null,
string? documentFilePath = null,
(string fileName, string contents)[]? additionalFiles = null,
bool inGlobalNamespace = false,
bool miscellaneousFile = false,
bool addDefaultImports = true)
bool addDefaultImports = true,
Action<RazorProjectBuilder>? projectConfigure = null)
{
// Using IsLegacy means null == component, so easier for test authors
var isComponent = fileKind != RazorFileKind.Legacy;
Expand All @@ -172,18 +174,23 @@ protected TextDocument CreateProjectAndRazorDocument(
var projectId = ProjectId.CreateNewId(debugName: TestProjectData.SomeProject.DisplayName);
var documentId = DocumentId.CreateNewId(projectId, debugName: documentFilePath);

return CreateProjectAndRazorDocument(remoteWorkspace, projectId, miscellaneousFile, documentId, documentFilePath, contents, additionalFiles, inGlobalNamespace, addDefaultImports);
return CreateProjectAndRazorDocument(remoteWorkspace, projectId, miscellaneousFile, documentId, documentFilePath, contents, additionalFiles, inGlobalNamespace, addDefaultImports, projectConfigure);
}

protected static TextDocument CreateProjectAndRazorDocument(CodeAnalysis.Workspace workspace, ProjectId projectId, bool miscellaneousFile, DocumentId documentId, string documentFilePath, string contents, (string fileName, string contents)[]? additionalFiles, bool inGlobalNamespace, bool addDefaultImports)
private protected static TextDocument CreateProjectAndRazorDocument(CodeAnalysis.Workspace workspace, ProjectId projectId, bool miscellaneousFile, DocumentId documentId, string documentFilePath, string contents, (string fileName, string contents)[]? additionalFiles, bool inGlobalNamespace, bool addDefaultImports, Action<RazorProjectBuilder>? projectConfigure)
{
return AddProjectAndRazorDocument(workspace.CurrentSolution, TestProjectData.SomeProject.FilePath, projectId, miscellaneousFile, documentId, documentFilePath, contents, additionalFiles, inGlobalNamespace, addDefaultImports);
return AddProjectAndRazorDocument(workspace.CurrentSolution, TestProjectData.SomeProject.FilePath, projectId, miscellaneousFile, documentId, documentFilePath, contents, additionalFiles, inGlobalNamespace, addDefaultImports, projectConfigure);
}

protected static TextDocument AddProjectAndRazorDocument(Solution solution, [DisallowNull] string? projectFilePath, ProjectId projectId, bool miscellaneousFile, DocumentId documentId, string documentFilePath, string contents, (string fileName, string contents)[]? additionalFiles, bool inGlobalNamespace, bool addDefaultImports)
private protected static TextDocument AddProjectAndRazorDocument(Solution solution, [DisallowNull] string? projectFilePath, ProjectId projectId, bool miscellaneousFile, DocumentId documentId, string documentFilePath, string contents, (string fileName, string contents)[]? additionalFiles, bool inGlobalNamespace, bool addDefaultImports, Action<RazorProjectBuilder>? projectConfigure)
{
var builder = new RazorProjectBuilder(projectId);

if (projectConfigure is not null)
{
projectConfigure(builder);
}

builder.AddReferences(miscellaneousFile
? Net461.ReferenceInfos.All.Select(r => r.Reference) // This isn't quite what Roslyn does, but its close enough for our tests
: AspNet80.ReferenceInfos.All.Select(r => r.Reference));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Text;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
Expand Down Expand Up @@ -40,6 +41,8 @@ public string? ProjectFilePath
public bool GenerateMSBuildProjectDirectory { get; set; } = true;
public bool GenerateAdditionalDocumentMetadata { get; set; } = true;

public RazorLanguageVersion RazorLanguageVersion { get; set; } = FallbackRazorConfiguration.Latest.LanguageVersion;

private readonly List<PortableExecutableReference> _references = [];
private readonly List<(DocumentId id, string name, SourceText text, string filePath)> _documents = [];
private readonly List<(DocumentId id, string name, SourceText text, string filePath)> _additionalDocuments = [];
Expand Down Expand Up @@ -111,7 +114,7 @@ public Solution Build(Solution solution)
globalConfigContent.AppendLine($"""
is_global = true

build_property.RazorLangVersion = {FallbackRazorConfiguration.Latest.LanguageVersion}
build_property.RazorLangVersion = {RazorLanguageVersion}
build_property.RazorConfiguration = {FallbackRazorConfiguration.Latest.ConfigurationName}
build_property.RootNamespace = {RootNamespace}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,18 @@ protected TextDocument CreateProjectAndRazorDocument(
return this.CreateProjectAndRazorDocument(contents);
}

protected override TextDocument CreateProjectAndRazorDocument(
private protected override TextDocument CreateProjectAndRazorDocument(
string contents,
RazorFileKind? fileKind = null,
string? documentFilePath = null,
(string fileName, string contents)[]? additionalFiles = null,
bool inGlobalNamespace = false,
bool miscellaneousFile = false,
bool addDefaultImports = true)
bool addDefaultImports = true,
Action<RazorProjectBuilder>? projectConfigure = null)
{
var remoteWorkspace = RemoteWorkspaceProvider.Instance.GetWorkspace();
var remoteDocument = base.CreateProjectAndRazorDocument(remoteWorkspace, contents, fileKind, documentFilePath, additionalFiles, inGlobalNamespace, miscellaneousFile, addDefaultImports);
var remoteDocument = base.CreateProjectAndRazorDocument(remoteWorkspace, contents, fileKind, documentFilePath, additionalFiles, inGlobalNamespace, miscellaneousFile, addDefaultImports, projectConfigure);

// In this project we simulate remote services running OOP by creating a different workspace with a different
// set of services to represent the devenv Roslyn side of things. We don't have any actual solution syncing set
Expand All @@ -116,7 +117,8 @@ protected override TextDocument CreateProjectAndRazorDocument(
contents,
additionalFiles,
inGlobalNamespace,
addDefaultImports);
addDefaultImports,
projectConfigure);
}

private TextDocument CreateLocalProjectAndRazorDocument(
Expand All @@ -128,9 +130,10 @@ private TextDocument CreateLocalProjectAndRazorDocument(
string contents,
(string fileName, string contents)[]? additionalFiles,
bool inGlobalNamespace,
bool addDefaultImports)
bool addDefaultImports,
Action<RazorProjectBuilder>? projectConfigure)
{
var razorDocument = CreateProjectAndRazorDocument(LocalWorkspace, projectId, miscellaneousFile, documentId, documentFilePath, contents, additionalFiles, inGlobalNamespace, addDefaultImports);
var razorDocument = CreateProjectAndRazorDocument(LocalWorkspace, projectId, miscellaneousFile, documentId, documentFilePath, contents, additionalFiles, inGlobalNamespace, addDefaultImports, projectConfigure);

// If we're creating remote and local workspaces, then we'll return the local document, and have to allow
// the remote service invoker to map from the local solution to the remote one.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public async Task HoverRequest_MultipleProjects_ReturnsResults()
var projectId = ProjectId.CreateNewId(debugName: TestProjectData.SomeProject.DisplayName);
var documentFilePath = TestProjectData.AnotherProjectComponentFile1.FilePath;
var documentId = DocumentId.CreateNewId(projectId, debugName: documentFilePath);
var otherDocument = AddProjectAndRazorDocument(document.Project.Solution, TestProjectData.AnotherProject.FilePath, projectId, miscellaneousFile: false, documentId, documentFilePath, otherInput.Text, additionalFiles: null, inGlobalNamespace: false, addDefaultImports: true);
var otherDocument = AddProjectAndRazorDocument(document.Project.Solution, TestProjectData.AnotherProject.FilePath, projectId, miscellaneousFile: false, documentId, documentFilePath, otherInput.Text, additionalFiles: null, inGlobalNamespace: false, addDefaultImports: true, projectConfigure: null);

// Make sure we have the document from our new fork
document = otherDocument.Project.Solution.GetAdditionalDocument(document.Id).AssumeNotNull();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Line Δ, Char Δ, Length, Type, Modifier(s), Text
0 0 1 razorTransition [] [@]
0 1 10 razorDirective [] [rendermode]
0 11 9 namespace name [] [Microsoft]
0 9 1 operator [] [.]
0 1 10 namespace name [] [AspNetCore]
0 10 1 operator [] [.]
0 1 10 namespace name [] [Components]
0 10 1 operator [] [.]
0 1 3 namespace name [] [Web]
0 3 1 operator [] [.]
0 1 10 class name [static] [RenderMode]
0 10 1 operator [] [.]
0 1 17 property name [static] [InteractiveServer]
2 0 4 markupCommentPunctuation [] [<!--]
0 4 47 markupComment [] [ above and below should be classified the same ]
0 47 3 markupCommentPunctuation [] [-->]
2 0 1 razorTransition [] [@]
0 1 1 razorTransition [] [{]
1 4 3 keyword [] [var]
0 4 1 local name [] [r]
0 2 1 operator [] [=]
0 2 9 namespace name [] [Microsoft]
0 9 1 operator [] [.]
0 1 10 namespace name [] [AspNetCore]
0 10 1 operator [] [.]
0 1 10 namespace name [] [Components]
0 10 1 operator [] [.]
0 1 3 namespace name [] [Web]
0 3 1 operator [] [.]
0 1 10 class name [static] [RenderMode]
0 10 1 operator [] [.]
0 1 15 property name [static] [InteractiveAuto]
0 15 1 punctuation [] [;]
1 0 1 razorTransition [] [}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Line Δ, Char Δ, Length, Type, Modifier(s), Text
0 0 1 razorTransition [] [@]
0 1 10 razorDirective [] [rendermode]
0 11 1 razorTransition [] [@]
0 1 1 razorTransition [] [(]
0 1 9 namespace name [] [Microsoft]
0 9 1 operator [] [.]
0 1 10 namespace name [] [AspNetCore]
0 10 1 operator [] [.]
0 1 10 namespace name [] [Components]
0 10 1 operator [] [.]
0 1 3 namespace name [] [Web]
0 3 1 operator [] [.]
0 1 10 class name [static] [RenderMode]
0 10 1 operator [] [.]
0 1 17 property name [static] [InteractiveServer]
0 17 1 razorTransition [] [)]
2 0 4 markupCommentPunctuation [] [<!--]
0 4 47 markupComment [] [ above and below should be classified the same ]
0 47 3 markupCommentPunctuation [] [-->]
2 0 1 razorTransition [] [@]
0 1 1 razorTransition [] [{]
1 4 3 keyword [] [var]
0 4 1 local name [] [r]
0 2 1 operator [] [=]
0 2 9 namespace name [] [Microsoft]
0 9 1 operator [] [.]
0 1 10 namespace name [] [AspNetCore]
0 10 1 operator [] [.]
0 1 10 namespace name [] [Components]
0 10 1 operator [] [.]
0 1 3 namespace name [] [Web]
0 3 1 operator [] [.]
0 1 10 class name [static] [RenderMode]
0 10 1 operator [] [.]
0 1 15 property name [static] [InteractiveAuto]
0 15 1 punctuation [] [;]
1 0 1 razorTransition [] [}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Line Δ, Char Δ, Length, Type, Modifier(s), Text
0 0 1 razorTransition [] [@]
0 1 10 razorDirective [] [rendermode]
0 11 1 razorTransition [] [@]
0 1 1 razorTransition [] [(]
0 1 9 namespace name [] [Microsoft]
0 9 1 operator [] [.]
0 1 10 variable [] [AspNetCore]
0 10 1 operator [] [.]
0 1 10 variable [] [Components]
0 10 1 operator [] [.]
0 1 3 variable [] [Web]
0 3 1 operator [] [.]
0 1 10 variable [] [RenderMode]
0 10 1 operator [] [.]
0 1 17 variable [] [InteractiveServer]
0 17 1 razorTransition [] [)]
2 0 4 markupCommentPunctuation [] [<!--]
0 4 47 markupComment [] [ above and below should be classified the same ]
0 47 3 markupCommentPunctuation [] [-->]
2 0 1 razorTransition [] [@]
0 1 1 razorTransition [] [{]
1 4 3 keyword [] [var]
0 4 1 local name [] [r]
0 2 1 operator [] [=]
0 2 9 namespace name [] [Microsoft]
0 9 1 operator [] [.]
0 1 10 variable [] [AspNetCore]
0 10 1 operator [] [.]
0 1 10 variable [] [Components]
0 10 1 operator [] [.]
0 1 3 variable [] [Web]
0 3 1 operator [] [.]
0 1 10 variable [] [RenderMode]
0 10 1 operator [] [.]
0 1 15 variable [] [InteractiveAuto]
0 15 1 punctuation [] [;]
1 0 1 razorTransition [] [}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Line Δ, Char Δ, Length, Type, Modifier(s), Text
0 0 1 razorTransition [] [@]
0 1 10 razorDirective [] [rendermode]
0 11 1 razorTransition [] [@]
0 1 1 razorTransition [razorCode] [(]
0 1 9 namespace name [razorCode] [Microsoft]
0 9 1 operator [razorCode] [.]
0 1 10 namespace name [razorCode] [AspNetCore]
0 10 1 operator [razorCode] [.]
0 1 10 namespace name [razorCode] [Components]
0 10 1 operator [razorCode] [.]
0 1 3 namespace name [razorCode] [Web]
0 3 1 operator [razorCode] [.]
0 1 10 class name [static, razorCode] [RenderMode]
0 10 1 operator [razorCode] [.]
0 1 17 property name [static, razorCode] [InteractiveServer]
0 17 1 razorTransition [razorCode] [)]
2 0 4 markupCommentPunctuation [] [<!--]
0 4 47 markupComment [] [ above and below should be classified the same ]
0 47 3 markupCommentPunctuation [] [-->]
2 0 1 razorTransition [razorCode] [@]
0 1 1 razorTransition [razorCode] [{]
1 0 4 markupTextLiteral [razorCode] [ ]
0 4 3 keyword [razorCode] [var]
0 3 1 markupTextLiteral [razorCode] [ ]
0 1 1 local name [razorCode] [r]
0 1 1 markupTextLiteral [razorCode] [ ]
0 1 1 operator [razorCode] [=]
0 1 1 markupTextLiteral [razorCode] [ ]
0 1 9 namespace name [razorCode] [Microsoft]
0 9 1 operator [razorCode] [.]
0 1 10 namespace name [razorCode] [AspNetCore]
0 10 1 operator [razorCode] [.]
0 1 10 namespace name [razorCode] [Components]
0 10 1 operator [razorCode] [.]
0 1 3 namespace name [razorCode] [Web]
0 3 1 operator [razorCode] [.]
0 1 10 class name [static, razorCode] [RenderMode]
0 10 1 operator [razorCode] [.]
0 1 15 property name [static, razorCode] [InteractiveAuto]
0 15 1 punctuation [razorCode] [;]
1 0 1 razorTransition [razorCode] [}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Line Δ, Char Δ, Length, Type, Modifier(s), Text
0 0 1 razorTransition [] [@]
0 1 10 razorDirective [] [rendermode]
0 11 1 razorTransition [] [@]
0 1 1 razorTransition [razorCode] [(]
0 1 9 namespace name [razorCode] [Microsoft]
0 9 1 operator [razorCode] [.]
0 1 10 variable [razorCode] [AspNetCore]
0 10 1 operator [razorCode] [.]
0 1 10 variable [razorCode] [Components]
0 10 1 operator [razorCode] [.]
0 1 3 variable [razorCode] [Web]
0 3 1 operator [razorCode] [.]
0 1 10 variable [razorCode] [RenderMode]
0 10 1 operator [razorCode] [.]
0 1 17 variable [razorCode] [InteractiveServer]
0 17 1 razorTransition [razorCode] [)]
2 0 4 markupCommentPunctuation [] [<!--]
0 4 47 markupComment [] [ above and below should be classified the same ]
0 47 3 markupCommentPunctuation [] [-->]
2 0 1 razorTransition [razorCode] [@]
0 1 1 razorTransition [razorCode] [{]
1 0 4 markupTextLiteral [razorCode] [ ]
0 4 3 keyword [razorCode] [var]
0 3 1 markupTextLiteral [razorCode] [ ]
0 1 1 local name [razorCode] [r]
0 1 1 markupTextLiteral [razorCode] [ ]
0 1 1 operator [razorCode] [=]
0 1 1 markupTextLiteral [razorCode] [ ]
0 1 9 namespace name [razorCode] [Microsoft]
0 9 1 operator [razorCode] [.]
0 1 10 variable [razorCode] [AspNetCore]
0 10 1 operator [razorCode] [.]
0 1 10 variable [razorCode] [Components]
0 10 1 operator [razorCode] [.]
0 1 3 variable [razorCode] [Web]
0 3 1 operator [razorCode] [.]
0 1 10 variable [razorCode] [RenderMode]
0 10 1 operator [razorCode] [.]
0 1 15 variable [razorCode] [InteractiveAuto]
0 15 1 punctuation [razorCode] [;]
1 0 1 razorTransition [razorCode] [}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Line Δ, Char Δ, Length, Type, Modifier(s), Text
0 0 1 razorTransition [] [@]
0 1 10 razorDirective [] [rendermode]
2 0 4 markupCommentPunctuation [] [<!--]
0 4 64 markupComment [] [ above and below should NOT be classified the same in Razor 9.0 ]
0 64 3 markupCommentPunctuation [] [-->]
2 0 1 razorTransition [] [@]
0 1 1 razorTransition [] [{]
1 4 3 keyword [] [var]
0 4 1 local name [] [r]
0 2 1 operator [] [=]
0 2 9 namespace name [] [Microsoft]
0 9 1 operator [] [.]
0 1 10 namespace name [] [AspNetCore]
0 10 1 operator [] [.]
0 1 10 namespace name [] [Components]
0 10 1 operator [] [.]
0 1 3 namespace name [] [Web]
0 3 1 operator [] [.]
0 1 10 class name [static] [RenderMode]
0 10 1 operator [] [.]
0 1 15 property name [static] [InteractiveAuto]
0 15 1 punctuation [] [;]
1 0 1 razorTransition [] [}]
Loading
Loading