diff --git a/fleet/andel/src/andel/operation/Operation.kt b/fleet/andel/src/andel/operation/Operation.kt index 110e3689c5cd..3be4b7e392d8 100644 --- a/fleet/andel/src/andel/operation/Operation.kt +++ b/fleet/andel/src/andel/operation/Operation.kt @@ -38,6 +38,89 @@ sealed class Op { } } +class OpsIterator( + private val owner: Any?, + private var cursor: Rope.Cursor>?, + private val size: Int, + private var index: Int = 0, + private var elementIndex: Int = 0 +): Iterator { + sealed interface Pos { + object Begin: Pos + object End: Pos + data class LenBefore(val offset: Int): Pos + data class LenAfter(val offset: Int): Pos + } + + override fun next(): Op { + require(hasNext()) + + if (elementIndex >= cursor!!.element.size) { + cursor = cursor!!.next(owner) + elementIndex = 0 + } + val op = cursor!!.element[elementIndex] + ++elementIndex + ++index + return op + } + + override fun hasNext(): Boolean { + return cursor != null && index < size + } + + fun prev(): Op { + require(hasPrev()) + + if (elementIndex == 0) { + cursor = cursor!!.prev(owner) + elementIndex = cursor!!.element.size + } + --elementIndex + --index + return cursor!!.element[elementIndex] + } + + fun hasPrev(): Boolean { + return index > 0 + } + + fun scanTo(pos: Pos) { + when (pos) { + Pos.Begin -> { + cursor = cursor!!.scan(owner, OperationMonoid.Count, 0) + index = 0 + elementIndex = 0 + } + Pos.End -> { + cursor = cursor!!.scan(owner, OperationMonoid.Count, size) + index = size + elementIndex = MAX_LEAF_SIZE + } + is Pos.LenBefore -> { + val c = cursor!! + cursor = c.scan(owner, OperationMonoid.LenBefore, pos.offset) + elementIndex = 0 + index = c.size(OperationMonoid.Count) + while (elementIndex < c.element.size && c.element[elementIndex].lenBefore <= pos.offset) { + ++elementIndex + ++index + } + } + is Pos.LenAfter -> { + val c = cursor!! + cursor = c.scan(owner, OperationMonoid.LenAfter, pos.offset) + elementIndex = 0 + index = c.size(OperationMonoid.Count) + while (elementIndex < c.element.size && c.element[elementIndex].lenAfter <= pos.offset) { + ++elementIndex + ++index + } + } + } + } +} + /** * After introducing Replace operation there still exists two asymmetries: * 1. Insert operations at the same place do not commute, so that {transform} function is not commutative @@ -52,13 +135,20 @@ sealed class Op { class Operation(internal val rope: OpsRope) { constructor(ops: List): this(OperationMonoid.ropeOf(listOf(ops.toTypedArray()))) - val ops: Sequence get() = sequence { + val ops: Sequence get() = sequence { yieldAll(begin()) } + + fun begin(): OpsIterator { val owner = Any() - var cursor: Rope.Cursor>? = rope.cursor(owner) - while (cursor != null) { - yieldAll(cursor.element.iterator()) - cursor = cursor.next(owner) - } + val iterator = OpsIterator(owner, rope.cursor(owner), size) + iterator.scanTo(OpsIterator.Pos.Begin) + return iterator + } + + fun end(): OpsIterator { + val owner = Any() + val iterator = OpsIterator(owner, rope.cursor(owner), size) + iterator.scanTo(OpsIterator.Pos.End) + return iterator } val size: Int diff --git a/fleet/andel/src/andel/rope/Rope.kt b/fleet/andel/src/andel/rope/Rope.kt index 0ac0a753d921..8a6c5445d0b5 100644 --- a/fleet/andel/src/andel/rope/Rope.kt +++ b/fleet/andel/src/andel/rope/Rope.kt @@ -94,16 +94,25 @@ class Rope internal constructor( /** * Returns a pointer to the next chunk. - * If [owner] is the same as before, the previous Cursur is recycled and it's internals are reused for creating a new one. + * If [owner] is the same as before, the previous Cursor is recycled and it's internals are reused for creating a new one. * */ fun next(owner: Any?): Cursor? = zipper.nextLeaf(owner, monoid)?.let { leaf -> cursor(leaf) } + /** + * Returns a pointer to the prev chunk. + * If [owner] is the same as before, the previous Cursor is recycled and it's internals are reused for creating a new one. + * */ + fun prev(owner: Any?): Cursor? = + zipper.prevLeaf(owner, monoid)?.let { leaf -> + cursor(leaf) + } + /** * Builds a new rope, accomodating all changes made to the cursor. - * If [owner] is the same as before, the previous Cursur is recycled and it's internals are reused. + * If [owner] is the same as before, the previous Cursor is recycled and it's internals are reused. * */ fun rope(owner: Any?): Rope = zipper.rope(owner, monoid)