From d2335c2a8e2ae13f9e5d98b526af4b01d2b2ab5e Mon Sep 17 00:00:00 2001 From: David Barbet Date: Tue, 20 Aug 2024 18:10:36 -0700 Subject: [PATCH 1/2] Fix error closing source link documents in VSCode --- ...rceDocumentMetadataAsSourceFileProvider.cs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs index 80244c24928b8..8afca7bca3c93 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs @@ -244,15 +244,19 @@ internal sealed class PdbSourceDocumentMetadataAsSourceFileProvider( return null; var symbolId = SymbolKey.Create(symbol, cancellationToken); - var navigateProject = metadataWorkspace.CurrentSolution.GetRequiredProject(projectId); - var documentInfos = CreateDocumentInfos(sourceFileInfos, encoding, navigateProject.Id, sourceWorkspace, sourceProject); + // Get a view of the solution with the document added, but do not actually update the workspace. + // TryAddDocumentToWorkspace is responsible for actually updating the solution with the new document(s). + // We just need a view with the document added so we can find the right location in the generated source. + var pendingSolution = metadataWorkspace.CurrentSolution; + var documentInfos = CreateDocumentInfos(sourceFileInfos, encoding, projectId, sourceWorkspace, sourceProject); if (documentInfos.Length > 0) { - metadataWorkspace.OnDocumentsAdded(documentInfos); - navigateProject = metadataWorkspace.CurrentSolution.GetRequiredProject(projectId); + pendingSolution = pendingSolution.AddDocuments(documentInfos); } + var navigateProject = pendingSolution.GetRequiredProject(projectId); + // If MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync can't find the actual document to navigate to, it will fall back // to the document passed in, which we just use the first document for. // TODO: Support results from multiple source files: https://github.com/dotnet/roslyn/issues/55834 @@ -319,20 +323,22 @@ private ImmutableArray CreateDocumentInfos( } // If a document has multiple symbols then we might already know about it - if (_fileToDocumentInfoMap.ContainsKey(info.FilePath)) + if (_fileToDocumentInfoMap.TryGetValue(info.FilePath, out var sourceDocumentInfo)) { + documents.Add(sourceDocumentInfo.DocumentInfo); continue; } var documentId = DocumentId.CreateNewId(projectId); - documents.Add(DocumentInfo.Create( + var documentInfo = DocumentInfo.Create( documentId, name: Path.GetFileName(info.FilePath), loader: info.Loader, filePath: info.FilePath, isGenerated: true) - .WithDesignTimeOnly(true)); + .WithDesignTimeOnly(true); + documents.Add(documentInfo); // If we successfully got something from SourceLink for this project then its nice to wait a bit longer // if the user performs subsequent navigation @@ -342,7 +348,7 @@ private ImmutableArray CreateDocumentInfos( } // In order to open documents in VS we need to understand the link from temp file to document and its encoding etc. - _fileToDocumentInfoMap[info.FilePath] = new(documentId, encoding, info.ChecksumAlgorithm, sourceProject.Id, sourceWorkspace); + _fileToDocumentInfoMap[info.FilePath] = new(documentId, encoding, info.ChecksumAlgorithm, sourceProject.Id, sourceWorkspace, documentInfo); } return documents.ToImmutableAndClear(); @@ -359,6 +365,7 @@ public bool TryAddDocumentToWorkspace(MetadataAsSourceWorkspace workspace, strin { if (_fileToDocumentInfoMap.TryGetValue(filePath, out var info)) { + workspace.OnDocumentAdded(info.DocumentInfo); workspace.OnDocumentOpened(info.DocumentId, sourceTextContainer); documentId = info.DocumentId; return true; @@ -376,6 +383,7 @@ public bool TryRemoveDocumentFromWorkspace(MetadataAsSourceWorkspace workspace, if (_fileToDocumentInfoMap.TryGetValue(filePath, out var info)) { workspace.OnDocumentClosed(info.DocumentId, new WorkspaceFileTextLoader(workspace.Services.SolutionServices, filePath, info.Encoding)); + workspace.OnDocumentRemoved(info.DocumentId); return true; } @@ -425,4 +433,4 @@ public void CleanupGeneratedFiles(MetadataAsSourceWorkspace workspace) internal sealed record SourceDocument(string FilePath, SourceHashAlgorithm ChecksumAlgorithm, ImmutableArray Checksum, byte[]? EmbeddedTextBytes, string? SourceLinkUrl); -internal record struct SourceDocumentInfo(DocumentId DocumentId, Encoding Encoding, SourceHashAlgorithm ChecksumAlgorithm, ProjectId SourceProjectId, Workspace SourceWorkspace); +internal record struct SourceDocumentInfo(DocumentId DocumentId, Encoding Encoding, SourceHashAlgorithm ChecksumAlgorithm, ProjectId SourceProjectId, Workspace SourceWorkspace, DocumentInfo DocumentInfo); From 75a501c4ec1813ac97a4024f23c0b1ba4a9393ca Mon Sep 17 00:00:00 2001 From: David Barbet Date: Thu, 22 Aug 2024 13:12:58 -0700 Subject: [PATCH 2/2] Update tests to access the original source text --- .../AbstractPdbSourceDocumentTests.cs | 19 +++++++++++++++++++ ...rceDocumentMetadataAsSourceFileProvider.cs | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs b/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs index 1e12366cfe7c1..c0f9e6a0b2a60 100644 --- a/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs +++ b/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs @@ -155,6 +155,14 @@ protected static async Task GenerateFileAndVerifyAsync( var masWorkspace = service.TryGetWorkspace(); + using var fileStream = File.OpenRead(file.FilePath); + var sourceText = SourceText.From(fileStream); + var text = SourceText.From(File.ReadAllText(file.FilePath)); + + var pdbService = (PdbSourceDocumentMetadataAsSourceFileProvider)workspace.ExportProvider.GetExportedValues().Single(s => s is PdbSourceDocumentMetadataAsSourceFileProvider); + + var documentInfo = pdbService.GetTestAccessor().Documents[file.FilePath]; + masWorkspace!.OnDocumentAdded(documentInfo.DocumentInfo); var document = masWorkspace!.CurrentSolution.Projects.First().Documents.First(d => d.FilePath == file.FilePath); // Mapping the project from the generated document should map back to the original project @@ -330,4 +338,15 @@ protected static string GetPdbPath(string path) { return Path.Combine(path, "reference.pdb"); } + + private class StaticSourceTextContainer(SourceText sourceText) : SourceTextContainer + { + public override SourceText CurrentText => sourceText; + + public override event EventHandler TextChanged + { + add { } + remove { } + } + } } diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs index 8afca7bca3c93..c0216482b1ca0 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs @@ -429,6 +429,23 @@ public void CleanupGeneratedFiles(MetadataAsSourceWorkspace workspace) _sourceLinkEnabledProjects.Clear(); _implementationAssemblyLookupService.Clear(); } + + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly PdbSourceDocumentMetadataAsSourceFileProvider _instance; + + internal TestAccessor(PdbSourceDocumentMetadataAsSourceFileProvider instance) + { + _instance = instance; + } + + public ImmutableDictionary Documents => _instance._fileToDocumentInfoMap.ToImmutableDictionary(); + } } internal sealed record SourceDocument(string FilePath, SourceHashAlgorithm ChecksumAlgorithm, ImmutableArray Checksum, byte[]? EmbeddedTextBytes, string? SourceLinkUrl);