added mrged blocks and README.md

This commit is contained in:
sergeych committed 2023-03-21 04:09:20 +01:00
1 parent 3f6a16224e
commit 2262c66b63
5 files changed
+2065 -16

No files matched your search

@@ -11,14 +11,68 @@ import net.sergeych.mp_logger.debug
import net.sergeych.sprintf.sprintf
/**
* Merge result. This version does not report (yet) conflicts, just merges it all.
* @param merged the best merged resut. In the case of the conflict usually has both variants concatennated in place
* Merge result. This version does not report (yet) conflicts, just merges it all. See also [blocks] for
* another representation of the merged data.
*
* @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
*/
class MergeResult<T>(
val merged: List<T>,
val changedAreas: List<ClosedRange<Int>>,
)
val changedAreas: List<IntRange>,
) {
val blocks: List<MergedBlock<T>> by lazy {
if (changedAreas.isEmpty())
listOf(MergedBlock.Unchanged(merged))
else {
val result = mutableListOf<MergedBlock<T>>()
var start = 0
for (r in changedAreas) {
if (start != r.start) {
result.add(MergedBlock.Unchanged(merged.slice(start until r.start)))
}
if (!r.isEmpty()) {
result.add(MergedBlock.Resolved(merged.slice(r)))
}
start = r.endInclusive + 1
}
if (start < merged.size) {
result.add(MergedBlock.Unchanged(merged.slice(start until merged.size)))
}
result
}
}
}
/**
* Merge3 sequential result block.
*/
sealed class MergedBlock<T> {
/**
* Merged data. Int the case of the conflict, the concatenated conflicting data
*/
abstract val data: List<T>
/**
* Data that are equals in both variants and therefore was not altered
*/
data class Unchanged<T>(override val data: List<T>) : MergedBlock<T>()
/**
* The portion of data that was merged without conflicts
*/
data class Resolved<T>(override val data: List<T>) : MergedBlock<T>()
/**
* The portion of data that can't be merged due to the conflicting changes.
* __Note it is not yet used.__
*/
@Suppress("unused")
data class Conflict<T>(val variantA: List<T>, val variantB: List<T>) : MergedBlock<T>() {
override val data = variantA + variantB
}
}
/**
* Perform 3-way merge. See [merge3] for details.
@@ -98,8 +152,7 @@ private class Merge3<T>(val source: List<T>, val variantA: List<T>, val variantB
}
}
private val conflicts = mutableListOf<ClosedRange<Int>>()
private val updates = mutableListOf<ClosedRange<Int>>()
private val updates = mutableListOf<IntRange>()
private fun applyDelta(delta: AbstractDelta<T>) {
when (delta) {
@@ -149,12 +202,12 @@ private class Merge3<T>(val source: List<T>, val variantA: List<T>, val variantB
// detect ranges
var conflictStart = -1
var changeStart = -1
fun closeConflict(index: Int) {
if (conflictStart >= 0) {
conflicts.add(conflictStart until index)
conflictStart = -1
}
}
// fun closeConflict(index: Int) {
// if (conflictStart >= 0) {
// conflicts.add(conflictStart until index)
// conflictStart = -1
// }
// }
fun closeUpdate(index: Int) {
if (changeStart >= 0) {
@@ -167,13 +220,13 @@ private class Merge3<T>(val source: List<T>, val variantA: List<T>, val variantB
when (count) {
0 -> {
// close conflict or update if was open
closeConflict(index)
// closeConflict(index)
closeUpdate(index)
}
1 -> {
// could be end of the conflict
closeConflict(index)
// closeConflict(index)
// open change if not opened
if (changeStart < 0) changeStart = index
}
@@ -191,7 +244,7 @@ private class Merge3<T>(val source: List<T>, val variantA: List<T>, val variantB
}
}
}
closeConflict(changeCount.size)
// closeConflict(changeCount.size)
closeUpdate(changeCount.size)
return MergeResult<T>(result, updates)
@@ -205,5 +258,5 @@ private class Merge3<T>(val source: List<T>, val variantA: List<T>, val variantB
*/
fun <T> merge3(source: List<T>, a: List<T>, b: List<T>, showDebug: Boolean = false): MergeResult<T> =
Merge3(source, a, b).also {
if( showDebug ) it.logLevel = Log.Level.DEBUG
if (showDebug) it.logLevel = Log.Level.DEBUG
}.perform()
@@ -32,6 +32,7 @@ class BasicTest {
println(m.changedAreas)
assertEquals("Bye friend", m.merged.str)
// assertTrue(m.noConflicts)
println(m.blocks)
}
@Test
fun testMergeWithConflicts() {