Ejemplo n.º 1
0
            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);
            }