Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add experimental SpacingAroundAngleBracketsRule #769

Merged
merged 10 commits into from
Jun 9, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class ExperimentalRuleSetProvider : RuleSetProvider {
EnumEntryNameCaseRule(),
SpacingAroundDoubleColonRule(),
SpacingBetweenDeclarationsWithCommentsRule(),
SpacingBetweenDeclarationsWithAnnotationsRule()
SpacingBetweenDeclarationsWithAnnotationsRule(),
SpacingAroundAngleBracketsRule()
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.pinterest.ktlint.ruleset.experimental

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.TYPE_ARGUMENT_LIST
import com.pinterest.ktlint.core.ast.ElementType.TYPE_PARAMETER_LIST
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.isWhiteSpaceWithoutNewline
import com.pinterest.ktlint.core.ast.nextLeaf
import com.pinterest.ktlint.core.ast.prevLeaf
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement

class SpacingAroundAngleBracketsRule : Rule("angle-brackets-rule") {
ymittal marked this conversation as resolved.
Show resolved Hide resolved

private fun String.trimBeforeLastLine() = this.substring(this.lastIndexOf('\n'))

override fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
) {
if (node.elementType.let { it == TYPE_PARAMETER_LIST || it == TYPE_ARGUMENT_LIST }) {
val openingBracket = node.firstChildNode
if (openingBracket != null) {
// Check for rogue spacing before an opening bracket: Map <String, Int>
val beforeLeftAngle = openingBracket.prevLeaf()
if (beforeLeftAngle?.elementType == WHITE_SPACE) {
ymittal marked this conversation as resolved.
Show resolved Hide resolved
emit(beforeLeftAngle.startOffset, "Unexpected spacing before \"<\"", true)
if (autoCorrect) {
beforeLeftAngle.treeParent.removeChild(beforeLeftAngle)
}
}

// Check for rogue spacing after an opening bracket
val afterLeftAngle = openingBracket.nextLeaf()
if (afterLeftAngle?.elementType == WHITE_SPACE) {
if (afterLeftAngle.isWhiteSpaceWithoutNewline()) {
// when spacing does not include any new lines, e.g. Map< String, Int>
emit(afterLeftAngle.startOffset, "Unexpected spacing after \"<\"", true)
if (autoCorrect) {
afterLeftAngle.treeParent.removeChild(afterLeftAngle)
}
} else {
// when spacing contains at least one new line, e.g.
// SomeGenericType<[whitespace]
//
// String, Int, String>
// gets converted to
// SomeGenericType<
// String, Int, String>
val newLineWithIndent = afterLeftAngle.text.trimBeforeLastLine()
if (autoCorrect) {
(afterLeftAngle as LeafElement).rawReplaceWithText(newLineWithIndent)
}
}
}
}

val closingBracket = node.lastChildNode
if (closingBracket != null) {
val beforeRightAngle = closingBracket.prevLeaf()
// Check for rogue spacing before a closing bracket
if (beforeRightAngle?.elementType == WHITE_SPACE) {
if (beforeRightAngle.isWhiteSpaceWithoutNewline()) {
// when spacing does not include any new lines, e.g. Map<String, Int >
emit(beforeRightAngle.startOffset, "Unexpected spacing before \">\"", true)
if (autoCorrect) {
beforeRightAngle.treeParent.removeChild(beforeRightAngle)
}
} else {
// when spacing contains at least one new line, e.g.
// SomeGenericType<String, Int, String[whitespace]
//
// >
// gets converted to
// SomeGenericType<String, Int, String
// >
val newLineWithIndent = beforeRightAngle.text.trimBeforeLastLine()
if (autoCorrect) {
(beforeRightAngle as LeafElement).rawReplaceWithText(newLineWithIndent)
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
package com.pinterest.ktlint.ruleset.standard
package com.pinterest.ktlint.ruleset.experimental

import com.pinterest.ktlint.test.diffFileFormat
import com.pinterest.ktlint.test.diffFileLint
import org.assertj.core.api.Assertions.assertThat
import org.junit.Ignore
import org.junit.Test

class SpacingAroundAngleBracketRuleTest {

@Test
@Ignore("https://github.com/pinterest/ktlint/issues/580")
fun testLint() {
assertThat(SpacingAroundRangeOperatorRule().diffFileLint("spec/angle-bracket-spacing/lint.kt.spec")).isEmpty()
assertThat(SpacingAroundAngleBracketsRule().diffFileLint("spec/angle-bracket-spacing/lint.kt.spec")).isEmpty()
}

@Test
@Ignore("https://github.com/pinterest/ktlint/issues/580")
fun testFormat() {
assertThat(
SpacingAroundRangeOperatorRule().diffFileFormat(
SpacingAroundAngleBracketsRule().diffFileFormat(
"spec/angle-bracket-spacing/format.kt.spec",
"spec/angle-bracket-spacing/format-expected.kt.spec"
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// TYPE_ARGUMENT_LIST
fun main() {
var a: Map<Int, String> = mapOf()
var b: Map<Int, String> = mapOf()
var c: Map<Int, String> = mapOf()

var d: Map<
Int, String
> = mapOf()

// Indentation would be fixed by another rule
var e: Map<
Int,
String
> = mapOf()

var nested: Map<
Int,
List<String>
> = mapOf()
}

// TYPE_PARAMETER_LIST
public class AngleTest<B : String> {
var a = 'str'
}

public class AngleTest<
B : String,
C : Map<
Int, List<String>
>
> {
var a = 'str'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// TYPE_ARGUMENT_LIST
fun main() {
var a: Map< Int, String> = mapOf()
var b: Map<Int, String > = mapOf()
var c: Map <Int, String> = mapOf()

var d: Map<


Int, String

> = mapOf()

// Indentation would be fixed by another rule
var e: Map<
Int,
String
> = mapOf()

var nested: Map<
Int,
List < String >
> = mapOf()
}

// TYPE_PARAMETER_LIST
public class AngleTest< B : String > {
var a = 'str'
}

public class AngleTest<

B : String,
C : Map <
Int, List< String >
>

> {
var a = 'str'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
fun main() {
var a: Map< Int, String> = mapOf()
var b: Map<Int, String > = mapOf()
var c: Map <Int, String> = mapOf()

var nested: Map<Int, List < String > > = mapOf()
}

public class AngleTest< B : String > {
var a = 'str'
}

// expect
// 2:14:Unexpected spacing after "<"
// 3:25:Unexpected spacing before ">"
// 4:13:Unexpected spacing before "<"
// 6:28:Unexpected spacing before "<"
// 6:30:Unexpected spacing after "<"
// 6:39:Unexpected spacing before ">"
// 6:41:Unexpected spacing before ">"
// 9:24:Unexpected spacing after "<"
// 9:35:Unexpected spacing before ">"

This file was deleted.

This file was deleted.

This file was deleted.