From 16332f6b5b5382b992672a9e78de8ba68b89b2eb Mon Sep 17 00:00:00 2001 From: Jean-Michel Fayard Date: Tue, 10 Sep 2019 20:43:18 +0200 Subject: [PATCH] Resolves #58 Detect indent from EditorConfig file https://github.com/jmfayard/buildSrcVersions/issues/58 --- plugin/build.gradle.kts | 1 - .../kotlin/de/fayard/BuildSrcVersionsTask.kt | 3 + .../src/main/kotlin/de/fayard/EditorConfig.kt | 100 ++++++++++++++++++ .../src/main/kotlin/de/fayard/PluginConfig.kt | 2 +- .../test/kotlin/de/fayard/EditorConfigTest.kt | 93 ++++++++++++++++ plugin/src/test/resources/.editorconfig | 3 + plugin/src/test/resources/a/.editorconfig | 7 ++ plugin/src/test/resources/a/b/.editorconfig | 11 ++ plugin/src/test/resources/a/b/c/.editorconfig | 3 + plugin/src/test/resources/maven/.editorconfig | 5 + .../buildSrc/src/main/kotlin/Libs.kt | 12 +-- .../buildSrc/src/main/kotlin/Versions.kt | 10 +- sample-kotlin/build.gradle.kts | 2 +- .../buildSrc/src/main/kotlin/Libs.kt | 34 +++--- .../buildSrc/src/main/kotlin/Versions.kt | 22 ++-- sample-versionsOnlyMode/KOTLIN_OBJECT.kt | 22 ++-- 16 files changed, 277 insertions(+), 53 deletions(-) create mode 100644 plugin/src/main/kotlin/de/fayard/EditorConfig.kt create mode 100644 plugin/src/test/kotlin/de/fayard/EditorConfigTest.kt create mode 100644 plugin/src/test/resources/.editorconfig create mode 100644 plugin/src/test/resources/a/.editorconfig create mode 100644 plugin/src/test/resources/a/b/.editorconfig create mode 100644 plugin/src/test/resources/a/b/c/.editorconfig create mode 100644 plugin/src/test/resources/maven/.editorconfig diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 1770e5187..9ad95c088 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -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") diff --git a/plugin/src/main/kotlin/de/fayard/BuildSrcVersionsTask.kt b/plugin/src/main/kotlin/de/fayard/BuildSrcVersionsTask.kt index 6404434c7..043fb70da 100644 --- a/plugin/src/main/kotlin/de/fayard/BuildSrcVersionsTask.kt +++ b/plugin/src/main/kotlin/de/fayard/BuildSrcVersionsTask.kt @@ -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} diff --git a/plugin/src/main/kotlin/de/fayard/EditorConfig.kt b/plugin/src/main/kotlin/de/fayard/EditorConfig.kt new file mode 100644 index 000000000..0766475f5 --- /dev/null +++ b/plugin/src/main/kotlin/de/fayard/EditorConfig.kt @@ -0,0 +1,100 @@ +package de.fayard + +import java.io.File + +data class Section(val name: String, val lines: MutableList>) { + 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
.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
{ + assert(name == NAME) + assert(canRead()) + val result = mutableListOf
() + 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 { + val result = mutableListOf() + 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 + } +} diff --git a/plugin/src/main/kotlin/de/fayard/PluginConfig.kt b/plugin/src/main/kotlin/de/fayard/PluginConfig.kt index 4cded1d1e..e3901eb3f 100644 --- a/plugin/src/main/kotlin/de/fayard/PluginConfig.kt +++ b/plugin/src/main/kotlin/de/fayard/PluginConfig.kt @@ -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 **/ diff --git a/plugin/src/test/kotlin/de/fayard/EditorConfigTest.kt b/plugin/src/test/kotlin/de/fayard/EditorConfigTest.kt new file mode 100644 index 000000000..6db7e79a9 --- /dev/null +++ b/plugin/src/test/kotlin/de/fayard/EditorConfigTest.kt @@ -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 + } + } +}) diff --git a/plugin/src/test/resources/.editorconfig b/plugin/src/test/resources/.editorconfig new file mode 100644 index 000000000..76a93c065 --- /dev/null +++ b/plugin/src/test/resources/.editorconfig @@ -0,0 +1,3 @@ +[*] +indent_style = space +indent_size = 2 diff --git a/plugin/src/test/resources/a/.editorconfig b/plugin/src/test/resources/a/.editorconfig new file mode 100644 index 000000000..616935606 --- /dev/null +++ b/plugin/src/test/resources/a/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*.{java,kt,kts}] +indent_style = space ; comment +indent_size = 3 # comment + +# comment diff --git a/plugin/src/test/resources/a/b/.editorconfig b/plugin/src/test/resources/a/b/.editorconfig new file mode 100644 index 000000000..d50dc8a52 --- /dev/null +++ b/plugin/src/test/resources/a/b/.editorconfig @@ -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 + + + diff --git a/plugin/src/test/resources/a/b/c/.editorconfig b/plugin/src/test/resources/a/b/c/.editorconfig new file mode 100644 index 000000000..1b502f72b --- /dev/null +++ b/plugin/src/test/resources/a/b/c/.editorconfig @@ -0,0 +1,3 @@ +[*.java] +indent_style = space +indent_size = 5 diff --git a/plugin/src/test/resources/maven/.editorconfig b/plugin/src/test/resources/maven/.editorconfig new file mode 100644 index 000000000..3e835cafe --- /dev/null +++ b/plugin/src/test/resources/maven/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*.py] +indent_style = space +indent_size = 2 diff --git a/sample-groovy/buildSrc/src/main/kotlin/Libs.kt b/sample-groovy/buildSrc/src/main/kotlin/Libs.kt index de6cbf592..da7d31b87 100644 --- a/sample-groovy/buildSrc/src/main/kotlin/Libs.kt +++ b/sample-groovy/buildSrc/src/main/kotlin/Libs.kt @@ -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 } diff --git a/sample-groovy/buildSrc/src/main/kotlin/Versions.kt b/sample-groovy/buildSrc/src/main/kotlin/Versions.kt index dfa90a9f7..0e9f7bfe2 100644 --- a/sample-groovy/buildSrc/src/main/kotlin/Versions.kt +++ b/sample-groovy/buildSrc/src/main/kotlin/Versions.kt @@ -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" /** * diff --git a/sample-kotlin/build.gradle.kts b/sample-kotlin/build.gradle.kts index 0ec53854b..3e8c5d843 100644 --- a/sample-kotlin/build.gradle.kts +++ b/sample-kotlin/build.gradle.kts @@ -32,7 +32,7 @@ buildSrcVersions { useFqdnFor("dependency") renameLibs = "Libs" renameVersions = "Versions" - indent = " " +// indent = " " rejectVersionIf { isNonStable(candidate.version) && isNonStable(currentVersion).not() } diff --git a/sample-kotlin/buildSrc/src/main/kotlin/Libs.kt b/sample-kotlin/buildSrc/src/main/kotlin/Libs.kt index 2996da380..e6b767396 100644 --- a/sample-kotlin/buildSrc/src/main/kotlin/Libs.kt +++ b/sample-kotlin/buildSrc/src/main/kotlin/Libs.kt @@ -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 } diff --git a/sample-kotlin/buildSrc/src/main/kotlin/Versions.kt b/sample-kotlin/buildSrc/src/main/kotlin/Versions.kt index 94e03da89..f3786de56 100644 --- a/sample-kotlin/buildSrc/src/main/kotlin/Versions.kt +++ b/sample-kotlin/buildSrc/src/main/kotlin/Versions.kt @@ -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" } diff --git a/sample-versionsOnlyMode/KOTLIN_OBJECT.kt b/sample-versionsOnlyMode/KOTLIN_OBJECT.kt index f6a944bf0..bdb137036 100644 --- a/sample-versionsOnlyMode/KOTLIN_OBJECT.kt +++ b/sample-versionsOnlyMode/KOTLIN_OBJECT.kt @@ -10,20 +10,20 @@ import kotlin.String * YOU are responsible for updating manually the dependency version. */ object Versions { - const val org_jetbrains_kotlin_jvm_gradle_plugin: String = "1.3.50" + const val org_jetbrains_kotlin_jvm_gradle_plugin: String = "1.3.50" - const val org_jetbrains_kotlin: String = "1.3.50" + const val org_jetbrains_kotlin: String = "1.3.50" - const val okhttp: String = "4.1.0" // available: "4.1.1" + const val okhttp: String = "4.1.0" // available: "4.1.1" - const val okio: String = "2.0.0" + const val okio: String = "2.0.0" - /** - * - * See issue 19: How to update Gradle itself? - * https://github.com/jmfayard/buildSrcVersions/issues/19 - */ - const val gradleLatestVersion: String = "5.6.1" + /** + * + * See issue 19: How to update Gradle itself? + * https://github.com/jmfayard/buildSrcVersions/issues/19 + */ + const val gradleLatestVersion: String = "5.6.1" - const val gradleCurrentVersion: String = "5.5.1" + const val gradleCurrentVersion: String = "5.5.1" }