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); }
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); }
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); }