-
Notifications
You must be signed in to change notification settings - Fork 503
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
Add rule to check that a single top level class name matches the file name #194
Add rule to check that a single top level class name matches the file name #194
Conversation
Hi Jacob (and thank you for the PR). Everything inside Starting from ktlint@0.22.0 file path is now accessible via Could you please update the PR to use that instead? Also,
|
- Get filepath from userData - Ignore non .kt files - Change offset to 0 for errror reporting - Removed ASTNodeExtensions, moved visit() method to internal util file
66d9966
to
1025de1
Compare
Thanks for the feedback! (and adding the file path to userData :)) Updated the PR. |
|
||
val topLevelClassNames = mutableListOf<String>() | ||
|
||
node.visit { |
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.
It's better to avoid whole AST traversal with something like
val topLevelClassNames = node.getChildren(null)
.filter { it.elementType == KtStubElementTypes.CLASS }
.mapNotNull { it.findChildByType(KtTokens.IDENTIFIER)?.text }
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 like this a lot more (removes the need to check for immediate parent and should also be more efficient :))
} | ||
} | ||
|
||
val name = Paths.get(node.getUserData(KtLint.FILE_PATH_USER_DATA_KEY)).fileName.toString().substringBefore(".") |
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.
node.getUserData(KtLint.FILE_PATH_USER_DATA_KEY)
should be extracted to a varible (referenced twice).
val name = Paths.get(node.getUserData(KtLint.FILE_PATH_USER_DATA_KEY)).fileName.toString().substringBefore(".") | ||
if (topLevelClassNames.size == 1 && name != topLevelClassNames.first()) { | ||
emit(0, | ||
"Single top level class name [${topLevelClassNames.first()}] does not match file name", |
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.
How about Class ${className} should be declared in a file named ${className}.kt
?
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.
Done!
- Filter immediate children to get top level classes - Changed error message
Perfect. Thank you so much, Jacob! |
This rule adheres to both Kotlin and Android Kotlin style guides:
https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names
https://android.github.io/kotlin-guides/style.html#naming
In order to make this change possible I had to plumb through the fileName into each Rule. This will also allow us to add additional style checks in the future (i.e. no underscores in file names, directory path matches package path, etc.)
Regarding autoFormat, I don't think there's a good solution here. If a violation occurs, then it's impossible for the tool to automatically determine whether to use the file name or the class name. The only alternative I can think of to this problem is to have Ktlint prompt the user for which name to use every time a violation occurs, which could be built after #79 is complete.