Code style: add option for blank lines before declarations

#KT-39024 Fixed
#KT-37420 Fixed
#KT-37891 Fixed
This commit is contained in:
Dmitry Gridin
2020-05-19 10:26:09 +07:00
parent 499a02ebe3
commit 9beb36ca2b
15 changed files with 1439 additions and 8 deletions
@@ -54,6 +54,7 @@ public class KotlinCodeStyleSettings extends CustomCodeStyleSettings {
public int WRAP_ELVIS_EXPRESSIONS = 1;
public boolean IF_RPAREN_ON_NEW_LINE = false;
public boolean ALLOW_TRAILING_COMMA = false;
public int BLANK_LINES_BEFORE_DECLARATION_WITH_COMMENT_OR_ANNOTATION_ON_SEPARATE_LINE = 1;
@ReflectionUtil.SkipInEquals
public String CODE_STYLE_DEFAULTS = null;
@@ -114,7 +114,7 @@ fun createSpacingBuilder(settings: CodeStyleSettings, builderUtil: KotlinSpacing
node.text.subSequence(0, elementStart.startOffset - node.startOffset).trimStart()
)
)
createSpacing(0, minLineFeeds = 2)
createSpacing(0, minLineFeeds = 1 + kotlinCustomSettings.BLANK_LINES_BEFORE_DECLARATION_WITH_COMMENT_OR_ANNOTATION_ON_SEPARATE_LINE)
else
null
})
@@ -131,7 +131,6 @@ fun createSpacingBuilder(settings: CodeStyleSettings, builderUtil: KotlinSpacing
// Case left for alternative constructors
inPosition(left = FUN, right = CLASS).emptyLinesIfLineBreakInLeft(1)
inPosition(left = ENUM_ENTRY, right = ENUM_ENTRY).emptyLinesIfLineBreakInLeft(
emptyLines = 0, numberOfLineFeedsOtherwise = 0, numSpacesOtherwise = 1
)
@@ -139,7 +138,7 @@ fun createSpacingBuilder(settings: CodeStyleSettings, builderUtil: KotlinSpacing
inPosition(parent = CLASS_BODY, left = SEMICOLON).customRule { parent, _, right ->
val klass = parent.requireNode().treeParent.psi as? KtClass ?: return@customRule null
if (klass.isEnum() && right.requireNode().elementType in DECLARATIONS) {
createSpacing(0, minLineFeeds = 2, keepBlankLines = settings.KEEP_BLANK_LINES_IN_DECLARATIONS)
createSpacing(0, minLineFeeds = 2, keepBlankLines = kotlinCommonSettings.KEEP_BLANK_LINES_IN_DECLARATIONS)
} else null
}
@@ -519,6 +519,7 @@ formatter.title.align.when.branches.in.columns=Align 'when' branches in columns
formatter.title.around.arrow.in.function.types=Around arrow in function types
formatter.title.around.arrow.in=Around arrow in "when" clause
formatter.title.around.when.branches.with=Around 'when' branches with {}
formatter.title.before.declaration.with.comment.or.annotation=Before declaration with comment or annotation
formatter.title.before.colon.after.declaration.name=Before colon, after declaration name
formatter.title.before.colon.in.new.type.definition=Before colon in new type definition
formatter.title.before.lambda.arrow=Before lambda arrow
@@ -598,6 +598,12 @@
<option name="when" path="Blank Lines" hit="Around 'when' branches with {}"/>
<option name="with" path="Blank Lines" hit="Around 'when' branches with {}"/>
<option name="before" path="Blank Lines" hit="Before '}':"/>
<option name="annotation" path="Blank Lines" hit="Before declaration with comment or annotation"/>
<option name="before" path="Blank Lines" hit="Before declaration with comment or annotation"/>
<option name="comment" path="Blank Lines" hit="Before declaration with comment or annotation"/>
<option name="declaration" path="Blank Lines" hit="Before declaration with comment or annotation"/>
<option name="or" path="Blank Lines" hit="Before declaration with comment or annotation"/>
<option name="with" path="Blank Lines" hit="Before declaration with comment or annotation"/>
<option name="blank" path="Blank Lines" hit="Blank Lines"/>
<option name="lines" path="Blank Lines" hit="Blank Lines"/>
<option name="at" path="Code Generation" hit="Block comment at first column"/>
@@ -1307,10 +1313,6 @@
<option name="for" hit="ML Completion for Kotlin"/>
<option name="kotlin" hit="ML Completion for Kotlin"/>
<option name="ml" hit="ML Completion for Kotlin"/>
<option name="experimental" hit="New Experimental Project Wizard"/>
<option name="new" hit="New Experimental Project Wizard"/>
<option name="project" hit="New Experimental Project Wizard"/>
<option name="wizard" hit="New Experimental Project Wizard"/>
<option name="converter" hit="New Java to Kotlin Converter"/>
<option name="java" hit="New Java to Kotlin Converter"/>
<option name="kotlin" hit="New Java to Kotlin Converter"/>
@@ -258,11 +258,18 @@ class KotlinLanguageCodeStyleSettingsProvider : LanguageCodeStyleSettingsProvide
"KEEP_BLANK_LINES_BEFORE_RBRACE",
"BLANK_LINES_AFTER_CLASS_HEADER"
)
showCustomOption(
KotlinCodeStyleSettings::BLANK_LINES_AROUND_BLOCK_WHEN_BRANCHES,
KotlinBundle.message("formatter.title.around.when.branches.with"),
CodeStyleSettingsCustomizable.BLANK_LINES
)
showCustomOption(
KotlinCodeStyleSettings::BLANK_LINES_BEFORE_DECLARATION_WITH_COMMENT_OR_ANNOTATION_ON_SEPARATE_LINE,
KotlinBundle.message("formatter.title.before.declaration.with.comment.or.annotation"),
CodeStyleSettingsCustomizable.BLANK_LINES
)
}
SettingsType.COMMENTER_SETTINGS -> {
consumer.showAllStandardOptions()
@@ -376,7 +383,24 @@ class KotlinLanguageCodeStyleSettingsProvider : LanguageCodeStyleSettingsProvide
}
fun run(f: () -> Unit) {
f()
}""".trimIndent()
}
class Bar {
@Annotation
val a = 42
@Annotation
val b = 43
fun c() {
a + b
}
fun d() = Unit
// smth
fun e() {
d()
}
fun f() = d()
}
""".trimIndent()
else -> """open class Some {
private val f: (Int)->Int = { a: Int -> a * 2 }
@@ -0,0 +1,376 @@
interface Some {
fun f1()
fun f2()
fun f3()
/* test */
fun f4()
/* test */ fun f5()
/**
* test
* 2
*/
fun f6()
fun f7()
@NotNull
fun f8()
fun f9()
}
interface Some2 {
fun f1()
fun f2()
fun f3()
@Ann
fun f4()
@Ann
fun f5()
@Ann(
42
)
fun f6()
fun f7()
@NotNull
fun f8()
fun f9()
}
abstract class Abstract() {
abstract fun f1()
abstract fun f2()
// test
abstract fun f3()
// test
/* test */ abstract fun f4()
abstract fun f5()
abstract fun f6()
/* test */ abstract fun f7()
}
abstract class Abstract() {
abstract fun f1()
abstract fun f2()
@Ann
abstract fun f3()
@Ann
@Ann
abstract fun f4()
abstract fun f5()
abstract fun f6()
@Ann
abstract fun f7()
}
fun f1() {}
class C1 {}
// -----
fun f2() {}
class C2
// -----
// test
fun f3 = 1
class C3 {}
// -----
/*test*/
fun f4 = 2
class C4
// -----
@Ann
fun f3 = 1
class C3 {}
// -----
@Ann
fun f4 = 2
class C4
// -----
class C5 {}
fun f5() {}
// -----
class C6
fun f6() {}
// -----
class C7 {}
fun f7 = 1
// -----
class D {
}
class E
: Some
class F
@Ann
fun some() = 1
class G
fun some() = 1
val a = 1
class H
val b = 1
// No lines
fun f1() {}
val p1 = 1
fun f2() {}
@Ann
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
class OneLine {
fun f1() {}
val p1 = 1
fun f2() {}
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
}
class TwoLines {
fun f1() {}
val p1 = 1
fun f2() {}
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
}
// No lines between
fun f2() {}
fun f1() {}
fun f3() = 1
fun f4() {}
fun f5() {
}
fun f6() = 1
fun f7() = 8
// One line
fun f2() {}
fun f1() {}
fun f3() = 1
fun f4() {}
fun f5() {
}
fun f6() = 1
fun f7() = 8
// test
fun f8() = 42
// Two lines between
fun l1() {}
fun l2() {}
fun l3() = 1
fun l4() {}
fun l5() {
}
fun l6() = 1
fun l7() = 8
// test
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() {
return 1
}
val p5: Int
class OneLine {
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() {
return 1
}
val p5: Int
}
class TwoLines {
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() {
return 1
}
val p5: Int
}
class Some(b: Boolean) {
// Comment.
constructor(b: Int) : this(b == 0)
/**
* test
* 2
*/
constructor(b: String) : this(b.isEmpty())
constructor(b: Long) : this(b == 0L)
@Ann
constructor(b: Int) : this(b == 0)
@Ann(
42
)
constructor(b: String) : this(b.isEmpty())
constructor(b: Long) : this(b == 0L)
}
enum class TestEnum {
/** A comment for the item #1. */
ITEM1,
/** A comment for the item #2. */
ITEM2,
/** A comment for the item #3. */
ITEM3
}
class Foo {
@Inject
lateinit var logger: Logger
@Inject
lateinit var userService: UserService
@Inject
override lateinit var configBridge: ConfigBridge
}
// SET_INT: BLANK_LINES_BEFORE_DECLARATION_WITH_COMMENT_OR_ANNOTATION_ON_SEPARATE_LINE = 2
@@ -0,0 +1,316 @@
interface Some {
fun f1()
fun f2()
fun f3()
/* test */
fun f4()
/* test */ fun f5()
/**
* test
* 2
*/
fun f6()
fun f7()
@NotNull
fun f8()
fun f9()
}
interface Some2 {
fun f1()
fun f2()
fun f3()
@Ann
fun f4()
@Ann fun f5()
@Ann(
42
)
fun f6()
fun f7()
@NotNull
fun f8()
fun f9()
}
abstract class Abstract() {
abstract fun f1()
abstract fun f2()
// test
abstract fun f3()
// test
/* test */ abstract fun f4()
abstract fun f5()
abstract fun f6()
/* test */ abstract fun f7()
}
abstract class Abstract() {
abstract fun f1()
abstract fun f2()
@Ann
abstract fun f3()
@Ann
@Ann abstract fun f4()
abstract fun f5()
abstract fun f6()
@Ann abstract fun f7()
}
fun f1() {}
class C1 {}
// -----
fun f2() {}
class C2
// -----
// test
fun f3 = 1
class C3 {}
// -----
/*test*/
fun f4 = 2
class C4
// -----
@Ann
fun f3 = 1
class C3 {}
// -----
@Ann
fun f4 = 2
class C4
// -----
class C5 {}
fun f5() {}
// -----
class C6
fun f6() {}
// -----
class C7 {}
fun f7 = 1
// -----
class D {
}
class E
: Some
class F
@Ann
fun some() = 1
class G
fun some() = 1
val a = 1
class H
val b = 1
// No lines
fun f1() {}
val p1 = 1
fun f2() {}
@Ann
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
class OneLine {
fun f1() {}
val p1 = 1
fun f2() {}
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
}
class TwoLines {
fun f1() {}
val p1 = 1
fun f2() {}
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
}
// No lines between
fun f2() {}
fun f1() {}
fun f3() = 1
fun f4() {}
fun f5() {
}
fun f6() = 1
fun f7() = 8
// One line
fun f2() {}
fun f1() {}
fun f3() = 1
fun f4() {}
fun f5() {
}
fun f6() = 1
fun f7() = 8
// test
fun f8() = 42
// Two lines between
fun l1() {}
fun l2() {}
fun l3() = 1
fun l4() {}
fun l5() {
}
fun l6() = 1
fun l7() = 8
// test
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() { return 1 }
val p5: Int
class OneLine {
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() { return 1 }
val p5: Int
}
class TwoLines {
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() { return 1 }
val p5: Int
}
class Some(b: Boolean) {
// Comment.
constructor(b: Int) : this(b == 0)
/**
* test
* 2
*/
constructor(b: String) : this(b.isEmpty())
constructor(b: Long) : this(b == 0L)
@Ann
constructor(b: Int) : this(b == 0)
@Ann(
42
)
constructor(b: String) : this(b.isEmpty())
constructor(b: Long) : this(b == 0L)
}
enum class TestEnum {
/** A comment for the item #1. */
ITEM1,
/** A comment for the item #2. */
ITEM2,
/** A comment for the item #3. */
ITEM3
}
class Foo {
@Inject
lateinit var logger: Logger
@Inject
lateinit var userService: UserService
@Inject
override lateinit var configBridge: ConfigBridge
}
// SET_INT: BLANK_LINES_BEFORE_DECLARATION_WITH_COMMENT_OR_ANNOTATION_ON_SEPARATE_LINE = 2
@@ -0,0 +1,335 @@
interface Some {
fun f1()
fun f2()
fun f3()
/* test */
fun f4()
/* test */ fun f5()
/**
* test
* 2
*/
fun f6()
fun f7()
@NotNull
fun f8()
fun f9()
}
interface Some2 {
fun f1()
fun f2()
fun f3()
@Ann
fun f4()
@Ann
fun f5()
@Ann(
42
)
fun f6()
fun f7()
@NotNull
fun f8()
fun f9()
}
abstract class Abstract() {
abstract fun f1()
abstract fun f2()
// test
abstract fun f3()
// test
/* test */ abstract fun f4()
abstract fun f5()
abstract fun f6()
/* test */ abstract fun f7()
}
abstract class Abstract() {
abstract fun f1()
abstract fun f2()
@Ann
abstract fun f3()
@Ann
@Ann
abstract fun f4()
abstract fun f5()
abstract fun f6()
@Ann
abstract fun f7()
}
fun f1() {}
class C1 {}
// -----
fun f2() {}
class C2
// -----
// test
fun f3 = 1
class C3 {}
// -----
/*test*/
fun f4 = 2
class C4
// -----
@Ann
fun f3 = 1
class C3 {}
// -----
@Ann
fun f4 = 2
class C4
// -----
class C5 {}
fun f5() {}
// -----
class C6
fun f6() {}
// -----
class C7 {}
fun f7 = 1
// -----
class D {
}
class E
: Some
class F
@Ann
fun some() = 1
class G
fun some() = 1
val a = 1
class H
val b = 1
// No lines
fun f1() {}
val p1 = 1
fun f2() {}
@Ann
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
class OneLine {
fun f1() {}
val p1 = 1
fun f2() {}
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
}
class TwoLines {
fun f1() {}
val p1 = 1
fun f2() {}
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
}
// No lines between
fun f2() {}
fun f1() {}
fun f3() = 1
fun f4() {}
fun f5() {
}
fun f6() = 1
fun f7() = 8
// One line
fun f2() {}
fun f1() {}
fun f3() = 1
fun f4() {}
fun f5() {
}
fun f6() = 1
fun f7() = 8
// test
fun f8() = 42
// Two lines between
fun l1() {}
fun l2() {}
fun l3() = 1
fun l4() {}
fun l5() {
}
fun l6() = 1
fun l7() = 8
// test
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() {
return 1
}
val p5: Int
class OneLine {
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() {
return 1
}
val p5: Int
}
class TwoLines {
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() {
return 1
}
val p5: Int
}
class Some(b: Boolean) {
// Comment.
constructor(b: Int) : this(b == 0)
/**
* test
* 2
*/
constructor(b: String) : this(b.isEmpty())
constructor(b: Long) : this(b == 0L)
@Ann
constructor(b: Int) : this(b == 0)
@Ann(
42
)
constructor(b: String) : this(b.isEmpty())
constructor(b: Long) : this(b == 0L)
}
enum class TestEnum {
/** A comment for the item #1. */
ITEM1,
/** A comment for the item #2. */
ITEM2,
/** A comment for the item #3. */
ITEM3
}
class Foo {
@Inject
lateinit var logger: Logger
@Inject
lateinit var userService: UserService
@Inject
override lateinit var configBridge: ConfigBridge
}
// SET_INT: BLANK_LINES_BEFORE_DECLARATION_WITH_COMMENT_OR_ANNOTATION_ON_SEPARATE_LINE = 0
@@ -0,0 +1,316 @@
interface Some {
fun f1()
fun f2()
fun f3()
/* test */
fun f4()
/* test */ fun f5()
/**
* test
* 2
*/
fun f6()
fun f7()
@NotNull
fun f8()
fun f9()
}
interface Some2 {
fun f1()
fun f2()
fun f3()
@Ann
fun f4()
@Ann fun f5()
@Ann(
42
)
fun f6()
fun f7()
@NotNull
fun f8()
fun f9()
}
abstract class Abstract() {
abstract fun f1()
abstract fun f2()
// test
abstract fun f3()
// test
/* test */ abstract fun f4()
abstract fun f5()
abstract fun f6()
/* test */ abstract fun f7()
}
abstract class Abstract() {
abstract fun f1()
abstract fun f2()
@Ann
abstract fun f3()
@Ann
@Ann abstract fun f4()
abstract fun f5()
abstract fun f6()
@Ann abstract fun f7()
}
fun f1() {}
class C1 {}
// -----
fun f2() {}
class C2
// -----
// test
fun f3 = 1
class C3 {}
// -----
/*test*/
fun f4 = 2
class C4
// -----
@Ann
fun f3 = 1
class C3 {}
// -----
@Ann
fun f4 = 2
class C4
// -----
class C5 {}
fun f5() {}
// -----
class C6
fun f6() {}
// -----
class C7 {}
fun f7 = 1
// -----
class D {
}
class E
: Some
class F
@Ann
fun some() = 1
class G
fun some() = 1
val a = 1
class H
val b = 1
// No lines
fun f1() {}
val p1 = 1
fun f2() {}
@Ann
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
class OneLine {
fun f1() {}
val p1 = 1
fun f2() {}
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
}
class TwoLines {
fun f1() {}
val p1 = 1
fun f2() {}
fun f3() = 1
val p2 = 1
fun f4() = 1
fun f4() {}
val p3: Int
get() = 1
fun f5() = 1
}
// No lines between
fun f2() {}
fun f1() {}
fun f3() = 1
fun f4() {}
fun f5() {
}
fun f6() = 1
fun f7() = 8
// One line
fun f2() {}
fun f1() {}
fun f3() = 1
fun f4() {}
fun f5() {
}
fun f6() = 1
fun f7() = 8
// test
fun f8() = 42
// Two lines between
fun l1() {}
fun l2() {}
fun l3() = 1
fun l4() {}
fun l5() {
}
fun l6() = 1
fun l7() = 8
// test
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() { return 1 }
val p5: Int
class OneLine {
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() { return 1 }
val p5: Int
}
class TwoLines {
val p1 by Some
val p2 = 1
val p3: Int get() = 3
val p4: Int
get() { return 1 }
val p5: Int
}
class Some(b: Boolean) {
// Comment.
constructor(b: Int) : this(b == 0)
/**
* test
* 2
*/
constructor(b: String) : this(b.isEmpty())
constructor(b: Long) : this(b == 0L)
@Ann
constructor(b: Int) : this(b == 0)
@Ann(
42
)
constructor(b: String) : this(b.isEmpty())
constructor(b: Long) : this(b == 0L)
}
enum class TestEnum {
/** A comment for the item #1. */
ITEM1,
/** A comment for the item #2. */
ITEM2,
/** A comment for the item #3. */
ITEM3
}
class Foo {
@Inject
lateinit var logger: Logger
@Inject
lateinit var userService: UserService
@Inject
override lateinit var configBridge: ConfigBridge
}
// SET_INT: BLANK_LINES_BEFORE_DECLARATION_WITH_COMMENT_OR_ANNOTATION_ON_SEPARATE_LINE = 0
@@ -41,4 +41,15 @@ enum class E10 {
val x = 1
}
enum class TestEnum {
/** A comment for the item #1. */
ITEM1,
/** A comment for the item #2. */
ITEM2,
/** A comment for the item #3. */
ITEM3
}
// SET_TRUE: KEEP_LINE_BREAKS
@@ -43,4 +43,15 @@ enum class E10 {
val x = 1
}
enum class TestEnum {
/** A comment for the item #1. */
ITEM1,
/** A comment for the item #2. */
ITEM2,
/** A comment for the item #3. */
ITEM3
}
// SET_TRUE: KEEP_LINE_BREAKS
@@ -37,4 +37,13 @@ enum class E10 { F, S, T;
val x = 1
}
enum class TestEnum {
/** A comment for the item #1. */
ITEM1,
/** A comment for the item #2. */
ITEM2,
/** A comment for the item #3. */
ITEM3
}
// SET_TRUE: KEEP_LINE_BREAKS
@@ -40,4 +40,15 @@ class TwoLines {
val p5: Int
}
class Foo {
@Inject
lateinit var logger: Logger
@Inject
lateinit var userService: UserService
@Inject
override lateinit var configBridge: ConfigBridge
}
+9
View File
@@ -34,4 +34,13 @@ class TwoLines {
val p5: Int
}
class Foo {
@Inject
lateinit var logger: Logger
@Inject
lateinit var userService: UserService
@Inject
override lateinit var configBridge: ConfigBridge
}
@@ -280,6 +280,16 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
runTest("idea/testData/formatter/EmptyLineBetweeAbstractFunctions.after.kt");
}
@TestMetadata("EmptyLineBetweeMultilineDeclarationTwo.after.kt")
public void testEmptyLineBetweeMultilineDeclarationTwo() throws Exception {
runTest("idea/testData/formatter/EmptyLineBetweeMultilineDeclarationTwo.after.kt");
}
@TestMetadata("EmptyLineBetweeMultilineDeclarationZero.after.kt")
public void testEmptyLineBetweeMultilineDeclarationZero() throws Exception {
runTest("idea/testData/formatter/EmptyLineBetweeMultilineDeclarationZero.after.kt");
}
@TestMetadata("EmptyLineBetweeSecondaryConstructors.after.kt")
public void testEmptyLineBetweeSecondaryConstructors() throws Exception {
runTest("idea/testData/formatter/EmptyLineBetweeSecondaryConstructors.after.kt");