Ejemplo n.º 1
0
        public virtual async Task <Solution> TryMergeFixesAsync(Solution oldSolution, IEnumerable <CodeAction> codeActions, CancellationToken cancellationToken)
        {
            Solution          currentSolution = oldSolution;
            RevisionsTracker  revisionsTracker;
            List <CodeAction> codeActionsToProcess = codeActions.ToList();

            revisionsTracker = new RevisionsTracker();
            foreach (var codeAction in codeActionsToProcess)
            {
                bool success = await IncludeCodeActionInRevisionsAsync(currentSolution, codeAction, revisionsTracker, cancellationToken);
            }
            currentSolution = await UpdateSolutionFromRevisionsAsync(currentSolution, revisionsTracker, cancellationToken);

            return(currentSolution);
        }
Ejemplo n.º 2
0
        private static async Task <Solution> UpdateSolutionFromRevisionsAsync(Solution oldSolution, RevisionsTracker revisionsTracker, CancellationToken cancellationToken)
        {
            var currentSolution = oldSolution;

            foreach (DocumentId removedDoc in revisionsTracker.removedDocuments)
            {
                currentSolution = currentSolution.RemoveDocument(removedDoc);
            }

            foreach (KeyValuePair <DocumentId, Document> doc in revisionsTracker.newDocumentsMap)
            {
                if (revisionsTracker.removedDocuments.Contains(doc.Key))
                {
                    break;
                }
                var documentText = await doc.Value.GetTextAsync(cancellationToken).ConfigureAwait(false);

                var existingMatches = currentSolution.GetDocumentIdsWithFilePath(doc.Value.FilePath).ToList();
                foreach (var existingMatch in existingMatches)
                {
                    currentSolution = currentSolution.RemoveDocument(existingMatch);
                }
                if (!currentSolution.ContainsDocument(doc.Key))
                {
                    currentSolution = currentSolution.AddDocument(DocumentInfo.Create(doc.Key, doc.Value.Name, doc.Value.Folders));
                }
                currentSolution = currentSolution.WithDocumentText(doc.Key, documentText);
            }

            foreach (var kvp in revisionsTracker.changedDocumentsMap)
            {
                if (revisionsTracker.removedDocuments.Contains(kvp.Key))
                {
                    break;
                }
                if (kvp.Value is Document changedDoc)
                {
                    var existingMatches = currentSolution.GetDocumentIdsWithFilePath(changedDoc.FilePath).ToList();
                    cancellationToken.ThrowIfCancellationRequested();
                    var documentText = await changedDoc.GetTextAsync(cancellationToken).ConfigureAwait(false);

                    currentSolution = currentSolution.WithDocumentText(kvp.Key, documentText);
                }
            }

            if (revisionsTracker.documentsToMergeMap != null)
            {
                var mergedDocuments       = new ConcurrentDictionary <DocumentId, SourceText>();
                var documentsToMergeArray = revisionsTracker.documentsToMergeMap.ToImmutableArray();
                var mergeTasks            = new Task[documentsToMergeArray.Length];
                for (int i = 0; i < documentsToMergeArray.Length; i++)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    var kvp              = documentsToMergeArray[i];
                    var documentId       = kvp.Key;
                    var documentsToMerge = kvp.Value;
                    var oldDocument      = oldSolution.GetDocument(documentId);

                    mergeTasks[i] = Task.Run(async() =>
                    {
                        var appliedChanges = (await documentsToMerge[0].GetTextChangesAsync(oldDocument, cancellationToken).ConfigureAwait(false)).ToList();

                        foreach (var document in documentsToMerge.Skip(1))
                        {
                            cancellationToken.ThrowIfCancellationRequested();
                            appliedChanges = await TryAddDocumentMergeChangesAsync(
                                oldDocument,
                                document,
                                appliedChanges,
                                cancellationToken).ConfigureAwait(false);
                        }

                        var oldText = await oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
                        var newText = oldText.WithChanges(appliedChanges);
                        mergedDocuments.TryAdd(documentId, newText);
                    });
                }

                await Task.WhenAll(mergeTasks).ConfigureAwait(false);

                foreach (var kvp in mergedDocuments)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    currentSolution = currentSolution.WithDocumentText(kvp.Key, kvp.Value);
                }
            }

            return(currentSolution);
        }
Ejemplo n.º 3
0
        public async Task <bool> IncludeCodeActionInRevisionsAsync(Solution oldSolution, CodeAction codeAction, RevisionsTracker revisionsTracker, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            ImmutableArray <CodeActionOperation> operations = await codeAction.GetPreviewOperationsAsync(cancellationToken).ConfigureAwait(false);

            ApplyChangesOperation singleApplyChangesOperation = null;

            foreach (var operation in operations)
            {
                if (!(operation is ApplyChangesOperation applyChangesOperation))
                {
                    continue;
                }

                if (singleApplyChangesOperation != null)
                {
                    // Already had an ApplyChangesOperation; only one is supported.
                    singleApplyChangesOperation = null;
                    break;
                }

                singleApplyChangesOperation = applyChangesOperation;
            }

            if (singleApplyChangesOperation == null)
            {
                return(false);
            }

            var changedSolution = singleApplyChangesOperation.ChangedSolution;

            if (await LazinatorCodeFixProvider.WhetherCodeFixFailed(changedSolution))
            {
                return(false);
            }

            var solutionChanges = changedSolution.GetChanges(oldSolution);

            foreach (var projectChange in solutionChanges.GetProjectChanges())
            {
                foreach (var removed in projectChange.GetRemovedDocuments())
                {
                    revisionsTracker.removedDocuments.Add(removed);
                }
            }

            var documentIdsForNew = solutionChanges
                                    .GetProjectChanges()
                                    .SelectMany(p => p.GetAddedDocuments());

            foreach (var documentId in documentIdsForNew)
            {
                if (revisionsTracker.newDocumentsMap.ContainsKey(documentId))
                {
                    throw new LazinatorCodeGenException("Internal exception. Did not expect multiple code fixes to produce same new document.");
                }
                revisionsTracker.newDocumentsMap[documentId] = changedSolution.GetDocument(documentId);
            }

            var documentIdsWithChanges = solutionChanges
                                         .GetProjectChanges()
                                         .SelectMany(p => p.GetChangedDocuments());

            foreach (var documentId in documentIdsWithChanges)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var document = changedSolution.GetDocument(documentId);

                Document existingDocument;
                if (revisionsTracker.changedDocumentsMap.TryGetValue(documentId, out existingDocument))
                {
                    if (existingDocument != null)
                    {
                        revisionsTracker.changedDocumentsMap[documentId] = null;
                        var documentsToMerge = new List <Document>();
                        documentsToMerge.Add(existingDocument);
                        documentsToMerge.Add(document);
                        revisionsTracker.documentsToMergeMap             = revisionsTracker.documentsToMergeMap ?? new Dictionary <DocumentId, List <Document> >();
                        revisionsTracker.documentsToMergeMap[documentId] = documentsToMerge;
                    }
                    else
                    {
                        revisionsTracker.documentsToMergeMap[documentId].Add(document);
                    }
                }
                else
                {
                    revisionsTracker.changedDocumentsMap[documentId] = document;
                }
            }

            return(true);
        }