LineOp.kt

package de.werkbaum.diff

/**
 * Eine Zeilen-Operation relativ zu einer Basisversion (0-basierter Index).
 *
 * Die drei Formen entsprechen 1:1 dem Draht-Format aus dem Live-Editing-Konzept
 * (`backend/docs/live-editing-proposal.md`, Abschnitt „Datenmodell"):
 *
 * - [Insert]  – [lines] werden **vor** [index] eingefügt; `index == Zeilenzahl`
 *               hängt an.
 * - [Delete]  – [count] Zeilen ab [index] entfallen.
 * - [Replace] – [count] Zeilen ab [index] werden durch [lines] ersetzt.
 *
 * Bewusst ein eigenes, Spring-freies Modell: Anwenden, Berechnen und Rebasen
 * sind reine Funktionen und damit ohne Kontext testbar (D54-Nachtrag 3 zieht
 * dieselbe Grenze im Frontend).
 */
sealed interface LineOp {
    val index: Int

    data class Insert(override val index: Int, val lines: List<String>) : LineOp
    data class Delete(override val index: Int, val count: Int) : LineOp
    data class Replace(override val index: Int, val count: Int, val lines: List<String>) : LineOp
}

/** Zahl der Zeilen, die diese Operation aus der Basis entfernt. */
val LineOp.removedCount: Int
    get() = when (this) {
        is LineOp.Insert -> 0
        is LineOp.Delete -> count
        is LineOp.Replace -> count
    }

/** Zeilen, die diese Operation einsetzt. */
val LineOp.insertedLines: List<String>
    get() = when (this) {
        is LineOp.Insert -> lines
        is LineOp.Delete -> emptyList()
        is LineOp.Replace -> lines
    }

/** Ende des betroffenen Basisbereichs, exklusiv. Bei [LineOp.Insert] gleich [LineOp.index]. */
val LineOp.endExclusive: Int
    get() = index + removedCount

/** Wie viele Zeilen das Dokument durch diese Operation länger (positiv) oder kürzer wird. */
val LineOp.lineDelta: Int
    get() = insertedLines.size - removedCount