Skip to content

Commit

Permalink
Replace picocli with clikt (#2556)
Browse files Browse the repository at this point in the history
Clikt is a kotlin library which allows more Kotlin native code. Also, it is blocking minifying the ktlint jar.

Closes #2550
  • Loading branch information
paul-dingemans authored Feb 24, 2024
1 parent 028c1eb commit cb17bbf
Show file tree
Hide file tree
Showing 12 changed files with 295 additions and 404 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ kotlinx-binary-compatibiltiy-validator = "org.jetbrains.kotlinx.binary-compatibi
kotlin-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler-embeddable", version.ref = "kotlin" }
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlin-plugin-dev = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinDev" }
clikt = "com.github.ajalt.clikt:clikt:4.2.2"
dokka = "org.jetbrains.dokka:dokka-gradle-plugin:1.9.10"
ec4j = "org.ec4j.core:ec4j-core:0.3.0"
picocli = "info.picocli:picocli:4.7.5"
logging = "io.github.oshai:kotlin-logging-jvm:6.0.3"
slf4j = "org.slf4j:slf4j-simple:2.0.12"
poko = "dev.drewhamilton.poko:poko-gradle-plugin:0.15.2"
Expand Down
2 changes: 1 addition & 1 deletion ktlint-cli/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ dependencies {
implementation(projects.ktlintCliReporterPlain)
implementation(projects.ktlintRuleEngine)
implementation(projects.ktlintRulesetStandard)
implementation(libs.picocli)
implementation(libs.clikt)
implementation(libs.logback)

runtimeOnly(projects.ktlintCliReporterCheckstyle)
Expand Down
42 changes: 7 additions & 35 deletions ktlint-cli/src/main/kotlin/com/pinterest/ktlint/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,20 @@

package com.pinterest.ktlint

import com.github.ajalt.clikt.core.subcommands
import com.pinterest.ktlint.cli.internal.GenerateEditorConfigSubCommand
import com.pinterest.ktlint.cli.internal.GitPreCommitHookSubCommand
import com.pinterest.ktlint.cli.internal.GitPrePushHookSubCommand
import com.pinterest.ktlint.cli.internal.KtlintCommandLine
import com.pinterest.ktlint.cli.internal.printCommandLineHelpOrVersionUsage
import picocli.CommandLine

// Ideally this file would have been moved to the cli package as well. This however is breaking change that is likely to affect each project
// that use either the Maven or Gradle and calls the Ktlint CLI. As those users likely will not read the changelog, this could lead to many
// issues. So the class is to be kept at the old location.
public fun main(args: Array<String>) {
val ktlintCommand = KtlintCommandLine()
val commandLine =
CommandLine(ktlintCommand)
.addSubcommand(GitPreCommitHookSubCommand.COMMAND_NAME, GitPreCommitHookSubCommand())
.addSubcommand(GitPrePushHookSubCommand.COMMAND_NAME, GitPrePushHookSubCommand())
.addSubcommand(GenerateEditorConfigSubCommand.COMMAND_NAME, GenerateEditorConfigSubCommand())
// Keep setUsageHelpAutoWidth after all addSubcommands
.setUsageHelpAutoWidth(true)
val parseResult = commandLine.parseArgs(*args)

// The logger needs to be configured for the ktlintCommand and all subcommands. The logger can however not be configured before the
// commandline has been parsed as otherwise the loglevel conversion is not yet executed.
ktlintCommand.configureLogger()

commandLine.printCommandLineHelpOrVersionUsage()

if (parseResult.hasSubcommand()) {
handleSubCommand(commandLine, parseResult)
} else {
ktlintCommand.run()
}
}

private fun handleSubCommand(
commandLine: CommandLine,
parseResult: CommandLine.ParseResult,
) {
when (val subCommand = parseResult.subcommand().commandSpec().userObject()) {
is GitPreCommitHookSubCommand -> subCommand.run()
is GitPrePushHookSubCommand -> subCommand.run()
is GenerateEditorConfigSubCommand -> subCommand.run()
else -> commandLine.usage(System.out, CommandLine.Help.Ansi.OFF)
}
KtlintCommandLine()
.subcommands(
GenerateEditorConfigSubCommand(),
GitPreCommitHookSubCommand(),
GitPrePushHookSubCommand(),
).main(args)
}
Original file line number Diff line number Diff line change
@@ -1,50 +1,36 @@
package com.pinterest.ktlint.cli.internal

import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.required
import com.github.ajalt.clikt.parameters.types.enum
import com.pinterest.ktlint.logger.api.initKtLintKLogger
import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride
import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CODE_STYLE_PROPERTY
import com.pinterest.ktlint.rule.engine.core.api.editorconfig.CodeStyleValue
import io.github.oshai.kotlinlogging.KotlinLogging
import picocli.CommandLine
import java.nio.file.Paths

private val LOGGER = KotlinLogging.logger {}.initKtLintKLogger()

@CommandLine.Command(
description = [
"Generate kotlin style section for '.editorconfig' file. Output should be copied manually to the '.editorconfig' file.",
],
mixinStandardHelpOptions = true,
versionProvider = KtlintVersionProvider::class,
)
internal class GenerateEditorConfigSubCommand : Runnable {
internal class GenerateEditorConfigSubCommand :
CliktCommand(
name = "generateEditorConfig",
help = "Generate kotlin style section for '.editorconfig' file. Output should be copied manually to the '.editorconfig' file.",
) {
// No default value is set as users should explicitly choose one of the code styles. In this way, it is more clear that the generated
// content is determined by the chosen value. If a default (ktlint_official) is set, and the user has not specified the code style, the
// user might not be aware that the value of the other properties are dependent on the code style.
@CommandLine.Parameters(
arity = "1",
paramLabel = "code-style",
description = [
"Code style to be used when generating the '.editorconfig'. Value should be one of 'ktlint_official' (recommended), " +
"'intellij_idea' or 'android_studio'.",
],
converter = [CodeStyleValueConverter::class],
)
var codeStyle: CodeStyleValue? = null

@CommandLine.ParentCommand
private lateinit var ktlintCommand: KtlintCommandLine

@CommandLine.Spec
private lateinit var commandSpec: CommandLine.Model.CommandSpec
private val codeStyle by
option("--code-style", help = "The code style affects the generated settings and their default values.")
.enum<CodeStyleValue>()
.required()

override fun run() {
commandSpec.commandLine().printCommandLineHelpOrVersionUsage()

val ktLintRuleEngine =
KtLintRuleEngine(
ruleProviders = ktlintCommand.ruleProviders(),
ruleProviders = (currentContext.parent?.command as KtlintCommandLine).ruleProviders,
editorConfigOverride = EditorConfigOverride.from(CODE_STYLE_PROPERTY to codeStyle),
isInvokedFromCli = true,
)
Expand All @@ -58,20 +44,4 @@ internal class GenerateEditorConfigSubCommand : Runnable {
LOGGER.info { "Nothing to add to .editorconfig file" }
}
}

internal companion object {
internal const val COMMAND_NAME = "generateEditorConfig"
}
}

private class CodeStyleValueConverter : CommandLine.ITypeConverter<CodeStyleValue> {
@Throws(Exception::class)
override fun convert(value: String?): CodeStyleValue =
when (value?.lowercase()?.replace("-", "_")) {
null -> CODE_STYLE_PROPERTY.defaultValue
"ktlint_official" -> CodeStyleValue.ktlint_official
"android_studio" -> CodeStyleValue.android_studio
"intellij_idea" -> CodeStyleValue.intellij_idea
else -> throw IllegalArgumentException("Invalid code style value")
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package com.pinterest.ktlint.cli.internal

import com.pinterest.ktlint.logger.api.initKtLintKLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import com.github.ajalt.clikt.core.CliktCommand
import java.io.File
import java.io.IOException
import java.math.BigInteger
import java.security.MessageDigest

private val LOGGER = KotlinLogging.logger {}.initKtLintKLogger()

private const val DEFAULT_GIT_DIR = ".git"
private const val DEFAULT_GIT_HOOKS_DIR = "hooks"

internal object GitHookInstaller {
internal abstract class GitHookCliktCommand(
name: String,
help: String,
) : CliktCommand(name = name, help = help) {
fun installGitHook(
gitHookName: String,
hookContentProvider: () -> ByteArray,
Expand All @@ -21,7 +21,7 @@ internal object GitHookInstaller {
try {
resolveGitHooksDir()
} catch (e: IOException) {
System.err.println(e.message)
echo(e.message, err = true)
exitKtLintProcess(1)
}
val gitHookFile = gitHooksDir.resolve(gitHookName)
Expand All @@ -33,11 +33,11 @@ internal object GitHookInstaller {

gitHookFile.writeBytes(hookContent)
gitHookFile.setExecutable(true)
LOGGER.info {
echo(
"${gitHookFile.path} is installed. Be aware that this hook assumes to find ktlint on the PATH. Either " +
"ensure that ktlint is actually added to the path or expand the ktlint command in the hook with the " +
"path."
}
"path.",
)
}

@Throws(IOException::class)
Expand Down Expand Up @@ -104,7 +104,7 @@ internal object GitHookInstaller {
!actualHookContent.contentEquals(expectedHookContent)
) {
val backupFile = hooksDir.resolve("$gitHookName.ktlint-backup.${actualHookContent.toUniqueId()}")
LOGGER.info { "Existing git hook ${hookFile.path} is copied to ${backupFile.path}" }
echo("Existing git hook ${hookFile.path} is copied to ${backupFile.path}")
hookFile.copyTo(backupFile, overwrite = true)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
package com.pinterest.ktlint.cli.internal

import picocli.CommandLine

@CommandLine.Command(
description = ["Install git hook to automatically check files for style violations on commit"],
mixinStandardHelpOptions = true,
versionProvider = KtlintVersionProvider::class,
)
internal class GitPreCommitHookSubCommand : Runnable {
@CommandLine.Spec
private lateinit var commandSpec: CommandLine.Model.CommandSpec

internal class GitPreCommitHookSubCommand :
GitHookCliktCommand(
name = "installGitPreCommitHook",
help = "Install git hook to automatically check files for style violations on commit",
) {
override fun run() {
commandSpec.commandLine().printCommandLineHelpOrVersionUsage()

GitHookInstaller.installGitHook("pre-commit") {
installGitHook(gitHookName = "pre-commit") {
"""
#!/bin/sh
Expand All @@ -24,8 +16,4 @@ internal class GitPreCommitHookSubCommand : Runnable {
""".trimIndent().toByteArray()
}
}

internal companion object {
internal const val COMMAND_NAME = "installGitPreCommitHook"
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
package com.pinterest.ktlint.cli.internal

import picocli.CommandLine

@CommandLine.Command(
description = ["Install git hook to automatically check files for style violations before push"],
mixinStandardHelpOptions = true,
versionProvider = KtlintVersionProvider::class,
)
internal class GitPrePushHookSubCommand : Runnable {
@CommandLine.Spec
private lateinit var commandSpec: CommandLine.Model.CommandSpec

internal class GitPrePushHookSubCommand :
GitHookCliktCommand(
name = "installGitPrePushHook",
help = "Install git hook to automatically check files for style violations before push",
) {
override fun run() {
commandSpec.commandLine().printCommandLineHelpOrVersionUsage()

GitHookInstaller.installGitHook("pre-push") {
installGitHook(gitHookName = "pre-push") {
"""
#!/bin/sh
Expand All @@ -24,8 +16,4 @@ internal class GitPrePushHookSubCommand : Runnable {
""".trimIndent().toByteArray()
}
}

internal companion object {
internal const val COMMAND_NAME = "installGitPrePushHook"
}
}
Loading

0 comments on commit cb17bbf

Please sign in to comment.