diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java
index 4531d49f5c1..4d969f888b9 100644
--- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java
+++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinExpressionParsing.java
@@ -118,7 +118,7 @@ public class KotlinExpressionParsing extends AbstractKotlinParsing {
LBRACKET // Collection literal expression
);
- private static final TokenSet STATEMENT_FIRST = TokenSet.orSet(
+ public static final TokenSet STATEMENT_FIRST = TokenSet.orSet(
EXPRESSION_FIRST,
TokenSet.create(
// declaration
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 705bc2a6923..1498ff4571f 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -798,6 +798,7 @@
+
1) return CANNOT_JOIN
+ if (!element1.isStatement()) return CANNOT_JOIN
+ if (!element2.isStatement()) return CANNOT_JOIN
+
+ document.replaceString(linebreak.textRange.startOffset, linebreak.textRange.endOffset, " ")
+ document.insertString(element1.textRange.endOffset, ";")
+
+ return linebreak.textRange.startOffset + 1
+ }
+
+ override fun tryJoinLines(document: Document, file: PsiFile, start: Int, end: Int) = CANNOT_JOIN
+
+ private fun PsiElement.firstMaterialSiblingSameLine(getNext: PsiElement.() -> PsiElement?): PsiElement? {
+ var element = this
+ do {
+ element = element.getNext() ?: return null
+ if (element.node.elementType !in WHITE_SPACE_OR_COMMENT_BIT_SET)
+ return element
+ }
+ while (!element.textContains('\n'))
+
+ return null
+ }
+
+ private fun PsiElement.isStatement(): Boolean {
+ if (this.node.elementType == LBRACE) return false
+
+ var firstSubElement: PsiElement = this
+ while (true) firstSubElement = firstSubElement.firstChild ?: break
+
+ // Emulates the `atSet(STATEMENT_FIRST)` check at [KotlinExpressionParsing.parseStatements]
+ return firstSubElement.node.elementType in KotlinExpressionParsing.STATEMENT_FIRST
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/joinLines/addSemicolon/ClassDeclarations.kt b/idea/testData/joinLines/addSemicolon/ClassDeclarations.kt
new file mode 100644
index 00000000000..f1718e52538
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/ClassDeclarations.kt
@@ -0,0 +1,2 @@
+class A
+class B
diff --git a/idea/testData/joinLines/addSemicolon/ClassDeclarations.kt.after b/idea/testData/joinLines/addSemicolon/ClassDeclarations.kt.after
new file mode 100644
index 00000000000..6a7c080e9c8
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/ClassDeclarations.kt.after
@@ -0,0 +1 @@
+class A class B
diff --git a/idea/testData/joinLines/addSemicolon/FunctionCalls.kt b/idea/testData/joinLines/addSemicolon/FunctionCalls.kt
new file mode 100644
index 00000000000..5b8317de172
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionCalls.kt
@@ -0,0 +1,4 @@
+fun foo() {
+ println()
+ println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/FunctionCalls.kt.after b/idea/testData/joinLines/addSemicolon/FunctionCalls.kt.after
new file mode 100644
index 00000000000..30685a33395
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionCalls.kt.after
@@ -0,0 +1,3 @@
+fun foo() {
+ println(); println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/FunctionCallsAndAComment.kt b/idea/testData/joinLines/addSemicolon/FunctionCallsAndAComment.kt
new file mode 100644
index 00000000000..6e7950c479a
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionCallsAndAComment.kt
@@ -0,0 +1,4 @@
+fun foo() {
+ println() // A very important comment
+ println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/FunctionCallsAndAComment.kt.after b/idea/testData/joinLines/addSemicolon/FunctionCallsAndAComment.kt.after
new file mode 100644
index 00000000000..95cc54376f8
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionCallsAndAComment.kt.after
@@ -0,0 +1,3 @@
+fun foo() {
+ println(); /* A very important comment*/ println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/FunctionCallsAndAMultilineComment.kt b/idea/testData/joinLines/addSemicolon/FunctionCallsAndAMultilineComment.kt
new file mode 100644
index 00000000000..1cc5761e841
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionCallsAndAMultilineComment.kt
@@ -0,0 +1,5 @@
+fun foo() {
+ println() // A very long
+ // and important comment
+ println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/FunctionCallsAndAMultilineComment.kt.after b/idea/testData/joinLines/addSemicolon/FunctionCallsAndAMultilineComment.kt.after
new file mode 100644
index 00000000000..f12ff978100
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionCallsAndAMultilineComment.kt.after
@@ -0,0 +1,4 @@
+fun foo() {
+ println() // A very long and important comment
+ println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/FunctionCallsAndSeveralComments.kt b/idea/testData/joinLines/addSemicolon/FunctionCallsAndSeveralComments.kt
new file mode 100644
index 00000000000..d46ef6ff5bd
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionCallsAndSeveralComments.kt
@@ -0,0 +1,4 @@
+fun foo() {
+ /*0*/println() /*1*/ /*2*/
+println() // 4
+}
diff --git a/idea/testData/joinLines/addSemicolon/FunctionCallsAndSeveralComments.kt.after b/idea/testData/joinLines/addSemicolon/FunctionCallsAndSeveralComments.kt.after
new file mode 100644
index 00000000000..e9faf4b6e17
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionCallsAndSeveralComments.kt.after
@@ -0,0 +1,3 @@
+fun foo() {
+ /*0*/println(); /*1*/ /*2*/ println() // 4
+}
diff --git a/idea/testData/joinLines/addSemicolon/FunctionCallsWithMutlineSeparator.kt b/idea/testData/joinLines/addSemicolon/FunctionCallsWithMutlineSeparator.kt
new file mode 100644
index 00000000000..9f8915a0675
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionCallsWithMutlineSeparator.kt
@@ -0,0 +1,5 @@
+fun foo() {
+ println()
+
+ println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/FunctionCallsWithMutlineSeparator.kt.after b/idea/testData/joinLines/addSemicolon/FunctionCallsWithMutlineSeparator.kt.after
new file mode 100644
index 00000000000..cc22964a57b
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionCallsWithMutlineSeparator.kt.after
@@ -0,0 +1,4 @@
+fun foo() {
+ println()
+ println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/FunctionDeclarations.kt b/idea/testData/joinLines/addSemicolon/FunctionDeclarations.kt
new file mode 100644
index 00000000000..92ee4965670
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionDeclarations.kt
@@ -0,0 +1,6 @@
+fun foo() {
+
+}
+fun boo() {
+
+}
diff --git a/idea/testData/joinLines/addSemicolon/FunctionDeclarations.kt.after b/idea/testData/joinLines/addSemicolon/FunctionDeclarations.kt.after
new file mode 100644
index 00000000000..36bb058ab9c
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/FunctionDeclarations.kt.after
@@ -0,0 +1,5 @@
+fun foo() {
+
+} fun boo() {
+
+}
diff --git a/idea/testData/joinLines/addSemicolon/InsideBraces.kt b/idea/testData/joinLines/addSemicolon/InsideBraces.kt
new file mode 100644
index 00000000000..60d527bea04
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/InsideBraces.kt
@@ -0,0 +1,5 @@
+fun foo() {
+ {
+ println()
+ }
+}
diff --git a/idea/testData/joinLines/addSemicolon/InsideBraces.kt.after b/idea/testData/joinLines/addSemicolon/InsideBraces.kt.after
new file mode 100644
index 00000000000..62c7da2e914
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/InsideBraces.kt.after
@@ -0,0 +1,4 @@
+fun foo() {
+ { println()
+ }
+}
diff --git a/idea/testData/joinLines/addSemicolon/InsideIfBlock.kt b/idea/testData/joinLines/addSemicolon/InsideIfBlock.kt
new file mode 100644
index 00000000000..00ea7f862d6
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/InsideIfBlock.kt
@@ -0,0 +1,6 @@
+fun foo() {
+ if(true){
+ println()
+ println()
+ }
+}
diff --git a/idea/testData/joinLines/addSemicolon/InsideIfBlock.kt.after b/idea/testData/joinLines/addSemicolon/InsideIfBlock.kt.after
new file mode 100644
index 00000000000..4dced9d9377
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/InsideIfBlock.kt.after
@@ -0,0 +1,5 @@
+fun foo() {
+ if(true){ println()
+ println()
+ }
+}
diff --git a/idea/testData/joinLines/addSemicolon/LocalAssingments.kt b/idea/testData/joinLines/addSemicolon/LocalAssingments.kt
new file mode 100644
index 00000000000..4f793f7a158
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/LocalAssingments.kt
@@ -0,0 +1,4 @@
+fun foo() {
+ val a = 1
+ val b = 1
+}
diff --git a/idea/testData/joinLines/addSemicolon/LocalAssingments.kt.after b/idea/testData/joinLines/addSemicolon/LocalAssingments.kt.after
new file mode 100644
index 00000000000..052e09e397f
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/LocalAssingments.kt.after
@@ -0,0 +1,3 @@
+fun foo() {
+ val a = 1; val b = 1
+}
diff --git a/idea/testData/joinLines/addSemicolon/LocalAssingmentsWithSemicolon.kt b/idea/testData/joinLines/addSemicolon/LocalAssingmentsWithSemicolon.kt
new file mode 100644
index 00000000000..b6376419278
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/LocalAssingmentsWithSemicolon.kt
@@ -0,0 +1,4 @@
+fun foo() {
+ val a = 1;
+ val b = 1
+}
diff --git a/idea/testData/joinLines/addSemicolon/LocalAssingmentsWithSemicolon.kt.after b/idea/testData/joinLines/addSemicolon/LocalAssingmentsWithSemicolon.kt.after
new file mode 100644
index 00000000000..052e09e397f
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/LocalAssingmentsWithSemicolon.kt.after
@@ -0,0 +1,3 @@
+fun foo() {
+ val a = 1; val b = 1
+}
diff --git a/idea/testData/joinLines/addSemicolon/LocalClasses.kt b/idea/testData/joinLines/addSemicolon/LocalClasses.kt
new file mode 100644
index 00000000000..a638e204420
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/LocalClasses.kt
@@ -0,0 +1,4 @@
+fun main(args: Array) {
+ class A
+ class B
+}
diff --git a/idea/testData/joinLines/addSemicolon/LocalClasses.kt.after b/idea/testData/joinLines/addSemicolon/LocalClasses.kt.after
new file mode 100644
index 00000000000..5c842b50457
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/LocalClasses.kt.after
@@ -0,0 +1,3 @@
+fun main(args: Array) {
+ class A; class B
+}
diff --git a/idea/testData/joinLines/addSemicolon/LocalFunctions.kt b/idea/testData/joinLines/addSemicolon/LocalFunctions.kt
new file mode 100644
index 00000000000..613373c8612
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/LocalFunctions.kt
@@ -0,0 +1,8 @@
+fun main(args: Array) {
+ fun foo() {
+
+ }
+ fun boo() {
+
+ }
+}
diff --git a/idea/testData/joinLines/addSemicolon/LocalFunctions.kt.after b/idea/testData/joinLines/addSemicolon/LocalFunctions.kt.after
new file mode 100644
index 00000000000..10ddc062c31
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/LocalFunctions.kt.after
@@ -0,0 +1,7 @@
+fun main(args: Array) {
+ fun foo() {
+
+ }; fun boo() {
+
+ }
+}
diff --git a/idea/testData/joinLines/addSemicolon/MemberFunctions.kt b/idea/testData/joinLines/addSemicolon/MemberFunctions.kt
new file mode 100644
index 00000000000..c479407e716
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/MemberFunctions.kt
@@ -0,0 +1,4 @@
+class A {
+ fun a(){}
+ fun b(){}
+}
diff --git a/idea/testData/joinLines/addSemicolon/MemberFunctions.kt.after b/idea/testData/joinLines/addSemicolon/MemberFunctions.kt.after
new file mode 100644
index 00000000000..4e2063cd856
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/MemberFunctions.kt.after
@@ -0,0 +1,3 @@
+class A {
+ fun a(){} fun b(){}
+}
diff --git a/idea/testData/joinLines/addSemicolon/WhileAndACall.kt b/idea/testData/joinLines/addSemicolon/WhileAndACall.kt
new file mode 100644
index 00000000000..8a529e24c46
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/WhileAndACall.kt
@@ -0,0 +1,4 @@
+fun foo() {
+ while (true) println()
+ println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/WhileAndACall.kt.after b/idea/testData/joinLines/addSemicolon/WhileAndACall.kt.after
new file mode 100644
index 00000000000..1204e9c200d
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/WhileAndACall.kt.after
@@ -0,0 +1,3 @@
+fun foo() {
+ while (true) println(); println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/WhileBlockAndACall.kt b/idea/testData/joinLines/addSemicolon/WhileBlockAndACall.kt
new file mode 100644
index 00000000000..16aec0f3406
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/WhileBlockAndACall.kt
@@ -0,0 +1,4 @@
+fun foo() {
+ while (true) { println() }
+ println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/WhileBlockAndACall.kt.after b/idea/testData/joinLines/addSemicolon/WhileBlockAndACall.kt.after
new file mode 100644
index 00000000000..3107aa31249
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/WhileBlockAndACall.kt.after
@@ -0,0 +1,3 @@
+fun foo() {
+ while (true) { println() }; println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/WhileBlockAndACall2.kt b/idea/testData/joinLines/addSemicolon/WhileBlockAndACall2.kt
new file mode 100644
index 00000000000..4090d058f12
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/WhileBlockAndACall2.kt
@@ -0,0 +1,6 @@
+fun foo() {
+ while (true) {
+ println()
+ }
+ println()
+}
diff --git a/idea/testData/joinLines/addSemicolon/WhileBlockAndACall2.kt.after b/idea/testData/joinLines/addSemicolon/WhileBlockAndACall2.kt.after
new file mode 100644
index 00000000000..a2cd76815d3
--- /dev/null
+++ b/idea/testData/joinLines/addSemicolon/WhileBlockAndACall2.kt.after
@@ -0,0 +1,5 @@
+fun foo() {
+ while (true) {
+ println()
+ }; println()
+}
diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/declarations/JoinLinesTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/declarations/JoinLinesTestGenerated.java
index 93a40aff339..d6cb192178d 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/intentions/declarations/JoinLinesTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/declarations/JoinLinesTestGenerated.java
@@ -36,6 +36,117 @@ public class JoinLinesTestGenerated extends AbstractJoinLinesTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/joinLines"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
+ @TestMetadata("idea/testData/joinLines/addSemicolon")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class AddSemicolon extends AbstractJoinLinesTest {
+ public void testAllFilesPresentInAddSemicolon() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/joinLines/addSemicolon"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("ClassDeclarations.kt")
+ public void testClassDeclarations() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/ClassDeclarations.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FunctionCalls.kt")
+ public void testFunctionCalls() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/FunctionCalls.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FunctionCallsAndAComment.kt")
+ public void testFunctionCallsAndAComment() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/FunctionCallsAndAComment.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FunctionCallsAndAMultilineComment.kt")
+ public void testFunctionCallsAndAMultilineComment() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/FunctionCallsAndAMultilineComment.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FunctionCallsAndSeveralComments.kt")
+ public void testFunctionCallsAndSeveralComments() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/FunctionCallsAndSeveralComments.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FunctionCallsWithMutlineSeparator.kt")
+ public void testFunctionCallsWithMutlineSeparator() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/FunctionCallsWithMutlineSeparator.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FunctionDeclarations.kt")
+ public void testFunctionDeclarations() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/FunctionDeclarations.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("InsideBraces.kt")
+ public void testInsideBraces() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/InsideBraces.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("InsideIfBlock.kt")
+ public void testInsideIfBlock() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/InsideIfBlock.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("LocalAssingments.kt")
+ public void testLocalAssingments() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/LocalAssingments.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("LocalAssingmentsWithSemicolon.kt")
+ public void testLocalAssingmentsWithSemicolon() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/LocalAssingmentsWithSemicolon.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("LocalClasses.kt")
+ public void testLocalClasses() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/LocalClasses.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("LocalFunctions.kt")
+ public void testLocalFunctions() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/LocalFunctions.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("MemberFunctions.kt")
+ public void testMemberFunctions() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/MemberFunctions.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("WhileAndACall.kt")
+ public void testWhileAndACall() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/WhileAndACall.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("WhileBlockAndACall.kt")
+ public void testWhileBlockAndACall() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/WhileBlockAndACall.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("WhileBlockAndACall2.kt")
+ public void testWhileBlockAndACall2() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/joinLines/addSemicolon/WhileBlockAndACall2.kt");
+ doTest(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/joinLines/declarationAndAssignment")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)