Skip to content

Commit

Permalink
Resolves #58 Detect indent from EditorConfig file
Browse files Browse the repository at this point in the history
  • Loading branch information
Jean-Michel Fayard committed Sep 10, 2019
1 parent 47ef29f commit 16332f6
Show file tree
Hide file tree
Showing 16 changed files with 277 additions and 53 deletions.
1 change: 0 additions & 1 deletion plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ dependencies {
testImplementation("io.kotlintest:kotlintest-runner-junit5:3.1.9")

implementation("com.github.ben-manes:gradle-versions-plugin:0.25.0")

implementation("com.squareup.okio:okio:2.1.0")
implementation( "com.squareup.moshi:moshi:1.7.0")
implementation("com.squareup:kotlinpoet:1.3.0")
Expand Down
3 changes: 3 additions & 0 deletions plugin/src/main/kotlin/de/fayard/BuildSrcVersionsTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ open class BuildSrcVersionsTask : DefaultTask() {
@TaskAction
fun taskAction() {
val extension : BuildSrcVersionsExtension = extension ?: project.extensions.getByType()
if (extension.indent == PluginConfig.DEFAULT_INDENT) {
extension.indent = EditorConfig.findIndentForKotlin(project.file("buildSrc/src/main/kotlin")) ?: " "
}
println("""
|Plugin configuration: $extension
|See documentation at ${PluginConfig.issue53PluginConfiguration}
Expand Down
100 changes: 100 additions & 0 deletions plugin/src/main/kotlin/de/fayard/EditorConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package de.fayard

import java.io.File

data class Section(val name: String, val lines: MutableList<Pair<String, String>>) {
operator fun get(key: String): String? {
return lines.firstOrNull { it.first == key }?.second
}
}

object EditorConfig {
const val NAME = ".editorconfig"
const val INDENT_STYLE = "indent_style"
const val INDENT_SIZE = "indent_size"
const val SPACE = "space"
const val TAB = "tab"

fun findIndentForKotlin(fromDir: File) : String? {
return findEditorConfig(fromDir)
.flatMap { file -> file.parseSections() }
.findIndentForKotlinFiles()
}

fun List<Section>.findIndentForKotlinFiles() : String? {
val section = this.sortedByDescending { it.priority() }
.filter { s ->
s[INDENT_STYLE] != null && s.priority() > 0
}.firstOrNull()
return section?.findIndent()
}

fun Section.findIndent() : String? {
val size = this[INDENT_SIZE]?.toIntOrNull()
val style = this[INDENT_STYLE]
return when {
style == TAB -> "\t"
style == SPACE && size != null -> List(size) { " " }.joinToString(separator = "")
else -> null
}
}


fun File.isRootEditorConfig() : Boolean = when {
name != NAME -> false
this.exists().not() -> false
else -> readLines().any { it.replace(" ", "").contains("root=true") }
}

fun Section.priority() = when {
name.contains("kt") -> 4
name.contains("gradle") -> 3
name.contains("java") -> 2
name == "*" -> 1
else -> -1
}

fun File.parseSections(): List<Section> {
assert(name == NAME)
assert(canRead())
val result = mutableListOf<Section>()
var currentSection: Section? = null
readLines().map { line ->
line.substringBefore("#").substringBefore(";")
}.forEach { line ->
val section = line.parseSection()
val split = line.split("=").map { it.trim() }
when {
section != null -> {
currentSection = Section(section, mutableListOf())
result.add(currentSection!!)
}
currentSection == null || line.isBlank() || split.size != 2 -> { }
else -> currentSection!!.lines.add(split.first() to split.last())
}
}
return result
}

fun String.parseSection() : String? {
val section = this.substringAfter("[", "").substringBefore("]", "").trim()
return if (section.isBlank()) null else section
}

fun findEditorConfig(fromDir: File) : List<File> {
val result = mutableListOf<File>()
var current: File? = fromDir
while (current != null) {
val ec = current.resolve(NAME)
if (ec.exists()) {
assert(ec.name == NAME)
result += ec
}
if (ec.isRootEditorConfig()) {
break
}
current = current.parentFile
}
return result
}
}
2 changes: 1 addition & 1 deletion plugin/src/main/kotlin/de/fayard/PluginConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ object PluginConfig {

const val DEFAULT_LIBS = "Libs"
const val DEFAULT_VERSIONS = "Versions"
const val DEFAULT_INDENT = " "
const val DEFAULT_INDENT = "from-editorconfig-file"
const val BENMANES_REPORT_PATH = "build/dependencyUpdates/report.json"

/** Documentation **/
Expand Down
93 changes: 93 additions & 0 deletions plugin/src/test/kotlin/de/fayard/EditorConfigTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package de.fayard

import io.kotlintest.matchers.haveSize
import io.kotlintest.matchers.withClue
import io.kotlintest.should
import io.kotlintest.shouldBe
import io.kotlintest.specs.FreeSpec
import io.kotlintest.tables.forAll
import io.kotlintest.tables.headers
import io.kotlintest.tables.row
import io.kotlintest.tables.table
import java.io.File



class EditorConfigTest: FreeSpec({
val testFolder = File("src/test/resources").absoluteFile

"Project Dir" {
withClue(testFolder.absolutePath) {
testFolder.name shouldBe "resources"
}
}

"Find files" {
val table = table(
headers("path", "expected size"),
row("a/b/c", 3),
row("a/b", 2),
row("a", 1),
row("libs", 2)
)

table.forAll { path, expectedSize ->
val list = EditorConfig.findEditorConfig(testFolder.resolve(path))
withClue(list.toString()) { list should haveSize(expectedSize) }
}
}

"Sections" {
with(EditorConfig) {
val sections = testResourceFile("a/b/.editorconfig").parseSections()
withClue(sections.toString()) {
sections.size shouldBe 2
val (kotlin, python) = sections
python shouldBe Section("*.py", mutableListOf("indent_style" to "tab", "insert_final_newline" to "true"))
kotlin shouldBe Section("*.kt", mutableListOf("indent_style" to "space", "indent_size" to "4"))
kotlin[INDENT_STYLE] shouldBe "space"
kotlin[INDENT_SIZE] shouldBe "4"
kotlin["insert_final_newline"] shouldBe null
}
}
}

with(EditorConfig) {
"Parse Indents" - {
"Tabs" {
val section = Section("*", mutableListOf(INDENT_STYLE to TAB, "key" to "value"))
section.findIndent() shouldBe "\t"
}
"Four spaces" {
val section = Section("*", mutableListOf(INDENT_STYLE to SPACE, "key" to "value", INDENT_SIZE to "4"))
section.findIndent() shouldBe " "
}
"Two spaces" {
val section = Section("*", mutableListOf(INDENT_STYLE to SPACE, "key" to "value", INDENT_SIZE to "2"))
section.findIndent() shouldBe " "
}
"space no size: invalid" {
val section = Section("*", mutableListOf(INDENT_STYLE to SPACE, "key" to "value"))
section.findIndent() shouldBe null
}
"no information" {
val section = Section("*", mutableListOf( "key" to "value"))
section.findIndent() shouldBe null
}
}
}

"Find indentation for Kotlin" {
val table = table(
headers("path", "indentation"),
row("libs", " "),
row("a", " "),
row("a/b", " "),
row("a/b/c", " "),
row("maven", null)
)
table.forAll { path, expectedIndent ->
EditorConfig.findIndentForKotlin(testFolder.resolve(path)) shouldBe expectedIndent
}
}
})
3 changes: 3 additions & 0 deletions plugin/src/test/resources/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[*]
indent_style = space
indent_size = 2
7 changes: 7 additions & 0 deletions plugin/src/test/resources/a/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
root = true

[*.{java,kt,kts}]
indent_style = space ; comment
indent_size = 3 # comment

# comment
11 changes: 11 additions & 0 deletions plugin/src/test/resources/a/b/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[*.kt]
indent_style = space
indent_size = 4 # 2 is better

# 4 space indentation
[*.py]
indent_style = tab
insert_final_newline = true



3 changes: 3 additions & 0 deletions plugin/src/test/resources/a/b/c/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[*.java]
indent_style = space
indent_size = 5
5 changes: 5 additions & 0 deletions plugin/src/test/resources/maven/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
root = true

[*.py]
indent_style = space
indent_size = 2
12 changes: 6 additions & 6 deletions sample-groovy/buildSrc/src/main/kotlin/Libs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import kotlin.String
* `$ ./gradlew buildSrcVersions`
*/
object Libs {
const val guava: String = "com.google.guava:guava:" + Versions.guava

const val guice: String = "com.google.inject:guice:" + Versions.guice

const val io_vertx_vertx_plugin_gradle_plugin: String =
"io.vertx.vertx-plugin:io.vertx.vertx-plugin.gradle.plugin:" +
Versions.io_vertx_vertx_plugin_gradle_plugin

const val vertx_stack_depchain: String = "io.vertx:vertx-stack-depchain:" +
Versions.vertx_stack_depchain

const val vertx_core: String = "io.vertx:vertx-core"

const val guava: String = "com.google.guava:guava:" + Versions.guava

const val guice: String = "com.google.inject:guice:" + Versions.guice
const val vertx_stack_depchain: String = "io.vertx:vertx-stack-depchain:" +
Versions.vertx_stack_depchain
}
10 changes: 5 additions & 5 deletions sample-groovy/buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import kotlin.String
* YOU are responsible for updating manually the dependency version.
*/
object Versions {
const val io_vertx_vertx_plugin_gradle_plugin: String = "0.3.1"
const val guava: String = "15.0"

const val vertx_stack_depchain: String = "3.6.2"
const val guice: String = "2.0"

const val vertx_core: String = "none"// No version. See buildSrcVersions#23
const val io_vertx_vertx_plugin_gradle_plugin: String = "0.3.1"

const val guava: String = "15.0"
const val vertx_core: String = "none"// No version. See buildSrcVersions#23

const val guice: String = "2.0"
const val vertx_stack_depchain: String = "3.6.2"

/**
*
Expand Down
2 changes: 1 addition & 1 deletion sample-kotlin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ buildSrcVersions {
useFqdnFor("dependency")
renameLibs = "Libs"
renameVersions = "Versions"
indent = " "
// indent = " "
rejectVersionIf {
isNonStable(candidate.version) && isNonStable(currentVersion).not()
}
Expand Down
34 changes: 17 additions & 17 deletions sample-kotlin/buildSrc/src/main/kotlin/Libs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,24 @@ import kotlin.String
* `$ ./gradlew buildSrcVersions`
*/
object Libs {
/**
* https://github.com/google/guava
*/
const val guava: String = "com.google.guava:guava:" + Versions.guava
/**
* https://github.com/google/guava
*/
const val guava: String = "com.google.guava:guava:" + Versions.guava

/**
* https://github.com/google/guice
*/
const val guice: String = "com.google.inject:guice:" + Versions.guice
/**
* https://github.com/google/guice
*/
const val guice: String = "com.google.inject:guice:" + Versions.guice

const val org_jetbrains_kotlin_jvm_gradle_plugin: String =
"org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:" +
Versions.org_jetbrains_kotlin_jvm_gradle_plugin
const val org_jetbrains_kotlin_jvm_gradle_plugin: String =
"org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:" +
Versions.org_jetbrains_kotlin_jvm_gradle_plugin

/**
* https://kotlinlang.org/
*/
const val kotlin_scripting_compiler_embeddable: String =
"org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:" +
Versions.kotlin_scripting_compiler_embeddable
/**
* https://kotlinlang.org/
*/
const val kotlin_scripting_compiler_embeddable: String =
"org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:" +
Versions.kotlin_scripting_compiler_embeddable
}
22 changes: 11 additions & 11 deletions sample-kotlin/buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ import kotlin.String
* YOU are responsible for updating manually the dependency version.
*/
object Versions {
const val guava: String = "15.0" // available: "23.0"
const val guava: String = "15.0" // available: "23.0"

const val guice: String = "2.0" // available: "4.2.2"
const val guice: String = "2.0" // available: "4.2.2"

const val org_jetbrains_kotlin_jvm_gradle_plugin: String = "1.3.11" // available: "1.3.50"
const val org_jetbrains_kotlin_jvm_gradle_plugin: String = "1.3.11" // available: "1.3.50"

const val kotlin_scripting_compiler_embeddable: String = "1.3.11" // available: "1.3.50"
const val kotlin_scripting_compiler_embeddable: String = "1.3.11" // available: "1.3.50"

/**
*
* See issue 19: How to update Gradle itself?
* https://github.com/jmfayard/buildSrcVersions/issues/19
*/
const val gradleLatestVersion: String = "5.6.2"
/**
*
* See issue 19: How to update Gradle itself?
* https://github.com/jmfayard/buildSrcVersions/issues/19
*/
const val gradleLatestVersion: String = "5.6.2"

const val gradleCurrentVersion: String = "5.6.2"
const val gradleCurrentVersion: String = "5.6.2"
}
Loading

0 comments on commit 16332f6

Please sign in to comment.