Skip to content

Commit

Permalink
Merge pull request #923 from Tapchicoma/701/cli
Browse files Browse the repository at this point in the history
Add 'generateEditorConfig' CLI subcommand.
  • Loading branch information
Tapchicoma committed Sep 28, 2020
2 parents 5a6e397 + 44f4b3d commit 38c6c4e
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 35 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Added
- Initial implementation IDE integration via '.editorconfig' based on rules default values ([#701](https://github.com/pinterest/ktlint/issues/701))
- CLI subcommand `generateEditorConfig` to generate '.editorconfig' content for Kotlin files ([#701](https://github.com/pinterest/ktlint/issues/701))

### Fixed
- ?
Expand Down
44 changes: 10 additions & 34 deletions ktlint/src/main/kotlin/com/pinterest/ktlint/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ import com.pinterest.ktlint.core.RuleExecutionException
import com.pinterest.ktlint.core.RuleSetProvider
import com.pinterest.ktlint.internal.ApplyToIDEAGloballySubCommand
import com.pinterest.ktlint.internal.ApplyToIDEAProjectSubCommand
import com.pinterest.ktlint.internal.GenerateEditorConfigSubCommand
import com.pinterest.ktlint.internal.GitPreCommitHookSubCommand
import com.pinterest.ktlint.internal.GitPrePushHookSubCommand
import com.pinterest.ktlint.internal.JarFiles
import com.pinterest.ktlint.internal.KtlintVersionProvider
import com.pinterest.ktlint.internal.PrintASTSubCommand
import com.pinterest.ktlint.internal.expandTilde
import com.pinterest.ktlint.internal.fileSequence
import com.pinterest.ktlint.internal.formatFile
import com.pinterest.ktlint.internal.lintFile
import com.pinterest.ktlint.internal.loadRulesets
import com.pinterest.ktlint.internal.location
import com.pinterest.ktlint.internal.printHelpOrVersionUsage
import com.pinterest.ktlint.internal.toFilesURIList
import com.pinterest.ktlint.reporter.plain.internal.Color
import java.io.File
import java.io.IOException
Expand Down Expand Up @@ -52,6 +55,7 @@ fun main(args: Array<String>) {
.addSubcommand(PrintASTSubCommand.COMMAND_NAME, PrintASTSubCommand())
.addSubcommand(ApplyToIDEAGloballySubCommand.COMMAND_NAME, ApplyToIDEAGloballySubCommand())
.addSubcommand(ApplyToIDEAProjectSubCommand.COMMAND_NAME, ApplyToIDEAProjectSubCommand())
.addSubcommand(GenerateEditorConfigSubCommand.COMMAND_NAME, GenerateEditorConfigSubCommand())
val parseResult = commandLine.parseArgs(*args)

commandLine.printHelpOrVersionUsage()
Expand All @@ -73,6 +77,7 @@ fun handleSubCommand(
is PrintASTSubCommand -> subCommand.run()
is ApplyToIDEAGloballySubCommand -> subCommand.run()
is ApplyToIDEAProjectSubCommand -> subCommand.run()
is GenerateEditorConfigSubCommand -> subCommand.run()
else -> commandLine.usage(System.out, CommandLine.Help.Ansi.OFF)
}
}
Expand Down Expand Up @@ -175,13 +180,13 @@ class KtlintCommandLine {
"To use a third-party reporter specify a path to a JAR file on the filesystem."
]
)
private var reporters = ArrayList<String>()
private var reporters: JarFiles = ArrayList<String>()

@Option(
names = ["--ruleset", "-R"],
description = ["A path to a JAR file containing additional ruleset(s)"]
)
private var rulesets = ArrayList<String>()
var rulesets: JarFiles = ArrayList<String>()

@Option(
names = ["--stdin"],
Expand All @@ -205,7 +210,7 @@ class KtlintCommandLine {
names = ["--experimental"],
description = ["Enabled experimental rules (ktlint-ruleset-experimental)"]
)
private var experimental: Boolean = false
var experimental: Boolean = false

@Parameters(hidden = true)
private var patterns = ArrayList<String>()
Expand All @@ -219,7 +224,7 @@ class KtlintCommandLine {

val start = System.currentTimeMillis()

val ruleSetProviders = loadRulesets(rulesets)
val ruleSetProviders = rulesets.loadRulesets(experimental, debug)
val reporter = loadReporter()
val userData = listOfNotNull(
"android" to android.toString(),
Expand Down Expand Up @@ -541,26 +546,6 @@ class KtlintCommandLine {
}
}

private fun loadRulesets(externalRulesetsJarPaths: List<String>) = ServiceLoader
.load(
RuleSetProvider::class.java,
URLClassLoader(externalRulesetsJarPaths.toFilesURIList().toTypedArray())
)
.associateBy {
val key = it.get().id
// standard should go first
if (key == "standard") "\u0000$key" else key
}
.filterKeys { experimental || it != "experimental" }
.toSortedMap()
.also {
if (debug) {
it.forEach { entry ->
println("[DEBUG] Discovered ruleset with \"${entry.key}\" id.")
}
}
}

private fun loadReporters(externalReportersJarPaths: List<String>) = ServiceLoader
.load(
ReporterProvider::class.java,
Expand All @@ -575,15 +560,6 @@ class KtlintCommandLine {
}
}

private fun List<String>.toFilesURIList() = map {
val jarFile = File(expandTilde(it))
if (!jarFile.exists()) {
println("Error: $it does not exist")
exitProcess(1)
}
jarFile.toURI().toURL()
}

private data class LintErrorWithCorrectionInfo(
val err: LintError,
val corrected: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.pinterest.ktlint.core.RuleSet
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.system.exitProcess

internal val workDir: String = File(".").canonicalPath

Expand All @@ -28,9 +29,23 @@ internal fun List<String>.fileSequence(): Sequence<File> {
.map(Path::toFile)
}

/**
* List of paths to Java `jar` files.
*/
internal typealias JarFiles = List<String>

internal fun JarFiles.toFilesURIList() = map {
val jarFile = File(expandTilde(it))
if (!jarFile.exists()) {
println("Error: $it does not exist")
exitProcess(1)
}
jarFile.toURI().toURL()
}

// a complete solution would be to implement https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html
// this implementation takes care only of the most commonly used case (~/)
internal fun expandTilde(path: String): String = path.replaceFirst(Regex("^~"), System.getProperty("user.home"))
private fun expandTilde(path: String): String = path.replaceFirst(Regex("^~"), System.getProperty("user.home"))

internal fun File.location(
relative: Boolean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.pinterest.ktlint.internal

import com.pinterest.ktlint.KtlintCommandLine
import com.pinterest.ktlint.core.KtLint
import com.pinterest.ktlint.core.api.FeatureInAlphaState
import picocli.CommandLine

@CommandLine.Command(
description = [
"EXPERIMENTAL!!! Generate kotlin style section for '.editorconfig' file.",
"Add output content into '.editorconfig' file"
],
mixinStandardHelpOptions = true,
versionProvider = KtlintVersionProvider::class
)
class GenerateEditorConfigSubCommand : Runnable {
@CommandLine.ParentCommand
private lateinit var ktlintCommand: KtlintCommandLine

@CommandLine.Spec
private lateinit var commandSpec: CommandLine.Model.CommandSpec

@OptIn(FeatureInAlphaState::class)
override fun run() {
commandSpec.commandLine().printHelpOrVersionUsage()

// For now we are using CLI invocation dir as path to load existing '.editorconfig'
val generatedEditorConfig = KtLint.generateKotlinEditorConfigSection(
KtLint.Params(
fileName = "./test.kt",
text = "",
ruleSets = ktlintCommand.rulesets
.loadRulesets(
ktlintCommand.experimental,
ktlintCommand.debug
)
.map { it.value.get() },
debug = ktlintCommand.debug,
cb = { _, _ -> Unit }
)
)

if (generatedEditorConfig.isNotBlank()) {
println(
"""
[*.{kt,kts}]
$generatedEditorConfig
""".trimIndent()
)
} else {
println("Nothing to add to .editorconfig file")
}
}

companion object {
internal const val COMMAND_NAME = "generateEditorConfig"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.pinterest.ktlint.internal

import com.pinterest.ktlint.core.RuleSetProvider
import java.net.URLClassLoader
import java.util.ServiceLoader

/**
* Load given list of paths to rulesets jars into map of ruleset providers.
*
* @return map of ruleset ids to ruleset providers
*/
internal fun JarFiles.loadRulesets(
loadExperimental: Boolean,
debug: Boolean
) = ServiceLoader
.load(
RuleSetProvider::class.java,
URLClassLoader(toFilesURIList().toTypedArray())
)
.associateBy {
val key = it.get().id
// standard should go first
if (key == "standard") "\u0000$key" else key
}
.filterKeys { loadExperimental || it != "experimental" }
.toSortedMap()
.also {
if (debug) {
it.forEach { entry ->
println("[DEBUG] Discovered ruleset with \"${entry.key}\" id.")
}
}
}

0 comments on commit 38c6c4e

Please sign in to comment.