Skip to content

Commit

Permalink
Reused 'DiktatProcessor' for inline fix and check (#1657)
Browse files Browse the repository at this point in the history
  • Loading branch information
nulls authored Apr 6, 2023
1 parent 0fd74b5 commit 4a9b71f
Show file tree
Hide file tree
Showing 44 changed files with 600 additions and 393 deletions.
27 changes: 27 additions & 0 deletions diktat-api/src/main/kotlin/org/cqfn/diktat/DiktatProcessor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,38 @@ interface DiktatProcessor {
*/
fun fix(file: Path, callback: DiktatCallback): String

/**
* Run `diktat fix` on provided [code] using [callback] for detected errors and returned formatted code.
*
* @param code
* @param isScript
* @param callback
* @return result of `diktat fix`
*/
fun fix(
code: String,
isScript: Boolean,
callback: DiktatCallback,
): String

/**
* Run `diktat check` on provided [file] using [callback] for detected errors.
*
* @param file
* @param callback
*/
fun check(file: Path, callback: DiktatCallback)

/**
* Run `diktat check` on provided [code] using [callback] for detected errors.
*
* @param code
* @param isScript
* @param callback
*/
fun check(
code: String,
isScript: Boolean,
callback: DiktatCallback,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,86 @@ import org.cqfn.diktat.DiktatProcessor
import org.cqfn.diktat.DiktatProcessorFactory
import org.cqfn.diktat.api.DiktatCallback
import org.cqfn.diktat.api.DiktatRuleSet
import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.wrap
import org.cqfn.diktat.ktlint.KtLintRuleSetWrapper.Companion.toKtLint
import org.cqfn.diktat.util.isKotlinScript

import com.pinterest.ktlint.core.KtLint
import com.pinterest.ktlint.core.LintError
import com.pinterest.ktlint.core.api.EditorConfigOverride

import java.nio.charset.StandardCharsets
import java.nio.file.Path

import kotlin.io.path.absolutePathString
import kotlin.io.path.readText

private typealias KtLintCallback = (LintError, Boolean) -> Unit

/**
* A factory to create [DiktatProcessor] using [DiktatProcessorFactory] and `KtLint` as engine
*/
class DiktatProcessorFactoryImpl : DiktatProcessorFactory {
override fun invoke(diktatRuleSet: DiktatRuleSet): DiktatProcessor = object : DiktatProcessor {
override fun fix(file: Path, callback: DiktatCallback): String = KtLint.format(ktLintParams(diktatRuleSet, file, callback.unwrap()))
override fun check(file: Path, callback: DiktatCallback) = KtLint.lint(ktLintParams(diktatRuleSet, file, callback.unwrap()))
override fun fix(file: Path, callback: DiktatCallback): String = KtLint.format(file.toKtLintParams(diktatRuleSet, callback))
override fun fix(
code: String,
isScript: Boolean,
callback: DiktatCallback
): String = KtLint.format(code.toKtLintParams(isScript, diktatRuleSet, callback))
override fun check(file: Path, callback: DiktatCallback) = KtLint.lint(file.toKtLintParams(diktatRuleSet, callback))
override fun check(
code: String,
isScript: Boolean,
callback: DiktatCallback
) = KtLint.lint(code.toKtLintParams(isScript, diktatRuleSet, callback))
}

private fun ktLintParams(
diktatRuleSet: DiktatRuleSet,
file: Path,
callback: LintErrorCallback,
): KtLint.ExperimentalParams = KtLint.ExperimentalParams(
fileName = file.absolutePathString(),
text = file.readText(StandardCharsets.UTF_8),
ruleSets = setOf(diktatRuleSet.toKtLint()),
userData = emptyMap(),
cb = callback,
script = file.isKotlinScript(),
editorConfigPath = null,
debug = false, // we do not use it
editorConfigOverride = EditorConfigOverride.emptyEditorConfigOverride,
isInvokedFromCli = false
)
companion object {
private fun Path.toKtLintParams(
diktatRuleSet: DiktatRuleSet,
callback: DiktatCallback,
): KtLint.ExperimentalParams = ktLintParams(
fileName = absolutePathString(),
text = readText(StandardCharsets.UTF_8).replace("\r\n", "\n").replace("\r", "\n"),
isScript = isKotlinScript(),
diktatRuleSet = diktatRuleSet,
callback = callback,
)

private fun String.toKtLintParams(
isScript: Boolean,
diktatRuleSet: DiktatRuleSet,
callback: DiktatCallback,
): KtLint.ExperimentalParams = ktLintParams(
fileName = if (isScript) "test.kts" else "test.kt",
text = this,
isScript = isScript,
diktatRuleSet = diktatRuleSet,
callback = callback,
)

private fun ktLintParams(
fileName: String,
text: String,
isScript: Boolean,
diktatRuleSet: DiktatRuleSet,
callback: DiktatCallback,
): KtLint.ExperimentalParams = KtLint.ExperimentalParams(
fileName = fileName,
text = text,
ruleSets = setOf(diktatRuleSet.toKtLint()),
userData = emptyMap(),
cb = callback.toKtLint(),
script = isScript,
editorConfigPath = null,
debug = false, // we do not use it
editorConfigOverride = EditorConfigOverride.emptyEditorConfigOverride,
isInvokedFromCli = false
)

private fun DiktatCallback.toKtLint(): KtLintCallback = { error, isCorrected ->
this(error.wrap(), isCorrected)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,12 @@ package org.cqfn.diktat.ktlint
import org.cqfn.diktat.api.DiktatCallback
import org.cqfn.diktat.api.DiktatRuleSet
import org.cqfn.diktat.common.config.rules.DIKTAT_RULE_SET_ID
import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.unwrap
import org.cqfn.diktat.ktlint.DiktatErrorImpl.Companion.wrap
import org.cqfn.diktat.ktlint.KtLintRuleSetWrapper.Companion.toKtLint
import com.pinterest.ktlint.core.KtLint
import com.pinterest.ktlint.core.KtLint.ExperimentalParams
import com.pinterest.ktlint.core.LintError
import mu.KotlinLogging
import org.intellij.lang.annotations.Language
import java.nio.file.Path
import kotlin.io.path.invariantSeparatorsPathString
import kotlin.io.path.relativeTo

private val log = KotlinLogging.logger { }

val defaultCallback = DiktatCallback { error, _ ->
log.warn { "Received linting error: $error" }
}

typealias LintErrorCallback = (LintError, Boolean) -> Unit

/**
* Makes sure this _rule id_ is qualified with a _rule set id_.
*
Expand All @@ -47,92 +33,73 @@ fun String.qualifiedWithRuleSetId(ruleSetId: String = DIKTAT_RULE_SET_ID): Strin
*/
fun Path.relativePathStringTo(sourceRootDir: Path): String = relativeTo(sourceRootDir).invariantSeparatorsPathString

/**
* @return [DiktatCallback] from KtLint [LintErrorCallback]
*/
fun LintErrorCallback.wrap(): DiktatCallback = DiktatCallback { error, isCorrected ->
this(error.unwrap(), isCorrected)
}

/**
* @return KtLint [LintErrorCallback] from [DiktatCallback] or exception
*/
fun DiktatCallback.unwrap(): LintErrorCallback = { error, isCorrected ->
this(error.wrap(), isCorrected)
}

/**
* Enables ignoring autocorrected errors when in "fix" mode (i.e. when
* [KtLint.format] is invoked).
* [com.pinterest.ktlint.core.KtLint.format] is invoked).
*
* Before version 0.47, _Ktlint_ only reported non-corrected errors in "fix"
* mode.
* Now, this has changed.
*
* @receiver the instance of _Ktlint_ parameters.
* @return the instance with the [callback][ExperimentalParams.cb] modified in
* such a way that it ignores corrected errors.
* @see KtLint.format
* @see ExperimentalParams.cb
* @return the instance [DiktatCallback] that ignores corrected errors.
* @see com.pinterest.ktlint.core.KtLint.format
* @since 1.2.4
*/
private fun ExperimentalParams.ignoreCorrectedErrors(): ExperimentalParams =
copy(cb = { error: LintError, corrected: Boolean ->
if (!corrected) {
cb(error, false)
}
})
private fun DiktatCallback.ignoreCorrectedErrors(): DiktatCallback = DiktatCallback { error, isCorrected ->
if (!isCorrected) {
this@ignoreCorrectedErrors(error, false)
}
}

/**
* @param ruleSetSupplier
* @param text
* @param fileName
* @param file
* @param cb callback to be called on unhandled [LintError]s
* @return formatted code
*/
@Suppress("LAMBDA_IS_NOT_LAST_PARAMETER")
fun format(
ruleSetSupplier: () -> DiktatRuleSet,
@Language("kotlin") text: String,
fileName: String,
cb: DiktatCallback = defaultCallback
): String {
val ruleSets = listOf(ruleSetSupplier().toKtLint())
return KtLint.format(
ExperimentalParams(
text = text,
ruleSets = ruleSets,
fileName = fileName.removeSuffix("_copy"),
script = fileName.removeSuffix("_copy").endsWith("kts"),
cb = cb.unwrap(),
debug = true,
).ignoreCorrectedErrors()
file: Path,
cb: DiktatCallback,
): String = DiktatProcessorFactoryImpl().invoke(ruleSetSupplier())
.fix(
file = file,
callback = cb.ignoreCorrectedErrors(),
)

/**
* @param ruleSetSupplier
* @param file
* @param cb callback to be called on unhandled [LintError]s
* @return formatted code
*/
@Suppress("LAMBDA_IS_NOT_LAST_PARAMETER")
fun lint(
ruleSetSupplier: () -> DiktatRuleSet,
file: Path,
cb: DiktatCallback = DiktatCallback.empty
) = DiktatProcessorFactoryImpl().invoke(ruleSetSupplier())
.check(
file = file,
callback = cb.ignoreCorrectedErrors(),
)
}

/**
* @param ruleSetSupplier
* @param text
* @param fileName
* @param cb callback to be called on unhandled [LintError]s
* @return formatted code
*/
@Suppress("LAMBDA_IS_NOT_LAST_PARAMETER")
fun lint(
ruleSetSupplier: () -> DiktatRuleSet,
@Language("kotlin") text: String,
fileName: String = "test.ks",
cb: DiktatCallback = DiktatCallback.empty
) {
val ruleSets = listOf(ruleSetSupplier().toKtLint())
KtLint.lint(
ExperimentalParams(
text = text,
ruleSets = ruleSets,
fileName = fileName.removeSuffix("_copy"),
script = fileName.removeSuffix("_copy").endsWith("kts"),
cb = cb.unwrap(),
debug = true,
).ignoreCorrectedErrors()
) = DiktatProcessorFactoryImpl().invoke(ruleSetSupplier())
.check(
code = text,
isScript = false,
callback = cb.ignoreCorrectedErrors(),
)
}
1 change: 1 addition & 0 deletions diktat-rules/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
implementation(libs.kotlin.logging)
testImplementation(projects.diktatTestFramework)
testImplementation(projects.diktatKtlintEngine)
testImplementation(libs.log4j2.slf4j)
testImplementation(libs.junit.jupiter)
testImplementation(libs.junit.platform.suite)
testImplementation(libs.assertj.core)
Expand Down
Loading

0 comments on commit 4a9b71f

Please sign in to comment.