@@ -7,14 +7,18 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY
7
7
import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_TARGET
8
8
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS
9
9
import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON
10
+ import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONSTRUCTOR_CALLEE
10
11
import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONSTRUCTOR_KEYWORD
11
12
import com.pinterest.ktlint.rule.engine.core.api.ElementType.FILE_ANNOTATION_LIST
12
13
import com.pinterest.ktlint.rule.engine.core.api.ElementType.GT
14
+ import com.pinterest.ktlint.rule.engine.core.api.ElementType.IDENTIFIER
13
15
import com.pinterest.ktlint.rule.engine.core.api.ElementType.MODIFIER_LIST
16
+ import com.pinterest.ktlint.rule.engine.core.api.ElementType.REFERENCE_EXPRESSION
14
17
import com.pinterest.ktlint.rule.engine.core.api.ElementType.RPAR
15
18
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_ARGUMENT_LIST
16
19
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_PROJECTION
17
20
import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_REFERENCE
21
+ import com.pinterest.ktlint.rule.engine.core.api.ElementType.USER_TYPE
18
22
import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT
19
23
import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_ARGUMENT_LIST
20
24
import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER
@@ -27,7 +31,9 @@ import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.STABLE
27
31
import com.pinterest.ktlint.rule.engine.core.api.children
28
32
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CODE_STYLE_PROPERTY
29
33
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CodeStyleValue
34
+ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CommaSeparatedListValueParser
30
35
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig
36
+ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfigProperty
31
37
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY
32
38
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY
33
39
import com.pinterest.ktlint.rule.engine.core.api.firstChildLeafOrSelf
@@ -45,6 +51,7 @@ import com.pinterest.ktlint.rule.engine.core.api.prevLeaf
45
51
import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe
46
52
import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe
47
53
import com.pinterest.ktlint.ruleset.standard.StandardRule
54
+ import org.ec4j.core.model.PropertyType
48
55
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
49
56
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
50
57
import org.jetbrains.kotlin.psi.psiUtil.siblings
@@ -88,6 +95,7 @@ public class AnnotationRule :
88
95
setOf (
89
96
INDENT_SIZE_PROPERTY ,
90
97
INDENT_STYLE_PROPERTY ,
98
+ ANNOTATIONS_WITH_PARAMETERS_NOT_TO_BE_WRAPPED_PROPERTY ,
91
99
),
92
100
visitorModifiers =
93
101
setOf (
@@ -99,6 +107,13 @@ public class AnnotationRule :
99
107
) {
100
108
private var codeStyle = CODE_STYLE_PROPERTY .defaultValue
101
109
private var indentConfig = IndentConfig .DEFAULT_INDENT_CONFIG
110
+ private var annotationsWithParametersNoToBeWrapped =
111
+ ANNOTATIONS_WITH_PARAMETERS_NOT_TO_BE_WRAPPED_PROPERTY .defaultValue
112
+
113
+ private val handleAllAnnotationsWithParametersSameAsAnnotationsWithoutParameters =
114
+ lazy {
115
+ annotationsWithParametersNoToBeWrapped.contains(" *" )
116
+ }
102
117
103
118
override fun beforeFirstNode (editorConfig : EditorConfig ) {
104
119
codeStyle = editorConfig[CODE_STYLE_PROPERTY ]
@@ -107,6 +122,8 @@ public class AnnotationRule :
107
122
indentStyle = editorConfig[INDENT_STYLE_PROPERTY ],
108
123
tabWidth = editorConfig[INDENT_SIZE_PROPERTY ],
109
124
)
125
+ annotationsWithParametersNoToBeWrapped =
126
+ editorConfig[ANNOTATIONS_WITH_PARAMETERS_NOT_TO_BE_WRAPPED_PROPERTY ]
110
127
}
111
128
112
129
override fun beforeVisitChildNodes (
@@ -165,7 +182,7 @@ public class AnnotationRule :
165
182
.children()
166
183
.filter { it.elementType == ANNOTATION_ENTRY }
167
184
.filter {
168
- it.isAnnotationEntryWithValueArgumentList () ||
185
+ it.isAnnotationEntryWithValueArgumentListThatShouldBeWrapped () ||
169
186
! it.isPrecededByOtherAnnotationEntryWithoutParametersOnTheSameLine()
170
187
}.forEachIndexed { index, annotationEntry ->
171
188
annotationEntry
@@ -198,9 +215,9 @@ public class AnnotationRule :
198
215
199
216
node
200
217
.children()
201
- .last { it.elementType == ANNOTATION_ENTRY }
202
- .lastChildLeafOrSelf()
203
- .nextCodeLeaf()
218
+ .lastOrNull { it.elementType == ANNOTATION_ENTRY }
219
+ ? .lastChildLeafOrSelf()
220
+ ? .nextCodeLeaf()
204
221
?.prevLeaf()
205
222
?.let { prevLeaf ->
206
223
// Let the indentation rule determine the exact indentation and only report and fix when the line needs to be wrapped
@@ -238,7 +255,7 @@ public class AnnotationRule :
238
255
require(elementType in ANNOTATION_CONTAINER )
239
256
return children()
240
257
.any {
241
- it.isAnnotationEntryWithValueArgumentList () &&
258
+ it.isAnnotationEntryWithValueArgumentListThatShouldBeWrapped () &&
242
259
it.treeParent.treeParent.elementType != VALUE_PARAMETER &&
243
260
it.treeParent.treeParent.elementType != VALUE_ARGUMENT &&
244
261
it.isNotReceiverTargetAnnotation()
@@ -350,11 +367,36 @@ public class AnnotationRule :
350
367
?.findChildByType(ANNOTATION_TARGET )
351
368
?.let { USE_SITE_TARGETS [it.text] }
352
369
353
- private fun ASTNode.isAnnotationEntryWithValueArgumentList () = getAnnotationEntryValueArgumentList() != null
370
+ private fun ASTNode.isAnnotationEntryWithValueArgumentListThatShouldBeWrapped () =
371
+ when {
372
+ handleAllAnnotationsWithParametersSameAsAnnotationsWithoutParameters.value -> {
373
+ // Do never distinct between annotation with and without parameters
374
+ false
375
+ }
354
376
355
- private fun ASTNode.getAnnotationEntryValueArgumentList () =
356
- takeIf { it.elementType == ANNOTATION_ENTRY }
357
- ?.findChildByType(VALUE_ARGUMENT_LIST )
377
+ annotationsWithParametersNoToBeWrapped.isEmpty() -> {
378
+ // All annotations with parameters should be wrapped as the whitelist is empty
379
+ true
380
+ }
381
+
382
+ elementType == ANNOTATION_ENTRY && findChildByType(VALUE_ARGUMENT_LIST ) != null -> {
383
+ // Only wrap annotation with parameters when it is not on the whitelist
384
+ getAnnotationIdentifier() !in annotationsWithParametersNoToBeWrapped
385
+ }
386
+
387
+ else -> {
388
+ // Annotation without parameter
389
+ false
390
+ }
391
+ }
392
+
393
+ private fun ASTNode.getAnnotationIdentifier () =
394
+ findChildByType(CONSTRUCTOR_CALLEE )
395
+ ?.findChildByType(TYPE_REFERENCE )
396
+ ?.findChildByType(USER_TYPE )
397
+ ?.findChildByType(REFERENCE_EXPRESSION )
398
+ ?.findChildByType(IDENTIFIER )
399
+ ?.text
358
400
359
401
private fun ASTNode.isLastAnnotationEntry () =
360
402
this ==
@@ -364,8 +406,8 @@ public class AnnotationRule :
364
406
365
407
private fun ASTNode.isPrecededByOtherAnnotationEntryWithoutParametersOnTheSameLine () =
366
408
siblings(forward = false )
367
- .takeWhile { ! it.isWhiteSpaceWithNewline() && ! it.isAnnotationEntryWithValueArgumentList () }
368
- .any { it.elementType == ANNOTATION_ENTRY && ! it.isAnnotationEntryWithValueArgumentList () }
409
+ .takeWhile { ! it.isWhiteSpaceWithNewline() && ! it.isAnnotationEntryWithValueArgumentListThatShouldBeWrapped () }
410
+ .any { it.elementType == ANNOTATION_ENTRY && ! it.isAnnotationEntryWithValueArgumentListThatShouldBeWrapped () }
369
411
370
412
private fun ASTNode.isPrecededByOtherAnnotationEntryOnTheSameLine () =
371
413
siblings(forward = false )
@@ -460,14 +502,24 @@ public class AnnotationRule :
460
502
return " \n " .plus(indentWithoutNewline)
461
503
}
462
504
463
- private companion object {
464
- val ANNOTATION_CONTAINER =
505
+ public companion object {
506
+ private val ANNOTATION_CONTAINER =
465
507
listOf (
466
508
ANNOTATED_EXPRESSION ,
467
509
FILE_ANNOTATION_LIST ,
468
510
MODIFIER_LIST ,
469
511
)
470
- val USE_SITE_TARGETS = AnnotationUseSiteTarget .entries.associateBy { it.renderName }
512
+ private val USE_SITE_TARGETS = AnnotationUseSiteTarget .entries.associateBy { it.renderName }
513
+ public val ANNOTATIONS_WITH_PARAMETERS_NOT_TO_BE_WRAPPED_PROPERTY : EditorConfigProperty <Set <String >> =
514
+ EditorConfigProperty (
515
+ type =
516
+ PropertyType .LowerCasingPropertyType (
517
+ " ktlint_annotation_handle_annotations_with_parameters_same_as_annotations_without_parameters" ,
518
+ " Handle listed annotations identical to annotations without parameters. Value is a comma separated list of names without the '@' prefix. Use '*' for all annotations with parameters." ,
519
+ CommaSeparatedListValueParser (),
520
+ ),
521
+ defaultValue = setOf (" unset" ),
522
+ )
471
523
}
472
524
}
473
525
0 commit comments