diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt index d1e2d17d459..88e561fcd0a 100644 --- a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt +++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/KotlinCommonBlock.kt @@ -19,6 +19,8 @@ import com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.KtNodeTypes.* import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings import org.jetbrains.kotlin.idea.formatter.NodeIndentStrategy.Companion.strategy +import org.jetbrains.kotlin.idea.formatter.trailingComma.addTrailingCommaIsAllowedFor +import org.jetbrains.kotlin.idea.formatter.trailingComma.needTrailingComma import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.kdoc.lexer.KDocTokens import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaHelper.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/TrailingCommaHelper.kt similarity index 97% rename from idea/formatter/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaHelper.kt rename to idea/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/TrailingCommaHelper.kt index 99f55c7d4b0..ce3ea35a6e7 100644 --- a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaHelper.kt +++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/TrailingCommaHelper.kt @@ -3,13 +3,14 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package org.jetbrains.kotlin.idea.formatter +package org.jetbrains.kotlin.idea.formatter.trailingComma import com.intellij.psi.PsiElement import com.intellij.psi.PsiErrorElement import com.intellij.psi.codeStyle.CodeStyleSettings import com.intellij.psi.tree.TokenSet import com.intellij.psi.util.PsiUtil +import org.jetbrains.kotlin.idea.formatter.kotlinCustomSettings import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaState.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/TrailingCommaState.kt similarity index 96% rename from idea/formatter/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaState.kt rename to idea/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/TrailingCommaState.kt index 0e6e5425409..ec6086d71a8 100644 --- a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaState.kt +++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/TrailingCommaState.kt @@ -3,10 +3,9 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package org.jetbrains.kotlin.idea.formatter +package org.jetbrains.kotlin.idea.formatter.trailingComma import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.idea.util.canAddTrailingComma import org.jetbrains.kotlin.idea.util.containsLineBreakInThis import org.jetbrains.kotlin.idea.util.isMultiline import org.jetbrains.kotlin.psi.KtDestructuringDeclaration diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/util.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/util.kt new file mode 100644 index 00000000000..c9c22cb3149 --- /dev/null +++ b/idea/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/util.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.formatter.trailingComma + +import com.intellij.lang.ASTNode +import com.intellij.openapi.util.UserDataHolder +import com.intellij.openapi.util.registry.Registry +import com.intellij.psi.PsiElement +import com.intellij.psi.codeStyle.CodeStyleSettings +import com.intellij.psi.tree.IElementType +import com.intellij.psi.tree.TokenSet +import com.intellij.psi.util.PsiUtilCore +import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings +import org.jetbrains.kotlin.idea.formatter.kotlinCustomSettings +import org.jetbrains.kotlin.idea.util.containsLineBreakInThis +import org.jetbrains.kotlin.psi.KtDestructuringDeclaration +import org.jetbrains.kotlin.psi.KtFunctionLiteral +import org.jetbrains.kotlin.psi.KtWhenEntry +import org.jetbrains.kotlin.psi.KtWhenExpression +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.utils.addToStdlib.cast + +fun KtFunctionLiteral.needTrailingComma(settings: CodeStyleSettings?, checkExistingTrailingComma: Boolean = true): Boolean = + needTrailingComma( + settings = settings, + trailingComma = { if (checkExistingTrailingComma) valueParameterList?.trailingComma else null }, + globalStartOffset = { valueParameterList?.startOffset }, + globalEndOffset = { arrow?.endOffset }, + ) + +fun KtWhenEntry.needTrailingComma(settings: CodeStyleSettings?, checkExistingTrailingComma: Boolean = true): Boolean = needTrailingComma( + settings = settings, + trailingComma = { if (checkExistingTrailingComma) trailingComma else null }, + additionalCheck = { !isElse && parent.cast().leftParenthesis != null }, + globalEndOffset = { arrow?.endOffset }, +) + +fun KtDestructuringDeclaration.needTrailingComma(settings: CodeStyleSettings?, checkExistingTrailingComma: Boolean = true): Boolean = + needTrailingComma( + settings = settings, + trailingComma = { if (checkExistingTrailingComma) trailingComma else null }, + globalStartOffset = { lPar?.startOffset }, + globalEndOffset = { rPar?.endOffset }, + ) + +private fun T.needTrailingComma( + settings: CodeStyleSettings?, + trailingComma: T.() -> PsiElement?, + additionalCheck: () -> Boolean = { true }, + globalStartOffset: T.() -> Int? = PsiElement::startOffset, + globalEndOffset: T.() -> Int? = PsiElement::endOffset, +): Boolean { + if (trailingComma() == null && settings?.kotlinCustomSettings?.addTrailingCommaIsAllowedFor(this) == false) return false + if (!additionalCheck()) return false + + val startOffset = globalStartOffset() ?: return false + val endOffset = globalEndOffset() ?: return false + return containsLineBreakInThis(startOffset, endOffset) +} + +fun trailingCommaIsAllowedOnCallSite(): Boolean = Registry.`is`("kotlin.formatter.allowTrailingCommaOnCallSite") + +private val TYPES_WITH_TRAILING_COMMA_ON_DECLARATION_SITE = TokenSet.create( + KtNodeTypes.TYPE_PARAMETER_LIST, + KtNodeTypes.DESTRUCTURING_DECLARATION, + KtNodeTypes.WHEN_ENTRY, + KtNodeTypes.FUNCTION_LITERAL, + KtNodeTypes.VALUE_PARAMETER_LIST, +) + +private val TYPES_WITH_TRAILING_COMMA_ON_CALL_SITE = TokenSet.create( + KtNodeTypes.COLLECTION_LITERAL_EXPRESSION, + KtNodeTypes.TYPE_ARGUMENT_LIST, + KtNodeTypes.INDICES, + KtNodeTypes.VALUE_ARGUMENT_LIST, +) + +private val TYPES_WITH_TRAILING_COMMA = TokenSet.orSet( + TYPES_WITH_TRAILING_COMMA_ON_DECLARATION_SITE, + TYPES_WITH_TRAILING_COMMA_ON_CALL_SITE, +) + +fun UserDataHolder.addTrailingCommaIsAllowedForThis(): Boolean { + val type = elementType(this) ?: return false + return type in TYPES_WITH_TRAILING_COMMA_ON_DECLARATION_SITE || + trailingCommaIsAllowedOnCallSite() && type in TYPES_WITH_TRAILING_COMMA_ON_CALL_SITE +} + +fun KotlinCodeStyleSettings.addTrailingCommaIsAllowedFor(element: UserDataHolder): Boolean = + ALLOW_TRAILING_COMMA && element.addTrailingCommaIsAllowedForThis() + +private fun elementType(userDataHolder: UserDataHolder): IElementType? = when (userDataHolder) { + is ASTNode -> PsiUtilCore.getElementType(userDataHolder) + is PsiElement -> PsiUtilCore.getElementType(userDataHolder) + else -> null +} + +fun ASTNode.canAddTrailingComma(): Boolean = psi?.canAddTrailingComma() == true +fun PsiElement.canAddTrailingComma(): Boolean = + if (this is KtWhenEntry && (isElse || parent.cast().leftParenthesis == null)) + false + else + canAddTrailingComma(this) + +private fun canAddTrailingComma(userDataHolder: UserDataHolder): Boolean = elementType(userDataHolder) in TYPES_WITH_TRAILING_COMMA \ No newline at end of file diff --git a/idea/formatter/src/org/jetbrains/kotlin/idea/util/FormatterUtil.kt b/idea/formatter/src/org/jetbrains/kotlin/idea/util/FormatterUtil.kt index 7983003421f..83fb6daa87d 100644 --- a/idea/formatter/src/org/jetbrains/kotlin/idea/util/FormatterUtil.kt +++ b/idea/formatter/src/org/jetbrains/kotlin/idea/util/FormatterUtil.kt @@ -6,33 +6,18 @@ package org.jetbrains.kotlin.idea.util import com.intellij.formatting.ASTBlock -import com.intellij.lang.ASTNode import com.intellij.openapi.util.TextRange -import com.intellij.openapi.util.UserDataHolder -import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiComment import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement import com.intellij.psi.PsiWhiteSpace -import com.intellij.psi.codeStyle.CodeStyleSettings -import com.intellij.psi.tree.IElementType -import com.intellij.psi.tree.TokenSet import com.intellij.psi.util.PsiUtil -import com.intellij.psi.util.PsiUtilCore -import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings -import org.jetbrains.kotlin.idea.formatter.kotlinCustomSettings import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.KtDestructuringDeclaration -import org.jetbrains.kotlin.psi.KtFunctionLiteral -import org.jetbrains.kotlin.psi.KtWhenEntry -import org.jetbrains.kotlin.psi.KtWhenExpression -import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.nextLeaf import org.jetbrains.kotlin.psi.psiUtil.prevLeaf import org.jetbrains.kotlin.psi.psiUtil.startOffset -import org.jetbrains.kotlin.utils.addToStdlib.cast /* * ASTBlock.node is nullable, this extension was introduced to minimize changes @@ -76,92 +61,7 @@ fun PsiElement.leaf(forward: Boolean = true, filter: (PsiElement) -> Boolean): P val PsiElement.isComma: Boolean get() = PsiUtil.getElementType(this) == KtTokens.COMMA -fun KtFunctionLiteral.needTrailingComma(settings: CodeStyleSettings?, checkExistingTrailingComma: Boolean = true): Boolean = - needTrailingComma( - settings = settings, - trailingComma = { if (checkExistingTrailingComma) valueParameterList?.trailingComma else null }, - globalStartOffset = { valueParameterList?.startOffset }, - globalEndOffset = { arrow?.endOffset }, - ) - -fun KtWhenEntry.needTrailingComma(settings: CodeStyleSettings?, checkExistingTrailingComma: Boolean = true): Boolean = needTrailingComma( - settings = settings, - trailingComma = { if (checkExistingTrailingComma) trailingComma else null }, - additionalCheck = { !isElse && parent.cast().leftParenthesis != null }, - globalEndOffset = { arrow?.endOffset }, -) - -fun KtDestructuringDeclaration.needTrailingComma(settings: CodeStyleSettings?, checkExistingTrailingComma: Boolean = true): Boolean = - needTrailingComma( - settings = settings, - trailingComma = { if (checkExistingTrailingComma) trailingComma else null }, - globalStartOffset = { lPar?.startOffset }, - globalEndOffset = { rPar?.endOffset }, - ) - -private fun T.needTrailingComma( - settings: CodeStyleSettings?, - trailingComma: T.() -> PsiElement?, - additionalCheck: () -> Boolean = { true }, - globalStartOffset: T.() -> Int? = PsiElement::startOffset, - globalEndOffset: T.() -> Int? = PsiElement::endOffset, -): Boolean { - if (trailingComma() == null && settings?.kotlinCustomSettings?.addTrailingCommaIsAllowedFor(this) == false) return false - if (!additionalCheck()) return false - - val startOffset = globalStartOffset() ?: return false - val endOffset = globalEndOffset() ?: return false - return containsLineBreakInThis(startOffset, endOffset) -} - fun PsiElement.containsLineBreakInThis(globalStartOffset: Int, globalEndOffset: Int): Boolean { val textRange = TextRange.create(globalStartOffset, globalEndOffset).shiftLeft(startOffset) return StringUtil.containsLineBreak(textRange.subSequence(text)) } - -fun trailingCommaIsAllowedOnCallSite(): Boolean = Registry.`is`("kotlin.formatter.allowTrailingCommaOnCallSite") - -private val TYPES_WITH_TRAILING_COMMA_ON_DECLARATION_SITE = TokenSet.create( - KtNodeTypes.TYPE_PARAMETER_LIST, - KtNodeTypes.DESTRUCTURING_DECLARATION, - KtNodeTypes.WHEN_ENTRY, - KtNodeTypes.FUNCTION_LITERAL, - KtNodeTypes.VALUE_PARAMETER_LIST, -) - -private val TYPES_WITH_TRAILING_COMMA_ON_CALL_SITE = TokenSet.create( - KtNodeTypes.COLLECTION_LITERAL_EXPRESSION, - KtNodeTypes.TYPE_ARGUMENT_LIST, - KtNodeTypes.INDICES, - KtNodeTypes.VALUE_ARGUMENT_LIST, -) - -private val TYPES_WITH_TRAILING_COMMA = TokenSet.orSet( - TYPES_WITH_TRAILING_COMMA_ON_DECLARATION_SITE, - TYPES_WITH_TRAILING_COMMA_ON_CALL_SITE, -) - -fun UserDataHolder.addTrailingCommaIsAllowedForThis(): Boolean { - val type = elementType(this) ?: return false - return type in TYPES_WITH_TRAILING_COMMA_ON_DECLARATION_SITE || - trailingCommaIsAllowedOnCallSite() && type in TYPES_WITH_TRAILING_COMMA_ON_CALL_SITE -} - -fun KotlinCodeStyleSettings.addTrailingCommaIsAllowedFor(element: UserDataHolder): Boolean = - ALLOW_TRAILING_COMMA && element.addTrailingCommaIsAllowedForThis() - -private fun elementType(userDataHolder: UserDataHolder): IElementType? = when (userDataHolder) { - is ASTNode -> PsiUtilCore.getElementType(userDataHolder) - is PsiElement -> PsiUtilCore.getElementType(userDataHolder) - else -> null -} - -fun ASTNode.canAddTrailingComma(): Boolean = psi?.canAddTrailingComma() == true - -fun PsiElement.canAddTrailingComma(): Boolean = - if (this is KtWhenEntry && (isElse || parent.cast().leftParenthesis == null)) - false - else - canAddTrailingComma(this) - -private fun canAddTrailingComma(userDataHolder: UserDataHolder): Boolean = elementType(userDataHolder) in TYPES_WITH_TRAILING_COMMA \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInsight/upDownMover/KotlinExpressionMover.kt b/idea/src/org/jetbrains/kotlin/idea/codeInsight/upDownMover/KotlinExpressionMover.kt index 2f1c6edf566..770ba97436e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInsight/upDownMover/KotlinExpressionMover.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInsight/upDownMover/KotlinExpressionMover.kt @@ -11,7 +11,7 @@ import com.intellij.openapi.editor.Editor import com.intellij.psi.* import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.idea.core.util.isMultiLine -import org.jetbrains.kotlin.idea.formatter.TrailingCommaHelper +import org.jetbrains.kotlin.idea.formatter.trailingComma.TrailingCommaHelper import org.jetbrains.kotlin.idea.util.isComma import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaPostFormatProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaPostFormatProcessor.kt index 2ce66da61e8..9f443219d39 100644 --- a/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaPostFormatProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaPostFormatProcessor.kt @@ -17,9 +17,9 @@ import com.intellij.psi.codeStyle.CodeStyleSettings import com.intellij.psi.impl.source.codeStyle.PostFormatProcessor import com.intellij.psi.impl.source.codeStyle.PostFormatProcessorHelper import com.intellij.psi.util.PsiUtil.getElementType -import org.jetbrains.kotlin.idea.formatter.TrailingCommaHelper.findInvalidCommas -import org.jetbrains.kotlin.idea.formatter.TrailingCommaHelper.needComma -import org.jetbrains.kotlin.idea.formatter.TrailingCommaHelper.trailingCommaOrLastElement +import org.jetbrains.kotlin.idea.formatter.trailingComma.TrailingCommaHelper.findInvalidCommas +import org.jetbrains.kotlin.idea.formatter.trailingComma.TrailingCommaHelper.needComma +import org.jetbrains.kotlin.idea.formatter.trailingComma.TrailingCommaHelper.trailingCommaOrLastElement import org.jetbrains.kotlin.idea.util.leafIgnoringWhitespace import org.jetbrains.kotlin.idea.util.leafIgnoringWhitespaceAndComments import org.jetbrains.kotlin.lexer.KtTokens diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaVisitor.kt b/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaVisitor.kt index 4ae3b883949..757f3891345 100644 --- a/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaVisitor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaVisitor.kt @@ -7,7 +7,7 @@ package org.jetbrains.kotlin.idea.formatter import com.intellij.openapi.progress.ProgressIndicatorProvider import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.idea.util.addTrailingCommaIsAllowedForThis +import org.jetbrains.kotlin.idea.formatter.trailingComma.addTrailingCommaIsAllowedForThis import org.jetbrains.kotlin.psi.* abstract class TrailingCommaVisitor : KtTreeVisitorVoid() { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/TrailingCommaInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/TrailingCommaInspection.kt index 16f5a72a789..eb52716edc1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/TrailingCommaInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/TrailingCommaInspection.kt @@ -18,7 +18,7 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor import com.intellij.psi.codeStyle.CodeStyleManager import org.jetbrains.kotlin.idea.KotlinBundle -import org.jetbrains.kotlin.idea.formatter.TrailingCommaHelper +import org.jetbrains.kotlin.idea.formatter.trailingComma.TrailingCommaHelper import org.jetbrains.kotlin.idea.formatter.TrailingCommaVisitor import org.jetbrains.kotlin.idea.formatter.kotlinCustomSettings import org.jetbrains.kotlin.idea.formatter.trailingCommaAllowedInModule diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/TrailingCommaIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/TrailingCommaIntention.kt index 23795311cf5..22eb5898d72 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/TrailingCommaIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/TrailingCommaIntention.kt @@ -10,7 +10,7 @@ import com.intellij.codeInsight.intention.LowPriorityAction import com.intellij.openapi.editor.Editor import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.formatter.kotlinCustomSettings -import org.jetbrains.kotlin.idea.util.addTrailingCommaIsAllowedForThis +import org.jetbrains.kotlin.idea.formatter.trailingComma.addTrailingCommaIsAllowedForThis import org.jetbrains.kotlin.psi.KtElement class TrailingCommaIntention : SelfTargetingIntention( diff --git a/idea/src/org/jetbrains/kotlin/idea/joinLines/JoinWithTrailingCommaHandler.kt b/idea/src/org/jetbrains/kotlin/idea/joinLines/JoinWithTrailingCommaHandler.kt index c96e2d6805f..c7bd538a094 100644 --- a/idea/src/org/jetbrains/kotlin/idea/joinLines/JoinWithTrailingCommaHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/joinLines/JoinWithTrailingCommaHandler.kt @@ -11,8 +11,8 @@ import com.intellij.openapi.editor.Document import com.intellij.psi.PsiFile import com.intellij.psi.codeStyle.CodeStyleManager import org.jetbrains.kotlin.idea.core.util.containsLineBreakInRange -import org.jetbrains.kotlin.idea.formatter.TrailingCommaHelper -import org.jetbrains.kotlin.idea.util.addTrailingCommaIsAllowedForThis +import org.jetbrains.kotlin.idea.formatter.trailingComma.TrailingCommaHelper +import org.jetbrains.kotlin.idea.formatter.trailingComma.addTrailingCommaIsAllowedForThis import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf