Skip to content

Commit

Permalink
Preserve semicolons in enums with members but no entries
Browse files Browse the repository at this point in the history
Inspired by facebook#425
  • Loading branch information
nreid260 committed Dec 16, 2023
1 parent c0511df commit bf26e68
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.psi.KtBreakExpression
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtCatchClause
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassBody
import org.jetbrains.kotlin.psi.KtClassInitializer
import org.jetbrains.kotlin.psi.KtClassLiteralExpression
Expand Down Expand Up @@ -1943,7 +1944,8 @@ class KotlinInputAstVisitor(
emitBracedBlock(body) { children ->
val (enumEntries, nonEnumEntryMembers) = children.partition { it is KtEnumEntry }

if (enumEntries.isNotEmpty()) {
val parent = body.parent
if (parent is KtClass && parent.isEnum()) {
builder.block(ZERO) {
builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
for (value in enumEntries) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.facebook.ktfmt.format

import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtContainerNodeForControlStructureBody
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtEnumEntry
Expand Down Expand Up @@ -51,13 +52,21 @@ internal class RedundantSemicolonDetector {
if (parent is KtStringTemplateExpression || parent is KtStringTemplateEntry) {
return false
}
if (parent is KtEnumEntry &&
parent.siblings(forward = true, withItself = false).any { it is KtDeclaration }) {
return false

if (parent is KtEnumEntry) {
// Terminationg comma of an enum entry list.
return !hasFollowingDeclaration(parent)
}

val prevLeaf = element.prevLeaf(false)
val grandparent = parent.parent
val prevConcreteSibling = element.getPrevSiblingIgnoringWhitespaceAndComments()
if (grandparent is KtClass && grandparent.isEnum()) {
val isFirstToken = prevConcreteSibling?.text == "{"
// Terminationg comma of an empty enum entry list.
return !isFirstToken || !hasFollowingDeclaration(element)
}

val prevLeaf = element.prevLeaf(false)
if ((prevConcreteSibling is KtIfExpression || prevConcreteSibling is KtWhileExpression) &&
prevLeaf is KtContainerNodeForControlStructureBody &&
prevLeaf.text.isEmpty()) {
Expand All @@ -78,4 +87,8 @@ internal class RedundantSemicolonDetector {

return true
}

private fun hasFollowingDeclaration(element: PsiElement): Boolean {
return element.siblings(forward = true, withItself = false).any { it is KtDeclaration }
}
}
62 changes: 62 additions & 0 deletions core/src/test/java/com/facebook/ktfmt/format/FormatterTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4649,6 +4649,68 @@ class FormatterTest {
|"""
.trimMargin())

@Test
fun `semicolon is removed from empty enum`() {
val code =
"""
|enum class SingleSemi {
| ;
|}
|
|enum class MultSemi {
| // a
| ;
| // b
| ;
| // c
| ;
|}
|"""
.trimMargin()
val expected =
"""
|enum class SingleSemi {}
|
|enum class MultSemi {
| // a
|
| // b
|
| // c
|
|}
|"""
.trimMargin()
assertThatFormatting(code).isEqualTo(expected)
}

@Test
fun `semicolon management in enum with no entries but other members`() {
val code =
"""
|enum class Empty {
| ;
|
| fun f() {}
| ;
| fun g() {}
|}
|"""
.trimMargin()
val expected =
"""
|enum class Empty {
| ;
|
| fun f() {}
|
| fun g() {}
|}
|"""
.trimMargin()
assertThatFormatting(code).isEqualTo(expected)
}

@Test
fun `handle varargs and spread operator`() =
assertFormatted(
Expand Down

0 comments on commit bf26e68

Please sign in to comment.