Skip to content

Commit

Permalink
add BigTextTransformer#restoreToOriginal(IntRange)
Browse files Browse the repository at this point in the history
  • Loading branch information
sunny-chung committed Sep 22, 2024
1 parent 72b34a5 commit 7600310
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ interface BigTextTransformer {

fun replace(range: IntRange, text: String, offsetMapping: BigTextTransformOffsetMapping)

// fun restoreToOriginal(range: IntRange)
fun restoreToOriginal(range: IntRange)
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,24 @@ class BigTextTransformerImpl(internal val delegate: BigTextImpl) : BigTextImpl(c
return BigTextTransformNodeValue()
}

fun insertOriginal(pos: Int, nodeValue: BigTextNodeValue) {
require(pos in 0 .. originalLength) { "Out of bound. pos = $pos, originalLength = $originalLength" }
fun insertOriginal(
pos: Int,
nodeValue: BigTextNodeValue,
bufferOffsetStart: Int = nodeValue.bufferOffsetStart,
bufferOffsetEndExclusive: Int = nodeValue.bufferOffsetEndExclusive,
) {
require(pos in 0..originalLength) { "Out of bound. pos = $pos, originalLength = $originalLength" }

insertChunkAtPosition(
position = pos,
chunkedStringLength = nodeValue.bufferLength,
chunkedStringLength = bufferOffsetEndExclusive - bufferOffsetStart,
ownership = BufferOwnership.Delegated,
buffer = nodeValue.buffer,
range = nodeValue.bufferOffsetStart until nodeValue.bufferOffsetEndExclusive
range = bufferOffsetStart until bufferOffsetEndExclusive
) {
bufferIndex = -1
bufferOffsetStart = nodeValue.bufferOffsetStart
bufferOffsetEndExclusive = nodeValue.bufferOffsetEndExclusive
this.bufferOffsetStart = bufferOffsetStart
this.bufferOffsetEndExclusive = bufferOffsetEndExclusive
this.buffer = nodeValue.buffer
this.bufferOwnership = BufferOwnership.Delegated

Expand Down Expand Up @@ -521,6 +526,41 @@ class BigTextTransformerImpl(internal val delegate: BigTextImpl) : BigTextImpl(c
override fun replace(range: IntRange, text: String) = transformReplace(range, text)

override fun replace(range: IntRange, text: String, offsetMapping: BigTextTransformOffsetMapping) = transformReplace(range, text, offsetMapping)

override fun restoreToOriginal(range: IntRange) {
val renderPositionAtOriginalStart = findTransformedPositionByOriginalPosition(range.start)
val renderPositionAtOriginalEnd = findTransformedPositionByOriginalPosition(range.endInclusive)

deleteTransformIf(range)
deleteOriginal(range)

// insert the original text from `delegate`
val originalNodeStart = delegate.tree.findNodeByCharIndex(range.start)
?: throw IndexOutOfBoundsException("Original node at position ${range.start} not found")
var nodePositionStart = delegate.tree.findPositionStart(originalNodeStart)
var insertPoint = range.start
var node = originalNodeStart
var insertOffsetStart = node.value.bufferOffsetStart + (range.start - nodePositionStart)
do {
val insertOffsetEndExclusive = if ((nodePositionStart + node.value.bufferLength) > (range.endInclusive + 1)) {
node.value.bufferOffsetStart + (range.endInclusive + 1 - nodePositionStart)
} else {
node.value.bufferOffsetEndExclusive
}
insertOriginal(insertPoint, node.value, insertOffsetStart, insertOffsetEndExclusive)

if (insertPoint + (insertOffsetEndExclusive - insertOffsetStart) > range.endInclusive) {
break
}

node = delegate.tree.nextNode(node)!!
nodePositionStart = delegate.tree.findPositionStart(node)
insertPoint += insertOffsetEndExclusive - insertOffsetStart
insertOffsetStart = node.value.bufferOffsetStart
} while (nodePositionStart <= range.endInclusive)

layout(maxOf(0, renderPositionAtOriginalStart - 1), minOf(length, renderPositionAtOriginalEnd + 1))
}
}

fun RedBlackTree<BigTextTransformNodeValue>.Node.transformedOffset(): Int =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.sunnychung.application.multiplatform.hellohttp.test.bigtext.transfor
import com.sunnychung.application.multiplatform.hellohttp.test.bigtext.randomString
import com.sunnychung.application.multiplatform.hellohttp.ux.bigtext.BigText
import com.sunnychung.application.multiplatform.hellohttp.ux.bigtext.BigTextImpl
import com.sunnychung.application.multiplatform.hellohttp.ux.bigtext.BigTextTransformOffsetMapping
import com.sunnychung.application.multiplatform.hellohttp.ux.bigtext.BigTextTransformerImpl
import com.sunnychung.application.multiplatform.hellohttp.ux.bigtext.isD
import org.junit.jupiter.api.BeforeEach
Expand Down Expand Up @@ -895,6 +896,67 @@ class BigTextTransformerImplTest {
}
}

@ParameterizedTest
@ValueSource(ints = [1048576, 64, 16])
fun restoreToOriginal(chunkSize: Int) {
val initialText = "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
val original = BigTextImpl(chunkSize = chunkSize)
original.append(initialText)
val transformed = BigTextTransformerImpl(original)

transformed.replace(11 .. 18, "ABCD", BigTextTransformOffsetMapping.Incremental)
"12345678901ABCD0123456789012345678901234567890123456789012345678901234567890".let { expected ->
assertEquals(expected, transformed.buildString())
assertEquals(initialText, original.buildString())
assertAllSubstring(expected, transformed)
}

transformed.insertAt(61, "EFGHIJ")
"12345678901ABCD012345678901234567890123456789012345678901EFGHIJ2345678901234567890".let { expected ->
assertEquals(expected, transformed.buildString())
assertEquals(initialText, original.buildString())
assertAllSubstring(expected, transformed)
}

transformed.restoreToOriginal(0 .. initialText.length - 1)
initialText.let { expected ->
assertEquals(expected, transformed.buildString())
assertEquals(expected, original.buildString())
assertAllSubstring(expected, transformed)
}
}

@ParameterizedTest
@ValueSource(ints = [1048576, 64, 16])
fun restoreToOriginalThenOriginalDelete(chunkSize: Int) {
val initialText = "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
val original = BigTextImpl(chunkSize = chunkSize)
original.append(initialText)
val transformed = BigTextTransformerImpl(original)

transformed.replace(11 .. 18, "ABCD", BigTextTransformOffsetMapping.WholeBlock)
"12345678901ABCD0123456789012345678901234567890123456789012345678901234567890".let { expected ->
assertEquals(expected, transformed.buildString())
assertEquals(initialText, original.buildString())
assertAllSubstring(expected, transformed)
}

transformed.restoreToOriginal(11 .. 18)
initialText.let { expected ->
assertEquals(expected, transformed.buildString())
assertEquals(expected, original.buildString())
assertAllSubstring(expected, transformed)
}

original.delete(18 .. 18)

"1234567890123456780123456789012345678901234567890123456789012345678901234567890".let { expected ->
assertEquals(expected, original.buildString())
assertEquals(expected, transformed.buildString())
assertAllSubstring(expected, transformed)
}
}

@BeforeEach
fun beforeEach() {
isD = false
Expand Down

0 comments on commit 7600310

Please sign in to comment.