-
Notifications
You must be signed in to change notification settings - Fork 439
Fix CodeGenerationFormat
to not add extra indentation, leave trailing spaces when adding newlines, or indent empty lines
#2833
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
Fix CodeGenerationFormat
to not add extra indentation, leave trailing spaces when adding newlines, or indent empty lines
#2833
Conversation
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.
Great changes. These have always bothered me but never enough that I set time aside to fix them. So, thank you for doing it 🙏🏽
First two commits look good, I have one comment about the indentation change.
self.rewrite($0.cast(SyntaxType.self)).cast(SyntaxType.self) | ||
var formattedChildren = children.map { child in | ||
var child = child.cast(SyntaxType.self) | ||
child.leadingTrivia = Trivia(pieces: child.leadingTrivia.drop(while: \.isWhitespace)) |
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.
If a child would span multiple lines, this only removes and re-adds whitespace for the first line and the subsequent lines would still be off. Can you think of a solution that immediately assigns the right indentation to the child? How many whitespaces (compared to indentedNewline
) are we stripping here? Are we just off one indentation level? If so, could we fix that by arranging the calls to increaseIndentationLevel
and decreaseIndentationLevel
correctly?
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 for the notice. Calling increaseIndentationLevel()
before rewriting the children seems to fix the issue of children that span multiple lines not getting indented probably properly.
Before:
body: bodyBuilder().map { | |
CodeBlockSyntax(statements: $0) | |
}, |
After:
body: bodyBuilder().map { | |
CodeBlockSyntax(statements: $0) | |
}, |
Regarding the removal and re-adding of leading whitespace, it was to prevent adding newlines and indentation to children that were already separated by newlines. I changed it so that indented newlines are only added to children that don't already start on newlines instead of removing the whitespace and adding it back to all children, although it should work either way.
Both of these changes are included in:
Fix CodeGenerationFormat to correctly indent certain children that span multiple lines
However...
... I found another issue while inspecting the generated code:
swift-syntax/Sources/SwiftSyntax/generated/syntaxNodes/SyntaxNodesQRS.swift
Lines 854 to 861 in b63eaaf
return Syntax(self) | |
.replacingChild( | |
at: 1, | |
with: collection, | |
rawNodeArena: arena, | |
allocationArena: arena | |
) | |
.cast(SequenceExprSyntax.self) |
The arguments and the right parenthesis of .replacingChild(...)
are missing a level of indentation. Prior to this pull request this particular case looked OK, but I believe it was a false positive, as most of the code formatted by CodeGenerationFormat
was over-indented.
I had fixed the over-indentation by overriding requiresIndent(_:)
,
swift-syntax/CodeGeneration/Sources/Utils/CodeGenerationFormat.swift
Lines 107 to 124 in b63eaaf
public override func requiresIndent(_ node: some SyntaxProtocol) -> Bool { | |
switch node.kind { | |
case .arrayElementList, .dictionaryElementList, .functionParameterList, .labeledExprList: | |
let indentManually = node.children(viewMode: .sourceAccurate).count > maxElementsOnSameLine | |
if indentManually { | |
return false | |
} | |
let startsOnNewline = | |
node.leadingTrivia.contains(where: \.isNewline) | |
|| node.previousToken(viewMode: .sourceAccurate)?.trailingTrivia.contains(where: \.isNewline) ?? false | |
if !startsOnNewline { | |
return false | |
} | |
default: | |
break | |
} | |
return super.requiresIndent(node) | |
} |
which revealed that .replacingChild(...)
is under-indented. The issue might have to do with the fact that the member function call starts on a newline, as it seems to be the only place where this issue manifests. Perhaps it has something to do with how BasicFormat
handles the indentation for this case? Any insight on this issue @ahoppen ? Either way, I'll look into this issue during the weekend and try to resolve it.
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 for the detailed analysis.
My guess would be that this is because the function call is modeled as follows (simplified):
- callee:
Syntax(self).replacingChild
- arguments:
(at: 1 ….)
Thus, the call’s indentation is anchored at the base of the callee, which is the indentation level of Syntax
here, not the indentation level of replacingChild
. I don’t have an immediate suggestion of how to fix this but didn’t think about it a lot either. Maybe you can come up with something. If my guess is correct, this would be an issue in BasicFormat
, not just CodeGenerationFormat
.
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.
Happy to take this PR as-is and fix that in a follow-up PR. That helps keep the reviews easier if the PRs are narrow in scope.
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 for the detailed analysis.
My guess would be that this is because the function call is modeled as follows (simplified):
- callee:
Syntax(self).replacingChild
- arguments:
(at: 1 ….)
Thus, the call’s indentation is anchored at the base of the callee, which is the indentation level of
Syntax
here, not the indentation level ofreplacingChild
. I don’t have an immediate suggestion of how to fix this but didn’t think about it a lot either. Maybe you can come up with something. If my guess is correct, this would be an issue inBasicFormat
, not justCodeGenerationFormat
.
Alright, thanks for the insight 👍
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.
Happy to take this PR as-is and fix that in a follow-up PR. That helps keep the reviews easier if the PRs are narrow in scope.
Sounds good to me. Shall I fix the merge conflicts now?
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.
Looks good to me. Thanks! Also good to see that the latest commit fixed some more formatting issues.
Could you rebase your changes onto main
to fix the merge conflict?
b63eaaf
to
df8f459
Compare
@ahoppen is this good to go now? |
Yes, let’s 🚢it |
@swift-ci Please test |
The macOS test failures are unrelated to your changes. I’m looking into them. |
Sorry, there’s a merge conflict now. Could you rebase? |
Sure, just a moment. |
…ng spaces when adding newlines, or indent empty lines
Head branch was pushed to by a user without write access
df8f459
to
29616eb
Compare
Done |
@swift-ci Please test |
@swift-ci Please test |
@swift-ci Please test macOS |
This fixes multiple issues with
CodeGenerationFormat
. Files were re-generated with each commit, so the differences in formatting caused by each change toCodeGenerationFormat
should be easy to review.Fix CodeGenerationFormat to not add extra indentation
Fix CodeGenerationFormat to not leave trailing spaces when adding newlines
Fix CodeGenerationFormat to not indent empty lines
ensuringTwoLeadingNewlines
used to add indented newlines, resulting in empty lines being indented. Not anymore.