From 4a6e02d98ab30514e47aee8f1454f19a8a86cec9 Mon Sep 17 00:00:00 2001 From: Peter Trifanov Date: Sun, 19 Dec 2021 14:31:36 +0300 Subject: [PATCH] ci: Add detekt static analyzer (#18) * Add detekt static analyzer * Enable detekt-formatting * Include in `check` task --- .github/workflows/build_and_test.yml | 2 + build.gradle.kts | 14 +++++ detekt.yml | 9 +++ gradle/libs.versions.toml | 3 + .../io/github/petertrr/diffutils/DiffUtils.kt | 27 +++++--- .../petertrr/diffutils/algorithm/Change.kt | 11 ++-- .../diffutils/algorithm/myers/MyersDiff.kt | 20 +++--- .../diffutils/algorithm/myers/PathNode.kt | 2 +- .../github/petertrr/diffutils/patch/Chunk.kt | 2 +- .../github/petertrr/diffutils/patch/Patch.kt | 14 ++--- .../diffutils/text/DiffRowGenerator.kt | 62 +++++++++++-------- .../petertrr/diffutils/text/StringUtils.kt | 13 +--- 12 files changed, 110 insertions(+), 69 deletions(-) create mode 100644 detekt.yml diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 7985da6..2d0979c 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -23,6 +23,8 @@ jobs: with: arguments: build gradle-version: wrapper + properties: | + detektAutoCorrect=false - name: Upload test reports if: ${{ failure() }} # runs only if previous step has failed, the entire workflow will still be marked as failed uses: actions/upload-artifact@v2 diff --git a/build.gradle.kts b/build.gradle.kts index 85beb7b..008731e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,11 @@ import io.github.petertrr.configurePublishing import io.github.petertrr.configureVersioning +import io.gitlab.arturbosch.detekt.Detekt import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest plugins { alias(libs.plugins.kotlin.multiplatform) + alias(libs.plugins.detekt) jacoco } @@ -52,6 +54,18 @@ tasks.withType { useJUnitPlatform() } +detekt { + buildUponDefaultConfig = true + config = files("detekt.yml") + autoCorrect = (findProperty("detektAutoCorrect") as String?)?.toBoolean() ?: true +} +dependencies { + detektPlugins(libs.detekt.formatting) +} +tasks.withType { + tasks.getByName("check").dependsOn(this) +} + // configure Jacoco-based code coverage reports for JVM tests executions jacoco { toolVersion = "0.8.7" diff --git a/detekt.yml b/detekt.yml new file mode 100644 index 0000000..0d2fab9 --- /dev/null +++ b/detekt.yml @@ -0,0 +1,9 @@ +style: + active: true + MaxLineLength: + active: true + maxLineLength: 180 +formatting: + active: true + MaximumLineLength: + active: false \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f1182ba..6688381 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,10 +1,13 @@ [versions] kotlin = "1.6.0" junit = "5.8.1" +detekt = "1.19.0" [plugins] kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } [libraries] junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" } junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" } +detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } diff --git a/src/commonMain/kotlin/io/github/petertrr/diffutils/DiffUtils.kt b/src/commonMain/kotlin/io/github/petertrr/diffutils/DiffUtils.kt index 076b2e7..dc013c3 100644 --- a/src/commonMain/kotlin/io/github/petertrr/diffutils/DiffUtils.kt +++ b/src/commonMain/kotlin/io/github/petertrr/diffutils/DiffUtils.kt @@ -20,6 +20,8 @@ * Implements the difference and patching engine */ +@file:Suppress("TooManyFunctions") + package io.github.petertrr.diffutils import io.github.petertrr.diffutils.algorithm.DiffAlgorithm @@ -53,9 +55,13 @@ public fun diff(original: List, revised: List, includeEqualParts: Bool /** * Computes the difference between the original and revised text. */ -public fun diff(sourceText: String, targetText: String, - progress: DiffAlgorithmListener?): Patch { - return diff(sourceText.split("\n"), +public fun diff( + sourceText: String, + targetText: String, + progress: DiffAlgorithmListener? +): Patch { + return diff( + sourceText.split("\n"), targetText.split("\n"), progress ) @@ -73,7 +79,8 @@ public fun diff(sourceText: String, targetText: String, * @return The patch describing the difference between the original and revised sequences. */ public fun diff( - source: List, target: List, + source: List, + target: List, equalizer: ((T, T) -> Boolean)? ): Patch { return if (equalizer != null) { @@ -85,8 +92,10 @@ public fun diff( } public fun diff( - original: List, revised: List, - algorithm: DiffAlgorithm, progress: DiffAlgorithmListener? + original: List, + revised: List, + algorithm: DiffAlgorithm, + progress: DiffAlgorithmListener? ): Patch { return diff(original, revised, algorithm, progress, false) } @@ -104,8 +113,10 @@ public fun diff( * `null`. */ public fun diff( - original: List, revised: List, - algorithm: DiffAlgorithm, progress: DiffAlgorithmListener?, + original: List, + revised: List, + algorithm: DiffAlgorithm, + progress: DiffAlgorithmListener?, includeEqualParts: Boolean ): Patch { return Patch.generate(original, revised, algorithm.computeDiff(original, revised, progress), includeEqualParts) diff --git a/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/Change.kt b/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/Change.kt index d0afa73..01985b5 100644 --- a/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/Change.kt +++ b/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/Change.kt @@ -20,9 +20,10 @@ package io.github.petertrr.diffutils.algorithm import io.github.petertrr.diffutils.patch.DeltaType -public data class Change(val deltaType: DeltaType, - val startOriginal: Int, - val endOriginal: Int, - val startRevised: Int, - val endRevised: Int +public data class Change( + val deltaType: DeltaType, + val startOriginal: Int, + val endOriginal: Int, + val startRevised: Int, + val endRevised: Int ) diff --git a/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/myers/MyersDiff.kt b/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/myers/MyersDiff.kt index 2e24327..4b21bb7 100644 --- a/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/myers/MyersDiff.kt +++ b/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/myers/MyersDiff.kt @@ -33,7 +33,7 @@ internal class MyersDiff(private val equalizer: (T, T) -> Boolean = { t1, t2 override fun computeDiff(source: List, target: List, progress: DiffAlgorithmListener?): List { progress?.diffStart() val path = buildPath(source, target, progress) - val result = buildRevision(path, source, target) + val result = buildRevision(path) progress?.diffEnd() return result } @@ -49,15 +49,15 @@ internal class MyersDiff(private val equalizer: (T, T) -> Boolean = { t1, t2 */ private fun buildPath(orig: List, rev: List, progress: DiffAlgorithmListener?): PathNode? { // these are local constants - val N = orig.size - val M = rev.size - val MAX = N + M + 1 - val size = 1 + 2 * MAX + val origSize = orig.size + val revSize = rev.size + val max = origSize + revSize + 1 + val size = 1 + 2 * max val middle = size / 2 val diagonal: Array = arrayOfNulls(size) diagonal[middle + 1] = PathNode(0, -1, snake = true, bootstrap = true, prev = null) - for (d in 0 until MAX) { - progress?.diffStep(d, MAX) + for (d in 0 until max) { + progress?.diffStep(d, max) var k = -d while (k <= d) { val kmiddle = middle + k @@ -75,7 +75,7 @@ internal class MyersDiff(private val equalizer: (T, T) -> Boolean = { t1, t2 diagonal[kminus] = null // no longer used var j = i - k var node = PathNode(i, j, snake = false, bootstrap = false, prev = prev) - while (i < N && j < M && equalizer.invoke(orig[i], rev[j])) { + while (i < origSize && j < revSize && equalizer.invoke(orig[i], rev[j])) { i++ j++ } @@ -83,7 +83,7 @@ internal class MyersDiff(private val equalizer: (T, T) -> Boolean = { t1, t2 node = PathNode(i, j, snake = true, bootstrap = false, prev = node) } diagonal[kmiddle] = node - if (i >= N && j >= M) { + if (i >= origSize && j >= revSize) { return diagonal[kmiddle] } k += 2 @@ -102,7 +102,7 @@ internal class MyersDiff(private val equalizer: (T, T) -> Boolean = { t1, t2 * @return A list of [Change]s corresponding to the path. * @throws IllegalStateException if a patch could not be built from the given path. */ - private fun buildRevision(actualPath: PathNode?, orig: List, rev: List): List { + private fun buildRevision(actualPath: PathNode?): List { var path: PathNode? = actualPath val changes: MutableList = mutableListOf() if (path!!.snake) { diff --git a/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/myers/PathNode.kt b/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/myers/PathNode.kt index 99eb5c2..e4b4bb8 100644 --- a/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/myers/PathNode.kt +++ b/src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/myers/PathNode.kt @@ -70,4 +70,4 @@ internal class PathNode( override fun toString() = generateSequence(this) { it.prev } .joinToString(prefix = "[", postfix = "]") { "(${it.i}, ${it.j})" } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/io/github/petertrr/diffutils/patch/Chunk.kt b/src/commonMain/kotlin/io/github/petertrr/diffutils/patch/Chunk.kt index e7451d9..ae60881 100644 --- a/src/commonMain/kotlin/io/github/petertrr/diffutils/patch/Chunk.kt +++ b/src/commonMain/kotlin/io/github/petertrr/diffutils/patch/Chunk.kt @@ -39,7 +39,7 @@ public data class Chunk( * the positions of changed lines of chunk in the text */ val changePosition: List? = null - ) { +) { /** * Verifies that this chunk's saved text matches the corresponding text in the given sequence. * diff --git a/src/commonMain/kotlin/io/github/petertrr/diffutils/patch/Patch.kt b/src/commonMain/kotlin/io/github/petertrr/diffutils/patch/Patch.kt index 5f90a4b..6da12f9 100644 --- a/src/commonMain/kotlin/io/github/petertrr/diffutils/patch/Patch.kt +++ b/src/commonMain/kotlin/io/github/petertrr/diffutils/patch/Patch.kt @@ -25,7 +25,7 @@ import io.github.petertrr.diffutils.algorithm.Change * * @param T The type of the compared elements in the 'lines'. */ -public class Patch(estimatedPatchSize: Int = 10) { +public class Patch { public var deltas: MutableList> = arrayListOf() get() { field.sortBy { it.source.position } @@ -84,15 +84,13 @@ public class Patch(estimatedPatchSize: Int = 10) { return Chunk(start, data.subList(start, end)) } - public fun generate(original: List, revised: List, _changes: List, includeEquals: Boolean): Patch { - val patch = Patch(_changes.size) + public fun generate(original: List, revised: List, changes: List, includeEquals: Boolean): Patch { + val patch = Patch() var startOriginal = 0 var startRevised = 0 - var changes: List = _changes - if (includeEquals) { - changes = _changes.sortedBy { it.startOriginal } - } - for (change in changes) { + changes.run { + if (includeEquals) sortedBy { it.startOriginal } else this + }.forEach { change -> if (includeEquals && startOriginal < change.startOriginal) { patch.addDelta( EqualDelta( diff --git a/src/commonMain/kotlin/io/github/petertrr/diffutils/text/DiffRowGenerator.kt b/src/commonMain/kotlin/io/github/petertrr/diffutils/text/DiffRowGenerator.kt index 1cec3d3..d703e94 100644 --- a/src/commonMain/kotlin/io/github/petertrr/diffutils/text/DiffRowGenerator.kt +++ b/src/commonMain/kotlin/io/github/petertrr/diffutils/text/DiffRowGenerator.kt @@ -35,6 +35,7 @@ import kotlin.math.min * spaces or/and blank lines and so on. All parameters for generating are * optional. If you do not specify them, the class will use the default values. */ +@Suppress("LongParameterList") public class DiffRowGenerator( /** * Set the column width of generated lines of original and revised @@ -50,7 +51,7 @@ public class DiffRowGenerator( /** * Provide an equalizer for diff processing. */ - public var equalizer: ((String, String) -> Boolean) = if (ignoreWhiteSpaces) IGNORE_WHITESPACE_EQUALIZER else DEFAULT_EQUALIZER, + private var equalizer: ((String, String) -> Boolean) = if (ignoreWhiteSpaces) IGNORE_WHITESPACE_EQUALIZER else DEFAULT_EQUALIZER, /** * Per default each character is separately processed. Setting this parameter to `true` @@ -163,7 +164,12 @@ public class DiffRowGenerator( * * @param endPos line number after previous delta end */ - private fun transformDeltaIntoDiffRow(original: List, endPos: Int, diffRows: MutableList, delta: Delta): Int { + private fun transformDeltaIntoDiffRow( + original: List, + endPos: Int, + diffRows: MutableList, + delta: Delta + ): Int { val orig: Chunk = delta.source val rev: Chunk = delta.target for (line in original.subList(endPos, orig.position)) { @@ -197,7 +203,7 @@ public class DiffRowGenerator( /** * Decompresses ChangeDeltas with different source and target size to a * ChangeDelta with same size and a following InsertDelta or DeleteDelta. - * With this problems of building DiffRows getting smaller. + * With this, problems of building DiffRows getting smaller. * If sizes are equal, returns a list with single element - [delta]. */ private fun decompressDeltas(delta: Delta): List> { @@ -271,6 +277,7 @@ public class DiffRowGenerator( * * @param delta the given delta */ + @Suppress("LongMethod") private fun generateInlineDiffs(delta: Delta): List { val orig = normalizeLines(delta.source.lines) val rev = normalizeLines(delta.target.lines) @@ -278,7 +285,7 @@ public class DiffRowGenerator( val joinedRev: String = rev.joinToString("\n") val origList = inlineDiffSplitter.invoke(joinedOrig).toMutableList() val revList = inlineDiffSplitter.invoke(joinedRev).toMutableList() - // todo: `origList.toList` and `revList.toList` are needed because otherwise `wrapInTag` results in ConcurrentModificationException + // copying of `origList` and `revList` is needed because otherwise `wrapInTag` results in ConcurrentModificationException val inlineDeltas: MutableList> = diff(origList.toList(), revList.toList(), equalizer) .deltas .asReversed() @@ -287,7 +294,7 @@ public class DiffRowGenerator( val inlineOrig: Chunk = inlineDelta.source val inlineRev: Chunk = inlineDelta.target when (inlineDelta.type) { - DeltaType.DELETE -> { + DeltaType.DELETE -> { wrapInTag( origList, inlineOrig.position, inlineOrig.position + inlineOrig.size(), DiffRow.Tag.DELETE, oldTag, processDiffs, replaceOriginalLinefeedInChangesWithSpaces && mergeOriginalRevised @@ -380,43 +387,43 @@ public class DiffRowGenerator( public companion object { internal val DEFAULT_EQUALIZER: (Any?, Any?) -> Boolean = { o1: Any?, o2: Any? -> o1 == o2 } internal val IGNORE_WHITESPACE_EQUALIZER: (String, String) -> Boolean = { original: String, revised: String -> - adjustWhitespace( - original - ) == adjustWhitespace(revised) - } + adjustWhitespace( + original + ) == adjustWhitespace(revised) + } internal val LINE_NORMALIZER_FOR_HTML: (String) -> String = { normalize(it) } /** * Splitting lines by character to achieve char by char diff checking. */ internal val SPLITTER_BY_CHARACTER = { line: String -> - val list: MutableList = ArrayList(line.length) - for (character in line.toCharArray()) { - list.add(character.toString()) - } - list.toList() + val list: MutableList = ArrayList(line.length) + for (character in line.toCharArray()) { + list.add(character.toString()) } + list.toList() + } internal val SPLIT_BY_WORD_PATTERN = Regex("\\s+|[,.\\[\\](){}/\\\\*+\\-#]") /** * Splitting lines by word to achieve word by word diff checking. */ internal val SPLITTER_BY_WORD = { line: String -> - splitStringPreserveDelimiter( - line, - SPLIT_BY_WORD_PATTERN - ) - } + splitStringPreserveDelimiter( + line, + SPLIT_BY_WORD_PATTERN + ) + } internal val WHITESPACE_PATTERN = Regex("\\s+") private fun adjustWhitespace(raw: String): String { return WHITESPACE_PATTERN.replace(raw.trim(), " ") } - internal fun splitStringPreserveDelimiter(str: String?, SPLIT_PATTERN: Regex): List { + internal fun splitStringPreserveDelimiter(str: String?, splitPattern: Regex): List { val list: MutableList = mutableListOf() if (str != null) { - val matchResults = SPLIT_PATTERN.findAll(str) + val matchResults = splitPattern.findAll(str) var pos = 0 for (matchResult in matchResults) { if (pos < matchResult.range.first) { @@ -440,10 +447,15 @@ public class DiffRowGenerator( * @param endPosition the position before which tag should should be closed. * @param tagGenerator the tag generator */ + @Suppress("LongParameterList", "ComplexMethod", "LoopWithTooManyJumpStatements", "NestedBlockDepth") internal fun wrapInTag( - sequence: MutableList, startPosition: Int, - endPosition: Int, tag: DiffRow.Tag, tagGenerator: (DiffRow.Tag, Boolean) -> String, - processDiffs: ((String) -> String)?, replaceLinefeedWithSpace: Boolean + sequence: MutableList, + startPosition: Int, + endPosition: Int, + tag: DiffRow.Tag, + tagGenerator: (DiffRow.Tag, Boolean) -> String, + processDiffs: ((String) -> String)?, + replaceLinefeedWithSpace: Boolean, ): MutableList { var endPos = endPosition while (endPos >= startPosition) { @@ -466,7 +478,7 @@ public class DiffRowGenerator( } endPos-- - //search position for end tag + // search position for end tag while (endPos > startPosition) { if ("\n" == sequence[endPos - 1]) { if (replaceLinefeedWithSpace) { diff --git a/src/commonMain/kotlin/io/github/petertrr/diffutils/text/StringUtils.kt b/src/commonMain/kotlin/io/github/petertrr/diffutils/text/StringUtils.kt index 04f6a60..a086f24 100644 --- a/src/commonMain/kotlin/io/github/petertrr/diffutils/text/StringUtils.kt +++ b/src/commonMain/kotlin/io/github/petertrr/diffutils/text/StringUtils.kt @@ -32,16 +32,6 @@ internal fun normalize(str: String): String { return htmlEntities(str).replace("\t", " ") } -internal fun wrapText(list: List, columnWidth: Int): List { - return list - .map { line -> - wrapText( - line, - columnWidth - ) - } -} - /** * Wrap the text with the given column width * @@ -62,7 +52,8 @@ internal fun wrapText(line: String, columnWidth: Int): String { while (length > widthIndex) { var breakPoint = widthIndex + delimiter * count if (b[breakPoint - 1].isHighSurrogate() && - b[breakPoint].isLowSurrogate()) { + b[breakPoint].isLowSurrogate() + ) { // Shift a breakpoint that would split a supplemental code-point. breakPoint += 1 if (breakPoint == b.length) {