diff --git a/build.gradle.kts b/build.gradle.kts index b5dab57..753fbbb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } group = "net.sergeych" -version = "0.0.4-SNAPSHOT" +version = "0.0.5-SNAPSHOT" repositories { mavenCentral() @@ -43,7 +43,7 @@ kotlin { val commonMain by getting { dependencies { implementation("dev.gitlive:kotlin-diff-utils:4.1.4") - implementation("net.sergeych:mp_stools:[1.3.3,)") + implementation("net.sergeych:mp_stools:[1.3.4,)") } } val commonTest by getting { diff --git a/src/commonMain/kotlin/net.sergeych.merge3/merge3.kt b/src/commonMain/kotlin/net.sergeych.merge3/merge3.kt index 9957bfd..27fe4ba 100644 --- a/src/commonMain/kotlin/net.sergeych.merge3/merge3.kt +++ b/src/commonMain/kotlin/net.sergeych.merge3/merge3.kt @@ -13,20 +13,23 @@ import net.sergeych.sprintf.sprintf * * @param merged the best merged data. In the case of the conflict usually has both variants concatennated in place * @param changedAreas ranges where data were altered. could be used to highlight changes + * @param sourceIndices indexes of items in source array (not included in result) in new result [merged]. If source + * item is not included int the merged array, its index will be -1 */ class MergeResult( - val merged: List, + val merged: MutableList, val changedAreas: List, + val sourceIndices: MutableList, ) { val blocks: List> by lazy { if (changedAreas.isEmpty()) - listOf(MergedBlock.Unchanged(merged)) + listOf(MergedBlock.Unchanged(merged, sourceIndices[0])) else { val result = mutableListOf>() var start = 0 for (r in changedAreas) { if (start != r.start) { - result.add(MergedBlock.Unchanged(merged.slice(start until r.start))) + result.add(MergedBlock.Unchanged(merged.slice(start until r.start), sourceIndices[start])) } if (!r.isEmpty()) { result.add(MergedBlock.Resolved(merged.slice(r))) @@ -34,11 +37,21 @@ class MergeResult( start = r.endInclusive + 1 } if (start < merged.size) { - result.add(MergedBlock.Unchanged(merged.slice(start until merged.size))) + result.add(MergedBlock.Unchanged(merged.slice(start until merged.size), sourceIndices[start])) } result } } + + fun toTrace(source: List): String { + val r = StringBuilder() + r.append(merged.indices.joinToString("") { "%3d".sprintf(it) } + "\n") + r.append(merged.joinToString("") { "%3s".sprintf(it) } + "\n") + r.append("---- source ----\n") + r.append(source.joinToString("") { "%3s".sprintf(it) } + "\n") + r.append(sourceIndices.joinToString("") { "%3s".sprintf(it) } + "\n") + return r.toString() + } } /** @@ -52,8 +65,9 @@ sealed class MergedBlock { /** * Data that are equals in both variants and therefore was not altered + * @param referenceIndex index if the first item of the unchanged data as it was in source array. */ - data class Unchanged(override val data: List) : MergedBlock() + data class Unchanged(override val data: List, val referenceIndex: Int) : MergedBlock() /** * The portion of data that was merged without conflicts @@ -74,16 +88,18 @@ sealed class MergedBlock { /** * Perform 3-way merge. See [merge3] for details. */ -private class Merge3(val source: List, val variantA: List, val variantB: List, - val showDebug: Boolean = false) { +private class Merge3( + val source: List, val variantA: List, val variantB: List, + val showDebug: Boolean = false, +) { private val reindex = source.indices.toMutableList() private val changeCount = MutableList(source.size) { 0 } val result = source.toMutableList() - private fun debug( f: ()-> String ) { - if( showDebug ) println(f()) + private fun debug(f: () -> String) { + if (showDebug) println(f()) } private fun trace() { @@ -253,7 +269,7 @@ private class Merge3(val source: List, val variantA: List, val variantB // closeConflict(changeCount.size) closeUpdate(changeCount.size) - return MergeResult(result, updates) + return MergeResult(result, updates, reindex) } } diff --git a/src/commonTest/kotlin/testMerge.kt b/src/commonTest/kotlin/testMerge.kt index 6cdd1b8..f323499 100644 --- a/src/commonTest/kotlin/testMerge.kt +++ b/src/commonTest/kotlin/testMerge.kt @@ -22,7 +22,7 @@ class BasicTest { @Test fun testMergeNoConflicts3() { Log.connectConsole() - val src = "1Hello, world!".toList() + val src = "Hello, world!".toList() val a = "Hello, friend!".toList() val b = "Bye world!".toList() @@ -31,6 +31,7 @@ class BasicTest { // println(m.conflicts) println(m.changedAreas) assertEquals("Bye friend!", m.merged.str) + println(m.toTrace(src)) // assertTrue(m.noConflicts) }