@@ -3,12 +3,19 @@ package com.pinterest.ktlint.ruleset.standard.rules
3
3
import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision
4
4
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION
5
5
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY
6
+ import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONTEXT_RECEIVER_LIST
6
7
import com.pinterest.ktlint.rule.engine.core.api.ElementType.MODIFIER_LIST
8
+ import com.pinterest.ktlint.rule.engine.core.api.IndentConfig
9
+ import com.pinterest.ktlint.rule.engine.core.api.IndentConfig.Companion.DEFAULT_INDENT_CONFIG
10
+ import com.pinterest.ktlint.rule.engine.core.api.Rule.VisitorModifier.RunAfterRule.Mode.REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED
7
11
import com.pinterest.ktlint.rule.engine.core.api.RuleId
8
12
import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint
9
13
import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL
10
14
import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE
11
15
import com.pinterest.ktlint.rule.engine.core.api.children20
16
+ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig
17
+ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY
18
+ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY
12
19
import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed
13
20
import com.pinterest.ktlint.rule.engine.core.api.isPartOfComment20
14
21
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace20
@@ -24,7 +31,30 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode
24
31
*/
25
32
@SinceKtlint(" 0.45" , EXPERIMENTAL )
26
33
@SinceKtlint(" 0.49" , STABLE )
27
- public class ModifierListSpacingRule : StandardRule (" modifier-list-spacing" ) {
34
+ public class ModifierListSpacingRule :
35
+ StandardRule (
36
+ id = " modifier-list-spacing" ,
37
+ visitorModifiers =
38
+ setOf (
39
+ VisitorModifier .RunAfterRule (ANNOTATION_RULE_ID , REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED ),
40
+ VisitorModifier .RunAfterRule (MODIFIER_ORDER_RULE_ID , REGARDLESS_WHETHER_RUN_AFTER_RULE_IS_LOADED_OR_DISABLED ),
41
+ ),
42
+ usesEditorConfigProperties =
43
+ setOf (
44
+ INDENT_SIZE_PROPERTY ,
45
+ INDENT_STYLE_PROPERTY ,
46
+ ),
47
+ ) {
48
+ private var indentConfig = DEFAULT_INDENT_CONFIG
49
+
50
+ override fun beforeFirstNode (editorConfig : EditorConfig ) {
51
+ indentConfig =
52
+ IndentConfig (
53
+ indentStyle = editorConfig[INDENT_STYLE_PROPERTY ],
54
+ tabWidth = editorConfig[INDENT_SIZE_PROPERTY ],
55
+ )
56
+ }
57
+
28
58
override fun beforeVisitChildNodes (
29
59
node : ASTNode ,
30
60
emit : (offset: Int , errorMessage: String , canBeAutoCorrected: Boolean ) -> AutocorrectDecision ,
@@ -48,33 +78,47 @@ public class ModifierListSpacingRule : StandardRule("modifier-list-spacing") {
48
78
node
49
79
.nextSibling { it.isWhiteSpace20 && it.nextLeaf?.isPartOfComment20 != true }
50
80
?.takeUnless {
51
- // Regardless of element type, a single white space is always ok and does not need to be checked.
52
- it.text == " "
53
- }?.takeUnless {
54
81
// A single newline after a comment is always ok and does not need further checking.
55
82
it.text.trim(' ' , ' \t ' ).contains(' \n ' ) && it.prevLeaf?.isPartOfComment20 == true
56
83
}?.let { whitespace ->
57
- if (node.isAnnotationElement() ||
58
- (node.elementType == MODIFIER_LIST && node.lastChildNode.isAnnotationElement())
59
- ) {
60
- if (whitespace.text.contains(" \n\n " )) {
61
- emit(whitespace.startOffset, " Single newline expected after annotation" , true )
62
- .ifAutocorrectAllowed {
63
- whitespace.replaceTextWith(" \n " .plus(whitespace.text.substringAfterLast(" \n " )))
64
- }
65
- } else if (! whitespace.text.contains(' \n ' ) && whitespace.text != " " ) {
66
- emit(whitespace.startOffset, " Single whitespace or newline expected after annotation" , true )
84
+ when {
85
+ node.isAnnotation() -> {
86
+ if (whitespace.text.contains(" \n\n " )) {
87
+ emit(whitespace.startOffset, " Single newline expected after annotation" , true )
88
+ .ifAutocorrectAllowed {
89
+ whitespace.replaceTextWith(" \n " .plus(whitespace.text.substringAfterLast(" \n " )))
90
+ }
91
+ } else if (! whitespace.text.contains(' \n ' ) && whitespace.text != " " ) {
92
+ emit(whitespace.startOffset, " Single whitespace or newline expected after annotation" , true )
93
+ .ifAutocorrectAllowed { whitespace.replaceTextWith(" " ) }
94
+ }
95
+ Unit
96
+ }
97
+
98
+ node.isContextReceiverList() -> {
99
+ if (! whitespace.text.contains(" \n " )) {
100
+ emit(whitespace.startOffset, " Single newline expected after context receiver list" , true )
101
+ .ifAutocorrectAllowed {
102
+ whitespace.replaceTextWith(indentConfig.parentIndentOf(node))
103
+ }
104
+ }
105
+ }
106
+
107
+ whitespace.text != " " -> {
108
+ emit(whitespace.startOffset, " Single whitespace expected after modifier" , true )
67
109
.ifAutocorrectAllowed { whitespace.replaceTextWith(" " ) }
68
110
}
69
- Unit
70
- } else {
71
- emit(whitespace.startOffset, " Single whitespace expected after modifier" , true )
72
- .ifAutocorrectAllowed { whitespace.replaceTextWith(" " ) }
73
111
}
74
112
}
75
113
}
76
114
115
+ private fun ASTNode.isAnnotation (): Boolean =
116
+ isAnnotationElement() || (elementType == MODIFIER_LIST && lastChildNode.isAnnotationElement())
117
+
77
118
private fun ASTNode?.isAnnotationElement () = this != null && (elementType == ANNOTATION || elementType == ANNOTATION_ENTRY )
119
+
120
+ private fun ASTNode.isContextReceiverList (): Boolean =
121
+ elementType == CONTEXT_RECEIVER_LIST || (elementType == MODIFIER_LIST && lastChildNode.isContextReceiverList())
78
122
}
79
123
80
124
public val MODIFIER_LIST_SPACING_RULE_ID : RuleId = ModifierListSpacingRule ().ruleId
0 commit comments