private static async Task <(ProjectChanges, IEnumerable <TextChange>)> GetChangesForCodeActionAsync(
            Document document,
            CodeAction codeAction,
            ProgressTracker progressTracker,
            IDocumentTextDifferencingService textDiffingService,
            CancellationToken cancellationToken)
        {
            // CodeAction.GetChangedSolutionAsync is only implemented for code actions that can fully compute the new
            // solution without deferred computation or taking a dependency on the main thread. In other cases, the
            // implementation of GetChangedSolutionAsync will throw an exception and the code action application is
            // expected to apply the changes by executing the operations in GetOperationsAsync (which may have other
            // side effects). This code cannot assume the input CodeAction supports GetChangedSolutionAsync, so it first
            // attempts to apply text changes obtained from GetOperationsAsync. Two forms are supported:
            //
            // 1. GetOperationsAsync returns an empty list of operations (i.e. no changes are required)
            // 2. GetOperationsAsync returns a list of operations, where the first change is an ApplyChangesOperation to
            //    change the text in the solution, and any remaining changes are deferred computation changes.
            //
            // If GetOperationsAsync does not adhere to one of these patterns, the code falls back to calling
            // GetChangedSolutionAsync since there is no clear way to apply the changes otherwise.
            var operations = await codeAction.GetOperationsAsync(cancellationToken).ConfigureAwait(false);

            Solution newSolution;

            if (operations.Length == 0)
            {
                newSolution = document.Project.Solution;
            }
            else if (operations.Length == 1 && operations[0] is ApplyChangesOperation applyChangesOperation)
            {
                newSolution = applyChangesOperation.ChangedSolution;
            }
            else
            {
                newSolution = await codeAction.GetRequiredChangedSolutionAsync(
                    progressTracker, cancellationToken : cancellationToken).ConfigureAwait(false);
            }

            var newDocument = newSolution.GetRequiredDocument(document.Id);

            // Use Line differencing to reduce the possibility of changes that overwrite existing code.
            var textChanges = await textDiffingService.GetTextChangesAsync(
                document, newDocument, TextDifferenceTypes.Line, cancellationToken).ConfigureAwait(false);

            var projectChanges = newDocument.Project.GetChanges(document.Project);

            return(projectChanges, textChanges);
        }