private async Task <Project> UpdateDocumentsAsync( Project project, IEnumerable <TextDocumentState> existingTextDocumentStates, ChecksumCollection oldChecksums, ChecksumCollection newChecksums, Func <Solution, ImmutableArray <DocumentInfo>, Solution> addDocuments, Func <Solution, DocumentId, Solution> removeDocument) { using var olds = SharedPools.Default <HashSet <Checksum> >().GetPooledObject(); using var news = SharedPools.Default <HashSet <Checksum> >().GetPooledObject(); olds.Object.UnionWith(oldChecksums); news.Object.UnionWith(newChecksums); // remove documents that exist in both side olds.Object.ExceptWith(newChecksums); news.Object.ExceptWith(oldChecksums); var oldMap = await GetDocumentMapAsync(existingTextDocumentStates, olds.Object).ConfigureAwait(false); var newMap = await GetDocumentMapAsync(_assetProvider, news.Object).ConfigureAwait(false); // added document ImmutableArray <DocumentInfo> .Builder?lazyDocumentsToAdd = null; foreach (var(documentId, newDocumentChecksums) in newMap) { if (!oldMap.ContainsKey(documentId)) { lazyDocumentsToAdd ??= ImmutableArray.CreateBuilder <DocumentInfo>(); // we have new document added var documentInfo = await _assetProvider.CreateDocumentInfoAsync(newDocumentChecksums.Checksum, _cancellationToken).ConfigureAwait(false); lazyDocumentsToAdd.Add(documentInfo); } } if (lazyDocumentsToAdd != null) { project = addDocuments(project.Solution, lazyDocumentsToAdd.ToImmutable()).GetProject(project.Id) !; } // changed document foreach (var(documentId, newDocumentChecksums) in newMap) { if (!oldMap.TryGetValue(documentId, out var oldDocumentChecksums)) { continue; } Contract.ThrowIfTrue(oldDocumentChecksums.Checksum == newDocumentChecksums.Checksum); var document = project.GetDocument(documentId) ?? project.GetAdditionalDocument(documentId) ?? project.GetAnalyzerConfigDocument(documentId); Contract.ThrowIfNull(document); project = await UpdateDocumentAsync(document, oldDocumentChecksums, newDocumentChecksums).ConfigureAwait(false); } // removed document foreach (var(documentId, _) in oldMap) { if (!newMap.ContainsKey(documentId)) { // we have a document removed project = removeDocument(project.Solution, documentId).GetProject(project.Id) !; } } return(project); }
private async Task <Project> UpdateDocumentsAsync( Project project, ProjectStateChecksums projectChecksums, IEnumerable <TextDocumentState> existingTextDocumentStates, ChecksumCollection oldChecksums, ChecksumCollection newChecksums, Func <Solution, ImmutableArray <DocumentInfo>, Solution> addDocuments, Func <Solution, ImmutableArray <DocumentId>, Solution> removeDocuments, CancellationToken cancellationToken) { using var olds = SharedPools.Default <HashSet <Checksum> >().GetPooledObject(); using var news = SharedPools.Default <HashSet <Checksum> >().GetPooledObject(); olds.Object.UnionWith(oldChecksums); news.Object.UnionWith(newChecksums); // remove documents that exist in both side olds.Object.ExceptWith(newChecksums); news.Object.ExceptWith(oldChecksums); var oldMap = await GetDocumentMapAsync(existingTextDocumentStates, olds.Object, cancellationToken).ConfigureAwait(false); var newMap = await GetDocumentMapAsync(_assetProvider, news.Object, cancellationToken).ConfigureAwait(false); // If more than two documents changed during a single update, perform a bulk synchronization on the // project to avoid large numbers of small synchronization calls during document updates. // 🔗 https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1365014 if (newMap.Count > 2) { await _assetProvider.SynchronizeProjectAssetsAsync(new[] { projectChecksums.Checksum }, cancellationToken).ConfigureAwait(false); } // added document ImmutableArray <DocumentInfo> .Builder?lazyDocumentsToAdd = null; foreach (var(documentId, newDocumentChecksums) in newMap) { if (!oldMap.ContainsKey(documentId)) { lazyDocumentsToAdd ??= ImmutableArray.CreateBuilder <DocumentInfo>(); // we have new document added var documentInfo = await _assetProvider.CreateDocumentInfoAsync(newDocumentChecksums.Checksum, cancellationToken).ConfigureAwait(false); lazyDocumentsToAdd.Add(documentInfo); } } if (lazyDocumentsToAdd != null) { project = addDocuments(project.Solution, lazyDocumentsToAdd.ToImmutable()).GetProject(project.Id) !; } // changed document foreach (var(documentId, newDocumentChecksums) in newMap) { if (!oldMap.TryGetValue(documentId, out var oldDocumentChecksums)) { continue; } Contract.ThrowIfTrue(oldDocumentChecksums.Checksum == newDocumentChecksums.Checksum); var document = project.GetDocument(documentId) ?? project.GetAdditionalDocument(documentId) ?? project.GetAnalyzerConfigDocument(documentId); Contract.ThrowIfNull(document); project = await UpdateDocumentAsync(document, oldDocumentChecksums, newDocumentChecksums, cancellationToken).ConfigureAwait(false); } // removed document ImmutableArray <DocumentId> .Builder?lazyDocumentsToRemove = null; foreach (var(documentId, _) in oldMap) { if (!newMap.ContainsKey(documentId)) { // we have a document removed lazyDocumentsToRemove ??= ImmutableArray.CreateBuilder <DocumentId>(); lazyDocumentsToRemove.Add(documentId); } } if (lazyDocumentsToRemove is not null) { project = removeDocuments(project.Solution, lazyDocumentsToRemove.ToImmutable()).GetProject(project.Id) !; } return(project); }