Skip to content

Commit edfab24

Browse files
authored
Introduce Myers algorithm in linear space (#112)
1 parent 875f4ce commit edfab24

38 files changed

+1841
-925
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ build
55
/.idea
66
*.iml
77
out
8-
.DS_Store
8+
.DS_Store
9+
.kotlin/

README.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ All credit for the implementation goes to original authors.
1313

1414
## Features
1515

16-
All features from version `4.10` of the original library are present, except for:
16+
All features from version `4.12` of the original library are present, except for:
1717

18+
- fuzzy patches
1819
- unified diff, which heavily uses file read/write and therefore needs a more complicated rewrite
19-
- diff-utils-jgit, which uses JVM-only jgit library
20+
- diff-utils-jgit, which uses JVM-only JGit
2021

2122
Please refer to the original guides for more information.
2223

@@ -29,8 +30,10 @@ Currently, artifacts for the following platforms are supported:
2930
- WebAssembly (JS and WASI)
3031
- Native
3132

32-
The supported Native targets are (following the Kotlin/Native [target support guidelines](https://kotlinlang.org/docs/native-target-support.html)):
33+
The supported Native targets are (following the Kotlin/Native [target support guidelines][1]):
3334

3435
| Tier 1 | Tier 2 | Tier 3 |
3536
|:---------|:---------|:---------|
3637
| macosX64 | linuxX64 | mingwX64 |
38+
39+
[1]: https://kotlinlang.org/docs/native-target-support.html

build.gradle.kts

+26-12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
77
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
88
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
99
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
10+
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
1011

1112
plugins {
1213
kotlin("multiplatform")
@@ -31,12 +32,17 @@ kotlin {
3132

3233
jvm {
3334
compilations.configureEach {
34-
compilerOptions.configure {
35-
// Minimum bytecode level is 52
36-
jvmTarget = JvmTarget.JVM_1_8
37-
38-
// Output interfaces with default methods
39-
freeCompilerArgs.add("-Xjvm-default=all")
35+
compileTaskProvider.configure {
36+
compilerOptions {
37+
// Minimum bytecode level is 52
38+
jvmTarget = JvmTarget.JVM_1_8
39+
40+
// Output interfaces with default methods
41+
freeCompilerArgs.addAll(
42+
"-Xjvm-default=all", // Output interfaces with default methods
43+
"-Xno-param-assertions", // Remove Intrinsics.checkNotNullParameter
44+
)
45+
}
4046
}
4147
}
4248

@@ -48,23 +54,31 @@ kotlin {
4854
}
4955

5056
js {
51-
browser()
52-
nodejs()
57+
val testConfig: (KotlinJsTest).() -> Unit = {
58+
useMocha {
59+
// Override default 2s timeout
60+
timeout = "120s"
61+
}
62+
}
63+
64+
browser {
65+
testTask(testConfig)
66+
}
67+
68+
nodejs {
69+
testTask(testConfig)
70+
}
5371
}
5472

5573
@OptIn(ExperimentalWasmDsl::class)
5674
wasmJs {
5775
browser()
5876
nodejs()
59-
applyBinaryen()
6077
}
6178

6279
@OptIn(ExperimentalWasmDsl::class)
6380
wasmWasi {
6481
nodejs()
65-
66-
// Available since 2.0
67-
// applyBinaryen()
6882
}
6983

7084
linuxX64()

detekt.yml

+9
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,12 @@ formatting:
1515
active: false
1616
TrailingCommaOnDeclarationSite:
1717
active: true
18+
complexity:
19+
ComplexCondition:
20+
active: false
21+
CyclomaticComplexMethod:
22+
active: false
23+
NestedBlockDepth:
24+
active: false
25+
LongParameterList:
26+
functionThreshold: 7

kotlin-js-store/yarn.lock

+502-395
Large diffs are not rendered by default.

src/commonMain/kotlin/io/github/petertrr/diffutils/DiffUtils.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package io.github.petertrr.diffutils
2222

2323
import io.github.petertrr.diffutils.algorithm.DiffAlgorithm
2424
import io.github.petertrr.diffutils.algorithm.DiffAlgorithmListener
25+
import io.github.petertrr.diffutils.algorithm.DiffEqualizer
2526
import io.github.petertrr.diffutils.algorithm.NoopAlgorithmListener
2627
import io.github.petertrr.diffutils.algorithm.myers.MyersDiff
2728
import io.github.petertrr.diffutils.patch.Patch
@@ -73,7 +74,7 @@ public fun diff(
7374
public fun <T> diff(
7475
source: List<T>,
7576
target: List<T>,
76-
equalizer: ((T, T) -> Boolean),
77+
equalizer: DiffEqualizer<T>,
7778
): Patch<T> =
7879
diff(
7980
source = source,

src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/Change.kt

+6-5
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919
package io.github.petertrr.diffutils.algorithm
2020

2121
import io.github.petertrr.diffutils.patch.DeltaType
22+
import kotlin.jvm.JvmField
2223

2324
public data class Change(
24-
val deltaType: DeltaType,
25-
val startOriginal: Int,
26-
val endOriginal: Int,
27-
val startRevised: Int,
28-
val endRevised: Int,
25+
@JvmField val deltaType: DeltaType,
26+
@JvmField val startOriginal: Int,
27+
@JvmField val endOriginal: Int,
28+
@JvmField val startRevised: Int,
29+
@JvmField val endRevised: Int,
2930
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2024 Peter Trifanov.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.github.petertrr.diffutils.algorithm
17+
18+
public fun interface DiffEqualizer<T> {
19+
public fun test(one: T, two: T): Boolean
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2024 Peter Trifanov.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.github.petertrr.diffutils.algorithm
17+
18+
public class EqualsDiffEqualizer<T> : DiffEqualizer<T> {
19+
override fun test(one: T, two: T): Boolean =
20+
one == two
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2024 Peter Trifanov.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.github.petertrr.diffutils.algorithm
17+
18+
public class IgnoreWsStringDiffEqualizer : DiffEqualizer<String> {
19+
private companion object {
20+
private val ws = Regex("\\s+")
21+
}
22+
23+
override fun test(one: String, two: String): Boolean =
24+
ws.replace(one.trim(), " ") == ws.replace(two.trim(), " ")
25+
}

src/commonMain/kotlin/io/github/petertrr/diffutils/algorithm/myers/MyersDiff.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ package io.github.petertrr.diffutils.algorithm.myers
2121
import io.github.petertrr.diffutils.algorithm.Change
2222
import io.github.petertrr.diffutils.algorithm.DiffAlgorithm
2323
import io.github.petertrr.diffutils.algorithm.DiffAlgorithmListener
24+
import io.github.petertrr.diffutils.algorithm.DiffEqualizer
25+
import io.github.petertrr.diffutils.algorithm.EqualsDiffEqualizer
2426
import io.github.petertrr.diffutils.patch.DeltaType
2527

2628
/**
2729
* A clean-room implementation of Eugene Myers greedy differencing algorithm.
2830
*/
29-
public class MyersDiff<T>(private val equalizer: (T, T) -> Boolean = { t1, t2 -> t1 == t2 }) : DiffAlgorithm<T> {
31+
public class MyersDiff<T>(private val equalizer: DiffEqualizer<T> = EqualsDiffEqualizer()) : DiffAlgorithm<T> {
3032
/**
3133
* Returns an empty diff if we get an error while procession the difference.
3234
*/
@@ -82,7 +84,7 @@ public class MyersDiff<T>(private val equalizer: (T, T) -> Boolean = { t1, t2 ->
8284
var j = i - k
8385
var node = PathNode(i, j, snake = false, bootstrap = false, prev = prev)
8486

85-
while (i < origSize && j < revSize && equalizer.invoke(orig[i], rev[j])) {
87+
while (i < origSize && j < revSize && equalizer.test(orig[i], rev[j])) {
8688
i++
8789
j++
8890
}

0 commit comments

Comments
 (0)