diff --git a/documentation/snapshot/docs/rules/experimental.md b/documentation/snapshot/docs/rules/experimental.md index 5c13e752a3..fffb10205d 100644 --- a/documentation/snapshot/docs/rules/experimental.md +++ b/documentation/snapshot/docs/rules/experimental.md @@ -285,3 +285,76 @@ Suppress or disable rule (1) ktlint_standard_square-brackets-spacing = disabled ``` +## When-entry bracing + +Enforce consistent usages of braces inside the when-statement. All when-entries in the when-statement should use braces around their bodies in case at least one when-entry has a multiline body, or when the body is surrounded by braces. + +Braces are helpful for following reasons: + +- Bodies of the when-conditions are all aligned at same column position +- Closing braces helps in separating the when-conditions + +This rule is not incorporated in the Kotlin Coding conventions, nor in the Android Kotlin Styleguide. It is based on similar behavior in enforcing consistent use of braces in if-else statements. As of that the rule is only enabled automatically for code style `ktlint_official`. It can be enabled explicitly for other code styles. + +=== "[:material-heart:](#) Ktlint" + + ```kotlin + val foo1 = + when (bar) { + BAR1 -> "bar1" + BAR2 -> "bar2" + else -> null + } + + val foo2 = + when (bar) { + BAR1 -> { + "bar1" + } + BAR2 -> { + "bar2" + } + else -> { + null + } + } + ``` + +=== "[:material-heart-off-outline:](#) Disallowed" + + ```kotlin + val foo3 = + when (bar) { + BAR1 -> "bar1" + BAR2 -> { + "bar2" + } + else -> null + } + + val foo4 = + when (bar) { + BAR1 -> "bar1" + BAR2 -> + "bar2" + else -> null + } + ``` + +Rule id: `standard:when-entry-spacing` + +Suppress or disable rule (1) +{ .annotate } + +1. Suppress rule in code with annotation below: + ```kotlin + @Suppress("ktlint:standard:when-entry-spacing") + ``` + Enable rule via `.editorconfig` + ```editorconfig + ktlint_standard_when-entry-spacing = enabled + ``` + Disable rule via `.editorconfig` + ```editorconfig + ktlint_standard_when-entry-spacing = disabled + ``` diff --git a/ktlint-cli-reporter-format/src/main/kotlin/com/pinterest/ktlint/cli/reporter/format/FormatReporter.kt b/ktlint-cli-reporter-format/src/main/kotlin/com/pinterest/ktlint/cli/reporter/format/FormatReporter.kt index 62e979f297..7e29eaf880 100644 --- a/ktlint-cli-reporter-format/src/main/kotlin/com/pinterest/ktlint/cli/reporter/format/FormatReporter.kt +++ b/ktlint-cli-reporter-format/src/main/kotlin/com/pinterest/ktlint/cli/reporter/format/FormatReporter.kt @@ -41,29 +41,33 @@ public class FormatReporter( val canNotBeAutocorrected = countCanNotBeAutoCorrected.getOrDefault(file, 0) val result = when { - canNotBeAutocorrected == 1 -> + canNotBeAutocorrected == 1 -> { if (format) { "Format not completed (1 violation needs manual fixing)" } else { "Format required (1 violation needs manual fixing)" } + } - canNotBeAutocorrected > 1 -> + canNotBeAutocorrected > 1 -> { if (format) { "Format not completed ($canNotBeAutocorrected violations need manual fixing)" } else { "Format required ($canNotBeAutocorrected violations need manual fixing)" } + } - countAutoCorrectPossibleOrDone.getOrDefault(file, 0) > 0 -> + countAutoCorrectPossibleOrDone.getOrDefault(file, 0) > 0 -> { if (format) { "Format completed (all violations have been fixed)" } else { "Format required (all violations can be autocorrected)" } + } - else -> + else -> { "Format not needed (no violations found)" + } } out.println( "${colorFileName(file)}${":".colored()} $result", diff --git a/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/FileUtils.kt b/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/FileUtils.kt index 5cef6b12aa..fa292897b9 100644 --- a/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/FileUtils.kt +++ b/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/FileUtils.kt @@ -158,14 +158,9 @@ internal fun FileSystem.fileSequence( private fun Path.findCommonParentDir(path: Path): Path = when { - path.startsWith(this) -> - this - - startsWith(path) -> - path - - else -> - this@findCommonParentDir.findCommonParentDir(path.parent) + path.startsWith(this) -> this + startsWith(path) -> path + else -> this@findCommonParentDir.findCommonParentDir(path.parent) } private fun FileSystem.expand( diff --git a/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt b/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt index 4235076011..b63b2a40f0 100644 --- a/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt +++ b/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt @@ -526,13 +526,16 @@ internal class KtlintCommandLine : CliktCommand(name = "ktlint") { } } when { - code.isStdIn -> print(formattedFileContent) + code.isStdIn -> { + print(formattedFileContent) + } - code.content != formattedFileContent -> + code.content != formattedFileContent -> { code .filePath ?.toFile() ?.writeText(formattedFileContent, charset("UTF-8")) + } } } } catch (e: Exception) { @@ -637,7 +640,7 @@ internal class KtlintCommandLine : CliktCommand(name = "ktlint") { private fun Exception.toKtlintCliError(code: Code): KtlintCliError = this.let { e -> when (e) { - is KtLintParseException -> + is KtLintParseException -> { KtlintCliError( line = e.line, col = e.col, @@ -645,6 +648,7 @@ internal class KtlintCommandLine : CliktCommand(name = "ktlint") { detail = "Not a valid Kotlin file (${e.message?.lowercase(Locale.getDefault())})", status = KOTLIN_PARSE_EXCEPTION, ) + } is KtLintRuleException -> { logger.debug(e) { "Internal Error (${e.ruleId}) in ${code.fileNameOrStdin()} at position '${e.line}:${e.col}" } @@ -661,7 +665,9 @@ internal class KtlintCommandLine : CliktCommand(name = "ktlint") { ) } - else -> throw e + else -> { + throw e + } } } diff --git a/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/CommandLineTestRunner.kt b/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/CommandLineTestRunner.kt index 8841bba05e..15ccdc88ce 100644 --- a/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/CommandLineTestRunner.kt +++ b/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/CommandLineTestRunner.kt @@ -128,7 +128,9 @@ class CommandLineTestRunner( arrayOf(comSpec, "/C") } - else -> arrayOf("/bin/sh", "-c") + else -> { + arrayOf("/bin/sh", "-c") + } } private fun ktlintCommand(arguments: List): String = @@ -189,7 +191,9 @@ class CommandLineTestRunner( } ?: PATH } - else -> PATH + else -> { + PATH + } } environment[pathKey] = "$JAVA_HOME_BIN_DIR${File.pathSeparator}${OsEnvironment()[PATH]}" } diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/IndentConfig.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/IndentConfig.kt index 385d136ff9..22445ab744 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/IndentConfig.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/IndentConfig.kt @@ -111,7 +111,9 @@ public class IndentConfig( val indent = getTextAfterLastNewLine(text) require(indent.matches(TABS_AND_SPACES)) return when (indentStyle) { - SPACE -> indent.replaceTabWithSpaces() + SPACE -> { + indent.replaceTabWithSpaces() + } TAB -> { "\t".repeat(indentLevelFrom(indent)) diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/EditorConfig.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/EditorConfig.kt index cf17c21c34..ff35c0a365 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/EditorConfig.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/EditorConfig.kt @@ -38,13 +38,15 @@ public class EditorConfig( */ public operator fun get(editorConfigProperty: EditorConfigProperty): T { when { - editorConfigProperty.deprecationError != null -> + editorConfigProperty.deprecationError != null -> { throw DeprecatedEditorConfigPropertyException( "Property '${editorConfigProperty.name}' is disallowed: ${editorConfigProperty.deprecationError}", ) + } - editorConfigProperty.deprecationWarning != null -> + editorConfigProperty.deprecationWarning != null -> { LOGGER.warn { "Property '${editorConfigProperty.name}' is deprecated: ${editorConfigProperty.deprecationWarning}" } + } } val property = properties.getOrElse(editorConfigProperty.name) { diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/MaxLineLengthEditorConfigProperty.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/MaxLineLengthEditorConfigProperty.kt index 59c6890118..4a221a5b3b 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/MaxLineLengthEditorConfigProperty.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/MaxLineLengthEditorConfigProperty.kt @@ -34,9 +34,11 @@ public val MAX_LINE_LENGTH_PROPERTY: EditorConfigProperty = * Internally, Ktlint uses integer 'Int.MAX_VALUE' to indicate that the max line length has to be ignored as this is easier * in comparisons to check whether the maximum length of a line is exceeded. */ - property.sourceValue == MAX_LINE_LENGTH_PROPERTY_OFF_EDITOR_CONFIG -> MAX_LINE_LENGTH_PROPERTY_OFF + property.sourceValue == MAX_LINE_LENGTH_PROPERTY_OFF_EDITOR_CONFIG -> { + MAX_LINE_LENGTH_PROPERTY_OFF + } - else -> + else -> { PropertyType .max_line_length .parse(property.sourceValue) @@ -55,6 +57,7 @@ public val MAX_LINE_LENGTH_PROPERTY: EditorConfigProperty = it.parsed } } + } } }, propertyWriter = { property -> diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtlintRuleEngineSuppression.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtlintRuleEngineSuppression.kt index 8ba6536919..321e77e133 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtlintRuleEngineSuppression.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtlintRuleEngineSuppression.kt @@ -35,9 +35,11 @@ public fun KtLintRuleEngine.insertSuppression( private fun ASTNode.findLeafElementAt(suppression: KtlintSuppression): ASTNode = when (suppression) { - is KtlintSuppressionForFile -> this + is KtlintSuppressionForFile -> { + this + } - is KtlintSuppressionAtOffset -> + is KtlintSuppressionAtOffset -> { findLeafElementAt(suppression.offsetFromStartOf(text)) ?.let { if (it.isWhiteSpace()) { @@ -48,6 +50,7 @@ private fun ASTNode.findLeafElementAt(suppression: KtlintSuppression): ASTNode = } } ?: throw KtlintSuppressionNoElementFoundException(suppression) + } } private fun KtlintSuppressionAtOffset.offsetFromStartOf(code: String): Int { diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/CodeFormatter.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/CodeFormatter.kt index d9df1f4919..b3cb713e49 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/CodeFormatter.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/CodeFormatter.kt @@ -53,7 +53,7 @@ internal class CodeFormatter( var codeContent = formattedCode(lineSeparator) val errors = mutableSetOf>() var formatRunCount = 0 - var mutated: Boolean = false + var mutated = false do { val newErrors = format(autocorrectHandler, code) errors.addAll(newErrors) @@ -168,10 +168,13 @@ internal class CodeFormatter( when { eolEditorConfigProperty == PropertyType.EndOfLineValue.crlf || eolEditorConfigProperty != PropertyType.EndOfLineValue.lf && - doesNotContain('\r') -> + doesNotContain('\r') -> { "\r\n".also { LOGGER.trace { "line separator: ${eolEditorConfigProperty.name} --> CRLF" } } + } - else -> "\n".also { LOGGER.trace { "line separator: ${eolEditorConfigProperty.name} --> LF" } } + else -> { + "\n".also { LOGGER.trace { "line separator: ${eolEditorConfigProperty.name} --> LF" } } + } } private fun Code.doesNotContain(char: Char) = content.lastIndexOf(char) != -1 diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt index a7a9f1c1fc..0db38f466e 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/KtlintSuppression.kt @@ -81,19 +81,23 @@ internal fun ASTNode.insertKtlintRuleSuppression( // - otherwise to the @SuppressWarnings annotation if found // - otherwise create a new @Suppress annotation when { - suppressionAnnotations.containsKey(SuppressAnnotationType.SUPPRESS) -> + suppressionAnnotations.containsKey(SuppressAnnotationType.SUPPRESS) -> { fullyQualifiedSuppressionIds.mergeInto( suppressionAnnotations.getValue(SuppressAnnotationType.SUPPRESS), SuppressAnnotationType.SUPPRESS, ) + } - suppressionAnnotations.containsKey(SuppressAnnotationType.SUPPRESS_WARNINGS) -> + suppressionAnnotations.containsKey(SuppressAnnotationType.SUPPRESS_WARNINGS) -> { fullyQualifiedSuppressionIds.mergeInto( suppressionAnnotations.getValue(SuppressAnnotationType.SUPPRESS_WARNINGS), SuppressAnnotationType.SUPPRESS_WARNINGS, ) + } - else -> targetASTNode.createSuppressAnnotation(SuppressAnnotationType.SUPPRESS, fullyQualifiedSuppressionIds) + else -> { + targetASTNode.createSuppressAnnotation(SuppressAnnotationType.SUPPRESS, fullyQualifiedSuppressionIds) + } } } @@ -400,9 +404,13 @@ internal fun String.isKtlintSuppressionId() = removePrefix(DOUBLE_QUOTE).startsW internal fun String.toFullyQualifiedKtlintSuppressionId(): String = when (this) { - KTLINT_SUPPRESSION_ID_ALL_RULES -> this + KTLINT_SUPPRESSION_ID_ALL_RULES -> { + this + } - KTLINT_PREFIX -> this.surroundWith(DOUBLE_QUOTE) + KTLINT_PREFIX -> { + this.surroundWith(DOUBLE_QUOTE) + } else -> { removeSurrounding(DOUBLE_QUOTE) diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/PositionInTextLocator.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/PositionInTextLocator.kt index 87e730930e..2685b97ff5 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/PositionInTextLocator.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/PositionInTextLocator.kt @@ -58,7 +58,9 @@ private class SegmentTree( r: Int, ): Int = when { - l > r -> -1 + l > r -> { + -1 + } else -> { val i = l + (r - l) / 2 diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt index 06be443451..d0f0433555 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/SuppressionLocator.kt @@ -59,15 +59,17 @@ internal class SuppressionLocator( val commentSuppressionsHints = mutableListOf() rootNode.findSuppressionHints { node -> when (val psi = node.psi) { - is PsiComment -> + is PsiComment -> { node .createSuppressionHintFromComment() ?.let { commentSuppressionsHints.add(it) } + } - is KtAnnotated -> + is KtAnnotated -> { psi .createSuppressionHintFromAnnotations() ?.let { suppressionHints.add(it) } + } } } @@ -93,22 +95,25 @@ internal class SuppressionLocator( .takeIf { it.isNotEmpty() } ?.let { parts -> when (parts[0]) { - formatterTags.formatterTagOff -> + formatterTags.formatterTagOff -> { CommentSuppressionHint( this, HashSet(parts.tail()), BLOCK_START, ) + } - formatterTags.formatterTagOn -> + formatterTags.formatterTagOn -> { CommentSuppressionHint( this, HashSet(parts.tail()), BLOCK_END, ) + } - else -> + else -> { null + } } } @@ -184,19 +189,23 @@ internal class SuppressionLocator( .flatMap { it.findRuleSuppressionIds() } .let { suppressedRuleIds -> when { - suppressedRuleIds.isEmpty() -> null + suppressedRuleIds.isEmpty() -> { + null + } - suppressedRuleIds.contains(ALL_KTLINT_RULES_SUPPRESSION_ID) -> + suppressedRuleIds.contains(ALL_KTLINT_RULES_SUPPRESSION_ID) -> { SuppressionHint( IntRange(startOffset, endOffset - 1), emptySet(), ) + } - else -> + else -> { SuppressionHint( IntRange(startOffset, endOffset - 1), suppressedRuleIds.toSet(), ) + } } } diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/RuleExecutionRuleFilter.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/RuleExecutionRuleFilter.kt index f79080f500..acccda6e53 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/RuleExecutionRuleFilter.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rulefilter/RuleExecutionRuleFilter.kt @@ -94,17 +94,21 @@ private class RuleExecutionFilter( private fun isRuleConditionallyEnabled(rule: Rule) = when { - rule is Rule.Experimental && rule is Rule.OfficialCodeStyle -> + rule is Rule.Experimental && rule is Rule.OfficialCodeStyle -> { isExperimentalEnabled(rule) && isOfficialCodeStyleEnabled(rule) + } - rule is Rule.Experimental -> + rule is Rule.Experimental -> { isExperimentalEnabled(rule) + } - rule is Rule.OfficialCodeStyle -> + rule is Rule.OfficialCodeStyle -> { isOfficialCodeStyleEnabled(rule) + } - else -> + else -> { isRuleSetEnabled(rule) + } } private fun isExperimentalEnabled(rule: Rule) = diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt index 43ec288cb8..eba445d21c 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/internal/rules/KtlintSuppressionRule.kt @@ -352,19 +352,22 @@ private class KtLintDirective( private fun ASTNode.ktlintDirectiveOrNull(ruleIdValidator: (String) -> Boolean): KtLintDirective? { val ktlintDirectiveString = when (elementType) { - EOL_COMMENT -> + EOL_COMMENT -> { text .removePrefix("//") .trim() + } - BLOCK_COMMENT -> + BLOCK_COMMENT -> { text .removePrefix("/*") .removeSuffix("*/") .trim() + } - else -> + else -> { return null + } } val ktlintDirectiveType = ktlintDirectiveString.toKtlintDirectiveTypeOrNull() diff --git a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt index 9ab4a9dae7..2bf0b4a3ad 100644 --- a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt +++ b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt @@ -527,8 +527,9 @@ private class AutoCorrectErrorRule : } } - STRING_VALUE_NOT_TO_BE_CORRECTED -> + STRING_VALUE_NOT_TO_BE_CORRECTED -> { emit(node.startOffset, ERROR_MESSAGE_CAN_NOT_BE_AUTOCORRECTED, false) + } } } } diff --git a/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api b/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api index d23b5980fe..6e9f14acbe 100644 --- a/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api +++ b/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api @@ -987,6 +987,16 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/ValueParameterCom public static final fun getVALUE_PARAMETER_COMMENT_RULE_ID ()Lcom/pinterest/ktlint/rule/engine/core/api/RuleId; } +public final class com/pinterest/ktlint/ruleset/standard/rules/WhenEntryBracing : com/pinterest/ktlint/ruleset/standard/StandardRule, com/pinterest/ktlint/rule/engine/core/api/Rule$Experimental, com/pinterest/ktlint/rule/engine/core/api/Rule$OfficialCodeStyle, com/pinterest/ktlint/rule/engine/core/api/RuleAutocorrectApproveHandler { + public fun ()V + public fun beforeFirstNode (Lcom/pinterest/ktlint/rule/engine/core/api/editorconfig/EditorConfig;)V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V +} + +public final class com/pinterest/ktlint/ruleset/standard/rules/WhenEntryBracingKt { + public static final fun getWHEN_ENTRY_BRACING_RULE_ID ()Lcom/pinterest/ktlint/rule/engine/core/api/RuleId; +} + public final class com/pinterest/ktlint/ruleset/standard/rules/WrappingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V public fun afterVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;Lkotlin/jvm/functions/Function3;)V diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetProvider.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetProvider.kt index 26a6b41dfd..d0db40ecd3 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetProvider.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetProvider.kt @@ -99,6 +99,7 @@ import com.pinterest.ktlint.ruleset.standard.rules.TypeParameterListSpacingRule import com.pinterest.ktlint.ruleset.standard.rules.UnnecessaryParenthesesBeforeTrailingLambdaRule import com.pinterest.ktlint.ruleset.standard.rules.ValueArgumentCommentRule import com.pinterest.ktlint.ruleset.standard.rules.ValueParameterCommentRule +import com.pinterest.ktlint.ruleset.standard.rules.WhenEntryBracing import com.pinterest.ktlint.ruleset.standard.rules.WrappingRule public class StandardRuleSetProvider : RuleSetProviderV3(RuleSetId.STANDARD) { @@ -200,6 +201,7 @@ public class StandardRuleSetProvider : RuleSetProviderV3(RuleSetId.STANDARD) { RuleProvider { ValueArgumentCommentRule() }, RuleProvider { ValueParameterCommentRule() }, RuleProvider { UnnecessaryParenthesesBeforeTrailingLambdaRule() }, + RuleProvider { WhenEntryBracing() }, RuleProvider { WrappingRule() }, ) } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt index 2ce641b009..fcb611475c 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt @@ -149,14 +149,17 @@ public class AnnotationRule : if (node.shouldWrapAnnotations()) { val expectedIndent = when { - node.elementType == ANNOTATED_EXPRESSION -> + node.elementType == ANNOTATED_EXPRESSION -> { indentConfig.siblingIndentOf(node) + } - node.hasAnnotationBeforeConstructor() -> + node.hasAnnotationBeforeConstructor() -> { indentConfig.siblingIndentOf(node.treeParent) + } - else -> + else -> { indentConfig.parentIndentOf(node) + } } node diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt index 53365e5b2f..29a28e18cb 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/BlankLineBeforeDeclarationRule.kt @@ -56,8 +56,9 @@ public class BlankLineBeforeDeclarationRule : OBJECT_DECLARATION, PROPERTY, PROPERTY_ACCESSOR, - -> + -> { visitDeclaration(node, emit) + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainMethodContinuationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainMethodContinuationRule.kt index 299ce796b9..80b2a9d010 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainMethodContinuationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainMethodContinuationRule.kt @@ -207,7 +207,9 @@ public class ChainMethodContinuationRule : chainOperators.size >= forceMultilineWhenChainOperatorCountGreaterOrEqualThanProperty } - else -> false + else -> { + false + } } private fun ChainedExpression.isChainedExpressionOnStringTemplate() = @@ -425,7 +427,9 @@ public class ChainMethodContinuationRule : .singleOrNull() } - else -> null + else -> { + null + } } private fun ASTNode.createBaseChainedExpression(chainOperator: ASTNode): ChainedExpression { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainWrappingRule.kt index cf0b7fbd75..ccea277ccf 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ChainWrappingRule.kt @@ -140,9 +140,13 @@ public class ChainWrappingRule : prevLeaf } - nextLeaf.isWhiteSpaceWithoutNewline() -> nextLeaf + nextLeaf.isWhiteSpaceWithoutNewline() -> { + nextLeaf + } - else -> null + else -> { + null + } } if (node.treeParent.elementType == OPERATION_REFERENCE) { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ContextReceiverWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ContextReceiverWrappingRule.kt index 1b14e576dd..5d6bdb91e3 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ContextReceiverWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ContextReceiverWrappingRule.kt @@ -66,11 +66,13 @@ public class ContextReceiverWrappingRule : emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when { - node.elementType == CONTEXT_RECEIVER_LIST -> + node.elementType == CONTEXT_RECEIVER_LIST -> { visitContextReceiverList(node, emit) + } - node.elementType == TYPE_ARGUMENT_LIST && node.isPartOf(CONTEXT_RECEIVER) -> + node.elementType == TYPE_ARGUMENT_LIST && node.isPartOf(CONTEXT_RECEIVER) -> { visitContextReceiverTypeArgumentList(node, emit) + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRule.kt index ea15c9ce25..44564b9f9e 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionNamingRule.kt @@ -159,7 +159,9 @@ public class FunctionNamingRule : it.annotationEntryName() in excludeWhenAnnotatedWith } - else -> false + else -> { + false + } } } ?: false diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt index b56b62e85f..52dde0537f 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunctionSignatureRule.kt @@ -571,7 +571,9 @@ public class FunctionSignatureRule : ) } - else -> false + else -> { + false + } } if (mergeWithFunctionSignature) { emit( diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRule.kt index 667ce3f08f..ee939b239f 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ImportOrderingRule.kt @@ -144,8 +144,9 @@ public class ImportOrderingRule : children.forEach { current -> when { - current.isWhiteSpace() && current.text.count { it == '\n' } > 1 -> + current.isWhiteSpace() && current.text.count { it == '\n' } > 1 -> { imports += current + } current.elementType == ElementType.IMPORT_DIRECTIVE -> { if (importTextSet.add(current.text)) { @@ -219,11 +220,12 @@ public class ImportOrderingRule : private val EDITOR_CONFIG_PROPERTY_PARSER: (String, String?) -> PropertyType.PropertyValue> = { _, value -> when { - value.isNullOrBlank() -> + value.isNullOrBlank() -> { PropertyType.PropertyValue.invalid( value, "Import layout must contain at least one entry of a wildcard symbol (*)", ) + } value == "idea" -> { LOGGER.warn { @@ -246,7 +248,7 @@ public class ImportOrderingRule : ) } - else -> + else -> { try { PropertyType.PropertyValue.valid( value, @@ -258,6 +260,7 @@ public class ImportOrderingRule : "Unexpected imports layout: $value", ) } + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt index 73c6bed24d..cbba25f518 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt @@ -215,11 +215,12 @@ public class IndentationRule : node.elementType == CONTEXT_RECEIVER_LIST || node.elementType == LONG_STRING_TEMPLATE_ENTRY || node.elementType == STRING_TEMPLATE || - node.elementType == VALUE_ARGUMENT_LIST -> + node.elementType == VALUE_ARGUMENT_LIST -> { startIndentContext( fromAstNode = node, lastChildIndent = "", ) + } (node.elementType == SUPER_TYPE_LIST && !node.isPrecededByComment()) || (node.isPartOfComment() && node.nextCodeSibling()?.elementType == SUPER_TYPE_LIST) -> { @@ -249,13 +250,15 @@ public class IndentationRule : } } - node.elementType == VALUE_ARGUMENT -> + node.elementType == VALUE_ARGUMENT -> { visitValueArgument(node) + } - node.elementType == SECONDARY_CONSTRUCTOR -> + node.elementType == SECONDARY_CONSTRUCTOR -> { visitSecondaryConstructor(node) + } - node.elementType == PARENTHESIZED -> + node.elementType == PARENTHESIZED -> { if (codeStyle == ktlint_official) { // Contrary to the IntelliJ IDEA default formatter, do not indent the closing RPAR startIndentContext( @@ -265,9 +268,10 @@ public class IndentationRule : } else if (node.treeParent.treeParent.elementType != IF) { startIndentContext(node) } + } node.elementType == TYPE_ARGUMENT_LIST || - node.elementType == TYPE_PARAMETER_LIST -> + node.elementType == TYPE_PARAMETER_LIST -> { if (codeStyle == ktlint_official) { // Contrary to the IntelliJ IDEA default formatter, do not indent the closing angle bracket startIndentContext( @@ -277,55 +281,68 @@ public class IndentationRule : } else { startIndentContext(node) } + } node.elementType == BINARY_WITH_TYPE || - node.elementType == USER_TYPE -> + node.elementType == USER_TYPE -> { startIndentContext(node) + } node.elementType == IS_EXPRESSION || node.elementType == PREFIX_EXPRESSION || - node.elementType == POSTFIX_EXPRESSION -> + node.elementType == POSTFIX_EXPRESSION -> { startIndentContext(node) + } node.elementType == DELEGATED_SUPER_TYPE_ENTRY || node.elementType == ANNOTATED_EXPRESSION || - node.elementType == TYPE_REFERENCE -> + node.elementType == TYPE_REFERENCE -> { startIndentContext( fromAstNode = node, childIndent = "", ) + } - node.elementType == IF -> + node.elementType == IF -> { visitIf(node) + } - node.elementType == LBRACE -> + node.elementType == LBRACE -> { visitLbrace(node) + } node.elementType == VALUE_PARAMETER_LIST && - node.treeParent.elementType != FUNCTION_LITERAL -> + node.treeParent.elementType != FUNCTION_LITERAL -> { startIndentContext( fromAstNode = node, lastChildIndent = "", ) + } node.elementType == LPAR && - node.nextCodeSibling()?.elementType == CONDITION -> + node.nextCodeSibling()?.elementType == CONDITION -> { visitLparBeforeCondition(node) + } - node.elementType == VALUE_PARAMETER -> + node.elementType == VALUE_PARAMETER -> { visitValueParameter(node) + } - node.elementType == FUN -> + node.elementType == FUN -> { visitFun(node) + } - node.elementType == CLASS -> + node.elementType == CLASS -> { visitClass(node) + } - node.elementType == OBJECT_DECLARATION -> + node.elementType == OBJECT_DECLARATION -> { visitObjectDeclaration(node) + } - node.elementType == BINARY_EXPRESSION -> + node.elementType == BINARY_EXPRESSION -> { visitBinaryExpression(node) + } node.elementType in CHAINABLE_EXPRESSION -> { if (codeStyle == ktlint_official && @@ -352,45 +369,57 @@ public class IndentationRule : } node.elementType == IDENTIFIER && - node.treeParent.elementType == PROPERTY -> + node.treeParent.elementType == PROPERTY -> { visitIdentifierInProperty(node) + } node.elementType == LITERAL_STRING_TEMPLATE_ENTRY && - node.nextCodeSibling()?.elementType == CLOSING_QUOTE -> + node.nextCodeSibling()?.elementType == CLOSING_QUOTE -> { visitWhiteSpaceBeforeClosingQuote(node, emit) + } - node.elementType == WHEN -> + node.elementType == WHEN -> { visitWhen(node) + } - node.elementType == WHEN_ENTRY -> + node.elementType == WHEN_ENTRY -> { visitWhenEntry(node) + } node.elementType == WHERE_KEYWORD && - node.nextCodeSibling()?.elementType == TYPE_CONSTRAINT_LIST -> + node.nextCodeSibling()?.elementType == TYPE_CONSTRAINT_LIST -> { visitWhereKeywordBeforeTypeConstraintList(node) + } - node.elementType == KDOC -> + node.elementType == KDOC -> { visitKdoc(node) + } node.elementType == PROPERTY_ACCESSOR || - node.elementType == TYPEALIAS -> + node.elementType == TYPEALIAS -> { visitPropertyAccessor(node) + } node.elementType == FOR || - node.elementType == WHILE -> + node.elementType == WHILE -> { visitConditionalLoop(node) + } - node.elementType == LBRACKET -> + node.elementType == LBRACKET -> { visitLBracket(node) + } - node.elementType == NULLABLE_TYPE -> + node.elementType == NULLABLE_TYPE -> { visitNullableType(node) + } - node.elementType == DESTRUCTURING_DECLARATION -> + node.elementType == DESTRUCTURING_DECLARATION -> { visitDestructuringDeclaration(node) + } - node.elementType == TRY -> + node.elementType == TRY -> { visitTryCatchFinally(node) + } else -> { LOGGER.trace { "No processing for ${node.elementType}: ${node.textWithEscapedTabAndNewline()}" } @@ -1149,14 +1178,18 @@ public class IndentationRule : val adjustedChildIndent = when { this == lastIndexContext.fromASTNode.firstChildLeafOrSelf() || - nextLeaf == lastIndexContext.fromASTNode.firstChildLeafOrSelf() -> + nextLeaf == lastIndexContext.fromASTNode.firstChildLeafOrSelf() -> { lastIndexContext.firstChildIndent + } this == lastIndexContext.toASTNode || - nextLeaf == lastIndexContext.toASTNode -> + nextLeaf == lastIndexContext.toASTNode -> { lastIndexContext.lastChildIndent + } - else -> lastIndexContext.childIndent + else -> { + lastIndexContext.childIndent + } } return lastIndexContext.nodeIndent + adjustedChildIndent } @@ -1233,7 +1266,9 @@ public class IndentationRule : TYPE_CONSTRAINT_CONTINUATION_INDENT } - else -> "" + else -> { + "" + } } val nodeIndent = text.substringAfterLast("\n") return if (nodeIndent.endsWith(acceptableTrailingSpaces)) { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt index e7a72931cd..1088658bcc 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt @@ -121,7 +121,9 @@ public class MultilineExpressionWrappingRule : .nextLeaf { !it.isWhiteSpaceWithoutNewline() && !it.isPartOfComment() } ?.takeIf { !it.isWhiteSpaceWithNewline() } when { - leafOnSameLineAfterMultilineExpression == null -> Unit + leafOnSameLineAfterMultilineExpression == null -> { + Unit + } leafOnSameLineAfterMultilineExpression.treeParent.elementType == OPERATION_REFERENCE -> { // When binary expressions are wrapped, each binary expression for itself is checked whether it is a diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveCommentsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveCommentsRule.kt index e3816dde27..d33eaaad5f 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveCommentsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoConsecutiveCommentsRule.kt @@ -129,11 +129,13 @@ public class NoConsecutiveCommentsRule : EOL_COMMENT, BLOCK_COMMENT, KDOC_START, - -> + -> { true + } - else -> + else -> { false + } } private fun ASTNode?.isEndOfComment() = @@ -141,11 +143,13 @@ public class NoConsecutiveCommentsRule : EOL_COMMENT, BLOCK_COMMENT, KDOC_END, - -> + -> { true + } - else -> + else -> { false + } } private fun ASTNode.commentType() = diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt index a7cdca8f9b..d4ccd8b115 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoSemicolonsRule.kt @@ -78,7 +78,9 @@ public class NoSemicolonsRule : private fun ASTNode?.doesNotRequirePreSemi() = when { - this == null -> true + this == null -> { + true + } this is PsiWhiteSpace -> { nextLeaf { @@ -94,7 +96,9 @@ public class NoSemicolonsRule : } } - else -> false + else -> { + false + } } private fun isNoSemicolonRequiredAfter(node: ASTNode): Boolean { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt index d0b209e09d..3d78892c69 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoTrailingSpacesRule.kt @@ -45,10 +45,11 @@ public class NoTrailingSpacesRule : StandardRule("no-trailing-spaces") { .mapIndexed { index, line -> val modifiedLine = when { - node.elementType != EOL_COMMENT && index == lines.size - 1 && node.nextLeaf() != null -> + node.elementType != EOL_COMMENT && index == lines.size - 1 && node.nextLeaf() != null -> { // Do not change the last line as it contains the indentation of the next element except // when it is an EOL comment which may also not contain trailing spaces line + } line.hasTrailingSpace() -> { val modifiedLine = line.trimEnd() @@ -60,7 +61,9 @@ public class NoTrailingSpacesRule : StandardRule("no-trailing-spaces") { modifiedLine } - else -> line + else -> { + line + } } violationOffset += line.length + 1 modifiedLine diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt index 4b3f280ff7..66cded850f 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoUnusedImportsRule.kt @@ -117,7 +117,9 @@ public class NoUnusedImportsRule : } } - BY_KEYWORD -> foundByKeyword = true + BY_KEYWORD -> { + foundByKeyword = true + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoWildcardImportsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoWildcardImportsRule.kt index d21a825530..7637801dfc 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoWildcardImportsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/NoWildcardImportsRule.kt @@ -68,19 +68,16 @@ public class NoWildcardImportsRule : private val PACKAGES_TO_USE_ON_DEMAND_IMPORT_PROPERTY_PARSER: (String, String?) -> PropertyType.PropertyValue> = { _, value -> - when { - else -> - try { - PropertyType.PropertyValue.valid( - value, - value?.let(Companion::parseAllowedWildcardImports) ?: emptyList(), - ) - } catch (e: IllegalArgumentException) { - PropertyType.PropertyValue.invalid( - value, - "Unexpected imports layout: $value", - ) - } + try { + PropertyType.PropertyValue.valid( + value, + value?.let(Companion::parseAllowedWildcardImports) ?: emptyList(), + ) + } catch (e: IllegalArgumentException) { + PropertyType.PropertyValue.invalid( + value, + "Unexpected imports layout: $value", + ) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRule.kt index 551d0c76c1..2232e6377e 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/ParameterListWrappingRule.kt @@ -128,20 +128,34 @@ public class ParameterListWrappingRule : private fun ASTNode.needToWrapParameterList() = when { - hasNoParameters() -> false + hasNoParameters() -> { + false + } - codeStyle != ktlint_official && isPartOfFunctionLiteralInNonKtlintOfficialCodeStyle() -> false + codeStyle != ktlint_official && isPartOfFunctionLiteralInNonKtlintOfficialCodeStyle() -> { + false + } - codeStyle == ktlint_official && containsAnnotatedParameter() -> true + codeStyle == ktlint_official && containsAnnotatedParameter() -> { + true + } - codeStyle == ktlint_official && isPartOfFunctionLiteralStartingOnSameLineAsClosingParenthesisOfPrecedingReferenceExpression() -> + codeStyle == ktlint_official && + isPartOfFunctionLiteralStartingOnSameLineAsClosingParenthesisOfPrecedingReferenceExpression() -> { false + } - textContains('\n') -> true + textContains('\n') -> { + true + } - isOnLineExceedingMaxLineLength() -> true + isOnLineExceedingMaxLineLength() -> { + true + } - else -> false + else -> { + false + } } private fun ASTNode.hasNoParameters(): Boolean { @@ -311,12 +325,8 @@ public class ParameterListWrappingRule : private fun errorMessage(node: ASTNode) = when (node.elementType) { LPAR -> """Unnecessary newline before "("""" - - VALUE_PARAMETER -> - "Parameter should start on a newline" - + VALUE_PARAMETER -> "Parameter should start on a newline" RPAR -> """Missing newline before ")"""" - else -> throw UnsupportedOperationException() } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt index 4e71d6dfff..66400cdc8b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundColonRule.kt @@ -187,7 +187,9 @@ public class SpacingAroundColonRule : StandardRule("colon-spacing") { private inline val ASTNode.spacingBefore: Boolean get() = when { - psi.parent is KtClassOrObject -> true + psi.parent is KtClassOrObject -> { + true + } psi.parent is KtConstructor<*> -> { // constructor : this/super @@ -199,10 +201,13 @@ public class SpacingAroundColonRule : StandardRule("colon-spacing") { true } - psi.parent.parent is KtTypeParameterList -> + psi.parent.parent is KtTypeParameterList -> { true + } - else -> false + else -> { + false + } } private inline val ASTNode.noSpacingBefore: Boolean diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt index 3281926526..692ff06ed7 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundDoubleColonRule.kt @@ -30,18 +30,24 @@ public class SpacingAroundDoubleColonRule : StandardRule("double-colon-spacing") var removeSingleWhiteSpace = false val spacingBefore = when { - node.isPartOf(CLASS_LITERAL_EXPRESSION) && prevLeaf is PsiWhiteSpace -> true + node.isPartOf(CLASS_LITERAL_EXPRESSION) && prevLeaf is PsiWhiteSpace -> { + true + } // Clazz::class - node.isPartOf(CALLABLE_REFERENCE_EXPRESSION) && prevLeaf is PsiWhiteSpace -> // String::length, ::isOdd + node.isPartOf(CALLABLE_REFERENCE_EXPRESSION) && prevLeaf is PsiWhiteSpace -> { + // String::length, ::isOdd if (node.treePrev == null) { // compose(length, ::isOdd), val predicate = ::isOdd removeSingleWhiteSpace = true !prevLeaf.textContains('\n') && prevLeaf.psi.textLength > 1 } else { // String::length, List::isEmpty !prevLeaf.textContains('\n') } + } - else -> false + else -> { + false + } } val spacingAfter = nextLeaf is PsiWhiteSpace when { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundParensRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundParensRule.kt index a48f70df15..02ebc26f5b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundParensRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/SpacingAroundParensRule.kt @@ -53,7 +53,9 @@ public class SpacingAroundParensRule : StandardRule("paren-spacing") { private fun ASTNode.isUnexpectedSpacingBeforeParenthesis(): Boolean = when { - !prevLeaf().isWhiteSpaceWithoutNewline() -> false + !prevLeaf().isWhiteSpaceWithoutNewline() -> { + false + } elementType == LPAR -> { treeParent?.elementType in elementListTokenSet && @@ -72,7 +74,9 @@ public class SpacingAroundParensRule : StandardRule("paren-spacing") { prevLeaf()?.prevSibling()?.elementType != LPAR } - else -> false + else -> { + false + } } private fun ASTNode.isUnexpectedSpacingBetweenIdentifierAndElementList() = diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt index 4c91958844..cfa3387d90 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/StatementWrappingRule.kt @@ -64,19 +64,22 @@ public class StatementWrappingRule : emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when (node.elementType) { - BLOCK -> + BLOCK -> { if (node.treeParent.elementType == FUNCTION_LITERAL) { // LBRACE and RBRACE are outside of BLOCK visitBlock(node.treeParent, emit) } else { visitBlock(node, emit) } + } - CLASS_BODY, WHEN -> + CLASS_BODY, WHEN -> { visitBlock(node, emit) + } - SEMICOLON -> + SEMICOLON -> { visitSemiColon(node, emit) + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRule.kt index 40e123e356..e22e4cab5b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnCallSiteRule.kt @@ -149,7 +149,7 @@ public class TrailingCommaOnCallSiteRule : else -> if (trailingCommaNode != null) TrailingCommaState.REDUNDANT else TrailingCommaState.NOT_EXISTS } when (trailingCommaState) { - TrailingCommaState.EXISTS -> + TrailingCommaState.EXISTS -> { if (!isTrailingCommaAllowed) { emit( trailingCommaNode!!.startOffset, @@ -159,8 +159,9 @@ public class TrailingCommaOnCallSiteRule : this.removeChild(trailingCommaNode) } } + } - TrailingCommaState.MISSING -> + TrailingCommaState.MISSING -> { if (isTrailingCommaAllowed) { val prevNode = inspectNode.prevCodeLeaf()!! emit( @@ -176,6 +177,7 @@ public class TrailingCommaOnCallSiteRule : } } } + } TrailingCommaState.REDUNDANT -> { emit( @@ -187,7 +189,9 @@ public class TrailingCommaOnCallSiteRule : } } - TrailingCommaState.NOT_EXISTS -> Unit + TrailingCommaState.NOT_EXISTS -> { + Unit + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt index 511e412698..80ae9adaf2 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TrailingCommaOnDeclarationSiteRule.kt @@ -263,12 +263,16 @@ public class TrailingCommaOnDeclarationSiteRule : TrailingCommaState.NOT_EXISTS } - isMultiline(psi) -> if (trailingCommaNode != null) TrailingCommaState.EXISTS else TrailingCommaState.MISSING + isMultiline(psi) -> { + if (trailingCommaNode != null) TrailingCommaState.EXISTS else TrailingCommaState.MISSING + } - else -> if (trailingCommaNode != null) TrailingCommaState.REDUNDANT else TrailingCommaState.NOT_EXISTS + else -> { + if (trailingCommaNode != null) TrailingCommaState.REDUNDANT else TrailingCommaState.NOT_EXISTS + } } when (trailingCommaState) { - TrailingCommaState.EXISTS -> + TrailingCommaState.EXISTS -> { if (isTrailingCommaAllowed) { inspectNode .treeParent @@ -295,8 +299,9 @@ public class TrailingCommaOnDeclarationSiteRule : this.removeChild(trailingCommaNode) } } + } - TrailingCommaState.MISSING -> + TrailingCommaState.MISSING -> { if (isTrailingCommaAllowed) { val leafBeforeArrowOrNull = leafBeforeArrowOrNull() val addNewLine = @@ -354,6 +359,7 @@ public class TrailingCommaOnDeclarationSiteRule : } } } + } TrailingCommaState.REDUNDANT -> { emit( @@ -365,7 +371,9 @@ public class TrailingCommaOnDeclarationSiteRule : } } - TrailingCommaState.NOT_EXISTS -> Unit + TrailingCommaState.NOT_EXISTS -> { + Unit + } } } @@ -409,17 +417,21 @@ public class TrailingCommaOnDeclarationSiteRule : private fun ASTNode.leafBeforeArrowOrNull() = when (psi) { - is KtWhenEntry -> + is KtWhenEntry -> { (psi as KtWhenEntry) .arrow ?.prevLeaf() + } - is KtFunctionLiteral -> + is KtFunctionLiteral -> { (psi as KtFunctionLiteral) .arrow ?.prevLeaf() + } - else -> null + else -> { + null + } } private fun ASTNode.findPreviousTrailingCommaNodeOrNull(): ASTNode? { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentListSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentListSpacingRule.kt index 6cd0159ab2..9d965c08db 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentListSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeArgumentListSpacingRule.kt @@ -59,8 +59,9 @@ public class TypeArgumentListSpacingRule : visitInsideTypeArgumentList(node, emit) } - ElementType.SUPER_TYPE_LIST, ElementType.SUPER_EXPRESSION -> + ElementType.SUPER_TYPE_LIST, ElementType.SUPER_EXPRESSION -> { visitInsideTypeArgumentList(node, emit) + } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterListSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterListSpacingRule.kt index afe845fd6a..ef21b1a974 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterListSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/TypeParameterListSpacingRule.kt @@ -251,7 +251,9 @@ public class TypeParameterListSpacingRule : emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, ) { when { - node.text == " " -> Unit + node.text == " " -> { + Unit + } node.textContains('\n') -> { emit(node.startOffset, "Expected a single space instead of newline", true) diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WhenEntryBracing.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WhenEntryBracing.kt new file mode 100644 index 0000000000..e8853ee310 --- /dev/null +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WhenEntryBracing.kt @@ -0,0 +1,169 @@ +package com.pinterest.ktlint.ruleset.standard.rules + +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision +import com.pinterest.ktlint.rule.engine.core.api.ElementType +import com.pinterest.ktlint.rule.engine.core.api.ElementType.ARROW +import com.pinterest.ktlint.rule.engine.core.api.ElementType.BLOCK +import com.pinterest.ktlint.rule.engine.core.api.ElementType.WHEN_ENTRY +import com.pinterest.ktlint.rule.engine.core.api.IndentConfig +import com.pinterest.ktlint.rule.engine.core.api.Rule +import com.pinterest.ktlint.rule.engine.core.api.RuleAutocorrectApproveHandler +import com.pinterest.ktlint.rule.engine.core.api.RuleId +import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint +import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL +import com.pinterest.ktlint.rule.engine.core.api.children +import com.pinterest.ktlint.rule.engine.core.api.editorconfig.EditorConfig +import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY +import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY +import com.pinterest.ktlint.rule.engine.core.api.ifAutocorrectAllowed +import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace +import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline +import com.pinterest.ktlint.rule.engine.core.api.nextSibling +import com.pinterest.ktlint.rule.engine.core.api.prevSibling +import com.pinterest.ktlint.ruleset.standard.StandardRule +import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import org.jetbrains.kotlin.com.intellij.psi.PsiFileFactory +import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtScript +import org.jetbrains.kotlin.psi.KtScriptInitializer +import org.jetbrains.kotlin.psi.KtWhenEntry +import org.jetbrains.kotlin.psi.KtWhenExpression +import org.jetbrains.kotlin.psi.psiUtil.getChildOfType +import org.jetbrains.kotlin.psi.psiUtil.siblings + +/** + * If any when condition is using curly braces, then all other when conditions should use braces as well. + * + * Braces are helpful for following reasons: + * - Bodies of the when-conditions are all aligned at same column position + * - Closing braces helps in separation the when-conditions + */ +@SinceKtlint("1.4.0", EXPERIMENTAL) +public class WhenEntryBracing : + StandardRule( + id = "when-entry-bracing", + usesEditorConfigProperties = + setOf( + INDENT_SIZE_PROPERTY, + INDENT_STYLE_PROPERTY, + ), + ), + RuleAutocorrectApproveHandler, + Rule.OfficialCodeStyle, + Rule.Experimental { + private var indentConfig = IndentConfig.DEFAULT_INDENT_CONFIG + + override fun beforeFirstNode(editorConfig: EditorConfig) { + indentConfig = + IndentConfig( + indentStyle = editorConfig[INDENT_STYLE_PROPERTY], + tabWidth = editorConfig[INDENT_SIZE_PROPERTY], + ) + } + + override fun beforeVisitChildNodes( + node: ASTNode, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, + ) { + if (node.elementType == ElementType.WHEN) { + visitWhenStatement(node, emit) + } + } + + private fun visitWhenStatement( + node: ASTNode, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, + ) { + if (node.hasAnyWhenEntryWithBlockAfterArrow() || node.hasAnyWhenEntryWithMultilineBody()) { + addBracesToWhenEntry(node, emitAndApprove) + } + } + + private fun ASTNode.hasAnyWhenEntryWithBlockAfterArrow() = children().any { it.elementType == WHEN_ENTRY && it.hasBlockAfterArrow() } + + private fun ASTNode.hasBlockAfterArrow(): Boolean { + require(elementType == WHEN_ENTRY) + return findChildByType(ARROW) + ?.siblings() + .orEmpty() + .any { it.elementType == BLOCK } + } + + private fun ASTNode.hasAnyWhenEntryWithMultilineBody() = children().any { it.elementType == WHEN_ENTRY && it.hasMultilineBody() } + + private fun ASTNode.hasMultilineBody(): Boolean { + require(elementType == WHEN_ENTRY) + return findChildByType(ARROW) + ?.siblings() + .orEmpty() + .any { it.isWhiteSpaceWithNewline() } + } + + private fun addBracesToWhenEntry( + node: ASTNode, + emitAndApprove: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> AutocorrectDecision, + ) { + node + .children() + .filter { it.elementType == WHEN_ENTRY } + .filter { !it.hasBlockAfterArrow() } + .forEach { whenEntry -> + whenEntry + .findChildByType(ARROW) + ?.let { arrow -> + val nonWhiteSpaceSibling = arrow.nextSibling { !it.isWhiteSpace() } ?: arrow + emitAndApprove( + nonWhiteSpaceSibling.startOffset, + "Body of when entry should be surrounded by braces if any when entry body is surrounded by braces " + + "or has a multiline body", + true, + ).ifAutocorrectAllowed { + arrow +// .nextSibling { it.isWhiteSpace() } + .surroundWithBraces() + } + } + } + } + + private fun ASTNode.surroundWithBraces() { + require(elementType == ARROW) + val whenEntryIndent = indentConfig.parentIndentOf(this).removePrefix("\n") + val whenEntry = + "${whenEntryIndent}true -> {" + + // Replace the whitespaces (possibly this could be a proper indent) at the beginning of the body with an indent. In case + // the body was already a multiline statement, then the second and following lines should already be properly indented. + indentConfig.childIndentOf(this) + + siblings() + .dropWhile { it.isWhiteSpace() } + .joinToString(separator = "") { it.text } + + "\n$whenEntryIndent}" + val blockExpression = createBlockExpression(whenEntry) + val prevSibling = prevSibling()!! + treeParent.removeRange(nextSibling()!!, null) + prevSibling.treeParent.addChild(PsiWhiteSpaceImpl(" "), null) + prevSibling.treeParent.addChild(blockExpression!!, null) + } + + private fun ASTNode.createBlockExpression(whenEntry: String) = + PsiFileFactory + .getInstance(psi.project) + .createFileFromText( + KotlinLanguage.INSTANCE, + """ + |when { + |$whenEntry + |} + """.trimMargin(), + ).getChildOfType() + ?.getChildOfType() + ?.getChildOfType() + ?.getChildOfType() + ?.getChildOfType() + ?.getChildOfType() + ?.node +} + +public val WHEN_ENTRY_BRACING_RULE_ID: RuleId = WhenEntryBracing().ruleId diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WhenEntryBracingTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WhenEntryBracingTest.kt new file mode 100644 index 0000000000..9284d9ec5d --- /dev/null +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WhenEntryBracingTest.kt @@ -0,0 +1,198 @@ +package com.pinterest.ktlint.ruleset.standard.rules + +import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule +import com.pinterest.ktlint.test.LintViolation +import org.junit.jupiter.api.Test + +class WhenEntryBracingTest { + private val whenEntryBracingRuleAssertThat = assertThatRule { WhenEntryBracing() } + + @Test + fun `Given a when-statement for which no entry has a block body then do not reformat`() { + val code = + """ + val foo = + when (bar) { + BAR1 -> "bar1" + BAR2 -> "bar2" + else -> null + } + """.trimIndent() + whenEntryBracingRuleAssertThat(code).hasNoLintViolations() + } + + @Test + fun `Given a when-statement for which all entries have a block body then do not reformat`() { + val code = + """ + val foo = + when (bar) { + BAR1 -> { "bar1" } + BAR2 -> { "bar2" } + else -> { null } + } + """.trimIndent() + whenEntryBracingRuleAssertThat(code).hasNoLintViolations() + } + + @Test + fun `Given a when-statement containing an entry with braces and an entry without braces then add braces to all entries`() { + val code = + """ + val foo = + when (bar) { + BAR1 -> { "bar1" } + BAR2 -> "bar2" + else -> null + } + """.trimIndent() + val formattedCode = + """ + val foo = + when (bar) { + BAR1 -> { + "bar1" + } + BAR2 -> { + "bar2" + } + else -> { + null + } + } + """.trimIndent() + @Suppress("ktlint:standard:argument-list-wrapping", "ktlint:standard:max-line-length") + whenEntryBracingRuleAssertThat(code) + .addAdditionalRuleProvider { + // Ensures that the first when entry is also wrapped to a multiline body + StatementWrappingRule() + }.hasLintViolations( + LintViolation(4, 17, "Body of when entry should be surrounded by braces if any when entry body is surrounded by braces or has a multiline body"), + LintViolation(5, 17, "Body of when entry should be surrounded by braces if any when entry body is surrounded by braces or has a multiline body"), + ).isFormattedAs(formattedCode) + } + + @Test + fun `Given a when-statement containing an entry with braces and an entry without braces which contains one multiline statement then add braces to all entries`() { + val code = + """ + val foo = + when (bar) { + BAR1 -> { "bar1" } + BAR2 -> "bar2" + .plus("bar3") + .plus("bar4") + else -> null + } + """.trimIndent() + val formattedCode = + """ + val foo = + when (bar) { + BAR1 -> { + "bar1" + } + BAR2 -> { + "bar2" + .plus("bar3") + .plus("bar4") + } + else -> { + null + } + } + """.trimIndent() + @Suppress("ktlint:standard:argument-list-wrapping", "ktlint:standard:max-line-length") + whenEntryBracingRuleAssertThat(code) + .addAdditionalRuleProvider { + // Ensures that the first when entry is also wrapped to a multiline body + StatementWrappingRule() + }.addAdditionalRuleProvider { + // Fix indent of the wrapped multiline statement + IndentationRule() + }.hasLintViolations( + LintViolation(4, 17, "Body of when entry should be surrounded by braces if any when entry body is surrounded by braces or has a multiline body"), + LintViolation(7, 17, "Body of when entry should be surrounded by braces if any when entry body is surrounded by braces or has a multiline body"), + ).isFormattedAs(formattedCode) + } + + @Test + fun `Given a when-statement containing an entry with braces and an entry without braces which starts with some EOL comments then add braces to all entries`() { + val code = + """ + val foo = + when (bar) { + BAR1 -> { "bar1" } + BAR2 -> // some comment 1 + // some comment 2 + "bar2" + else -> null + } + """.trimIndent() + val formattedCode = + """ + val foo = + when (bar) { + BAR1 -> { + "bar1" + } + BAR2 -> { + // some comment 1 + // some comment 2 + "bar2" + } + else -> { + null + } + } + """.trimIndent() + @Suppress("ktlint:standard:argument-list-wrapping", "ktlint:standard:max-line-length") + whenEntryBracingRuleAssertThat(code) + .addAdditionalRuleProvider { + // Ensures that the first when entry is also wrapped to a multiline body + StatementWrappingRule() + }.hasLintViolations( + LintViolation(4, 17, "Body of when entry should be surrounded by braces if any when entry body is surrounded by braces or has a multiline body"), + LintViolation(7, 17, "Body of when entry should be surrounded by braces if any when entry body is surrounded by braces or has a multiline body"), + ).isFormattedAs(formattedCode) + } + + @Test + fun `Given a when-statement with a multiline body not contained in a block then add braces to all entries`() { + val code = + """ + val foo = + when (bar) { + BAR1 -> "bar1" + BAR2 -> + "bar2" + else -> null + } + """.trimIndent() + val formattedCode = + """ + val foo = + when (bar) { + BAR1 -> { + "bar1" + } + BAR2 -> { + "bar2" + } + else -> { + null + } + } + """.trimIndent() + @Suppress("ktlint:standard:argument-list-wrapping", "ktlint:standard:max-line-length") + whenEntryBracingRuleAssertThat(code) + .addAdditionalRuleProvider { + // Ensures that the first when entry is also wrapped to a multiline body + StatementWrappingRule() + }.hasLintViolations( + LintViolation(3, 17, "Body of when entry should be surrounded by braces if any when entry body is surrounded by braces or has a multiline body"), + LintViolation(5, 13, "Body of when entry should be surrounded by braces if any when entry body is surrounded by braces or has a multiline body"), + LintViolation(6, 17, "Body of when entry should be surrounded by braces if any when entry body is surrounded by braces or has a multiline body"), + ).isFormattedAs(formattedCode) + } +}