parent
6a67a4e840
commit
446a7adf6f
@ -1,10 +1,10 @@
|
||||
plugins {
|
||||
kotlin("multiplatform") version "1.7.21"
|
||||
kotlin("multiplatform") version "1.9.22"
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
group = "net.sergeych"
|
||||
version = "0.0.8-SNAPSHOT"
|
||||
version = "0.0.10"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@ -14,7 +14,7 @@ repositories {
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
jvmToolchain(8)
|
||||
jvmToolchain(11)
|
||||
withJava()
|
||||
testRuns["test"].executionTask.configure {
|
||||
useJUnitPlatform()
|
||||
@ -29,25 +29,63 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
val hostOs = System.getProperty("os.name")
|
||||
val isMingwX64 = hostOs.startsWith("Windows")
|
||||
val nativeTarget = when {
|
||||
hostOs == "Mac OS X" -> macosX64("native")
|
||||
hostOs == "Linux" -> linuxX64("native")
|
||||
isMingwX64 -> mingwX64("native")
|
||||
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
|
||||
// val hostOs = System.getProperty("os.name")
|
||||
// val isMingwX64 = hostOs.startsWith("Windows")
|
||||
// val nativeTarget = when {
|
||||
// hostOs == "Mac OS X" -> macosX64("native")
|
||||
// hostOs == "Linux" -> linuxX64("native")
|
||||
// isMingwX64 -> mingwX64("native")
|
||||
// else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
|
||||
// }
|
||||
|
||||
wasmJs {
|
||||
browser()
|
||||
binaries.executable()
|
||||
}
|
||||
|
||||
listOf(
|
||||
iosX64(),
|
||||
iosArm64(),
|
||||
iosSimulatorArm64()
|
||||
).forEach {
|
||||
it.binaries.framework {
|
||||
baseName = "morozilko_lib"
|
||||
isStatic = true
|
||||
}
|
||||
}
|
||||
|
||||
listOf(
|
||||
macosX64(),
|
||||
macosArm64()
|
||||
).forEach {
|
||||
it.binaries.framework {
|
||||
baseName = "morozilko_lib"
|
||||
isStatic = true
|
||||
}
|
||||
}
|
||||
|
||||
linuxX64 {
|
||||
binaries.staticLib {
|
||||
baseName = "morozilko_lib"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mingwX64 {
|
||||
binaries.staticLib {
|
||||
baseName = "morozilko_lib"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
// implementation("dev.gitlive:kotlin-diff-utils:4.1.4")
|
||||
// implementation("net.sergeych:mp_stools:[1.4.7,)")
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation("net.sergeych:mp_stools:[1.3.4,)")
|
||||
implementation(kotlin("test"))
|
||||
}
|
||||
}
|
||||
@ -55,8 +93,8 @@ kotlin {
|
||||
val jvmTest by getting
|
||||
val jsMain by getting
|
||||
val jsTest by getting
|
||||
val nativeMain by getting
|
||||
val nativeTest by getting
|
||||
// val nativeMain by getting
|
||||
// val nativeTest by getting
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -44,7 +44,7 @@ class MeyersDiff<T> : DiffAlgorithmI<T> {
|
||||
override fun computeDiff(source: List<T>, target: List<T>, progress: DiffAlgorithmListener?): List<Change> {
|
||||
progress?.diffStart()
|
||||
val path = buildPath(source, target, progress)
|
||||
val result = buildRevision(path, source, target)
|
||||
val result = buildRevision(path)
|
||||
progress?.diffEnd()
|
||||
return result
|
||||
}
|
||||
@ -116,7 +116,7 @@ class MeyersDiff<T> : DiffAlgorithmI<T> {
|
||||
* @throws DifferentiationFailedException if a [Patch] could not be
|
||||
* built from the given path.
|
||||
*/
|
||||
private fun buildRevision(actualPath: PathNode, orig: List<T>, rev: List<T>): List<Change> {
|
||||
private fun buildRevision(actualPath: PathNode): List<Change> {
|
||||
var path: PathNode? = actualPath
|
||||
val changes: MutableList<Change> = ArrayList()
|
||||
if (path!!.isSnake) {
|
||||
|
@ -62,7 +62,7 @@ abstract class AbstractDelta<T>(val type: DeltaType, val source: Chunk<T>, val t
|
||||
if (this::class != other::class) {
|
||||
return false
|
||||
}
|
||||
val other = other as AbstractDelta<*>
|
||||
other as AbstractDelta<*>
|
||||
if (source != other.source) {
|
||||
return false
|
||||
}
|
||||
@ -70,4 +70,14 @@ abstract class AbstractDelta<T>(val type: DeltaType, val source: Chunk<T>, val t
|
||||
false
|
||||
} else type == other.type
|
||||
}
|
||||
|
||||
/**
|
||||
* If this delta is partially performed by [existing], update it si it only changes necessary
|
||||
* data. If this delta copies completely an existing delta, return null.
|
||||
*
|
||||
* Base class implementation only removes complete dupes
|
||||
*/
|
||||
open fun optimizeAgainst(existing: AbstractDelta<T>): AbstractDelta<T>? {
|
||||
return if( this == existing ) null else this
|
||||
}
|
||||
}
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package dev.gitlive.difflib.patch
|
||||
|
||||
import net.sergeych.merge3.overlaps
|
||||
|
||||
/**
|
||||
* Holds the information about the part of text involved in the diff process
|
||||
*
|
||||
@ -28,7 +30,7 @@ package dev.gitlive.difflib.patch
|
||||
*
|
||||
*
|
||||
* @author [](dm.naumenko@gmail.com>Dmitry Naumenko</a>
|
||||
@param <T> The type of the compared elements in the 'lines'.
|
||||
* @param <T> The type of the compared elements in the 'lines'.
|
||||
) */
|
||||
class Chunk<T> {
|
||||
|
||||
@ -130,8 +132,8 @@ class Chunk<T> {
|
||||
if (this::class != other::class) {
|
||||
return false
|
||||
}
|
||||
val other = other as Chunk<*>?
|
||||
if (lines != other!!.lines) {
|
||||
other as Chunk<*>
|
||||
if (lines != other.lines) {
|
||||
return false
|
||||
}
|
||||
return position == other.position
|
||||
@ -140,4 +142,11 @@ class Chunk<T> {
|
||||
override fun toString(): String {
|
||||
return "[position: " + position + ", size: " + size() + ", lines: " + lines + "]"
|
||||
}
|
||||
|
||||
/**
|
||||
* Range that this chunk covers (start incusive, end non inclusive)
|
||||
*/
|
||||
fun range() = position..< position+size()
|
||||
|
||||
fun overlaps(target: Chunk<T>): Boolean = range() overlaps target.range()
|
||||
}
|
@ -29,7 +29,7 @@ class InsertDelta<T>
|
||||
* @param revised The original chunk. Must not be `null`.
|
||||
*/
|
||||
(original: Chunk<T>, revised: Chunk<T>) : AbstractDelta<T>(DeltaType.INSERT, original, revised) {
|
||||
// @Throws(PatchFailedException::class)
|
||||
// @Throws(PatchFailedException::class)
|
||||
protected override fun applyTo(target: MutableList<T>) {
|
||||
val position = source.position
|
||||
val lines: List<T> = this.target.lines
|
||||
@ -54,4 +54,35 @@ class InsertDelta<T>
|
||||
override fun withChunks(original: Chunk<T>, revised: Chunk<T>): AbstractDelta<T> {
|
||||
return InsertDelta(original, revised)
|
||||
}
|
||||
|
||||
/**
|
||||
* Eliminates inserting the same text in the same positions
|
||||
*/
|
||||
override fun optimizeAgainst(existing: AbstractDelta<T>): AbstractDelta<T>? {
|
||||
return super.optimizeAgainst(existing)?.let { x ->
|
||||
if (existing !is InsertDelta)
|
||||
x
|
||||
else {
|
||||
// this algorithm only eliminates deltas that have the common prefix:
|
||||
if (existing.target.position != target.position)
|
||||
return this
|
||||
// remove common prefix:
|
||||
var offset = 0
|
||||
while (existing.target.lines[offset] == target.lines[offset]) {
|
||||
offset++
|
||||
if (offset >= target.lines.size || offset >= existing.target.lines.size) break
|
||||
}
|
||||
if (offset >= target.lines.size) {
|
||||
// removed completely
|
||||
return null
|
||||
}
|
||||
// removed a part:
|
||||
val tail = target.lines.drop(offset)
|
||||
return InsertDelta(
|
||||
Chunk(source.position, source.lines.drop(offset), source.changePosition?.let { it + offset }),
|
||||
Chunk(target.position + offset, tail, target.changePosition?.let { it + offset })
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -56,7 +56,7 @@ class DiffRow(
|
||||
if (this::class != other::class) {
|
||||
return false
|
||||
}
|
||||
val other = other as DiffRow
|
||||
other as DiffRow
|
||||
if (newLine != other.newLine) {
|
||||
return false
|
||||
}
|
||||
|
@ -12,84 +12,71 @@ import dev.gitlive.difflib.patch.Chunk
|
||||
class UnifiedDiffReader internal constructor(lineReader: LineReader) {
|
||||
private val nextLine: LineReader
|
||||
private val data = UnifiedDiff()
|
||||
private val DIFF_COMMAND: UnifiedDiffLine = UnifiedDiffLine(true, "^diff\\s") { match: MatchResult, line: String ->
|
||||
private val DIFF_COMMAND: UnifiedDiffLine = UnifiedDiffLine(true, "^diff\\s") { _, line: String ->
|
||||
processDiff(
|
||||
match,
|
||||
line
|
||||
)
|
||||
}
|
||||
private val SIMILARITY_INDEX: UnifiedDiffLine = UnifiedDiffLine(true, "^similarity index (\\d+)%$") { match: MatchResult, line: String ->
|
||||
private val SIMILARITY_INDEX: UnifiedDiffLine = UnifiedDiffLine(true, "^similarity index (\\d+)%$") { match: MatchResult, _ ->
|
||||
processSimilarityIndex(
|
||||
match,
|
||||
line
|
||||
match
|
||||
)
|
||||
}
|
||||
private val INDEX: UnifiedDiffLine = UnifiedDiffLine(true, "^index\\s[\\da-zA-Z]+\\.\\.[\\da-zA-Z]+(\\s(\\d+))?$") { match: MatchResult, line: String ->
|
||||
private val INDEX: UnifiedDiffLine = UnifiedDiffLine(true, "^index\\s[\\da-zA-Z]+\\.\\.[\\da-zA-Z]+(\\s(\\d+))?$") { _: MatchResult, line: String ->
|
||||
processIndex(
|
||||
match,
|
||||
line
|
||||
)
|
||||
}
|
||||
private val FROM_FILE: UnifiedDiffLine = UnifiedDiffLine(true, "^---\\s") { match: MatchResult, line: String ->
|
||||
private val FROM_FILE: UnifiedDiffLine = UnifiedDiffLine(true, "^---\\s") { _, line: String ->
|
||||
processFromFile(
|
||||
match,
|
||||
line
|
||||
)
|
||||
}
|
||||
private val TO_FILE: UnifiedDiffLine = UnifiedDiffLine(true, "^\\+\\+\\+\\s") { match: MatchResult, line: String ->
|
||||
private val TO_FILE: UnifiedDiffLine = UnifiedDiffLine(true, "^\\+\\+\\+\\s") { _, line: String ->
|
||||
processToFile(
|
||||
match,
|
||||
line
|
||||
)
|
||||
}
|
||||
private val RENAME_FROM: UnifiedDiffLine = UnifiedDiffLine(true, "^rename\\sfrom\\s(.+)$") { match: MatchResult, line: String ->
|
||||
private val RENAME_FROM: UnifiedDiffLine = UnifiedDiffLine(true, "^rename\\sfrom\\s(.+)$") { match: MatchResult, _: String ->
|
||||
processRenameFrom(
|
||||
match,
|
||||
line
|
||||
match
|
||||
)
|
||||
}
|
||||
private val RENAME_TO: UnifiedDiffLine = UnifiedDiffLine(true, "^rename\\sto\\s(.+)$") { match: MatchResult, line: String ->
|
||||
private val RENAME_TO: UnifiedDiffLine = UnifiedDiffLine(true, "^rename\\sto\\s(.+)$") { match: MatchResult, _: String ->
|
||||
processRenameTo(
|
||||
match,
|
||||
line
|
||||
match
|
||||
)
|
||||
}
|
||||
private val NEW_FILE_MODE: UnifiedDiffLine = UnifiedDiffLine(true, "^new\\sfile\\smode\\s(\\d+)") { match: MatchResult, line: String ->
|
||||
private val NEW_FILE_MODE: UnifiedDiffLine = UnifiedDiffLine(true, "^new\\sfile\\smode\\s(\\d+)") { match: MatchResult, _: String ->
|
||||
processNewFileMode(
|
||||
match,
|
||||
line
|
||||
match
|
||||
)
|
||||
}
|
||||
private val DELETED_FILE_MODE: UnifiedDiffLine = UnifiedDiffLine(true, "^deleted\\sfile\\smode\\s(\\d+)") { match: MatchResult, line: String ->
|
||||
private val DELETED_FILE_MODE: UnifiedDiffLine = UnifiedDiffLine(true, "^deleted\\sfile\\smode\\s(\\d+)") { match: MatchResult, _: String ->
|
||||
processDeletedFileMode(
|
||||
match,
|
||||
line
|
||||
match
|
||||
)
|
||||
}
|
||||
private val BINARY_FILE_CHANGED: UnifiedDiffLine = UnifiedDiffLine(true, "Binary files (.*) and (.*) differ") { match: MatchResult, line: String ->
|
||||
processBinaryFileChange(match, line)
|
||||
private val BINARY_FILE_CHANGED: UnifiedDiffLine = UnifiedDiffLine(true, "Binary files (.*) and (.*) differ") { _, _ ->
|
||||
processBinaryFileChange()
|
||||
}
|
||||
private val CHUNK: UnifiedDiffLine = UnifiedDiffLine(false, UNIFIED_DIFF_CHUNK_REGEXP) { match: MatchResult, chunkStart: String ->
|
||||
private val CHUNK: UnifiedDiffLine = UnifiedDiffLine(false, UNIFIED_DIFF_CHUNK_REGEXP) { match: MatchResult, _: String ->
|
||||
processChunk(
|
||||
match,
|
||||
chunkStart
|
||||
match
|
||||
)
|
||||
}
|
||||
private val LINE_NORMAL = UnifiedDiffLine("^\\s") { match: MatchResult, line: String ->
|
||||
private val LINE_NORMAL = UnifiedDiffLine("^\\s") { _: MatchResult, line: String ->
|
||||
processNormalLine(
|
||||
match,
|
||||
line
|
||||
)
|
||||
}
|
||||
private val LINE_DEL = UnifiedDiffLine("^-") { match: MatchResult, line: String ->
|
||||
private val LINE_DEL = UnifiedDiffLine("^-") { _: MatchResult, line: String ->
|
||||
processDelLine(
|
||||
match,
|
||||
line
|
||||
)
|
||||
}
|
||||
private val LINE_ADD = UnifiedDiffLine("^\\+") { match: MatchResult, line: String ->
|
||||
private val LINE_ADD = UnifiedDiffLine("^\\+") { _: MatchResult, line: String ->
|
||||
processAddLine(
|
||||
match,
|
||||
line
|
||||
)
|
||||
}
|
||||
@ -238,14 +225,14 @@ class UnifiedDiffReader internal constructor(lineReader: LineReader) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun processDiff(match: MatchResult, line: String) {
|
||||
private fun processDiff(line: String) {
|
||||
val fromTo = parseFileNames(line)
|
||||
actualFile!!.fromFile = fromTo[0]
|
||||
actualFile!!.toFile = fromTo[1]
|
||||
actualFile!!.diffCommand = line
|
||||
}
|
||||
|
||||
private fun processSimilarityIndex(match: MatchResult, line: String) {
|
||||
private fun processSimilarityIndex(match: MatchResult) {
|
||||
actualFile!!.similarityIndex = match.groupValues[1].toInt()
|
||||
}
|
||||
|
||||
@ -285,7 +272,7 @@ class UnifiedDiffReader internal constructor(lineReader: LineReader) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun processNormalLine(match: MatchResult, line: String) {
|
||||
private fun processNormalLine(line: String) {
|
||||
val cline = line.substring(1)
|
||||
originalTxt.add(cline)
|
||||
revisedTxt.add(cline)
|
||||
@ -293,7 +280,7 @@ class UnifiedDiffReader internal constructor(lineReader: LineReader) {
|
||||
addLineIdx++
|
||||
}
|
||||
|
||||
private fun processAddLine(match: MatchResult, line: String) {
|
||||
private fun processAddLine(line: String) {
|
||||
val cline = line.substring(1)
|
||||
revisedTxt.add(cline)
|
||||
addLineIdx++
|
||||
@ -301,7 +288,7 @@ class UnifiedDiffReader internal constructor(lineReader: LineReader) {
|
||||
addLineIdxList.add(new_ln - 1 + addLineIdx)
|
||||
}
|
||||
|
||||
private fun processDelLine(match: MatchResult, line: String) {
|
||||
private fun processDelLine(line: String) {
|
||||
val cline = line.substring(1)
|
||||
originalTxt.add(cline)
|
||||
delLineIdx++
|
||||
@ -309,7 +296,7 @@ class UnifiedDiffReader internal constructor(lineReader: LineReader) {
|
||||
delLineIdxList.add(old_ln - 1 + delLineIdx)
|
||||
}
|
||||
|
||||
private fun processChunk(match: MatchResult, chunkStart: String) {
|
||||
private fun processChunk(match: MatchResult) {
|
||||
// finalizeChunk();
|
||||
old_ln = toInteger(match, 1, 1)
|
||||
old_size = toInteger(match, 2, 1)
|
||||
@ -323,37 +310,37 @@ class UnifiedDiffReader internal constructor(lineReader: LineReader) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun processIndex(match: MatchResult, line: String) {
|
||||
private fun processIndex(line: String) {
|
||||
actualFile!!.index = line.substring(6)
|
||||
}
|
||||
|
||||
private fun processFromFile(match: MatchResult, line: String) {
|
||||
private fun processFromFile(line: String) {
|
||||
actualFile!!.fromFile = extractFileName(line)
|
||||
actualFile!!.fromTimestamp = extractTimestamp(line)
|
||||
}
|
||||
|
||||
private fun processToFile(match: MatchResult, line: String) {
|
||||
private fun processToFile(line: String) {
|
||||
actualFile!!.toFile = extractFileName(line)
|
||||
actualFile!!.toTimestamp = extractTimestamp(line)
|
||||
}
|
||||
|
||||
private fun processRenameFrom(match: MatchResult, line: String) {
|
||||
private fun processRenameFrom(match: MatchResult) {
|
||||
actualFile!!.renameFrom = match.groupValues[1]
|
||||
}
|
||||
|
||||
private fun processRenameTo(match: MatchResult, line: String) {
|
||||
private fun processRenameTo(match: MatchResult) {
|
||||
actualFile!!.renameTo = match.groupValues[1]
|
||||
}
|
||||
|
||||
private fun processNewFileMode(match: MatchResult, line: String) {
|
||||
private fun processNewFileMode(match: MatchResult) {
|
||||
actualFile!!.newFileMode = match.groupValues[1]
|
||||
}
|
||||
|
||||
private fun processDeletedFileMode(match: MatchResult, line: String) {
|
||||
private fun processDeletedFileMode(match: MatchResult) {
|
||||
actualFile!!.deletedFileMode = match.groupValues[1]
|
||||
}
|
||||
|
||||
private fun processBinaryFileChange(match: MatchResult, line: String) {
|
||||
private fun processBinaryFileChange() {
|
||||
// Nothing happens yet
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,6 @@ private class Merge3<T>(
|
||||
}
|
||||
|
||||
// private fun trace() {
|
||||
//
|
||||
// debug { result.indices.joinToString("") { "%3d".sprintf(it) } }
|
||||
// debug { result.joinToString("") { "%3s".sprintf(it.toString()) } }
|
||||
// debug {
|
||||
@ -205,22 +204,25 @@ private class Merge3<T>(
|
||||
|
||||
fun perform(): MergeResult<T> {
|
||||
val dA = diff(source, variantA).getDeltas()
|
||||
val dB = diff(source, variantB).getDeltas()
|
||||
val dB = removeDupes(diff(source, variantB).getDeltas(), dA)
|
||||
|
||||
|
||||
|
||||
debug { "dA: $dA" }
|
||||
debug { "dB: $dB" }
|
||||
|
||||
// trace()
|
||||
|
||||
debug { "adding first set" }
|
||||
for (d in dA) {
|
||||
debug { "adding dA $d" }
|
||||
applyDelta(d)
|
||||
}
|
||||
// optimized nust be applied first
|
||||
for (d in dB) {
|
||||
debug { "adding dB $d" }
|
||||
applyDelta(d)
|
||||
}
|
||||
// then apply full
|
||||
for (d in dA) {
|
||||
debug { "adding dA $d" }
|
||||
applyDelta(d)
|
||||
}
|
||||
|
||||
// detect ranges
|
||||
var conflictStart = -1
|
||||
@ -272,6 +274,25 @@ private class Merge3<T>(
|
||||
|
||||
return MergeResult<T>(result, updates, reindex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove deltas from [deltas] that are already included into [existing]. It might require
|
||||
* modifying existing deltas or removing it completely
|
||||
*/
|
||||
private fun removeDupes(
|
||||
deltas: List<AbstractDelta<T>>,
|
||||
existing: List<AbstractDelta<T>>,
|
||||
): List<AbstractDelta<T>> {
|
||||
// partial solutions: complete removal
|
||||
return deltas.filter { it !in existing }.mapNotNull { s0 ->
|
||||
var s: AbstractDelta<T>? = s0
|
||||
for (x in existing) {
|
||||
s = s?.optimizeAgainst(x)
|
||||
if( s == null ) break
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
18
src/commonMain/kotlin/net.sergeych.merge3/range_tools.kt
Normal file
18
src/commonMain/kotlin/net.sergeych.merge3/range_tools.kt
Normal file
@ -0,0 +1,18 @@
|
||||
package net.sergeych.merge3
|
||||
|
||||
/**
|
||||
* Check that other is inside this (inclusive)
|
||||
*/
|
||||
operator fun <T: Comparable<T>>ClosedRange<T>.contains(other: ClosedRange<T>): Boolean =
|
||||
other.start >= start && other.endInclusive <= endInclusive
|
||||
|
||||
|
||||
/**
|
||||
* Check that [other] overlaps (e.g., other has non-empty intersection with this), this also
|
||||
* includes the case when other is inside this or this is inside other completely. Use [contains] to exclude
|
||||
* the containing.
|
||||
*/
|
||||
|
||||
infix fun <T: Comparable<T>>ClosedRange<T>.overlaps(other: ClosedRange<T>): Boolean {
|
||||
return start in other || endInclusive in other
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
import net.sergeych.merge3.MergeResult
|
||||
import net.sergeych.merge3.MergedBlock
|
||||
import net.sergeych.merge3.merge3
|
||||
import net.sergeych.mp_logger.Log
|
||||
import net.sergeych.sprintf.sprintf
|
||||
//import net.sergeych.mp_logger.Log
|
||||
//import net.sergeych.sprintf.sprintf
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
val List<Char>.str: String get() = joinToString("")
|
||||
|
||||
fun <T>MergeResult<T>.toTrace(source: List<T>): 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()
|
||||
}
|
||||
// fun <T>MergeResult<T>.toTrace(source: List<T>): 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()
|
||||
// }
|
||||
|
||||
class BasicTest {
|
||||
@Test
|
||||
@ -27,25 +27,25 @@ class BasicTest {
|
||||
val b = "Bye world!!".toList()
|
||||
|
||||
val m = merge3(src, b, a)
|
||||
println(m.merged.str)
|
||||
// println(m.merged.str)
|
||||
// println(m.conflicts)
|
||||
println(m.changedAreas)
|
||||
// println(m.changedAreas)
|
||||
assertEquals("Bye cruel world!!!", m.merged.str)
|
||||
// assertTrue(m.noConflicts)
|
||||
}
|
||||
@Test
|
||||
fun testMergeNoConflicts3() {
|
||||
Log.connectConsole()
|
||||
// Log.connectConsole()
|
||||
val src = "Hello, world!".toList()
|
||||
val a = "Hello, friend!".toList()
|
||||
val b = "Bye world!".toList()
|
||||
|
||||
val m = merge3(src, a, b, true)
|
||||
println(m.merged.str)
|
||||
val m = merge3(src, a, b)
|
||||
// println(m.merged.str)
|
||||
// println(m.conflicts)
|
||||
println(m.changedAreas)
|
||||
// println(m.changedAreas)
|
||||
assertEquals("Bye friend!", m.merged.str)
|
||||
println(m.toTrace(src))
|
||||
// println(m.toTrace(src))
|
||||
// assertTrue(m.noConflicts)
|
||||
}
|
||||
|
||||
@ -56,103 +56,142 @@ class BasicTest {
|
||||
val b = "Bye world".toList()
|
||||
|
||||
val m = merge3(src, b, a)
|
||||
println(m.merged.str)
|
||||
// println(m.merged.str)
|
||||
// println(m.conflicts)
|
||||
println(m.changedAreas)
|
||||
// println(m.changedAreas)
|
||||
assertEquals("Bye friend", m.merged.str)
|
||||
// assertTrue(m.noConflicts)
|
||||
println(m.blocks)
|
||||
// println(m.blocks)
|
||||
}
|
||||
@Test
|
||||
fun testMergeWithConflicts() {
|
||||
Log.connectConsole()
|
||||
// Log.connectConsole()
|
||||
val src = "Hello world".toList()
|
||||
val a = "Hello 123".toList()
|
||||
val b = "Hello 456".toList()
|
||||
|
||||
val m = merge3(src, b, a, false)
|
||||
println(m.merged.str)
|
||||
val m = merge3(src, b, a)
|
||||
// println(m.merged.str)
|
||||
// println(m.conflicts)
|
||||
println(m.changedAreas)
|
||||
assertEquals("Hello 123456", m.merged.str)
|
||||
// println(m.changedAreas)
|
||||
assertEquals("Hello 456123", m.merged.str)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMergeIndexes() {
|
||||
Log.connectConsole()
|
||||
// Log.connectConsole()
|
||||
val src = "Hello".toList()
|
||||
val a = "123 Hello".toList()
|
||||
val b = "456 Hello".toList()
|
||||
|
||||
val m = merge3(src, b, a, true)
|
||||
val m = merge3(src, b, a)
|
||||
// println(m.toTrace(src))
|
||||
// println(m.changedAreas)
|
||||
// for( b in m.blocks )
|
||||
// println(b)
|
||||
val unchanged = m.blocks.last() as MergedBlock.Unchanged
|
||||
println(unchanged)
|
||||
// println(unchanged)
|
||||
assertEquals(0, unchanged.referenceIndex)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMergeIndexes2() {
|
||||
Log.connectConsole()
|
||||
// Log.connectConsole()
|
||||
val src = "Hello".toList()
|
||||
val a = "1Hello".toList()
|
||||
val b = "2Hello".toList()
|
||||
|
||||
val m = merge3(src, b, a, true)
|
||||
val m = merge3(src, b, a)
|
||||
val unchanged = m.blocks.last() as MergedBlock.Unchanged
|
||||
println(unchanged)
|
||||
// println(unchanged)
|
||||
assertEquals(0, unchanged.referenceIndex)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMergeFilter() {
|
||||
Log.connectConsole()
|
||||
// Log.connectConsole()
|
||||
val source = "lxcvv".toList()
|
||||
val our = "lssdfasdwerwev".toList()
|
||||
val their = "lxcvasdfasdfs".toList()
|
||||
val result = merge3(source, their, our)
|
||||
println("got result: ${result.toTrace(source)}")
|
||||
println("got result: ${result.blocks}")
|
||||
// println("got result: ${result.toTrace(source)}")
|
||||
// println("got result: ${result.blocks}")
|
||||
result.blocks.filter { it is MergedBlock.Unchanged }
|
||||
assertTrue(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMergeEmpty() {
|
||||
Log.connectConsole()
|
||||
// Log.connectConsole()
|
||||
val source = "".toList()
|
||||
val our = "".toList()
|
||||
val their = "".toList()
|
||||
val result = merge3(source, their, our)
|
||||
println("got result: ${result.toTrace(source)}")
|
||||
println("got result: ${result.blocks}")
|
||||
// println("got result: ${result.toTrace(source)}")
|
||||
// println("got result: ${result.blocks}")
|
||||
result.blocks.filter { it is MergedBlock.Unchanged }
|
||||
assertTrue(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMergeUnchanged() {
|
||||
Log.connectConsole()
|
||||
fun testMergeUnchanged1() {
|
||||
val source = "abc".toList()
|
||||
val their = "abDEF".toList()
|
||||
val our = "abcGHY".toList()
|
||||
val their = "abcDEF".toList()
|
||||
val our = "abcDEF".toList()
|
||||
|
||||
val result = merge3(source, their, our)
|
||||
println(result.merged.str)
|
||||
assertTrue(false)
|
||||
// println(result.merged.str)
|
||||
// println(result.blocks)
|
||||
assertEquals("abcDEF", result.merged.str)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMergeConflictOrder() {
|
||||
fun testMergePartial1() {
|
||||
val source = "abc".toList()
|
||||
val their = "abcDEF1".toList()
|
||||
val our = "abcDEF".toList()
|
||||
|
||||
val result = merge3(source, their, our)
|
||||
// println(result.merged.str)
|
||||
// println(result.blocks)
|
||||
assertEquals("abcDEF1", result.merged.str)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMergePartial2() {
|
||||
val source = "abc".toList()
|
||||
val their = "abcDEF".toList()
|
||||
val our = "abcDEF1".toList()
|
||||
|
||||
val result = merge3(source, their, our)
|
||||
// println(result.merged.str)
|
||||
// println(result.blocks)
|
||||
assertEquals("abcDEF1", result.merged.str)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMergeConflictOrder1() {
|
||||
// Log.connectConsole()
|
||||
val source = "abc".toList()
|
||||
val their = "abcDEF".toList()
|
||||
val our = "abcGHY".toList()
|
||||
|
||||
val result = merge3(source, their, our)
|
||||
// println(result.merged.str)
|
||||
// println(result.blocks)
|
||||
// the order is unexpected:
|
||||
assertTrue { result.merged.str in listOf("abcDEFGHY", "abcGHYDEF") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMergeConflictOrder2() {
|
||||
val source = "ABAB".toList()
|
||||
val our = "ACAC".toList()
|
||||
val their = "ADAD".toList()
|
||||
|
||||
val result = merge3(source, their, our)
|
||||
println(result.merged.str)
|
||||
assertTrue(false)
|
||||
// println(result.merged.str)
|
||||
// The order is undetermied:
|
||||
assertTrue(result.merged.str in listOf("ACDADC", "ADCACD", "ADCADC", "ACDACD"))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user