diff --git a/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs b/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs index a10159c8efe1b..f5457cf655138 100644 --- a/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs +++ b/src/Features/Core/Portable/Workspace/MiscellaneousFileUtilities.cs @@ -27,7 +27,11 @@ internal static ProjectInfo CreateMiscellaneousProjectInfoForDocument( var fileExtension = PathUtilities.GetExtension(filePath); var fileName = PathUtilities.GetFileName(filePath); - var languageServices = services.GetLanguageServices(languageInformation.LanguageName); + // For Razor files we need to override the language name to C# as thats what code is generated + var isRazor = languageInformation.LanguageName == "Razor"; + var languageName = isRazor ? LanguageNames.CSharp : languageInformation.LanguageName; + + var languageServices = services.GetLanguageServices(languageName); var compilationOptions = languageServices.GetService()?.GetDefaultCompilationOptions(); // Use latest language version which is more permissive, as we cannot find out language version of the project which the file belongs to @@ -63,7 +67,7 @@ internal static ProjectInfo CreateMiscellaneousProjectInfoForDocument( version: VersionStamp.Create(), name: FeaturesResources.Miscellaneous_Files, assemblyName: assemblyName, - language: languageInformation.LanguageName, + language: languageName, compilationOutputInfo: default, checksumAlgorithm: checksumAlgorithm, // Miscellaneous files projects are never fully loaded since, by definition, it won't know @@ -71,7 +75,8 @@ internal static ProjectInfo CreateMiscellaneousProjectInfoForDocument( hasAllInformation: sourceCodeKind == SourceCodeKind.Script), compilationOptions: compilationOptions, parseOptions: parseOptions, - documents: [documentInfo], + documents: isRazor ? null : [documentInfo], + additionalDocuments: isRazor ? [documentInfo] : null, metadataReferences: metadataReferences); return projectInfo; diff --git a/src/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs b/src/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs index e74031f9958a3..6baf6e5f6c967 100644 --- a/src/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs +++ b/src/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs @@ -36,7 +36,7 @@ internal sealed class LspMiscellaneousFilesWorkspace(ILspServices lspServices, I /// Calls to this method and are made /// from LSP text sync request handling which do not run concurrently. /// - public Document? AddMiscellaneousDocument(Uri uri, SourceText documentText, string languageId, ILspLogger logger) + public TextDocument? AddMiscellaneousDocument(Uri uri, SourceText documentText, string languageId, ILspLogger logger) { var documentFilePath = ProtocolConversions.GetDocumentFilePathFromUri(uri); @@ -63,6 +63,12 @@ internal sealed class LspMiscellaneousFilesWorkspace(ILspServices lspServices, I this, documentFilePath, sourceTextLoader, languageInformation, documentText.ChecksumAlgorithm, Services.SolutionServices, []); OnProjectAdded(projectInfo); + if (languageInformation.LanguageName == "Razor") + { + var docId = projectInfo.AdditionalDocuments.Single().Id; + return CurrentSolution.GetRequiredAdditionalDocument(docId); + } + var id = projectInfo.Documents.Single().Id; return CurrentSolution.GetRequiredDocument(id); } diff --git a/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs b/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs index b31700c4ea598..5c06a1171e131 100644 --- a/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs @@ -180,6 +180,34 @@ void M() Assert.Null(GetMiscellaneousDocument(testLspServer)); } + [Theory, CombinatorialData] + public async Task TestLooseFile_RazorFile(bool mutatingLspWorkspace) + { + var source = "
"; + + // Create a server that supports LSP misc files and verify no misc files present. + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); + Assert.Null(GetMiscellaneousDocument(testLspServer)); + Assert.Null(GetMiscellaneousAdditionalDocument(testLspServer)); + + // Open an empty loose file and make a request to verify it gets added to the misc workspace. + var looseFileUri = ProtocolConversions.CreateAbsoluteUri(@"C:\SomeFile.razor"); + await testLspServer.OpenDocumentAsync(looseFileUri, source).ConfigureAwait(false); + + // Trigger a request and assert we got a file in the misc workspace. + await AssertFileInMiscWorkspaceAsync(testLspServer, looseFileUri).ConfigureAwait(false); + Assert.Null(GetMiscellaneousDocument(testLspServer)); + Assert.NotNull(GetMiscellaneousAdditionalDocument(testLspServer)); + + // Trigger another request and assert we got a file in the misc workspace. + await AssertFileInMiscWorkspaceAsync(testLspServer, looseFileUri).ConfigureAwait(false); + Assert.NotNull(GetMiscellaneousAdditionalDocument(testLspServer)); + + await testLspServer.CloseDocumentAsync(looseFileUri).ConfigureAwait(false); + Assert.Null(GetMiscellaneousDocument(testLspServer)); + Assert.Null(GetMiscellaneousAdditionalDocument(testLspServer)); + } + [Theory, CombinatorialData] public async Task TestLooseFile_RequestedTwiceAndClosed(bool mutatingLspWorkspace) { @@ -306,7 +334,12 @@ private static async Task AssertFileInMainWorkspaceAsync(TestLspServer testLspSe private static Document? GetMiscellaneousDocument(TestLspServer testLspServer) { - return testLspServer.GetManagerAccessor().GetLspMiscellaneousFilesWorkspace()!.CurrentSolution.Projects.SingleOrDefault()?.Documents.Single(); + return testLspServer.GetManagerAccessor().GetLspMiscellaneousFilesWorkspace()!.CurrentSolution.Projects.SingleOrDefault()?.Documents.SingleOrDefault(); + } + + private static TextDocument? GetMiscellaneousAdditionalDocument(TestLspServer testLspServer) + { + return testLspServer.GetManagerAccessor().GetLspMiscellaneousFilesWorkspace()!.CurrentSolution.Projects.SingleOrDefault()?.AdditionalDocuments.SingleOrDefault(); } private static async Task RunGetHoverAsync(TestLspServer testLspServer, LSP.Location caret)