-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Compute TextChange based on syntax instead of text #67120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,6 @@ | |
| using System.Linq; | ||
| using System.Threading; | ||
| using Microsoft.CodeAnalysis.BraceCompletion; | ||
| using Microsoft.CodeAnalysis.Editing; | ||
| using Microsoft.CodeAnalysis.Formatting; | ||
| using Microsoft.CodeAnalysis.Formatting.Rules; | ||
| using Microsoft.CodeAnalysis.Indentation; | ||
|
|
@@ -25,7 +24,8 @@ internal abstract class AbstractCurlyBraceOrBracketCompletionService : AbstractC | |
| /// Annotation used to find the closing brace location after formatting changes are applied. | ||
| /// The closing brace location is then used as the caret location. | ||
| /// </summary> | ||
| private static readonly SyntaxAnnotation s_closingBraceSyntaxAnnotation = new(nameof(s_closingBraceSyntaxAnnotation)); | ||
| private static readonly SyntaxAnnotation s_closingBraceFormatAnnotation = new(nameof(s_closingBraceFormatAnnotation)); | ||
| private static readonly SyntaxAnnotation s_closingBraceNewlineAnnotation = new(nameof(s_closingBraceNewlineAnnotation)); | ||
|
|
||
| protected abstract ImmutableArray<AbstractFormattingRule> GetBraceFormattingIndentationRulesAfterReturn(IndentationOptions options); | ||
|
|
||
|
|
@@ -115,18 +115,18 @@ private static bool ContainsOnlyWhitespace(SourceText text, int openingPosition, | |
| if (closingPointLine - openingPointLine == 1) | ||
| { | ||
| // Handling syntax tree directly to avoid parsing in potentially UI blocking code-path | ||
| var closingToken = document.Root.FindTokenOnLeftOfPosition(context.ClosingPoint); | ||
| var newLineString = options.FormattingOptions.NewLine; | ||
| newLineEdit = new TextChange(new TextSpan(closingToken.FullSpan.Start, 0), newLineString); | ||
| var closingToken = FindClosingBraceToken(document.Root, closingPoint); | ||
| var annotatedNewline = SyntaxFactory.EndOfLine(options.FormattingOptions.NewLine) | ||
| .WithAdditionalAnnotations(SpecializedCollections.SingletonEnumerable(s_closingBraceNewlineAnnotation)); | ||
genlu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
genlu marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| var newClosingToken = closingToken.WithPrependedLeadingTrivia(SpecializedCollections.SingletonEnumerable(annotatedNewline)); | ||
|
|
||
| var generator = document.LanguageServices.GetRequiredService<SyntaxGeneratorInternal>(); | ||
| var endOfLine = generator.EndOfLine(newLineString); | ||
|
|
||
| var rootToFormat = document.Root.ReplaceToken(closingToken, closingToken.WithPrependedLeadingTrivia(endOfLine)); | ||
| var rootToFormat = document.Root.ReplaceToken(closingToken, newClosingToken); | ||
| annotatedNewline = rootToFormat.GetAnnotatedTrivia(s_closingBraceNewlineAnnotation).Single(); | ||
| document = document.WithChangedRoot(rootToFormat, cancellationToken); | ||
|
|
||
| // Modify the closing point location to adjust for the newly inserted line. | ||
| closingPoint += newLineString.Length; | ||
| // Calculate text change for adding a newline and adjust closing point location. | ||
| closingPoint = annotatedNewline.Token.Span.End; | ||
| newLineEdit = new TextChange(new TextSpan(annotatedNewline.SpanStart, 0), annotatedNewline.ToString()); | ||
| } | ||
|
|
||
| // Format the text that contains the newly inserted line. | ||
|
|
@@ -147,11 +147,23 @@ private static bool ContainsOnlyWhitespace(SourceText text, int openingPosition, | |
| // Set the caret position to the properly indented column in the desired line. | ||
| var caretPosition = GetIndentedLinePosition(newDocument, newDocument.Text, desiredCaretLine.LineNumber, options, cancellationToken); | ||
|
|
||
| // The new line edit is calculated against the original text, d0, to get text d1. | ||
| // The formatting edits are calculated against d1 to get text d2. | ||
| // Merge the formatting and new line edits into a set of whitespace only text edits that all apply to d0. | ||
| var overallChanges = newLineEdit != null ? GetMergedChanges(newLineEdit.Value, formattingChanges, newDocument.Text) : formattingChanges; | ||
| return new BraceCompletionResult(overallChanges, caretPosition); | ||
| return new BraceCompletionResult(GetOverallChanges(), caretPosition); | ||
|
|
||
| ImmutableArray<TextChange> GetOverallChanges() | ||
| { | ||
| // The new line edit is calculated against the original text, d0, to get text d1. | ||
| // The formatting edits are calculated against d1 to get text d2. | ||
| // Merge the formatting and new line edits into a set of whitespace only text edits that all apply to d0. | ||
| if (!newLineEdit.HasValue) | ||
| return formattingChanges; | ||
|
|
||
| // Depending on options, we might not get any formatting change. | ||
| // In this case, the newline edit is the only change. | ||
| if (formattingChanges.IsEmpty) | ||
| return ImmutableArray.Create(newLineEdit.Value); | ||
|
||
|
|
||
| return GetMergedChanges(newLineEdit.Value, formattingChanges, newDocument.Text); | ||
| } | ||
|
|
||
| static TextLine GetLineBetweenCurlys(int closingPosition, SourceText text) | ||
| { | ||
|
|
@@ -165,8 +177,8 @@ static LinePosition GetIndentedLinePosition(ParsedDocument document, SourceText | |
| var indentation = indentationService.GetIndentation(document, lineNumber, options, cancellationToken); | ||
|
|
||
| var baseLinePosition = sourceText.Lines.GetLinePosition(indentation.BasePosition); | ||
| var offsetOfBacePosition = baseLinePosition.Character; | ||
| var totalOffset = offsetOfBacePosition + indentation.Offset; | ||
| var offsetOfBasePosition = baseLinePosition.Character; | ||
| var totalOffset = offsetOfBasePosition + indentation.Offset; | ||
| var indentedLinePosition = new LinePosition(lineNumber, totalOffset); | ||
| return indentedLinePosition; | ||
| } | ||
|
|
@@ -247,19 +259,24 @@ static ImmutableArray<TextChange> GetMergedChanges(TextChange newLineEdit, Immut | |
| } | ||
|
|
||
| var newRoot = result.GetFormattedRoot(cancellationToken); | ||
| var newClosingPoint = newRoot.GetAnnotatedTokens(s_closingBraceSyntaxAnnotation).Single().SpanStart + 1; | ||
| var newClosingPoint = newRoot.GetAnnotatedTokens(s_closingBraceFormatAnnotation).Single().SpanStart + 1; | ||
|
|
||
| var textChanges = result.GetTextChanges(cancellationToken).ToImmutableArray(); | ||
| return (newRoot, textChanges, newClosingPoint); | ||
|
|
||
| SyntaxNode GetSyntaxRootWithAnnotatedClosingBrace(SyntaxNode originalRoot, int closingBraceEndPoint) | ||
| { | ||
| var closeBraceToken = originalRoot.FindToken(closingBraceEndPoint - 1); | ||
| Debug.Assert(IsValidClosingBraceToken(closeBraceToken)); | ||
|
|
||
| var newCloseBraceToken = closeBraceToken.WithAdditionalAnnotations(s_closingBraceSyntaxAnnotation); | ||
| var closeBraceToken = FindClosingBraceToken(originalRoot, closingBraceEndPoint); | ||
| var newCloseBraceToken = closeBraceToken.WithAdditionalAnnotations(s_closingBraceFormatAnnotation); | ||
| return originalRoot.ReplaceToken(closeBraceToken, newCloseBraceToken); | ||
| } | ||
| } | ||
|
|
||
| private SyntaxToken FindClosingBraceToken(SyntaxNode root, int closingBraceEndPoint) | ||
| { | ||
| var closeBraceToken = root.FindToken(closingBraceEndPoint - 1); | ||
| Debug.Assert(IsValidClosingBraceToken(closeBraceToken)); | ||
| return closeBraceToken; | ||
| } | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you clarify what is was about this prior code that was causing the problem?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We previous assume calling formatter on the brace pair (including added newline in-between) would always cause text changes. But it's not true with certain option combination, which would cause us the throw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks!