diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dc2c0b..a818b04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [**Semantic Versioning v2.0.0**](https://semver.org/ * The hook will now also print the configured ktlint version * New `limit` configuration property for adding the `--limit=` flag to the ktlint invocation +* New `codeStyle` configuration property for adding the `--code-stye=` flag to the ktlint invocation ### Deprecated ### diff --git a/README.md b/README.md index c4f03d0..76c9328 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ ktlint { version.set("0.49.0") // set the version of ktlint // the following configuration properties are optional - android.set(true) // add the --android flag to ktlint + codeStyle.set(AndroidStudio) // add the --code-style=android_studio flag to ktlint limit.set(5) // add the --limit=5 flag to ktlint installGitPreCommitHookBeforeBuild.set(true) // automatically installs the hook every time before a build is started } @@ -105,7 +105,7 @@ ktlint { version.set("0.49.0") // set the version of ktlint // the following configuration properties are optional - android.set(true) // add the --android flag to ktlint + codeStyle.set(AndroidStudio) // add the --code-style=android_studio flag to ktlint limit.set(5) // add the --limit=5 flag to ktlint installGitPreCommitHookBeforeBuild.set(true) // automatically installs the hook every time before a build is started } @@ -130,7 +130,7 @@ ktlint { version = '0.49.0' // set the version of ktlint // the following configuration properties are optional - android = true // add the --android flag to ktlint + codeStyle = 'android_studio' // add the --code-style=android_studio flag to ktlint limit = 5 // add the --limit=5 flag to ktlint installGitPreCommitHookBeforeBuild = true // automatically installs the hook every time before a build is started } @@ -162,7 +162,7 @@ ktlint { version = '0.49.0' // set the version of ktlint // the following configuration properties are optional - android = true // add the --android flag to ktlint + codeStyle = 'android_studio' // add the --code-style=android_studio flag to ktlint limit = 5 // add the --limit=5 flag to ktlint installGitPreCommitHookBeforeBuild = true // automatically installs the hook every time before a build is started } @@ -175,7 +175,7 @@ ktlint { ## To-Do List ## * [x] Configuration property to add the `--android` flag to `ktlint` -* [ ] Configuration property to add the `--code-style=` flag to `ktlint` +* [x] Configuration property to add the `--code-style=` flag to `ktlint` * [ ] Automatically detecting Android projects and adding the `--android` / `--code-style=android_studio` flag if that is the case * [ ] Configuration property to add the `--disabled_rules=` flag to `ktlint` diff --git a/demo/build.gradle.kts b/demo/build.gradle.kts index 7ac51fc..f16009e 100644 --- a/demo/build.gradle.kts +++ b/demo/build.gradle.kts @@ -12,7 +12,7 @@ repositories { ktlint { version.set("0.49.0") - // android.set(true) + // codeStyle.set(AndroidStudio) // limit.set(5) installGitPreCommitHookBeforeBuild.set(true) } diff --git a/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/CodeStyle.kt b/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/CodeStyle.kt new file mode 100644 index 0000000..083153e --- /dev/null +++ b/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/CodeStyle.kt @@ -0,0 +1,8 @@ +package io.github.mfederczuk.gradle.plugin.ktlint + +internal sealed class CodeStyle { + + object Default : CodeStyle() + + data class Specific(val name: String) : CodeStyle() +} diff --git a/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintGitPreCommitHookInstallationTask.kt b/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintGitPreCommitHookInstallationTask.kt index 9d0bb3b..bd2075f 100644 --- a/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintGitPreCommitHookInstallationTask.kt +++ b/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintGitPreCommitHookInstallationTask.kt @@ -43,6 +43,9 @@ internal abstract class KtlintGitPreCommitHookInstallationTask : DefaultTask() { @get:Input abstract val taskName: Property + @get:Input + abstract val codeStyle: Property + @get:Input abstract val projectType: Property @@ -56,6 +59,7 @@ internal abstract class KtlintGitPreCommitHookInstallationTask : DefaultTask() { fun installKtlintGitPreCommitHook() { val ktlintClasspathJarFiles: Iterable = this.ktlintClasspathJarFiles.get() val taskName: String = this.taskName.get() + val codeStyle: CodeStyle = this.codeStyle.get() val projectType: ProjectType = this.projectType.get() val errorLimit: ErrorLimit = this.errorLimit.get() val ktlintVersion: SemVer = this.ktlintVersion.get() @@ -64,6 +68,7 @@ internal abstract class KtlintGitPreCommitHookInstallationTask : DefaultTask() { .loadHookScript( ktlintClasspathJarFiles.toList(), taskName, + codeStyle, projectType, errorLimit, ktlintVersion, @@ -83,6 +88,7 @@ internal abstract class KtlintGitPreCommitHookInstallationTask : DefaultTask() { private fun loadHookScript( ktlintClasspathJarFiles: List, taskName: String, + codeStyle: CodeStyle, projectType: ProjectType, errorLimit: ErrorLimit, ktlintVersion: SemVer, @@ -100,13 +106,16 @@ internal abstract class KtlintGitPreCommitHookInstallationTask : DefaultTask() { replace placeholder "HOOK_INSTALLATION_TASK_NAME" with taskName - replace placeholder "KTLINT_ANDROID_OPT_ARG" with when (projectType) { - ProjectType.OTHER -> "" - ProjectType.ANDROID -> { - if (ktlintVersion >= SemVer(0, 49, 0)) { - "--code-style=android_studio" - } else { - "--android" + replace placeholder "KTLINT_CODE_STYLE_OPT_ARG" with run { + if (ktlintVersion >= SemVer(0, 49, 0)) { + when (codeStyle) { + is CodeStyle.Default -> "" + is CodeStyle.Specific -> "--code-style=${codeStyle.name}" + } + } else { + when (projectType) { + ProjectType.OTHER -> "" + ProjectType.ANDROID -> "--android" } } } diff --git a/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintPlugin.kt b/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintPlugin.kt index 9714146..cd15a96 100644 --- a/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintPlugin.kt +++ b/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintPlugin.kt @@ -14,6 +14,7 @@ import org.gradle.api.Task import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ResolveException +import org.gradle.api.logging.Logger import org.gradle.api.plugins.ExtensionContainer import org.gradle.api.provider.Provider import org.gradle.kotlin.dsl.create @@ -39,30 +40,7 @@ public class KtlintPlugin : Plugin { override fun apply(project: Project) { val extension: KtlintPluginExtension = this.createExtension(extensionContainer = project.extensions) - val ktlintVersionProvider: Provider = extension.version - .map { versionString: String -> - val requestedKtlintVersion: SemVer? = SemVer.parseOrNull(versionString) - - if ((requestedKtlintVersion == null) && SemVer.isValid(versionString.removePrefix(prefix = "v"))) { - val msg: String = - "String \"$versionString\" is not a valid semantic version.\n" + - "Remove the leading 'v' character and " + - "use \"${versionString.removePrefix(prefix = "v")}\" instead" - error(msg) - } - - checkNotNull(requestedKtlintVersion) { - "String \"$versionString\" is not not a valid semantic version.\n" + - "Ensure that the version was correctly copied from https://github.com/pinterest/ktlint/releases" - } - - check(requestedKtlintVersion >= KTLINT_MIN_SUPPORTED_VERSION) { - "Configured ktlint version ($requestedKtlintVersion) is lower than " + - "minimum supported ktlint version $KTLINT_MIN_SUPPORTED_VERSION" - } - - requestedKtlintVersion - } + val ktlintVersionProvider: Provider = extension.checkedKtlintVersion val ktlintClasspathJarFilesProvider: Provider> = ktlintVersionProvider .map> { version: SemVer -> @@ -90,13 +68,14 @@ public class KtlintPlugin : Plugin { this.registerGitPreCommitHookInstallationTask( project, ktlintClasspathJarFilesProvider, + codeStyleProvider = extension.codeStyleAsDistinctType, projectTypeProvider, errorLimitProvider, ktlintVersionProvider, ) project.afterEvaluate { - this@KtlintPlugin.setupAutomaticGitPreCommitHookInstallation(project = this@afterEvaluate) + this@KtlintPlugin.afterEvaluate(project = this@afterEvaluate) } } @@ -133,6 +112,7 @@ public class KtlintPlugin : Plugin { private fun registerGitPreCommitHookInstallationTask( project: Project, ktlintClasspathJarFilesProvider: Provider>, + codeStyleProvider: Provider, projectTypeProvider: Provider, errorLimitProvider: Provider, ktlintVersionProvider: Provider, @@ -143,18 +123,68 @@ public class KtlintPlugin : Plugin { this@register.ktlintClasspathJarFiles.set(ktlintClasspathJarFilesProvider) this@register.taskName.set(KTLINT_GIT_PRE_COMMIT_HOOK_INSTALLATION_TASK_NAME) + this@register.codeStyle.set(codeStyleProvider) this@register.projectType.set(projectTypeProvider) this@register.errorLimit.set(errorLimitProvider) this@register.ktlintVersion.set(ktlintVersionProvider) } } - private fun setupAutomaticGitPreCommitHookInstallation(project: Project) { + // region afterEvaluate + + private fun afterEvaluate(project: Project) { val extension: KtlintPluginExtension? = project.extensions.findByType() checkNotNull(extension) { "Extension of type ${KtlintPluginExtension::class.java.name} not found in $project".internalErrorMsg } + this.checkCodeStyleProperties(logger = project.logger, extension) + + this.setupAutomaticGitPreCommitHookInstallation(project, extension) + } + + private fun checkCodeStyleProperties(logger: Logger, extension: KtlintPluginExtension) { + val ktlintVersion: SemVer = extension.checkedKtlintVersion.get() + val codeStyleProvider: Provider = extension.codeStyleAsDistinctType + + if (ktlintVersion < SemVer(0, 49, 0)) { + when (codeStyleProvider.get()) { + is CodeStyle.Default -> Unit + is CodeStyle.Specific -> { + val msg: String = + "The property `codeStyle` is only available for ktlint version 0.49.0 and above.\n" + + "Either bump the configured ktlint version up or use the property `android` instead" + incompatibleConfiguration(msg) + } + } + + return + } + + val isAndroidProject: Boolean = extension.android.get() + if (!isAndroidProject) { + return + } + + when (codeStyleProvider.get()) { + is CodeStyle.Default -> { + val msg: String = + "Since ktlint version 0.49.0 the --android flag is deprecated.\n" + + "Consider migrating to the --code-style flag. " + + "(Kotlin: `codeStyle.set(AndroidStudio)` / Groovy: `codeStyle = 'android_studio'`)" + logger.warn(msg) + } + + is CodeStyle.Specific -> { + val msg: String = + "Both properties `codeStyle` and `android` are set.\n" + + "Use either one or none, but not both" + incompatibleConfiguration(msg) + } + } + } + + private fun setupAutomaticGitPreCommitHookInstallation(project: Project, extension: KtlintPluginExtension) { val installGitPreCommitHookBeforeBuild: Boolean = extension.installGitPreCommitHookBeforeBuild.get() if (!installGitPreCommitHookBeforeBuild) { return @@ -177,4 +207,49 @@ public class KtlintPlugin : Plugin { targetTask.dependsOn(gitPreCommitHookInstallationTask) } + + // endregion + + // region extension extensions... yeah + + private val KtlintPluginExtension.checkedKtlintVersion: Provider + get() { + return this@checkedKtlintVersion.version + .map { versionString: String -> + val requestedKtlintVersion: SemVer? = SemVer.parseOrNull(versionString) + + if ((requestedKtlintVersion == null) && SemVer.isValid(versionString.removePrefix(prefix = "v"))) { + val msg: String = + "String \"$versionString\" is not a valid semantic version.\n" + + "Remove the leading 'v' character and " + + "use \"${versionString.removePrefix(prefix = "v")}\" instead" + error(msg) + } + + checkNotNull(requestedKtlintVersion) { + "String \"$versionString\" is not not a valid semantic version.\n" + + "Ensure that the version was correctly copied from https://github.com/pinterest/ktlint/releases" + } + + check(requestedKtlintVersion >= KTLINT_MIN_SUPPORTED_VERSION) { + "Configured ktlint version ($requestedKtlintVersion) is lower than " + + "minimum supported ktlint version $KTLINT_MIN_SUPPORTED_VERSION" + } + + requestedKtlintVersion + } + } + + private val KtlintPluginExtension.codeStyleAsDistinctType: Provider + get() { + return this@codeStyleAsDistinctType.codeStyle + .map(CodeStyle::Specific) + .orElse(CodeStyle.Default) + } + + // endregion + + private fun incompatibleConfiguration(msg: String): Nothing { + error("Incompatible configuration; $msg") + } } diff --git a/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintPluginExtension.kt b/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintPluginExtension.kt index 7d2c6ab..5dacea0 100644 --- a/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintPluginExtension.kt +++ b/plugin/src/main/java/io/github/mfederczuk/gradle/plugin/ktlint/KtlintPluginExtension.kt @@ -12,18 +12,63 @@ public interface KtlintPluginExtension { * The version of ktlint to use. * * This property doesn't have a default value. - * A version must be explicitly set, otherwise attempting to run tasks will fail. + * A version must be explicitly set, otherwise configuration will fail. */ public val version: Property // region ktlint CLI flags + // region --code-style= + + /** + * Code style "`ktlint_official`". + * + * @see codeStyle + */ + @Suppress("PropertyName") + public val KtlintOfficial: String get() = "ktlint_official" + + /** + * Code style "`intellij_idea`". + * + * @see codeStyle + */ + @Suppress("PropertyName") + public val IntellijIdea: String get() = "intellij_idea" + + /** + * Code style "`android_studio`". + * + * @see codeStyle + */ + @Suppress("PropertyName") + public val AndroidStudio: String get() = "android_studio" + + /** + * + * Adds the flag `--code-style=` to the ktlint invocation. + * + * This property requires that the [version] property be set to `0.49.0` or above, otherwise configuration will + * fail. + * + * The default is no explicit code style / whatever ktlint uses as the default. + * + * @see KtlintOfficial + * @see IntellijIdea + * @see AndroidStudio + */ + public val codeStyle: Property + + // endregion + /** * Whether or not this project is an android project. * * If set to `true`, prior to ktlint version `0.49.0`, then the flag `--android` will be added to the `ktlint` * invocation. For ktlint version `0.49.0` and onwards, the flag `--code-style=android_studio` is added instead. * + * This and the [codeStyle] property are mutually exclusive; If both are set, the configuration will fail. + * * The default value is `false`. */ @Deprecated( diff --git a/plugin/src/main/resources/io/github/mfederczuk/gradle/plugin/ktlint/git/hooks/other/pre-commit.template.sh b/plugin/src/main/resources/io/github/mfederczuk/gradle/plugin/ktlint/git/hooks/other/pre-commit.template.sh index cd0e1c6..209e45f 100644 --- a/plugin/src/main/resources/io/github/mfederczuk/gradle/plugin/ktlint/git/hooks/other/pre-commit.template.sh +++ b/plugin/src/main/resources/io/github/mfederczuk/gradle/plugin/ktlint/git/hooks/other/pre-commit.template.sh @@ -133,8 +133,8 @@ readonly using_intellij_idea_terminal #region running ktlint -ktlint_android_opt_arg=//KTLINT_ANDROID_OPT_ARG::quoted_string// -readonly ktlint_android_opt_arg +ktlint_code_style_opt_arg=//KTLINT_CODE_STYLE_OPT_ARG::quoted_string// +readonly ktlint_code_style_opt_arg ktlint_relative_opt_arg='--relative' if $using_intellij_idea_terminal; then @@ -154,7 +154,7 @@ printf 'Running ktlint (v%s)...\n' "$ktlint_version" >&2 exc=0 java -classpath "$ktlint_classpath" "$ktlint_main_class_name" \ - $ktlint_android_opt_arg \ + $ktlint_code_style_opt_arg \ $ktlint_relative_opt_arg \ $ktlint_limit_opt_arg \ --patterns-from-stdin='' < "$staged_kotlin_filename_list_file_pathname" || diff --git a/plugin/src/main/resources/io/github/mfederczuk/gradle/plugin/ktlint/git/hooks/windows/pre-commit.template.sh b/plugin/src/main/resources/io/github/mfederczuk/gradle/plugin/ktlint/git/hooks/windows/pre-commit.template.sh index 0ff3a08..ce837fc 100644 --- a/plugin/src/main/resources/io/github/mfederczuk/gradle/plugin/ktlint/git/hooks/windows/pre-commit.template.sh +++ b/plugin/src/main/resources/io/github/mfederczuk/gradle/plugin/ktlint/git/hooks/windows/pre-commit.template.sh @@ -152,8 +152,8 @@ readonly using_intellij_idea_terminal #region running ktlint -ktlint_android_opt_arg=//KTLINT_ANDROID_OPT_ARG::quoted_string// -readonly ktlint_android_opt_arg +ktlint_code_style_opt_arg=//KTLINT_CODE_STYLE_OPT_ARG::quoted_string// +readonly ktlint_code_style_opt_arg ktlint_relative_opt_arg='--relative' if $using_intellij_idea_terminal; then @@ -173,7 +173,7 @@ printf 'Running ktlint (v%s)...\n' "$ktlint_version" >&2 exc=0 java -classpath "$ktlint_classpath" "$ktlint_main_class_name" \ - $ktlint_android_opt_arg \ + $ktlint_code_style_opt_arg \ $ktlint_relative_opt_arg \ $ktlint_limit_opt_arg \ --patterns-from-stdin='' < "$staged_kotlin_filename_list_file_pathname" ||