public async override Task <FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
        {
            if (context.IsFormatOnType)
            {
                // We don't want to handle OnTypeFormatting here.
                return(result);
            }

            // Apply previous edits if any.
            var originalText   = context.SourceText;
            var changedText    = originalText;
            var changedContext = context;

            if (result.Edits.Length > 0)
            {
                var changes = result.Edits.Select(e => e.AsTextChange(originalText)).ToArray();
                changedText    = changedText.WithChanges(changes);
                changedContext = await context.WithTextAsync(changedText);

                cancellationToken.ThrowIfCancellationRequested();
            }

            // Format the razor bits of the file
            var syntaxTree = changedContext.CodeDocument.GetSyntaxTree();
            var edits      = FormatRazor(changedContext, syntaxTree);

            // Compute the final combined set of edits
            var formattingChanges = edits.Select(e => e.AsTextChange(changedText));

            changedText = changedText.WithChanges(formattingChanges);
            var finalChanges = SourceTextDiffer.GetMinimalTextChanges(originalText, changedText, lineDiffOnly: false);
            var finalEdits   = finalChanges.Select(f => f.AsTextEdit(originalText)).ToArray();

            return(new FormattingResult(finalEdits));
        }
        private (RazorLanguageKind, TextEdit[]) GetFormattedEdits(RazorCodeDocument codeDocument, string expected, int positionBeforeTriggerChar)
        {
            var mappingService = new DefaultRazorDocumentMappingService();
            var languageKind   = mappingService.GetLanguageKind(codeDocument, positionBeforeTriggerChar);

            var expectedText = SourceText.From(expected);

            var(expectedCodeDocument, _) = CreateCodeDocumentAndSnapshot(expectedText, codeDocument.Source.FilePath, fileKind: codeDocument.GetFileKind());

            var edits = Array.Empty <TextEdit>();

            if (languageKind == RazorLanguageKind.CSharp)
            {
                var beforeCSharpText = SourceText.From(codeDocument.GetCSharpDocument().GeneratedCode);
                var afterCSharpText  = SourceText.From(expectedCodeDocument.GetCSharpDocument().GeneratedCode);
                edits = SourceTextDiffer.GetMinimalTextChanges(beforeCSharpText, afterCSharpText, lineDiffOnly: false).Select(c => c.AsTextEdit(beforeCSharpText)).ToArray();
            }
            else if (languageKind == RazorLanguageKind.Html)
            {
                var beforeHtmlText = SourceText.From(codeDocument.GetHtmlDocument().GeneratedHtml);
                var afterHtmlText  = SourceText.From(expectedCodeDocument.GetHtmlDocument().GeneratedHtml);
                edits = SourceTextDiffer.GetMinimalTextChanges(beforeHtmlText, afterHtmlText, lineDiffOnly: false).Select(c => c.AsTextEdit(beforeHtmlText)).ToArray();
            }

            return(languageKind, edits);
        }
        public async override Task <FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
        {
            if (!context.IsFormatOnType || result.Kind != RazorLanguageKind.CSharp)
            {
                // We don't want to handle regular formatting or non-C# on type formatting here.
                return(result);
            }

            // Normalize and re-map the C# edits.
            var codeDocument    = context.CodeDocument;
            var csharpText      = SourceText.From(codeDocument.GetCSharpDocument().GeneratedCode);
            var normalizedEdits = NormalizeTextEdits(csharpText, result.Edits);
            var mappedEdits     = RemapTextEdits(codeDocument, normalizedEdits, result.Kind);
            var filteredEdits   = FilterCSharpTextEdits(context, mappedEdits);

            if (filteredEdits.Length == 0)
            {
                // There are no CSharp edits for us to apply. No op.
                return(new FormattingResult(filteredEdits));
            }

            // Find the lines that were affected by these edits.
            var originalText = codeDocument.GetSourceText();
            var changes      = filteredEdits.Select(e => e.AsTextChange(originalText));
            var changedText  = originalText.WithChanges(changes);

            TrackEncompassingChange(originalText, changedText, out var spanBeforeChange, out var spanAfterChange);
            var rangeBeforeEdit = spanBeforeChange.AsRange(originalText);
            var rangeAfterEdit  = spanAfterChange.AsRange(changedText);

            // Create a new formatting context for the changed razor document.
            var changedContext = await context.WithTextAsync(changedText);

            cancellationToken.ThrowIfCancellationRequested();

            // Now, for each affected line in the edited version of the document, remove x amount of spaces
            // at the front to account for extra indentation applied by the C# formatter.
            // This should be based on context.
            // For instance, lines inside @code/@functions block should be reduced one level
            // and lines inside @{} should be reduced by two levels.
            var indentationChanges = AdjustCSharpIndentation(changedContext, (int)rangeAfterEdit.Start.Line, (int)rangeAfterEdit.End.Line);

            if (indentationChanges.Count > 0)
            {
                // Apply the edits that modify indentation.
                changedText    = changedText.WithChanges(indentationChanges);
                changedContext = await changedContext.WithTextAsync(changedText);
            }

            // We make an optimistic attempt at fixing corner cases.
            changedText = CleanupDocument(changedContext, rangeAfterEdit);

            // Now that we have made all the necessary changes to the document. Let's diff the original vs final version and return the diff.
            var finalChanges = SourceTextDiffer.GetMinimalTextChanges(originalText, changedText, lineDiffOnly: false);
            var finalEdits   = finalChanges.Select(f => f.AsTextEdit(originalText)).ToArray();

            return(new FormattingResult(finalEdits));
        }
示例#4
0
        private static TextEdit[] NormalizeTextEdits(SourceText originalText, TextEdit[] edits)
        {
            var changes      = edits.Select(e => e.AsTextChange(originalText));
            var changedText  = originalText.WithChanges(changes);
            var cleanChanges = SourceTextDiffer.GetMinimalTextChanges(originalText, changedText, lineDiffOnly: false);
            var cleanEdits   = cleanChanges.Select(c => c.AsTextEdit(originalText)).ToArray();

            return(cleanEdits);
        }
示例#5
0
        public override async Task <TextEdit[]> FormatAsync(
            DocumentUri uri,
            DocumentSnapshot documentSnapshot,
            Range range,
            FormattingOptions options,
            CancellationToken cancellationToken)
        {
            if (uri is null)
            {
                throw new ArgumentNullException(nameof(uri));
            }

            if (documentSnapshot is null)
            {
                throw new ArgumentNullException(nameof(documentSnapshot));
            }

            if (range is null)
            {
                throw new ArgumentNullException(nameof(range));
            }

            if (options is null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var codeDocument = await documentSnapshot.GetGeneratedOutputAsync();

            using var context = FormattingContext.Create(uri, documentSnapshot, codeDocument, options, _workspaceFactory);
            var originalText = context.SourceText;

            var result = new FormattingResult(Array.Empty <TextEdit>());

            foreach (var pass in _formattingPasses)
            {
                cancellationToken.ThrowIfCancellationRequested();
                result = await pass.ExecuteAsync(context, result, cancellationToken);
            }

            var filteredEdits = result.Edits.Where(e => range.LineOverlapsWith(e.Range));

            // Make sure the edits actually change something, or its not worth responding
            var textChanges = filteredEdits.Select(e => e.AsTextChange(originalText));
            var changedText = originalText.WithChanges(textChanges);

            if (changedText.ContentEquals(originalText))
            {
                return(Array.Empty <TextEdit>());
            }

            // Only send back the minimum edits
            var minimalChanges = SourceTextDiffer.GetMinimalTextChanges(originalText, changedText, lineDiffOnly: false);
            var finalEdits     = minimalChanges.Select(f => f.AsTextEdit(originalText)).ToArray();

            return(finalEdits);
        }
        public async override Task <FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
        {
            if (context.IsFormatOnType || result.Kind != RazorLanguageKind.Razor)
            {
                // We don't want to handle OnTypeFormatting here.
                return(result);
            }

            // Apply previous edits if any.
            var originalText   = context.SourceText;
            var changedText    = originalText;
            var changedContext = context;

            if (result.Edits.Length > 0)
            {
                var changes = result.Edits.Select(e => e.AsTextChange(originalText)).ToArray();
                changedText    = changedText.WithChanges(changes);
                changedContext = await context.WithTextAsync(changedText);
            }

            cancellationToken.ThrowIfCancellationRequested();

            // Apply original C# edits
            var csharpEdits = await FormatCSharpAsync(changedContext, cancellationToken);

            if (csharpEdits.Count > 0)
            {
                var csharpChanges = csharpEdits.Select(c => c.AsTextChange(changedText));
                changedText    = changedText.WithChanges(csharpChanges);
                changedContext = await changedContext.WithTextAsync(changedText);
            }

            cancellationToken.ThrowIfCancellationRequested();

            // We make an optimistic attempt at fixing corner cases.
            var cleanupChanges = CleanupDocument(changedContext);

            changedText    = changedText.WithChanges(cleanupChanges);
            changedContext = await changedContext.WithTextAsync(changedText);

            cancellationToken.ThrowIfCancellationRequested();

            var indentationChanges = await AdjustIndentationAsync(changedContext, cancellationToken);

            if (indentationChanges.Count > 0)
            {
                // Apply the edits that modify indentation.
                changedText = changedText.WithChanges(indentationChanges);
            }

            var finalChanges = SourceTextDiffer.GetMinimalTextChanges(originalText, changedText, lineDiffOnly: false);
            var finalEdits   = finalChanges.Select(f => f.AsTextEdit(originalText)).ToArray();

            return(new FormattingResult(finalEdits));
        }
示例#7
0
        public async override Task <FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
        {
            if (context.IsFormatOnType || result.Kind != RazorLanguageKind.Razor)
            {
                // We don't want to handle OnTypeFormatting here.
                return(result);
            }

            // Apply previous edits if any.
            var originalText   = context.SourceText;
            var changedText    = originalText;
            var changedContext = context;

            if (result.Edits.Length > 0)
            {
                var changes = result.Edits.Select(e => e.AsTextChange(originalText)).ToArray();
                changedText    = changedText.WithChanges(changes);
                changedContext = await context.WithTextAsync(changedText);
            }

            cancellationToken.ThrowIfCancellationRequested();

            // Apply original C# edits
            var csharpEdits = await FormatCSharpAsync(changedContext, cancellationToken);

            if (csharpEdits.Count > 0)
            {
                var csharpChanges = csharpEdits.Select(c => c.AsTextChange(changedText));
                changedText    = changedText.WithChanges(csharpChanges);
                changedContext = await changedContext.WithTextAsync(changedText);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var indentationChanges = await AdjustIndentationAsync(changedContext, cancellationToken);

            if (indentationChanges.Count > 0)
            {
                // Apply the edits that modify indentation.
                changedText = changedText.WithChanges(indentationChanges);
            }

            // Allow benchmarks to specify a different diff algorithm
            if (!context.Options.TryGetValue("UseSourceTextDiffer", out var useSourceTextDiffer))
            {
                useSourceTextDiffer = new BooleanNumberString(false);
            }

            var finalChanges = useSourceTextDiffer.Bool ? SourceTextDiffer.GetMinimalTextChanges(originalText, changedText, lineDiffOnly: false) : changedText.GetTextChanges(originalText);
            var finalEdits   = finalChanges.Select(f => f.AsTextEdit(originalText)).ToArray();

            return(new FormattingResult(finalEdits));
        }
示例#8
0
        public async override Task <FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
        {
            if (context.IsFormatOnType)
            {
                // We don't want to handle OnTypeFormatting here.
                return(result);
            }

            var originalText = context.SourceText;

            var htmlEdits = await HtmlFormatter.FormatAsync(context, cancellationToken);

            // Allow benchmarks to specify a different diff algorithm
            if (!context.Options.TryGetValue("UseSourceTextDiffer", out var useSourceTextDiffer))
            {
                useSourceTextDiffer = new BooleanNumberString(false);
            }

            var normalizedEdits = htmlEdits;

            if (useSourceTextDiffer.Bool)
            {
                normalizedEdits = NormalizeTextEdits(originalText, htmlEdits);
            }

            var mappedEdits = RemapTextEdits(context.CodeDocument, normalizedEdits, RazorLanguageKind.Html);
            var changes     = mappedEdits.Select(e => e.AsTextChange(originalText));

            var changedText    = originalText;
            var changedContext = context;

            if (changes.Any())
            {
                changedText = originalText.WithChanges(changes);
                // Create a new formatting context for the changed razor document.
                changedContext = await context.WithTextAsync(changedText);
            }

            var indentationChanges = AdjustRazorIndentation(changedContext);

            if (indentationChanges.Count > 0)
            {
                // Apply the edits that adjust indentation.
                changedText = changedText.WithChanges(indentationChanges);
            }

            var finalChanges = useSourceTextDiffer.Bool ? SourceTextDiffer.GetMinimalTextChanges(originalText, changedText, lineDiffOnly: false) : changedText.GetTextChanges(originalText);
            var finalEdits   = finalChanges.Select(f => f.AsTextEdit(originalText)).ToArray();

            return(new FormattingResult(finalEdits));
        }
        protected static TextEdit[] NormalizeTextEdits(SourceText originalText, TextEdit[] edits)
        {
            if (originalText is null)
            {
                throw new ArgumentNullException(nameof(originalText));
            }

            if (edits is null)
            {
                throw new ArgumentNullException(nameof(edits));
            }

            var changes      = edits.Select(e => e.AsTextChange(originalText));
            var changedText  = originalText.WithChanges(changes);
            var cleanChanges = SourceTextDiffer.GetMinimalTextChanges(originalText, changedText, lineDiffOnly: false);
            var cleanEdits   = cleanChanges.Select(c => c.AsTextEdit(originalText)).ToArray();

            return(cleanEdits);
        }
示例#10
0
        public async override Task <FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
        {
            if (context.IsFormatOnType)
            {
                // We don't want to handle OnTypeFormatting here.
                return(result);
            }

            var originalText = context.SourceText;

            var htmlEdits = await HtmlFormatter.FormatAsync(context, context.Range, cancellationToken);

            var normalizedEdits = NormalizeTextEdits(originalText, htmlEdits);
            var mappedEdits     = RemapTextEdits(context.CodeDocument, normalizedEdits, RazorLanguageKind.Html);
            var changes         = mappedEdits.Select(e => e.AsTextChange(originalText));

            var changedText    = originalText;
            var changedContext = context;

            if (changes.Any())
            {
                changedText = originalText.WithChanges(changes);
                // Create a new formatting context for the changed razor document.
                changedContext = await context.WithTextAsync(changedText);
            }

            var indentationChanges = AdjustRazorIndentation(changedContext);

            if (indentationChanges.Count > 0)
            {
                // Apply the edits that adjust indentation.
                changedText = changedText.WithChanges(indentationChanges);
            }

            var finalChanges = SourceTextDiffer.GetMinimalTextChanges(originalText, changedText, lineDiffOnly: false);
            var finalEdits   = finalChanges.Select(f => f.AsTextEdit(originalText)).ToArray();

            return(new FormattingResult(finalEdits));
        }
 public void CharDiff_LargeFile_OneCharChanged()
 {
     SourceTextDiffer.GetMinimalTextChanges(_largeFileOriginal, _largeFileMinimalChanges, lineDiffOnly: false);
 }
 public void LineDiff_LargeFile_SignificantlyDifferent()
 {
     SourceTextDiffer.GetMinimalTextChanges(_largeFileOriginal, _largeFileSignificantChanges, lineDiffOnly: true);
 }
示例#13
0
        private RazorDocumentRangeFormattingResponse Format(RazorDocumentRangeFormattingParams @params)
        {
            if (@params.Kind == RazorLanguageKind.Razor)
            {
                throw new InvalidOperationException("We shouldn't be asked to format Razor language kind.");
            }

            var options  = @params.Options;
            var response = new RazorDocumentRangeFormattingResponse();

            if (@params.Kind == RazorLanguageKind.CSharp)
            {
                var codeDocument     = _documents[@params.HostDocumentFilePath];
                var csharpSourceText = codeDocument.GetCSharpSourceText();
                var csharpDocument   = GetCSharpDocument(codeDocument, @params.Options);
                if (!csharpDocument.TryGetSyntaxRoot(out var root))
                {
                    throw new InvalidOperationException("Couldn't get syntax root.");
                }
                var spanToFormat = @params.ProjectedRange.AsTextSpan(csharpSourceText);

                var changes = Formatter.GetFormattedTextChanges(root, spanToFormat, csharpDocument.Project.Solution.Workspace);

                response.Edits = changes.Select(c => c.AsTextEdit(csharpSourceText)).ToArray();
            }
            else if (@params.Kind == RazorLanguageKind.Html)
            {
                response.Edits = Array.Empty <TextEdit>();

                var codeDocument  = _documents[@params.HostDocumentFilePath];
                var generatedHtml = codeDocument.GetHtmlDocument().GeneratedHtml;
                generatedHtml = generatedHtml.Replace("\r", "", StringComparison.Ordinal).Replace("\n", "\r\n", StringComparison.Ordinal);

                // Get formatted baseline file
                var baselineInputFileName  = Path.ChangeExtension(_baselineFileName, ".input.html");
                var baselineOutputFileName = Path.ChangeExtension(_baselineFileName, ".output.html");

                var baselineInputFile  = TestFile.Create(baselineInputFileName, GetType().GetTypeInfo().Assembly);
                var baselineOutputFile = TestFile.Create(baselineOutputFileName, GetType().GetTypeInfo().Assembly);

                if (GenerateBaselines)
                {
                    if (baselineInputFile.Exists())
                    {
                        // If it already exists, we only want to update if the input is different.
                        var inputContent = baselineInputFile.ReadAllText();
                        if (string.Equals(inputContent, generatedHtml, StringComparison.Ordinal))
                        {
                            return(response);
                        }
                    }

                    var baselineInputFilePath = Path.Combine(_projectPath, baselineInputFileName);
                    File.WriteAllText(baselineInputFilePath, generatedHtml);

                    var baselineOutputFilePath = Path.Combine(_projectPath, baselineOutputFileName);
                    File.WriteAllText(baselineOutputFilePath, generatedHtml);

                    return(response);
                }

                if (!baselineInputFile.Exists())
                {
                    throw new XunitException($"The resource {baselineInputFileName} was not found.");
                }

                if (!baselineOutputFile.Exists())
                {
                    throw new XunitException($"The resource {baselineOutputFileName} was not found.");
                }

                var baselineInputHtml = baselineInputFile.ReadAllText();
                if (!string.Equals(baselineInputHtml, generatedHtml, StringComparison.Ordinal))
                {
                    throw new XunitException($"The baseline for {_baselineFileName} is out of date.");
                }

                var baselineOutputHtml = baselineOutputFile.ReadAllText();
                var baselineInputText  = SourceText.From(baselineInputHtml);
                var baselineOutputText = SourceText.From(baselineOutputHtml);
                var changes            = SourceTextDiffer.GetMinimalTextChanges(baselineInputText, baselineOutputText, lineDiffOnly: false);
                var edits = changes.Select(c => c.AsTextEdit(baselineInputText)).ToArray();
                response.Edits = edits;
            }

            return(response);
        }
示例#14
0
        public async override Task <FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
        {
            if (!context.IsFormatOnType || result.Kind != RazorLanguageKind.CSharp)
            {
                // We don't want to handle regular formatting or non-C# on type formatting here.
                return(result);
            }

            // Normalize and re-map the C# edits.
            var codeDocument    = context.CodeDocument;
            var csharpText      = SourceText.From(codeDocument.GetCSharpDocument().GeneratedCode);
            var normalizedEdits = NormalizeTextEdits(csharpText, result.Edits);
            var mappedEdits     = RemapTextEdits(codeDocument, normalizedEdits, result.Kind);
            var filteredEdits   = FilterCSharpTextEdits(context, mappedEdits);

            if (filteredEdits.Length == 0)
            {
                // There are no CSharp edits for us to apply. No op.
                return(new FormattingResult(filteredEdits));
            }

            // Find the lines that were affected by these edits.
            var originalText = codeDocument.GetSourceText();
            var changes      = filteredEdits.Select(e => e.AsTextChange(originalText));

            // Apply the format on type edits sent over by the client.
            var formattedText  = originalText.WithChanges(changes);
            var changedContext = await context.WithTextAsync(formattedText);

            TrackEncompassingChange(originalText, changes, out _, out var spanAfterFormatting);
            var rangeAfterFormatting = spanAfterFormatting.AsRange(formattedText);

            cancellationToken.ThrowIfCancellationRequested();

            // We make an optimistic attempt at fixing corner cases.
            var cleanupChanges = CleanupDocument(changedContext, rangeAfterFormatting);
            var cleanedText    = formattedText.WithChanges(cleanupChanges);

            changedContext = await changedContext.WithTextAsync(cleanedText);

            cancellationToken.ThrowIfCancellationRequested();

            // At this point we should have applied all edits that adds/removes newlines.
            // Let's now ensure the indentation of each of those lines is correct.

            // We only want to adjust the range that was affected.
            // We need to take into account the lines affected by formatting as well as cleanup.
            var cleanupLineDelta = LineDelta(formattedText, cleanupChanges);
            var rangeToAdjust    = new Range(rangeAfterFormatting.Start, new Position(rangeAfterFormatting.End.Line + cleanupLineDelta, 0));

            Debug.Assert(rangeToAdjust.End.IsValid(cleanedText), "Invalid range. This is unexpected.");

            var indentationChanges = AdjustIndentation(changedContext, cancellationToken, rangeToAdjust);

            if (indentationChanges.Count > 0)
            {
                // Apply the edits that modify indentation.
                cleanedText = cleanedText.WithChanges(indentationChanges);
            }

            // Now that we have made all the necessary changes to the document. Let's diff the original vs final version and return the diff.
            var finalChanges = SourceTextDiffer.GetMinimalTextChanges(originalText, cleanedText, lineDiffOnly: false);
            var finalEdits   = finalChanges.Select(f => f.AsTextEdit(originalText)).ToArray();

            return(new FormattingResult(finalEdits));
        }
        public async override Task <FormattingResult> ExecuteAsync(FormattingContext context, FormattingResult result, CancellationToken cancellationToken)
        {
            if (!context.IsFormatOnType || result.Kind != RazorLanguageKind.CSharp)
            {
                // We don't want to handle regular formatting or non-C# on type formatting here.
                return(result);
            }

            // Normalize and re-map the C# edits.
            var codeDocument    = context.CodeDocument;
            var csharpText      = codeDocument.GetCSharpSourceText();
            var normalizedEdits = NormalizeTextEdits(csharpText, result.Edits);
            var mappedEdits     = RemapTextEdits(codeDocument, normalizedEdits, result.Kind);
            var filteredEdits   = FilterCSharpTextEdits(context, mappedEdits);

            if (filteredEdits.Length == 0)
            {
                // There are no CSharp edits for us to apply. No op.
                return(new FormattingResult(filteredEdits));
            }

            // Find the lines that were affected by these edits.
            var originalText = codeDocument.GetSourceText();
            var changes      = filteredEdits.Select(e => e.AsTextChange(originalText));

            // Apply the format on type edits sent over by the client.
            var formattedText  = ApplyChangesAndTrackChange(originalText, changes, out _, out var spanAfterFormatting);
            var changedContext = await context.WithTextAsync(formattedText);

            var rangeAfterFormatting = spanAfterFormatting.AsRange(formattedText);

            cancellationToken.ThrowIfCancellationRequested();

            // We make an optimistic attempt at fixing corner cases.
            var cleanupChanges = CleanupDocument(changedContext, rangeAfterFormatting);
            var cleanedText    = formattedText.WithChanges(cleanupChanges);

            changedContext = await changedContext.WithTextAsync(cleanedText);

            cancellationToken.ThrowIfCancellationRequested();

            // At this point we should have applied all edits that adds/removes newlines.
            // Let's now ensure the indentation of each of those lines is correct.

            // We only want to adjust the range that was affected.
            // We need to take into account the lines affected by formatting as well as cleanup.
            var lineDelta = LineDelta(formattedText, cleanupChanges);

            // Okay hear me out, I know this looks lazy, but it totally makes sense.
            // This method is called with edits that the C# formatter wants to make, and from those edits we work out which
            // other edits to apply etc. Fine, all good so far. BUT its totally possible that the user typed a closing brace
            // in the same position as the C# formatter thought it should be, on the line _after_ the code that the C# formatter
            // reformatted.
            //
            // For example, given:
            // if (true){
            //     }
            //
            // If the C# formatter is happy with the placement of that close brace then this method will get two edits:
            //  * On line 1 to indent the if by 4 spaces
            //  * On line 1 to add a newline and 4 spaces in front of the opening brace
            //
            // We'll happy format lines 1 and 2, and ignore the closing brace altogether. So, by looking one line further
            // we won't have that problem.
            if (rangeAfterFormatting.End.Line + lineDelta < cleanedText.Lines.Count)
            {
                lineDelta++;
            }

            var rangeToAdjust = new Range(rangeAfterFormatting.Start, new Position(rangeAfterFormatting.End.Line + lineDelta, 0));

            Debug.Assert(rangeToAdjust.End.IsValid(cleanedText), "Invalid range. This is unexpected.");

            var indentationChanges = await AdjustIndentationAsync(changedContext, cancellationToken, rangeToAdjust);

            if (indentationChanges.Count > 0)
            {
                // Apply the edits that modify indentation.
                cleanedText = cleanedText.WithChanges(indentationChanges);
            }

            // Now that we have made all the necessary changes to the document. Let's diff the original vs final version and return the diff.
            var finalChanges = SourceTextDiffer.GetMinimalTextChanges(originalText, cleanedText, lineDiffOnly: false);
            var finalEdits   = finalChanges.Select(f => f.AsTextEdit(originalText)).ToArray();

            return(new FormattingResult(finalEdits));
        }