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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user