diff --git a/idea/src/org/jetbrains/kotlin/idea/editor/KotlinEditorOptions.java b/idea/src/org/jetbrains/kotlin/idea/editor/KotlinEditorOptions.java index e63ec555ea8..5fa0a022d40 100644 --- a/idea/src/org/jetbrains/kotlin/idea/editor/KotlinEditorOptions.java +++ b/idea/src/org/jetbrains/kotlin/idea/editor/KotlinEditorOptions.java @@ -33,6 +33,7 @@ import org.jetbrains.annotations.Nullable; public class KotlinEditorOptions implements PersistentStateComponent { private boolean donTShowConversionDialog = false; private boolean enableJavaToKotlinConversion = true; + private boolean autoAddValKeywordToDataClassParameters = true; public boolean isDonTShowConversionDialog() { return donTShowConversionDialog; @@ -42,6 +43,14 @@ public class KotlinEditorOptions implements PersistentStateComponent\n" + - "}", + "fun test() {\n" + + "\n" + + "}", - "fun test() {\n" + - " {}\n" + - "}" + "fun test() {\n" + + " {}\n" + + "}" ) fun testAutoIndentLeftOpenBrace() = doTypeTest( - '{', + '{', - "fun test() {\n" + - " \n" + - "}", + "fun test() {\n" + + " \n" + + "}", - "fun test() {\n" + - " {}\n" + - "}" + "fun test() {\n" + + " {}\n" + + "}" ) fun testTypeStringTemplateStartWithCloseBraceAfter() = doTypeTest( - '{', - """fun foo() { "$" }""", - """fun foo() { "$dollar{}" }""" + '{', + """fun foo() { "$" }""", + """fun foo() { "$dollar{}" }""" ) fun testTypeStringTemplateStartBeforeStringWithExistingDollar() = doTypeTest( - '{', - """fun foo() { "$something" }""", - """fun foo() { "$dollar{something" }""" + '{', + """fun foo() { "$something" }""", + """fun foo() { "$dollar{something" }""" ) fun testTypeStringTemplateStartBeforeStringWithNoDollar() = doTypeTest( @@ -93,45 +93,45 @@ class TypedHandlerTest : LightCodeInsightTestCase() { ) fun testKT3575() = doTypeTest( - '{', - """val x = "$]" """, - """val x = "$dollar{}]" """ + '{', + """val x = "$]" """, + """val x = "$dollar{}]" """ ) fun testAutoCloseRawStringInEnd() = doTypeTest( - '"', - """val x = """"", - """val x = ""${'"'}""${'"'}""" + '"', + """val x = """"", + """val x = ""${'"'}""${'"'}""" ) fun testNoAutoCloseRawStringInEnd() = doTypeTest( - '"', - """val x = ""${'"'}""", - """val x = ""${'"'}"""" + '"', + """val x = ""${'"'}""", + """val x = ""${'"'}"""" ) fun testAutoCloseRawStringInMiddle() = doTypeTest( - '"', - """ + '"', + """ val x = "" val y = 12 """.trimIndent(), - """ + """ val x = ""${'"'}""${'"'} val y = 12 """.trimIndent() ) fun testNoAutoCloseBetweenMultiQuotes() = doTypeTest( - '"', - """val x = ""${'"'}${'"'}""/**/""", - """val x = ""${'"'}${'"'}""/**/""" + '"', + """val x = ""${'"'}${'"'}""/**/""", + """val x = ""${'"'}${'"'}""/**/""" ) fun testNoAutoCloseBetweenMultiQuotes1() = doTypeTest( - '"', - """val x = ""${'"'}""${'"'}/**/""", - """val x = ""${'"'}""${'"'}/**/""" + '"', + """val x = ""${'"'}""${'"'}/**/""", + """val x = ""${'"'}""${'"'}/**/""" ) fun testNoAutoCloseAfterEscape() = doTypeTest( @@ -141,322 +141,322 @@ class TypedHandlerTest : LightCodeInsightTestCase() { ) fun testAutoCloseBraceInFunctionDeclaration() = doTypeTest( - '{', - "fun foo() ", - "fun foo() {}" + '{', + "fun foo() ", + "fun foo() {}" ) fun testAutoCloseBraceInLocalFunctionDeclaration() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " fun bar() \n" + - "}", + "fun foo() {\n" + + " fun bar() \n" + + "}", - "fun foo() {\n" + - " fun bar() {}\n" + - "}" + "fun foo() {\n" + + " fun bar() {}\n" + + "}" ) fun testAutoCloseBraceInAssignment() = doTypeTest( - '{', - "fun foo() {\n" + - " val a = \n" + - "}", + '{', + "fun foo() {\n" + + " val a = \n" + + "}", - "fun foo() {\n" + - " val a = {}\n" + - "}" + "fun foo() {\n" + + " val a = {}\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnSameLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " if() foo()\n" + - "}", + "fun foo() {\n" + + " if() foo()\n" + + "}", - "fun foo() {\n" + - " if() {foo()\n" + - "}" + "fun foo() {\n" + + " if() {foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnSameLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " if(true) {} else foo()\n" + - "}", + "fun foo() {\n" + + " if(true) {} else foo()\n" + + "}", - "fun foo() {\n" + - " if(true) {} else {foo()\n" + - "}" + "fun foo() {\n" + + " if(true) {} else {foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedTryOnSameLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " try foo()\n" + - "}", + "fun foo() {\n" + + " try foo()\n" + + "}", - "fun foo() {\n" + - " try {foo()\n" + - "}" + "fun foo() {\n" + + " try {foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedCatchOnSameLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " try {} catch (e: Exception) foo()\n" + - "}", + "fun foo() {\n" + + " try {} catch (e: Exception) foo()\n" + + "}", - "fun foo() {\n" + - " try {} catch (e: Exception) {foo()\n" + - "}" + "fun foo() {\n" + + " try {} catch (e: Exception) {foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedFinallyOnSameLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " try {} catch (e: Exception) finally foo()\n" + - "}", + "fun foo() {\n" + + " try {} catch (e: Exception) finally foo()\n" + + "}", - "fun foo() {\n" + - " try {} catch (e: Exception) finally {foo()\n" + - "}" + "fun foo() {\n" + + " try {} catch (e: Exception) finally {foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedWhileSurroundOnSameLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " while() foo()\n" + - "}", + "fun foo() {\n" + + " while() foo()\n" + + "}", - "fun foo() {\n" + - " while() {foo()\n" + - "}" + "fun foo() {\n" + + " while() {foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedWhileSurroundOnNewLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " while()\n" + - "\n" + - " foo()\n" + - "}", + "fun foo() {\n" + + " while()\n" + + "\n" + + " foo()\n" + + "}", - "fun foo() {\n" + - " while()\n" + - " {\n" + - " foo()\n" + - "}" + "fun foo() {\n" + + " while()\n" + + " {\n" + + " foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnOtherLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " if(true) \n" + - " foo()\n" + - "}", + "fun foo() {\n" + + " if(true) \n" + + " foo()\n" + + "}", - "fun foo() {\n" + - " if(true) {\n" + - " foo()\n" + - "}" + "fun foo() {\n" + + " if(true) {\n" + + " foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnOtherLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " if(true) {} else \n" + - " foo()\n" + - "}", + "fun foo() {\n" + + " if(true) {} else \n" + + " foo()\n" + + "}", - "fun foo() {\n" + - " if(true) {} else {\n" + - " foo()\n" + - "}" + "fun foo() {\n" + + " if(true) {} else {\n" + + " foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedTryOnOtherLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " try \n" + - " foo()\n" + - "}", + "fun foo() {\n" + + " try \n" + + " foo()\n" + + "}", - "fun foo() {\n" + - " try {\n" + - " foo()\n" + - "}" + "fun foo() {\n" + + " try {\n" + + " foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnNewLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " if(true)\n" + - " \n" + - " foo()\n" + - "}", + "fun foo() {\n" + + " if(true)\n" + + " \n" + + " foo()\n" + + "}", - "fun foo() {\n" + - " if(true)\n" + - " {\n" + - " foo()\n" + - "}" + "fun foo() {\n" + + " if(true)\n" + + " {\n" + + " foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnNewLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " if(true) {} else\n" + - " \n" + - " foo()\n" + - "}", + "fun foo() {\n" + + " if(true) {} else\n" + + " \n" + + " foo()\n" + + "}", - "fun foo() {\n" + - " if(true) {} else\n" + - " {\n" + - " foo()\n" + - "}" + "fun foo() {\n" + + " if(true) {} else\n" + + " {\n" + + " foo()\n" + + "}" ) fun testDoNotAutoCloseBraceInUnfinishedTryOnNewLine() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " try\n" + - " \n" + - " foo()\n" + - "}", + "fun foo() {\n" + + " try\n" + + " \n" + + " foo()\n" + + "}", - "fun foo() {\n" + - " try\n" + - " {\n" + - " foo()\n" + - "}" + "fun foo() {\n" + + " try\n" + + " {\n" + + " foo()\n" + + "}" ) fun testAutoCloseBraceInsideFor() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " for (elem in some.filter ) {\n" + - " }\n" + - "}", + "fun foo() {\n" + + " for (elem in some.filter ) {\n" + + " }\n" + + "}", - "fun foo() {\n" + - " for (elem in some.filter {}) {\n" + - " }\n" + - "}" + "fun foo() {\n" + + " for (elem in some.filter {}) {\n" + + " }\n" + + "}" ) fun testAutoCloseBraceInsideForAfterCloseParen() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " for (elem in some.foo(true) ) {\n" + - " }\n" + - "}", + "fun foo() {\n" + + " for (elem in some.foo(true) ) {\n" + + " }\n" + + "}", - "fun foo() {\n" + - " for (elem in some.foo(true) {}) {\n" + - " }\n" + - "}" + "fun foo() {\n" + + " for (elem in some.foo(true) {}) {\n" + + " }\n" + + "}" ) fun testAutoCloseBraceBeforeIf() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " if (true) {}\n" + - "}", + "fun foo() {\n" + + " if (true) {}\n" + + "}", - "fun foo() {\n" + - " {if (true) {}\n" + - "}" + "fun foo() {\n" + + " {if (true) {}\n" + + "}" ) fun testAutoCloseBraceInIfCondition() = doTypeTest( - '{', + '{', - "fun foo() {\n" + - " if (some.hello (12) )\n" + - "}", + "fun foo() {\n" + + " if (some.hello (12) )\n" + + "}", - "fun foo() {\n" + - " if (some.hello (12) {})\n" + - "}" + "fun foo() {\n" + + " if (some.hello (12) {})\n" + + "}" ) - + fun testInsertSpaceAfterRightBraceOfNestedLambda() = doTypeTest( '{', "val t = Array(100) { Array(200) }", "val t = Array(100) { Array(200) {} }" ) - + fun testAutoInsertParenInStringLiteral() = doTypeTest( - '(', - """fun f() { println("$dollar{f}") }""", - """fun f() { println("$dollar{f()}") }""" + '(', + """fun f() { println("$dollar{f}") }""", + """fun f() { println("$dollar{f()}") }""" ) fun testAutoInsertParenInCode() = doTypeTest( - '(', - """fun f() { val a = f }""", - """fun f() { val a = f() }""" + '(', + """fun f() { val a = f }""", + """fun f() { val a = f() }""" ) fun testSplitStringByEnter() = doTypeTest( - '\n', - """val s = "foobar"""", - "val s = \"foo\" +\n" + - " \"bar\"" + '\n', + """val s = "foobar"""", + "val s = \"foo\" +\n" + + " \"bar\"" ) fun testSplitStringByEnterEmpty() = doTypeTest( - '\n', - """val s = """"", - "val s = \"\" +\n" + - " \"\"" + '\n', + """val s = """"", + "val s = \"\" +\n" + + " \"\"" ) fun testSplitStringByEnterBeforeEscapeSequence() = doTypeTest( - '\n', - """val s = "foo\nbar"""", - "val s = \"foo\" +\n" + - " \"\\nbar\"" + '\n', + """val s = "foo\nbar"""", + "val s = \"foo\" +\n" + + " \"\\nbar\"" ) fun testSplitStringByEnterBeforeSubstitution() = doTypeTest( - '\n', - """val s = "foo${dollar}bar"""", - "val s = \"foo\" +\n" + - " \"${dollar}bar\"" + '\n', + """val s = "foo${dollar}bar"""", + "val s = \"foo\" +\n" + + " \"${dollar}bar\"" ) fun testSplitStringByEnterAddParentheses() = doTypeTest( - '\n', - """val l = "foobar".length()""", - "val l = (\"foo\" +\n" + - " \"bar\").length()" + '\n', + """val l = "foobar".length()""", + "val l = (\"foo\" +\n" + + " \"bar\").length()" ) fun testSplitStringByEnterExistingParentheses() = doTypeTest( - '\n', - """val l = ("asdf" + "foobar").length()""", - "val l = (\"asdf\" + \"foo\" +\n" + - " \"bar\").length()" + '\n', + """val l = ("asdf" + "foobar").length()""", + "val l = (\"asdf\" + \"foo\" +\n" + + " \"bar\").length()" ) fun testTypeLtInFunDeclaration() { @@ -493,125 +493,132 @@ class TypedHandlerTest : LightCodeInsightTestCase() { fun testColonOfSuperTypeList() { doTypeTest( - ':', - """ + ':', + """ |open class A |class B | """, - """ + """ |open class A |class B | : - """) + """ + ) } fun testColonOfSuperTypeListInObject() { doTypeTest( - ':', - """ + ':', + """ |interface A |object B | """, - """ + """ |interface A |object B | : - """) + """ + ) } fun testColonOfSuperTypeListInCompanionObject() { doTypeTest( - ':', - """ + ':', + """ |interface A |class B { | companion object | |} """, - """ + """ |interface A |class B { | companion object | : |} - """) + """ + ) } fun testColonOfSuperTypeListBeforeBody() { doTypeTest( - ':', - """ + ':', + """ |open class A |class B | { |} """, - """ + """ |open class A |class B | : { |} - """) + """ + ) } fun testColonOfSuperTypeListNotNullIndent() { doTypeTest( - ':', - """ + ':', + """ |fun test() { | open class A | class B | |} """, - """ + """ |fun test() { | open class A | class B | : |} - """) + """ + ) } fun testChainCallContinueWithDot() { doTypeTest( - '.', - """ + '.', + """ |class Test{ fun test() = this } |fun some() { | Test() | |} """, - """ + """ |class Test{ fun test() = this } |fun some() { | Test() | . |} - """) + """ + ) } fun testChainCallContinueWithSafeCall() { doTypeTest( - '.', - """ + '.', + """ |class Test{ fun test() = this } |fun some() { | Test() | ? |} """, - """ + """ |class Test{ fun test() = this } |fun some() { | Test() | ?. |} - """) + """ + ) } fun testContinueWithElvis() { @@ -672,11 +679,11 @@ class TypedHandlerTest : LightCodeInsightTestCase() { fun testSpaceAroundRange() { doTypeTest( - '.', - """ + '.', + """ | val test = 1 """, - """ + """ | val test = 1 . """ ) @@ -684,8 +691,8 @@ class TypedHandlerTest : LightCodeInsightTestCase() { fun testIndentBeforeElseWithBlock() { doTypeTest( - '\n', - """ + '\n', + """ |fun test(b: Boolean) { | if (b) { | } @@ -693,7 +700,7 @@ class TypedHandlerTest : LightCodeInsightTestCase() { | } |} """, - """ + """ |fun test(b: Boolean) { | if (b) { | } @@ -707,8 +714,8 @@ class TypedHandlerTest : LightCodeInsightTestCase() { fun testIndentBeforeElseWithoutBlock() { doTypeTest( - '\n', - """ + '\n', + """ |fun test(b: Boolean) { | if (b) | foo() @@ -716,7 +723,7 @@ class TypedHandlerTest : LightCodeInsightTestCase() { | } |} """, - """ + """ |fun test(b: Boolean) { | if (b) | foo() @@ -730,14 +737,14 @@ class TypedHandlerTest : LightCodeInsightTestCase() { fun testIndentOnFinishedVariableEndAfterEquals() { doTypeTest( - '\n', - """ + '\n', + """ |fun test() { | val a = | foo() |} """, - """ + """ |fun test() { | val a = | @@ -749,13 +756,13 @@ class TypedHandlerTest : LightCodeInsightTestCase() { fun testIndentNotFinishedVariableEndAfterEquals() { doTypeTest( - '\n', - """ + '\n', + """ |fun test() { | val a = |} """, - """ + """ |fun test() { | val a = | @@ -766,37 +773,37 @@ class TypedHandlerTest : LightCodeInsightTestCase() { fun testSmartEnterWithTabsOnConstructorParameters() { doTypeTest( - '\n', - """ + '\n', + """ |class A( | a: Int, |) """, - """ + """ |class A( | a: Int, | |) """, - enableSmartEnterWithTabs() + enableSmartEnterWithTabs() ) } fun testSmartEnterWithTabsInMethodParameters() { doTypeTest( - '\n', - """ + '\n', + """ |fun method( | arg1: String, |) {} """, - """ + """ |fun method( | arg1: String, | |) {} """, - enableSmartEnterWithTabs() + enableSmartEnterWithTabs() ) } @@ -821,6 +828,75 @@ class TypedHandlerTest : LightCodeInsightTestCase() { ) } + fun testValInserterOnClass() = + testValInserter(',', """data class xxx(val x: Int)""", """data class xxx(val x: Int,)""") + + fun testValInserterOnSimpleDataClass() = + testValInserter(',', """data class xxx(x: Int)""", """data class xxx(val x: Int,)""") + + fun testValInserterOnValWithComment() = + testValInserter(',', """data class xxx(x: Int /*comment*/ )""", """data class xxx(val x: Int /*comment*/ ,)""") + + fun testValInserterOnValWithInitializer() = + testValInserter(',', """data class xxx(x: Int = 2)""", """data class xxx(val x: Int = 2,)""") + + fun testValInserterOnValWithInitializerWithOutType() = + testValInserter(',', """data class xxx(x = 2)""", """data class xxx(x = 2,)""") + + fun testValInserterOnValWithGenericType() = + testValInserter(',', """data class xxx(x: A)""", """data class xxx(val x: A,)""") + + fun testValInserterOnValWithNoType() = + testValInserter(',', """data class xxx(x)""", """data class xxx(x,)""") + + fun testValInserterOnValWithIncompleteGenericType() = + testValInserter(',', """data class xxx(x: A)""", """data class xxx(x: A)""") + + fun testValInserterOnValWithInvalidComma() = + testValInserter(',', """data class xxx(x: A)""", """data class xxx(x:, A)""") + + fun testValInserterOnValWithInvalidGenericType() = + testValInserter(',', """data class xxx(x: A>)""", """data class xxx(x: A>,)""") + + fun testValInserterOnInMultiline() = + testValInserter( + ',', + """ + |data class xxx( + | val a: A, + | b: B + | val c: C + |) + """, + """ + |data class xxx( + | val a: A, + | val b: B, + | val c: C + |) + """ + ) + + fun testValInserterOnValInsertedInsideOtherParameters() = + testValInserter( + ',', + """data class xxx(val a: A, b: Aval c: A)""", + """data class xxx(val a: A, val b: A,val c: A)""" + ) + + fun testValInserterOnSimpleInlineClass() = + testValInserter(')', """inline class xxx(a: A)""", """inline class xxx(val a: A)""") + + fun testValInserterOnValInsertedWithSquare() = + testValInserter(')', """data class xxx(val a: A, b: A)""", """data class xxx(val a: A, val b: A)""") + + + fun testValInserterOnTypingMissedSquare() = + testValInserter(')', """data class xxx(val a: A, b: A""", """data class xxx(val a: A, val b: A)""") + + fun testValInserterWithDisabledSetting() = + testValInserter(',', """data class xxx(x: Int)""", """data class xxx(x: Int,)""", inserterEnabled = false) + fun testEnterInFunctionWithExpressionBody() { doTypeTest( '\n', @@ -908,6 +984,17 @@ class TypedHandlerTest : LightCodeInsightTestCase() { } } + private fun testValInserter(ch: Char, beforeText: String, afterText: String, inserterEnabled: Boolean = true) { + val editorOptions = KotlinEditorOptions.getInstance() + val wasEnabled = editorOptions.isAutoAddValKeywordToDataClassParameters + try { + editorOptions.isAutoAddValKeywordToDataClassParameters = inserterEnabled + doTypeTest(ch, beforeText, afterText) + } finally { + editorOptions.isAutoAddValKeywordToDataClassParameters = wasEnabled + } + } + private fun doLtGtTestNoAutoClose(initText: String) { doLtGtTest(initText, false)