Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'generateEditorConfig' CLI subcommand. #923

Merged
merged 3 commits into from
Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
41 changes: 8 additions & 33 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,18 @@ 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.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 +54,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 +76,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 @@ -181,7 +185,7 @@ class KtlintCommandLine {
names = ["--ruleset", "-R"],
description = ["A path to a JAR file containing additional ruleset(s)"]
)
private var rulesets = ArrayList<String>()
var rulesets = ArrayList<String>()

@Option(
names = ["--stdin"],
Expand All @@ -205,7 +209,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 +223,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 +545,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 +559,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,18 @@ internal fun List<String>.fileSequence(): Sequence<File> {
.map(Path::toFile)
}

internal fun List<String>.toFilesURIList() = map {
Tapchicoma marked this conversation as resolved.
Show resolved Hide resolved
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 List<String>.loadRulesets(
loadExperimental: Boolean,
debug: Boolean
) = ServiceLoader
.load(
RuleSetProvider::class.java,
URLClassLoader(this.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.")
}
}
}