From 8b1dabe34289c5e7a3d0d0c4c6f888ec384e6828 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 29 Apr 2024 14:00:36 +1000 Subject: [PATCH 01/10] If we don't have any project info, then just add to the misc files project --- .../ProjectSystem/RazorProjectService.cs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) 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 c725852e10c..14f10ab46b9 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs @@ -57,18 +57,9 @@ private async Task AddDocumentNeedsLocksAsync(string filePath, CancellationToken { var textDocumentPath = FilePathNormalizer.Normalize(filePath); - var added = false; - foreach (var projectSnapshot in _snapshotResolver.FindPotentialProjects(textDocumentPath)) - { - added = true; - await AddDocumentToProjectAsync(projectSnapshot, textDocumentPath, cancellationToken).ConfigureAwait(false); - } - - if (!added) - { - var miscFilesProject = await _snapshotResolver.GetMiscellaneousProjectAsync(cancellationToken).ConfigureAwait(false); - await AddDocumentToProjectAsync(miscFilesProject, textDocumentPath, cancellationToken).ConfigureAwait(false); - } + _logger.LogDebug($"Adding {filePath} to the miscellaneous files project, because we don't have project info (yet?)"); + var miscFilesProject = await _snapshotResolver.GetMiscellaneousProjectAsync(cancellationToken).ConfigureAwait(false); + await AddDocumentToProjectAsync(miscFilesProject, textDocumentPath, cancellationToken).ConfigureAwait(false); async Task AddDocumentToProjectAsync(IProjectSnapshot projectSnapshot, string textDocumentPath, CancellationToken cancellationToken) { From 499eff368bfa4dbbc307e78565616f178f83b73f Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 29 Apr 2024 14:03:16 +1000 Subject: [PATCH 02/10] Rename methods --- .../ProjectSystem/IRazorProjectService.cs | 2 +- .../ProjectSystem/RazorProjectService.cs | 8 ++--- .../RazorFileSynchronizer.cs | 2 +- .../DefaultRazorComponentSearchEngineTest.cs | 6 ++-- .../RazorFileSynchronizerTest.cs | 4 +-- .../RazorProjectServiceTest.cs | 36 +++++++++---------- .../Refactoring/RenameEndpointTest.cs | 18 +++++----- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/IRazorProjectService.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/IRazorProjectService.cs index fad6a15871a..6529e4dd0f9 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/IRazorProjectService.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/IRazorProjectService.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem; internal interface IRazorProjectService { - Task AddDocumentAsync(string filePath, CancellationToken cancellationToken); + Task AddDocumentToMiscProjectAsync(string filePath, CancellationToken cancellationToken); Task OpenDocumentAsync(string filePath, SourceText sourceText, int version, CancellationToken cancellationToken); Task UpdateDocumentAsync(string filePath, SourceText sourceText, int version, CancellationToken cancellationToken); Task CloseDocumentAsync(string filePath, CancellationToken cancellationToken); 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 14f10ab46b9..d9fae976855 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs @@ -46,14 +46,14 @@ public void Dispose() _gate.Dispose(); } - public async Task AddDocumentAsync(string filePath, CancellationToken cancellationToken) + public async Task AddDocumentToMiscProjectAsync(string filePath, CancellationToken cancellationToken) { using var _ = await _gate.EnterAsync(cancellationToken).ConfigureAwait(false); - await AddDocumentNeedsLocksAsync(filePath, cancellationToken).ConfigureAwait(false); + await AddDocumentToMiscProjectNeedsLocksAsync(filePath, cancellationToken).ConfigureAwait(false); } - private async Task AddDocumentNeedsLocksAsync(string filePath, CancellationToken cancellationToken) + private async Task AddDocumentToMiscProjectNeedsLocksAsync(string filePath, CancellationToken cancellationToken) { var textDocumentPath = FilePathNormalizer.Normalize(filePath); @@ -122,7 +122,7 @@ public async Task OpenDocumentAsync(string filePath, SourceText sourceText, int { // Document hasn't been added. This usually occurs when VSCode trumps all other initialization // processes and pre-initializes already open documents. - await AddDocumentNeedsLocksAsync(filePath, cancellationToken).ConfigureAwait(false); + await AddDocumentToMiscProjectNeedsLocksAsync(filePath, cancellationToken).ConfigureAwait(false); } await ActOnDocumentInMultipleProjectsAsync( diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorFileSynchronizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorFileSynchronizer.cs index 9365f5a251d..0bd75c62689 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorFileSynchronizer.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorFileSynchronizer.cs @@ -17,7 +17,7 @@ internal class RazorFileSynchronizer(IRazorProjectService projectService) : IRaz public Task RazorFileChangedAsync(string filePath, RazorFileChangeKind kind, CancellationToken cancellationToken) => kind switch { - RazorFileChangeKind.Added => _projectService.AddDocumentAsync(filePath, cancellationToken), + RazorFileChangeKind.Added => _projectService.AddDocumentToMiscProjectAsync(filePath, cancellationToken), RazorFileChangeKind.Removed => _projectService.RemoveDocumentAsync(filePath, cancellationToken), _ => Task.CompletedTask }; diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DefaultRazorComponentSearchEngineTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DefaultRazorComponentSearchEngineTest.cs index 7bcf0743ab0..da60ae13339 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DefaultRazorComponentSearchEngineTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DefaultRazorComponentSearchEngineTest.cs @@ -77,10 +77,10 @@ await projectService.AddProjectAsync( displayName: "", DisposalToken); - await projectService.AddDocumentAsync(s_componentFilePath1, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath1, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath1, SourceText.From(""), version: 1, DisposalToken); - await projectService.AddDocumentAsync(s_componentFilePath2, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath2, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath2, SourceText.From("@namespace Test"), version: 1, DisposalToken); await projectService.AddProjectAsync( @@ -91,7 +91,7 @@ await projectService.AddProjectAsync( displayName: "", DisposalToken); - await projectService.AddDocumentAsync(s_componentFilePath3, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath3, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath3, SourceText.From(""), version: 1, DisposalToken); } diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorFileSynchronizerTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorFileSynchronizerTest.cs index 4871b27d660..74c9ad85dea 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorFileSynchronizerTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorFileSynchronizerTest.cs @@ -22,7 +22,7 @@ public async Task RazorFileChanged_Added_AddsRazorDocument() var filePath = "/path/to/file.razor"; var projectService = new StrictMock(); projectService - .Setup(service => service.AddDocumentAsync(filePath, It.IsAny())) + .Setup(service => service.AddDocumentToMiscProjectAsync(filePath, It.IsAny())) .Returns(Task.CompletedTask) .Verifiable(); var synchronizer = new RazorFileSynchronizer(projectService.Object); @@ -41,7 +41,7 @@ public async Task RazorFileChanged_Added_AddsCSHTMLDocument() var filePath = "/path/to/file.cshtml"; var projectService = new StrictMock(); projectService - .Setup(service => service.AddDocumentAsync(filePath, It.IsAny())) + .Setup(service => service.AddDocumentToMiscProjectAsync(filePath, It.IsAny())) .Returns(Task.CompletedTask) .Verifiable(); var synchronizer = new RazorFileSynchronizer(projectService.Object); 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 2a029d5c23f..7acebd44c2a 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs @@ -439,7 +439,7 @@ public async Task CloseDocument_ClosesDocumentInOwnerProject() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -472,7 +472,7 @@ public async Task CloseDocument_ClosesDocumentInAllOwnerProjects() ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); var ownerProjectKey2 = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var ownerProject1 = _projectManager.GetLoadedProject(ownerProjectKey1); @@ -499,7 +499,7 @@ public async Task CloseDocument_ClosesDocumentInMiscellaneousProject() // Arrange const string DocumentFilePath = "document.cshtml"; - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); @@ -529,7 +529,7 @@ public async Task OpenDocument_OpensAlreadyAddedDocumentInOwnerProject() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -561,7 +561,7 @@ public async Task OpenDocument_OpensAlreadyAddedDocumentInAllOwnerProjects() ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); var ownerProjectKey2 = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); var ownerProject1 = _projectManager.GetLoadedProject(ownerProjectKey1); var ownerProject2 = _projectManager.GetLoadedProject(ownerProjectKey2); @@ -587,7 +587,7 @@ public async Task OpenDocument_OpensAlreadyAddedDocumentInMiscellaneousProject() // Arrange const string DocumentFilePath = "document.cshtml"; - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); @@ -638,14 +638,14 @@ public async Task AddDocument_NoopsIfDocumentIsAlreadyAdded() // Arrange const string DocumentFilePath = "document.cshtml"; - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); using var listener = _projectManager.ListenToNotifications(); // Act - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); // Assert listener.AssertNoNotifications(); @@ -668,7 +668,7 @@ public async Task AddDocument_AddsDocumentToOwnerProject() using var listener = _projectManager.ListenToNotifications(); // Act - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); // Assert listener.AssertNotifications( @@ -688,7 +688,7 @@ public async Task AddDocument_AddsDocumentToMiscellaneousProject() using var listener = _projectManager.ListenToNotifications(); // Act - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); // Assert listener.AssertNotifications( @@ -708,7 +708,7 @@ public async Task RemoveDocument_RemovesDocumentFromOwnerProject() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -738,7 +738,7 @@ public async Task RemoveDocument_RemovesDocumentFromAllOwnerProjects() ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); var ownerProjectKey2 = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); var ownerProject1 = _projectManager.GetLoadedProject(ownerProjectKey1); var ownerProject2 = _projectManager.GetLoadedProject(ownerProjectKey2); @@ -767,7 +767,7 @@ public async Task RemoveOpenDocument_RemovesDocumentFromOwnerProject_MovesToMisc var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -794,7 +794,7 @@ public async Task RemoveDocument_RemovesDocumentFromMiscellaneousProject() // Arrange const string DocumentFilePath = "document.cshtml"; - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); @@ -863,7 +863,7 @@ public async Task UpdateDocument_ChangesDocumentInOwnerProject() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -896,7 +896,7 @@ public async Task UpdateDocument_ChangesDocumentInAllOwnerProjects() ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); var ownerProjectKey2 = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var ownerProject1 = _projectManager.GetLoadedProject(ownerProjectKey1); @@ -923,7 +923,7 @@ public async Task UpdateDocument_ChangesDocumentInMiscProject() // Arrange const string DocumentFilePath = "document.cshtml"; - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); @@ -953,7 +953,7 @@ public async Task UpdateDocument_TracksKnownDocumentVersion() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs index 45ec426d786..106b0032753 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs @@ -644,12 +644,12 @@ await projectManager.UpdateAsync(updater => updater.ProjectWorkspaceStateChanged(projectKey1, ProjectWorkspaceState.Create(tagHelpers)); }); - await projectService.AddDocumentAsync(s_componentFilePath1, DisposalToken); - await projectService.AddDocumentAsync(s_componentFilePath2, DisposalToken); - await projectService.AddDocumentAsync(s_directoryFilePath1, DisposalToken); - await projectService.AddDocumentAsync(s_directoryFilePath2, DisposalToken); - await projectService.AddDocumentAsync(s_componentFilePath1337, DisposalToken); - await projectService.AddDocumentAsync(s_indexFilePath1, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath1, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath2, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_directoryFilePath1, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_directoryFilePath2, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath1337, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_indexFilePath1, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath1, SourceText.From(ComponentText1), version: 42, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath2, SourceText.From(ComponentText2), version: 42, DisposalToken); @@ -666,9 +666,9 @@ await projectManager.UpdateAsync(updater => updater.ProjectWorkspaceStateChanged(projectKey2, ProjectWorkspaceState.Create(tagHelpers)); }); - await projectService.AddDocumentAsync(s_componentFilePath3, DisposalToken); - await projectService.AddDocumentAsync(s_componentFilePath4, DisposalToken); - await projectService.AddDocumentAsync(s_componentWithParamFilePath, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath3, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath4, DisposalToken); + await projectService.AddDocumentToMiscProjectAsync(s_componentWithParamFilePath, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath3, SourceText.From(ComponentText3), version: 42, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath4, SourceText.From(ComponentText4), version: 42, DisposalToken); From 8a31344b9add29b678033bbc9ecb82159ab8e74a Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 29 Apr 2024 14:05:02 +1000 Subject: [PATCH 03/10] Remove unnecessary local method --- .../ProjectSystem/RazorProjectService.cs | 66 +++++++++---------- 1 file changed, 31 insertions(+), 35 deletions(-) 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 d9fae976855..b479b050b9f 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs @@ -59,49 +59,45 @@ private async Task AddDocumentToMiscProjectNeedsLocksAsync(string filePath, Canc _logger.LogDebug($"Adding {filePath} to the miscellaneous files project, because we don't have project info (yet?)"); var miscFilesProject = await _snapshotResolver.GetMiscellaneousProjectAsync(cancellationToken).ConfigureAwait(false); - await AddDocumentToProjectAsync(miscFilesProject, textDocumentPath, cancellationToken).ConfigureAwait(false); - async Task AddDocumentToProjectAsync(IProjectSnapshot projectSnapshot, string textDocumentPath, CancellationToken cancellationToken) + if (miscFilesProject.GetDocument(FilePathNormalizer.Normalize(textDocumentPath)) is not null) { - if (projectSnapshot.GetDocument(FilePathNormalizer.Normalize(textDocumentPath)) is not null) - { - // Document already added. This usually occurs when VSCode has already pre-initialized - // open documents and then we try to manually add all known razor documents. - return; - } + // Document already added. This usually occurs when VSCode has already pre-initialized + // open documents and then we try to manually add all known razor documents. + return; + } - var targetFilePath = textDocumentPath; - var projectDirectory = FilePathNormalizer.GetNormalizedDirectoryName(projectSnapshot.FilePath); - if (targetFilePath.StartsWith(projectDirectory, FilePathComparison.Instance)) - { - // Make relative - targetFilePath = textDocumentPath[projectDirectory.Length..]; - } + var targetFilePath = textDocumentPath; + var projectDirectory = FilePathNormalizer.GetNormalizedDirectoryName(miscFilesProject.FilePath); + if (targetFilePath.StartsWith(projectDirectory, FilePathComparison.Instance)) + { + // Make relative + targetFilePath = textDocumentPath[projectDirectory.Length..]; + } - // Representing all of our host documents with a re-normalized target path to workaround GetRelatedDocument limitations. - var normalizedTargetFilePath = targetFilePath.Replace('/', '\\').TrimStart('\\'); + // Representing all of our host documents with a re-normalized target path to workaround GetRelatedDocument limitations. + var normalizedTargetFilePath = targetFilePath.Replace('/', '\\').TrimStart('\\'); - var hostDocument = new HostDocument(textDocumentPath, normalizedTargetFilePath); - var textLoader = _remoteTextLoaderFactory.Create(textDocumentPath); + var hostDocument = new HostDocument(textDocumentPath, normalizedTargetFilePath); + var textLoader = _remoteTextLoaderFactory.Create(textDocumentPath); - _logger.LogInformation($"Adding document '{filePath}' to project '{projectSnapshot.Key}'."); + _logger.LogInformation($"Adding document '{filePath}' to project '{miscFilesProject.Key}'."); - await _projectManager - .UpdateAsync( - static (updater, state) => updater.DocumentAdded(state.key, state.hostDocument, state.textLoader), - state: (key: projectSnapshot.Key, hostDocument, textLoader), - cancellationToken) - .ConfigureAwait(false); + await _projectManager + .UpdateAsync( + static (updater, state) => updater.DocumentAdded(state.key, state.hostDocument, state.textLoader), + state: (key: miscFilesProject.Key, hostDocument, textLoader), + cancellationToken) + .ConfigureAwait(false); - // Adding a document to a project could also happen because a target was added to a project, or we're moving a document - // from Misc Project to a real one, and means the newly added document could actually already be open. - // If it is, we need to make sure we start generating it so we're ready to handle requests that could start coming in. - if (_projectManager.IsDocumentOpen(textDocumentPath) && - _projectManager.TryGetLoadedProject(projectSnapshot.Key, out var project) && - project.GetDocument(textDocumentPath) is { } document) - { - _ = document.GetGeneratedOutputAsync(); - } + // Adding a document to a project could also happen because a target was added to a project, or we're moving a document + // from Misc Project to a real one, and means the newly added document could actually already be open. + // If it is, we need to make sure we start generating it so we're ready to handle requests that could start coming in. + if (_projectManager.IsDocumentOpen(textDocumentPath) && + _projectManager.TryGetLoadedProject(miscFilesProject.Key, out var project) && + project.GetDocument(textDocumentPath) is { } document) + { + _ = document.GetGeneratedOutputAsync(); } } From 31c9282dbf144d01d94ff52eb07b672cb0cf1e44 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 29 Apr 2024 14:06:22 +1000 Subject: [PATCH 04/10] Remove unnecessary code Since the misc files project is not a real project, no razor file will actually be a child of it. It's not even a real path that exists on disk --- .../ProjectSystem/RazorProjectService.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) 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 b479b050b9f..411e6b221a6 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs @@ -67,16 +67,8 @@ private async Task AddDocumentToMiscProjectNeedsLocksAsync(string filePath, Canc return; } - var targetFilePath = textDocumentPath; - var projectDirectory = FilePathNormalizer.GetNormalizedDirectoryName(miscFilesProject.FilePath); - if (targetFilePath.StartsWith(projectDirectory, FilePathComparison.Instance)) - { - // Make relative - targetFilePath = textDocumentPath[projectDirectory.Length..]; - } - // Representing all of our host documents with a re-normalized target path to workaround GetRelatedDocument limitations. - var normalizedTargetFilePath = targetFilePath.Replace('/', '\\').TrimStart('\\'); + var normalizedTargetFilePath = textDocumentPath.Replace('/', '\\').TrimStart('\\'); var hostDocument = new HostDocument(textDocumentPath, normalizedTargetFilePath); var textLoader = _remoteTextLoaderFactory.Create(textDocumentPath); From 4238928446d514046f906f371ed19510eb45531b Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 29 Apr 2024 15:46:48 +1000 Subject: [PATCH 05/10] Restore functionality in tests Sadly most of the usage of the AddDocument method, that took advantage of the fact that it would try to find a real project, were tests. --- .../DefaultRazorComponentSearchEngineTest.cs | 8 +-- .../RazorProjectServiceTest.cs | 36 ++++++------ .../Refactoring/RenameEndpointTest.cs | 20 +++---- .../TestRazorProjectService.cs | 58 +++++++++++++++++++ 4 files changed, 90 insertions(+), 32 deletions(-) create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TestRazorProjectService.cs diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DefaultRazorComponentSearchEngineTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DefaultRazorComponentSearchEngineTest.cs index da60ae13339..5d9270e17e2 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DefaultRazorComponentSearchEngineTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/DefaultRazorComponentSearchEngineTest.cs @@ -62,7 +62,7 @@ protected override async Task InitializeAsync() return textLoaderMock.Object; }); - var projectService = new RazorProjectService( + var projectService = new TestRazorProjectService( remoteTextLoaderFactoryMock.Object, snapshotResolver, documentVersionCache, @@ -77,10 +77,10 @@ await projectService.AddProjectAsync( displayName: "", DisposalToken); - await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath1, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_componentFilePath1, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath1, SourceText.From(""), version: 1, DisposalToken); - await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath2, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_componentFilePath2, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath2, SourceText.From("@namespace Test"), version: 1, DisposalToken); await projectService.AddProjectAsync( @@ -91,7 +91,7 @@ await projectService.AddProjectAsync( displayName: "", DisposalToken); - await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath3, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_componentFilePath3, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath3, SourceText.From(""), version: 1, DisposalToken); } 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 7acebd44c2a..a325b560726 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs @@ -31,7 +31,7 @@ public class RazorProjectServiceTest : LanguageServerTestBase private readonly TestProjectSnapshotManager _projectManager; private readonly SnapshotResolver _snapshotResolver; private readonly DocumentVersionCache _documentVersionCache; - private readonly RazorProjectService _projectService; + private readonly TestRazorProjectService _projectService; public RazorProjectServiceTest(ITestOutputHelper testOutput) : base(testOutput) @@ -45,7 +45,7 @@ public RazorProjectServiceTest(ITestOutputHelper testOutput) .Setup(x => x.Create(It.IsAny())) .Returns(CreateEmptyTextLoader()); - _projectService = new RazorProjectService( + _projectService = new TestRazorProjectService( remoteTextLoaderFactoryMock.Object, _snapshotResolver, _documentVersionCache, @@ -439,7 +439,7 @@ public async Task CloseDocument_ClosesDocumentInOwnerProject() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -472,7 +472,7 @@ public async Task CloseDocument_ClosesDocumentInAllOwnerProjects() ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); var ownerProjectKey2 = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var ownerProject1 = _projectManager.GetLoadedProject(ownerProjectKey1); @@ -499,7 +499,7 @@ public async Task CloseDocument_ClosesDocumentInMiscellaneousProject() // Arrange const string DocumentFilePath = "document.cshtml"; - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); @@ -529,7 +529,7 @@ public async Task OpenDocument_OpensAlreadyAddedDocumentInOwnerProject() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -561,7 +561,7 @@ public async Task OpenDocument_OpensAlreadyAddedDocumentInAllOwnerProjects() ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); var ownerProjectKey2 = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); var ownerProject1 = _projectManager.GetLoadedProject(ownerProjectKey1); var ownerProject2 = _projectManager.GetLoadedProject(ownerProjectKey2); @@ -638,14 +638,14 @@ public async Task AddDocument_NoopsIfDocumentIsAlreadyAdded() // Arrange const string DocumentFilePath = "document.cshtml"; - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); using var listener = _projectManager.ListenToNotifications(); // Act - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); // Assert listener.AssertNoNotifications(); @@ -668,7 +668,7 @@ public async Task AddDocument_AddsDocumentToOwnerProject() using var listener = _projectManager.ListenToNotifications(); // Act - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); // Assert listener.AssertNotifications( @@ -688,7 +688,7 @@ public async Task AddDocument_AddsDocumentToMiscellaneousProject() using var listener = _projectManager.ListenToNotifications(); // Act - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); // Assert listener.AssertNotifications( @@ -708,7 +708,7 @@ public async Task RemoveDocument_RemovesDocumentFromOwnerProject() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -738,7 +738,7 @@ public async Task RemoveDocument_RemovesDocumentFromAllOwnerProjects() ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); var ownerProjectKey2 = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); var ownerProject1 = _projectManager.GetLoadedProject(ownerProjectKey1); var ownerProject2 = _projectManager.GetLoadedProject(ownerProjectKey2); @@ -767,7 +767,7 @@ public async Task RemoveOpenDocument_RemovesDocumentFromOwnerProject_MovesToMisc var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -863,7 +863,7 @@ public async Task UpdateDocument_ChangesDocumentInOwnerProject() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); @@ -896,7 +896,7 @@ public async Task UpdateDocument_ChangesDocumentInAllOwnerProjects() ProjectFilePath, IntermediateOutputPath1, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); var ownerProjectKey2 = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath2, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var ownerProject1 = _projectManager.GetLoadedProject(ownerProjectKey1); @@ -923,7 +923,7 @@ public async Task UpdateDocument_ChangesDocumentInMiscProject() // Arrange const string DocumentFilePath = "document.cshtml"; - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); await _projectService.OpenDocumentAsync(DocumentFilePath, s_emptyText, version: 42, DisposalToken); var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); @@ -953,7 +953,7 @@ public async Task UpdateDocument_TracksKnownDocumentVersion() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs index 106b0032753..7321a563ba8 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Refactoring/RenameEndpointTest.cs @@ -629,7 +629,7 @@ public async Task Handle_Rename_SingleServer_DoesNotDelegateForRazor() return textLoaderMock.Object; }); - var projectService = new RazorProjectService( + var projectService = new TestRazorProjectService( remoteTextLoaderFactoryMock.Object, snapshotResolver, documentVersionCache, @@ -644,12 +644,12 @@ await projectManager.UpdateAsync(updater => updater.ProjectWorkspaceStateChanged(projectKey1, ProjectWorkspaceState.Create(tagHelpers)); }); - await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath1, DisposalToken); - await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath2, DisposalToken); - await projectService.AddDocumentToMiscProjectAsync(s_directoryFilePath1, DisposalToken); - await projectService.AddDocumentToMiscProjectAsync(s_directoryFilePath2, DisposalToken); - await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath1337, DisposalToken); - await projectService.AddDocumentToMiscProjectAsync(s_indexFilePath1, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_componentFilePath1, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_componentFilePath2, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_directoryFilePath1, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_directoryFilePath2, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_componentFilePath1337, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_indexFilePath1, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath1, SourceText.From(ComponentText1), version: 42, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath2, SourceText.From(ComponentText2), version: 42, DisposalToken); @@ -666,9 +666,9 @@ await projectManager.UpdateAsync(updater => updater.ProjectWorkspaceStateChanged(projectKey2, ProjectWorkspaceState.Create(tagHelpers)); }); - await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath3, DisposalToken); - await projectService.AddDocumentToMiscProjectAsync(s_componentFilePath4, DisposalToken); - await projectService.AddDocumentToMiscProjectAsync(s_componentWithParamFilePath, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_componentFilePath3, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_componentFilePath4, DisposalToken); + await projectService.AddDocumentToPotentialProjectsAsync(s_componentWithParamFilePath, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath3, SourceText.From(ComponentText3), version: 42, DisposalToken); await projectService.UpdateDocumentAsync(s_componentFilePath4, SourceText.From(ComponentText4), version: 42, DisposalToken); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TestRazorProjectService.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TestRazorProjectService.cs new file mode 100644 index 00000000000..bfefe440cfe --- /dev/null +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/TestRazorProjectService.cs @@ -0,0 +1,58 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Language; +using Microsoft.AspNetCore.Razor.LanguageServer.Common; +using Microsoft.AspNetCore.Razor.Serialization; +using Microsoft.AspNetCore.Razor.Utilities; +using Microsoft.CodeAnalysis.Razor; +using Microsoft.CodeAnalysis.Razor.Logging; +using Microsoft.CodeAnalysis.Razor.ProjectSystem; + +namespace Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem; + +internal class TestRazorProjectService( + RemoteTextLoaderFactory remoteTextLoaderFactory, + ISnapshotResolver snapshotResolver, + IDocumentVersionCache documentVersionCache, + IProjectSnapshotManager projectManager, + ILoggerFactory loggerFactory) + : RazorProjectService(remoteTextLoaderFactory, snapshotResolver, documentVersionCache, projectManager, loggerFactory) +{ + private readonly ISnapshotResolver _snapshotResolver = snapshotResolver; + + public async Task AddDocumentToPotentialProjectsAsync(string textDocumentPath, CancellationToken cancellationToken) + { + foreach (var projectSnapshot in _snapshotResolver.FindPotentialProjects(textDocumentPath)) + { + var normalizedProjectPath = FilePathNormalizer.NormalizeDirectory(projectSnapshot.FilePath); + var documents = ImmutableArray + .CreateRange(projectSnapshot.DocumentFilePaths) + .Add(textDocumentPath) + .Select(d => new DocumentSnapshotHandle(d, d, FileKinds.GetFileKindFromFilePath(d))) + .ToImmutableArray(); + + await this.UpdateProjectAsync(projectSnapshot.Key, projectSnapshot.Configuration, projectSnapshot.RootNamespace, projectSnapshot.DisplayName, projectSnapshot.ProjectWorkspaceState, + documents, cancellationToken).ConfigureAwait(false); + } + } + + private static string GetTargetPath(string documentFilePath, string normalizedProjectPath) + { + var targetFilePath = FilePathNormalizer.Normalize(documentFilePath); + if (targetFilePath.StartsWith(normalizedProjectPath, FilePathComparison.Instance)) + { + // Make relative + targetFilePath = documentFilePath[normalizedProjectPath.Length..]; + } + + // Representing all of our host documents with a re-normalized target path to workaround GetRelatedDocument limitations. + var normalizedTargetFilePath = targetFilePath.Replace('/', '\\').TrimStart('\\'); + + return normalizedTargetFilePath; + } +} From 4e7a4073257d620a36de8d2b207ab37a80bc3361 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 29 Apr 2024 15:48:15 +1000 Subject: [PATCH 06/10] Update tests to represent their current behaviour --- .../RazorProjectServiceTest.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 a325b560726..c3ac4f30aa9 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs @@ -606,7 +606,7 @@ public async Task OpenDocument_OpensAlreadyAddedDocumentInMiscellaneousProject() } [Fact] - public async Task OpenDocument_OpensAndAddsDocumentToOwnerProject() + public async Task OpenDocument_OpensAndAddsDocumentToMiscellaneousProject() { // Arrange const string ProjectFilePath = "C:/path/to/project.csproj"; @@ -617,7 +617,7 @@ public async Task OpenDocument_OpensAndAddsDocumentToOwnerProject() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - var ownerProject = _projectManager.GetLoadedProject(ownerProjectKey); + var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); using var listener = _projectManager.ListenToNotifications(); @@ -626,8 +626,8 @@ public async Task OpenDocument_OpensAndAddsDocumentToOwnerProject() // Assert listener.AssertNotifications( - x => x.DocumentAdded(DocumentFilePath, ownerProject.Key), - x => x.DocumentChanged(DocumentFilePath, ownerProject.Key)); + x => x.DocumentAdded(DocumentFilePath, miscProject.Key), + x => x.DocumentChanged(DocumentFilePath, miscProject.Key)); Assert.True(_projectManager.IsDocumentOpen(DocumentFilePath)); } @@ -678,7 +678,7 @@ public async Task AddDocument_AddsDocumentToOwnerProject() } [Fact] - public async Task AddDocument_AddsDocumentToMiscellaneousProject() + public async Task AddDocumentToMiscProjectAsync_AddsDocumentToMiscellaneousProject() { // Arrange const string DocumentFilePath = "document.cshtml"; @@ -688,7 +688,7 @@ public async Task AddDocument_AddsDocumentToMiscellaneousProject() using var listener = _projectManager.ListenToNotifications(); // Act - await _projectService.AddDocumentToPotentialProjectsAsync(DocumentFilePath, DisposalToken); + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); // Assert listener.AssertNotifications( From 26cc4aad7c692feab8312c932bbc138075cb0edc Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 29 Apr 2024 15:49:29 +1000 Subject: [PATCH 07/10] Add new test to fully represent the real world case This was mostly covered by other tests, but they used the project snapshot manager directly. --- .../RazorProjectServiceTest.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) 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 c3ac4f30aa9..d2e269c69c5 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs @@ -182,6 +182,44 @@ await _projectService.UpdateProjectAsync( Assert.Empty(miscProject.DocumentFilePaths); } + [Fact] + public async Task UpdateProject_MovesDocumentsFromMisc_ViaService() + { + // Arrange + const string DocumentFilePath = "C:/path/to/file.cshtml"; + const string ProjectFilePath = "C:/path/to/project.csproj"; + const string IntermediateOutputPath = "C:/path/to/obj"; + const string RootNamespace = "TestRootNamespace"; + + var ownerProjectKey = await _projectService.AddProjectAsync( + ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); + + await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); + + var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); + + var project = _projectManager.GetLoadedProject(ownerProjectKey); + + var addedDocument = new DocumentSnapshotHandle(DocumentFilePath, DocumentFilePath, FileKinds.Legacy); + + // Act + await _projectService.UpdateProjectAsync( + project.Key, + project.Configuration, + project.RootNamespace, + project.DisplayName, + ProjectWorkspaceState.Default, + [addedDocument], + DisposalToken); + + // Assert + project = _projectManager.GetLoadedProject(ownerProjectKey); + var projectFilePaths = project.DocumentFilePaths.OrderBy(path => path); + Assert.Equal(projectFilePaths, [addedDocument.FilePath]); + miscProject = _projectManager.GetLoadedProject(miscProject.Key); + Assert.Empty(miscProject.DocumentFilePaths); + } + [Fact] public async Task UpdateProject_MovesExistingDocumentToMisc() { From 9a5a795a29f521d63f9c7e2ed5b3310a55e909ce Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 29 Apr 2024 15:49:58 +1000 Subject: [PATCH 08/10] Use the LSP project engine factory The default factory can't handle the misc files project, so these tests were failing unnecessarily --- .../RazorProjectServiceTest.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 d2e269c69c5..9202a7cc3a3 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs @@ -36,7 +36,9 @@ public class RazorProjectServiceTest : LanguageServerTestBase public RazorProjectServiceTest(ITestOutputHelper testOutput) : base(testOutput) { - _projectManager = CreateProjectSnapshotManager(); + var optionsMonitor = TestRazorLSPOptionsMonitor.Create(); + var projectEngineFactoryProvider = new LspProjectEngineFactoryProvider(optionsMonitor); + _projectManager = CreateProjectSnapshotManager(projectEngineFactoryProvider); _snapshotResolver = new SnapshotResolver(_projectManager, LoggerFactory); _documentVersionCache = new DocumentVersionCache(_projectManager); From f1846a16f9e458eb194aa0c4217e0e6d8b074a75 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 30 Apr 2024 11:07:48 +1000 Subject: [PATCH 09/10] Fixes after merge --- .../ProjectSystem/RazorProjectService.cs | 6 +++--- .../RazorProjectServiceTest.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) 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 0a13957d0c1..bda63141f8d 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs @@ -50,7 +50,7 @@ private void AddDocumentToMiscProjectCore(ProjectSnapshotManager.Updater updater var textDocumentPath = FilePathNormalizer.Normalize(filePath); _logger.LogDebug($"Adding {filePath} to the miscellaneous files project, because we don't have project info (yet?)"); - var miscFilesProject = await _snapshotResolver.GetMiscellaneousProjectAsync(cancellationToken).ConfigureAwait(false); + var miscFilesProject = _snapshotResolver.GetMiscellaneousProject(); if (miscFilesProject.GetDocument(FilePathNormalizer.Normalize(textDocumentPath)) is not null) { @@ -67,13 +67,13 @@ private void AddDocumentToMiscProjectCore(ProjectSnapshotManager.Updater updater _logger.LogInformation($"Adding document '{filePath}' to project '{miscFilesProject.Key}'."); - updater.DocumentAdded(projectSnapshot.Key, hostDocument, textLoader); + updater.DocumentAdded(miscFilesProject.Key, hostDocument, textLoader); // Adding a document to a project could also happen because a target was added to a project, or we're moving a document // from Misc Project to a real one, and means the newly added document could actually already be open. // If it is, we need to make sure we start generating it so we're ready to handle requests that could start coming in. if (_projectManager.IsDocumentOpen(textDocumentPath) && - _projectManager.TryGetLoadedProject(projectSnapshot.Key, out var project) && + _projectManager.TryGetLoadedProject(miscFilesProject.Key, out var project) && project.GetDocument(textDocumentPath) is { } document) { document.GetGeneratedOutputAsync().Forget(); 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 9f9eba9f6ef..49ea5dc39a1 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/RazorProjectServiceTest.cs @@ -34,7 +34,7 @@ public class RazorProjectServiceTest(ITestOutputHelper testOutput) : LanguageSer private TestProjectSnapshotManager _projectManager; private SnapshotResolver _snapshotResolver; private DocumentVersionCache _documentVersionCache; - private RazorProjectService _projectService; + private TestRazorProjectService _projectService; #nullable enable protected override async Task InitializeAsync() @@ -202,7 +202,7 @@ public async Task UpdateProject_MovesDocumentsFromMisc_ViaService() await _projectService.AddDocumentToMiscProjectAsync(DocumentFilePath, DisposalToken); - var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); + var miscProject = _snapshotResolver.GetMiscellaneousProject(); var project = _projectManager.GetLoadedProject(ownerProjectKey); @@ -657,7 +657,7 @@ public async Task OpenDocument_OpensAndAddsDocumentToMiscellaneousProject() var ownerProjectKey = await _projectService.AddProjectAsync( ProjectFilePath, IntermediateOutputPath, RazorConfiguration.Default, RootNamespace, displayName: null, DisposalToken); - var miscProject = await _snapshotResolver.GetMiscellaneousProjectAsync(DisposalToken); + var miscProject = _snapshotResolver.GetMiscellaneousProject(); using var listener = _projectManager.ListenToNotifications(); From 0442cab41d0443950b5512564558a5b2d655e8ce Mon Sep 17 00:00:00 2001 From: David Wengier Date: Tue, 30 Apr 2024 11:10:27 +1000 Subject: [PATCH 10/10] Comments --- .../ProjectSystem/RazorProjectService.cs | 3 ++- .../RazorFileSynchronizer.cs | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) 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 bda63141f8d..0c368b4c2a6 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorProjectService.cs @@ -93,7 +93,8 @@ public Task OpenDocumentAsync(string filePath, SourceText sourceText, int versio if (!_snapshotResolver.TryResolveDocumentInAnyProject(textDocumentPath, out var document)) { // Document hasn't been added. This usually occurs when VSCode trumps all other initialization - // processes and pre-initializes already open documents. + // processes and pre-initializes already open documents. We add this to the misc project, and + // if/when we get project info from the client, it will be migrated to a real project. AddDocumentToMiscProjectCore(updater, filePath); } diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorFileSynchronizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorFileSynchronizer.cs index 0bd75c62689..c89c54ef303 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorFileSynchronizer.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/RazorFileSynchronizer.cs @@ -17,6 +17,10 @@ internal class RazorFileSynchronizer(IRazorProjectService projectService) : IRaz public Task RazorFileChangedAsync(string filePath, RazorFileChangeKind kind, CancellationToken cancellationToken) => kind switch { + // We put the new file in the misc files project, so we don't confuse the client by sending updates for + // a razor file that we guess is going to be in a project, when the client might not have received that + // info yet. When the client does find out, it will tell us by updating the project info, and we'll + // migrate the file as necessary. RazorFileChangeKind.Added => _projectService.AddDocumentToMiscProjectAsync(filePath, cancellationToken), RazorFileChangeKind.Removed => _projectService.RemoveDocumentAsync(filePath, cancellationToken), _ => Task.CompletedTask