-
Notifications
You must be signed in to change notification settings - Fork 506
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
Remove usages of getPsi() #2901
base: master
Are you sure you want to change the base?
Conversation
Thanks for your contributions. So far, I have just skimmed your changes. I will do a detailed review later. To not block you, I have approved the workflow so that the pipeline will be trigger when you add changes.
What lead you to analyzing/resolving this problem? Are you resolving problems with Ktlint CLI, ktlint-intellij-plugin, or with any API Consumer? Of course, all will benefit from improved performance. But I am just curious to understand where you are coming from. |
My strategy so far has been:
My biggest concern is that even though I ensure tests pass, some of my changes are likely to cause unforseable bugs. I feel bad introducing these bugs, but I feel its neccesary to migrate the code to more lightweight APIs. I hope you agree. Also, I still have a lot to learn about these APIs and could be making mistakes. One idea I have is that if this gets merged, the first release after this merge is a beta release allowing a time for people to report bugs, since the tests probably aren't covering every corner case. I don't think these changes can be released as a stable version right away. I am looking forward to your feedback. |
The other main concern that I have is that the ASTNode API doesn't do as much logic for you, so its harder to maintain and develop. It seems with a bit of dedication we could build an ASTNode-based library of extension functions to mimic a lot of the logic of psi, but I have mixed feelings about that. |
There are 13 more Rule classes that have usages of This could technically be merged as is. The remaining work is just to remove more usages of |
Also I am curious, would ktlint possibly migrate to the analysis API in the future? I don't know much about this, but I wonder if newer APIs like that resolve the problem, like maybe they are both lightweight and also could offer more support with the logic like psi does? Just a thought. |
Looking into it more, apparently the Analysis API is overkill for a formatter and has lots of overhead. This leads me to think maybe creating a small lightweight ASTNode-based support library, largely just copying a lot of the logic from Psi classes, will be helpful. |
Basically expanding |
Interesting to know this. Personally I use ktlint CLI only on small projects, so I have never noticed this.
Ktlint uses both the ASTNode interface as well as Psi. I expect that it dependended on the knowdledge/experience of rule developers which approach was used. In most cases I prefer the ASTNode, as it is way more easy to comprehend. On the other hand Psi ofter gives access to helper functions, but I find them typically hard to find. But I have never (until this issue) seen a reason to migrate away from PSI fully.
I guess that the cancellation checking especially makes sense when running within the context of IntelliJ IDEA. For ktlint (including the ktlint-intellij-plugin) this does not seem relevant though.
Sounds reasonable.
I have no problem with this. I am quite confident about the test converage of most rules. Those tests cover both linting and formatting. But I hope that I can count on your support in case such errors will occur.
After each merge, a SNAPSHOT version of ktlint is released. The ktlint build pipeline does not support BETA builds as far as I know. Also, that would not make a lot of sense to me, as users of ktlint do not seem to be interested/invested in testing new versions before the actual release. That is the main reason that after major/minor release usually one patch version is needed a couple of weeks after the major/minor release.
This is indeed a concern. But there is no need to be stressed about it too much. We don't need to replace 100% of Psi with AST. First we can pick the low hanging fruit. By doing so, we learn more about what works or doesn't work. Those learnings will help with future decision making.
Agree. Let's first work on wrapping/merging this part.
I see no reason to migrate to the analysis API. Expanding ASTNodeExtension is fine. Let's see how this works out. Thanks for your efforts sofar. It is nice to have input from a totally different perspective. I will start detailed review. |
I cannot make a strong commitment to make fixes in a timely manner due to my other priorities, but please feel free to flag me on any bug report that involves my changes and I will help when I can. |
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.
This is really a promising initative!
Please don't be dishartened by the amount of review remarks. Most of them are relatively minor because you are not fully acquainted with the code base.
I do have some concerns about replacing all Psi code with own AstNode although the readability is improved on lots of places. Sofar, I have seen no changes that should not have been ported from Psi.
@@ -6,6 +6,7 @@ public final class com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtensionKt | |||
public static final fun findCompositeParentElementOfType (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lorg/jetbrains/kotlin/com/intellij/psi/tree/IElementType;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; | |||
public static final fun firstChildLeafOrSelf (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; | |||
public static final fun getColumn (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)I | |||
public static final fun getPrevSiblingIgnoringWhitespaceAndComments (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;)Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode; |
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.
Whenever a function is added to the public API, we get a responsibility to maintain it at least until the next major release. As of that we should be careful with:
- Naming of the function
- Avoiding duplication
For each public method that is added to the ASTNodeExtension
the following is required:
- API documentation
- Unit tests in
ASTNodeExtensionTest
. Typically the methods need multiple units tests, which should be collected in an inner class.
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.
I'm taking the shortcut of making this internal. Hope that's ok.
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.
nevermind, since it is a duplicate I will delete 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.
Making a function internal wouldn't work anyways, as it would not expose the function to for example the rules modules.
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.
API docs are still missing on new methods findChildByTypeRecursively
and endOffset
.
...le-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt
Outdated
Show resolved
Hide resolved
...le-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt
Outdated
Show resolved
Hide resolved
...le-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt
Show resolved
Hide resolved
...le-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/ASTNodeExtension.kt
Outdated
Show resolved
Hide resolved
...com/pinterest/ktlint/ruleset/standard/rules/SpacingBetweenDeclarationsWithAnnotationsRule.kt
Outdated
Show resolved
Hide resolved
...ain/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt
Outdated
Show resolved
Hide resolved
if (leafBeforeArrowOrNull.elementType == ElementType.WHITE_SPACE) { | ||
(leafBeforeArrowOrNull.psi as LeafPsiElement).rawReplaceWithText(indent) |
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.
Replace with:
if (leafBeforeArrowOrNull.isWhiteSpace()) {
(leafBeforeArrowOrNull?.psi as LeafPsiElement).rawReplaceWithText(indent)
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.
The second line was not changed.
My IDEA shows an error:
Strangely enough it can be compiled successfully, and all tests succeed. But still I like not to see any errors ;-)
We can make it even betters, as we do need to convert to psi:
(leafBeforeArrowOrNull as LeafElement).rawReplaceWithText(indent)
...ain/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt
Outdated
Show resolved
Hide resolved
...ain/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt
Outdated
Show resolved
Hide resolved
This is a tangential question: would ktlint consider using detekt to enforce rules on its own code? I ask because I notice you keep a really tight ship in terms of code style (in ways that are beyond just formating), and detekt may be able to automate and enforce some of that. Also, I could see detekt and ktlint sharing a common library for some basic ASTNode operations, but that's a separate point. |
I have considered this at some point, but never followed up upon it. As Detekt also contains Ktlint, I wonder what happens when I make a change in ktlint that conflicts with the older version of Ktlint in Detekt.
That could be an option, but it might be hard to align on the governance of that library. Also, it might communicate to the world that it is a complete library for ASTNode operations and as of that get many feature requests not needed by Ktlint / Detekt. |
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.
Hi Matt,
I did review your changes about a week ago, but did not mention that explicitly yet. Can have a look at the unresolved issues?
Description
I observed a performance bottleneck in
com.pinterest.ktlint.rule.engine.core.api.isPartOfComment
. CPU profiling showed a significant amount of time being wasted doing stuff related to checking for cancellation, like IntelliJ GUI stuff.Checklist
Before submitting the PR, please check following (checks which are not relevant may be ignored):
Closes #<xxx>
orFixes #<xxx>
(replace<xxx>
with issue number)Documentation is updated. See difference between snapshot and release documentation
This PR doesn't include new tests, doesn't reference an issue, and doesn't change documentaiton. It is a performance optimization.
According to IntelliJ's Usages, there are about 65 other usages of
ASTNode.getPsi()
in the ktlint codebase. I'm not sure if all of them could be removed, but maybe we could try to remove as many of them as possible? If this PR looks good, I suggest we follow up by doing that.