blob: 06dd72eea29e5c31d7cc4ef6cf6e8f318b7a23c0 [file] [log] [blame]
package com.github.shyiko.ktlint.ruleset.standard
import com.github.shyiko.ktlint.core.Rule
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet
import org.jetbrains.kotlin.com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.lexer.KtTokens.ANDAND
import org.jetbrains.kotlin.lexer.KtTokens.ARROW
import org.jetbrains.kotlin.lexer.KtTokens.DIV
import org.jetbrains.kotlin.lexer.KtTokens.DIVEQ
import org.jetbrains.kotlin.lexer.KtTokens.ELVIS
import org.jetbrains.kotlin.lexer.KtTokens.EQ
import org.jetbrains.kotlin.lexer.KtTokens.EQEQ
import org.jetbrains.kotlin.lexer.KtTokens.EQEQEQ
import org.jetbrains.kotlin.lexer.KtTokens.EXCLEQ
import org.jetbrains.kotlin.lexer.KtTokens.EXCLEQEQEQ
import org.jetbrains.kotlin.lexer.KtTokens.GT
import org.jetbrains.kotlin.lexer.KtTokens.GTEQ
import org.jetbrains.kotlin.lexer.KtTokens.LT
import org.jetbrains.kotlin.lexer.KtTokens.LTEQ
import org.jetbrains.kotlin.lexer.KtTokens.MINUS
import org.jetbrains.kotlin.lexer.KtTokens.MINUSEQ
import org.jetbrains.kotlin.lexer.KtTokens.MUL
import org.jetbrains.kotlin.lexer.KtTokens.MULTEQ
import org.jetbrains.kotlin.lexer.KtTokens.OROR
import org.jetbrains.kotlin.lexer.KtTokens.PERC
import org.jetbrains.kotlin.lexer.KtTokens.PERCEQ
import org.jetbrains.kotlin.lexer.KtTokens.PLUS
import org.jetbrains.kotlin.lexer.KtTokens.PLUSEQ
import org.jetbrains.kotlin.psi.KtImportDirective
import org.jetbrains.kotlin.psi.KtPrefixExpression
import org.jetbrains.kotlin.psi.KtSuperExpression
import org.jetbrains.kotlin.psi.KtTypeArgumentList
import org.jetbrains.kotlin.psi.KtTypeParameterList
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
class SpacingAroundOperatorsRule : Rule("op-spacing") {
private val tokenSet = TokenSet.create(MUL, PLUS, MINUS, DIV, PERC, LT, GT, LTEQ, GTEQ, EQEQEQ, EXCLEQEQEQ, EQEQ,
EXCLEQ, ANDAND, OROR, ELVIS, EQ, MULTEQ, DIVEQ, PERCEQ, PLUSEQ, MINUSEQ, ARROW)
override fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
) {
if (tokenSet.contains(node.elementType) && node is LeafPsiElement &&
!node.isPartOf(KtPrefixExpression::class) && // not unary
!node.isPartOf(KtTypeArgumentList::class) && // C<T>
!(node.elementType == MUL && node.isPartOf(KtValueArgument::class)) && // fn(*array)
!node.isPartOf(KtImportDirective::class) && // import *
!node.isPartOf(KtSuperExpression::class) // super<T>
) {
if ((node.elementType == GT || node.elementType == LT) &&
// fun <T>fn(): T {}
node.getNonStrictParentOfType(KtTypeParameterList::class.java)?.parent?.node?.elementType !=
KtStubElementTypes.FUNCTION) {
return
}
val spacingBefore = PsiTreeUtil.prevLeaf(node, true) is PsiWhiteSpace ||
node.elementType == GT
val spacingAfter = PsiTreeUtil.nextLeaf(node, true) is PsiWhiteSpace ||
node.elementType == LT
when {
!spacingBefore && !spacingAfter -> {
emit(node.startOffset, "Missing spacing around \"${node.text}\"", true)
if (autoCorrect) {
node.rawInsertBeforeMe(PsiWhiteSpaceImpl(" "))
node.rawInsertAfterMe(PsiWhiteSpaceImpl(" "))
}
}
!spacingBefore -> {
emit(node.startOffset, "Missing spacing before \"${node.text}\"", true)
if (autoCorrect) {
node.rawInsertBeforeMe(PsiWhiteSpaceImpl(" "))
}
}
!spacingAfter -> {
emit(node.startOffset + 1, "Missing spacing after \"${node.text}\"", true)
if (autoCorrect) {
node.rawInsertAfterMe(PsiWhiteSpaceImpl(" "))
}
}
}
}
}
}