Handle ; between property and accessor

Summary:
See example in test. When a semicolon separates the accessor of a property we may not insert a newline.
We could alternatively

Reviewed By: hick209

Differential Revision: D24195894

fbshipit-source-id: 91e34262b42210c6aff660c136073a62ebb52002
diff --git a/core/src/main/java/com/facebook/ktfmt/KotlinInputAstVisitor.kt b/core/src/main/java/com/facebook/ktfmt/KotlinInputAstVisitor.kt
index ef630b2..31a427f 100644
--- a/core/src/main/java/com/facebook/ktfmt/KotlinInputAstVisitor.kt
+++ b/core/src/main/java/com/facebook/ktfmt/KotlinInputAstVisitor.kt
@@ -1074,24 +1074,35 @@
     }
 
     // for example `by lazy { compute() }`
+    var hasSemicolon = false
     if (delegate != null) {
       builder.space()
       builder.token("by")
       builder.space()
       delegate.accept(this)
+      hasSemicolon = delegate.nextSibling?.text == ";" == true
     } else if (initializer != null) {
       builder.space()
       builder.token("=")
       builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
+      hasSemicolon = initializer.nextSibling?.text == ";" == true
       builder.block(expressionBreakIndent) { initializer.accept(this) }
     }
     if (name != null) {
       builder.close() // close block for named values
     }
+    if (hasSemicolon) {
+      builder.token(";")
+    }
+    // for example `private set` or `get = 2 * field`
     if (accessors?.isNotEmpty() == true) {
       for (accessor in accessors) {
         builder.block(blockIndent) {
-          builder.forcedBreak()
+          if (hasSemicolon) {
+            builder.space()
+          } else {
+            builder.forcedBreak()
+          }
           visitFunctionLikeExpression(
               accessor.modifierList,
               accessor.namePlaceholder.text,
diff --git a/core/src/main/java/com/facebook/ktfmt/RedundantElementRemover.kt b/core/src/main/java/com/facebook/ktfmt/RedundantElementRemover.kt
index 15c4d2e..64b0cba 100644
--- a/core/src/main/java/com/facebook/ktfmt/RedundantElementRemover.kt
+++ b/core/src/main/java/com/facebook/ktfmt/RedundantElementRemover.kt
@@ -121,6 +121,13 @@
         prevLeaf.text.isEmpty()) {
       return false
     }
+
+    // do not remove `;` in `val a = 5; private set`
+    val nextSibling = element.nextSibling
+    if (nextSibling != null && '\n' !in nextSibling.text) {
+      return false
+    }
+
     return true
   }
 }
diff --git a/core/src/test/java/com/facebook/ktfmt/FormatterKtTest.kt b/core/src/test/java/com/facebook/ktfmt/FormatterKtTest.kt
index d08d564..9b0aecf 100644
--- a/core/src/test/java/com/facebook/ktfmt/FormatterKtTest.kt
+++ b/core/src/test/java/com/facebook/ktfmt/FormatterKtTest.kt
@@ -343,6 +343,16 @@
       |""".trimMargin())
 
   @Test
+  fun `properties with accessors and semicolons on same line`() =
+      assertFormatted(
+          """
+      |class Foo {
+      |  var x = false; private set
+      |  internal val a by lazy { 5 }; internal get
+      |}
+      |""".trimMargin())
+
+  @Test
   fun `a property with a too long name being broken on multiple lines`() =
       assertFormatted(
           """