Skip to content

Commit

Permalink
Do another general cleanup (#114)
Browse files Browse the repository at this point in the history
- Updates the documentation
- Refactor utility functions to be more Kotlin-idiomatic
- Refactor `DiffRowGenerator` to allow passing a custom algorithm,
instead of a custom equalizer only
  • Loading branch information
lppedd authored Jun 22, 2024
1 parent b90ea21 commit 63eb2b7
Show file tree
Hide file tree
Showing 12 changed files with 100 additions and 130 deletions.
59 changes: 20 additions & 39 deletions src/commonMain/kotlin/io/github/petertrr/diffutils/DiffUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ package io.github.petertrr.diffutils

import io.github.petertrr.diffutils.algorithm.DiffAlgorithm
import io.github.petertrr.diffutils.algorithm.DiffAlgorithmListener
import io.github.petertrr.diffutils.algorithm.DiffEqualizer
import io.github.petertrr.diffutils.algorithm.NoopAlgorithmListener
import io.github.petertrr.diffutils.algorithm.myers.MyersDiff
import io.github.petertrr.diffutils.patch.Patch
import io.github.petertrr.diffutils.patch.PatchFailedException
import io.github.petertrr.diffutils.text.DiffRowGenerator
import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads
Expand All @@ -35,16 +33,16 @@ import kotlin.jvm.JvmOverloads
private val lineBreak = Regex("\r\n|\r|\n")

/**
* Computes the difference between the source and target text.
* Computes the difference between two strings.
*
* By default, uses the Myers algorithm.
*
* @param sourceText The original text
* @param targetText The target text
* @param sourceText A string representing the original text
* @param targetText A string representing the revised text
* @param algorithm The diff algorithm to use
* @param progress The diff algorithm progress listener
* @param includeEqualParts Whether to include equal data parts into the patch. `false` by default.
* @return The patch describing the difference between the original and target text
* @return The patch describing the difference between the original and revised strings
*/
@JvmOverloads
public fun diff(
Expand All @@ -62,37 +60,17 @@ public fun diff(
includeEqualParts = includeEqualParts,
)

/**
* Computes the difference between the source and target list of elements using the Myers algorithm.
*
* @param source The original elements
* @param target The target elements
* @param equalizer The equalizer to replace the default compare algorithm [Any.equals].
* If `null`, the default equalizer of the default algorithm is used.
* @return The patch describing the difference between the source and target sequences
*/
public fun <T> diff(
source: List<T>,
target: List<T>,
equalizer: DiffEqualizer<T>,
): Patch<T> =
diff(
source = source,
target = target,
algorithm = MyersDiff(equalizer),
)

/**
* Computes the difference between the original and target list of elements.
*
* By default, uses the Meyers algorithm.
* By default, uses the Myers algorithm.
*
* @param source The original elements
* @param target The target elements
* @param source A list representing the original sequence of elements
* @param target A list representing the revised sequence of elements
* @param algorithm The diff algorithm to use
* @param progress The diff algorithm progress listener
* @param includeEqualParts Whether to include equal data parts into the patch. `false` by default.
* @return The patch describing the difference between the original and target sequences
* @param includeEqualParts Whether to include equal parts in the resulting patch. `false` by default.
* @return The patch describing the difference between the original and revised sequences
*/
@JvmOverloads
public fun <T> diff(
Expand All @@ -114,6 +92,10 @@ public fun <T> diff(
*
* This one uses the "trick" to make out of texts lists of characters,
* like [DiffRowGenerator] does and merges those changes at the end together again.
*
* @param original A string representing the original text
* @param revised A string representing the revised text
* @return The patch describing the difference between the original and revised text
*/
public fun diffInline(original: String, revised: String): Patch<String> {
val origChars = original.toCharArray()
Expand Down Expand Up @@ -142,22 +124,21 @@ public fun diffInline(original: String, revised: String): Patch<String> {
}

/**
* Patch the original text with the given patch.
* Applies the given patch to the original list and returns the revised list.
*
* @param original The original text
* @param original A list representing the original sequence of elements
* @param patch The patch to apply
* @return The revised text
* @throws PatchFailedException If the patch cannot be applied
* @return A list representing the revised sequence of elements
*/
public fun <T> patch(original: List<T>, patch: Patch<T>): List<T> =
patch.applyTo(original)

/**
* Unpatch the revised text for a given patch
* Applies the given patch to the revised list and returns the original list.
*
* @param revised The revised text
* @param patch The given patch
* @return The original text
* @param revised A list representing the revised sequence of elements
* @param patch The patch to apply
* @return A list representing the original sequence of elements
*/
public fun <T> unpatch(revised: List<T>, patch: Patch<T>): List<T> =
patch.restore(revised)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,57 +18,57 @@
*/
@file:JvmName("StringUtils")

package io.github.petertrr.diffutils.text
package io.github.petertrr.diffutils

import kotlin.jvm.JvmName

/**
* Replaces all opening and closing tags (`<` and `>`)
* with their escaped sequences (`&lt;` and `&gt;`).
*/
internal fun htmlEntities(str: String): String =
str.replace("<", "&lt;").replace(">", "&gt;")
internal fun String.htmlEntities(): String =
replace("<", "&lt;").replace(">", "&gt;")

/**
* Normalizes a string by escaping some HTML meta characters
* and replacing tabs with 4 spaces each.
*/
internal fun normalize(str: String): String =
htmlEntities(str).replace("\t", " ")
internal fun String.normalize(): String =
htmlEntities().replace("\t", " ")

/**
* Wrap the text with the given column width
* Wrap the text with the given column width.
*/
internal fun wrapText(line: String, columnWidth: Int): String {
internal fun String.wrapText(columnWidth: Int): String {
require(columnWidth >= 0) { "Column width must be greater than or equal to 0" }

if (columnWidth == 0) {
return line
return this
}

val length = line.length
val delimiter = "<br/>".length
val length = length
val delimiterLength = "<br/>".length
var widthIndex = columnWidth
val b = StringBuilder(line)
val sb = StringBuilder(this)
var count = 0

while (length > widthIndex) {
var breakPoint = widthIndex + delimiter * count
var breakPoint = widthIndex + delimiterLength * count

if (b[breakPoint - 1].isHighSurrogate() && b[breakPoint].isLowSurrogate()) {
if (sb[breakPoint - 1].isHighSurrogate() && sb[breakPoint].isLowSurrogate()) {
// Shift a breakpoint that would split a supplemental code-point.
breakPoint += 1

if (breakPoint == b.length) {
if (breakPoint == sb.length) {
// Break before instead of after if this is the last code-point.
breakPoint -= 2
}
}

b.insert(breakPoint, "<br/>")
sb.insert(breakPoint, "<br/>")
widthIndex += columnWidth
count++
}

return b.toString()
return sb.toString()
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public class MyersDiff<T>(private val equalizer: DiffEqualizer<T> = EqualsDiffEq
val max = origSize + revSize + 1
val size = 1 + 2 * max
val middle = size / 2
val diagonal: Array<PathNode?> = arrayOfNulls(size)
val diagonal = arrayOfNulls<PathNode>(size)
diagonal[middle + 1] = PathNode(0, -1, snake = true, bootstrap = true, prev = null)

for (d in 0..<max) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public class MyersDiffWithLinearSpace<T>(
progress: DiffAlgorithmListener,
) {
progress.diffStep((end1 - start1) / 2 + (end2 - start2) / 2, -1)

val middle = getMiddleSnake(data, start1, end1, start2, end2)

if (middle == null ||
Expand All @@ -65,28 +64,29 @@ public class MyersDiffWithLinearSpace<T>(
++i
++j
} else {
// index is less than 0 here if data.script is empty
val index = data.script.size - 1

// TODO: compress these commands
if (end1 - start1 > end2 - start2) {
if (data.script.isEmpty() ||
data.script[data.script.size - 1].endOriginal != i ||
data.script[data.script.size - 1].deltaType != DeltaType.DELETE
if (index < 0 ||
data.script[index].endOriginal != i ||
data.script[index].deltaType != DeltaType.DELETE
) {
data.script.add(Change(DeltaType.DELETE, i, i + 1, j, j))
} else {
data.script[data.script.size - 1] =
data.script[data.script.size - 1].copy(endOriginal = i + 1)
data.script[index] = data.script[index].copy(endOriginal = i + 1)
}

++i
} else {
if (data.script.isEmpty() ||
data.script[data.script.size - 1].endRevised != j ||
data.script[data.script.size - 1].deltaType != DeltaType.INSERT
if (index < 0 ||
data.script[index].endRevised != j ||
data.script[index].deltaType != DeltaType.INSERT
) {
data.script.add(Change(DeltaType.INSERT, i, i, j, j + 1))
} else {
data.script[data.script.size - 1] =
data.script[data.script.size - 1].copy(endRevised = j + 1)
data.script[index] = data.script[index].copy(endRevised = j + 1)
}

++j
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,7 @@ internal class PathNode(
return null
}

return if (!snake && prev != null) {
prev.previousSnake()
} else {
this
}
return if (!snake && prev != null) prev.previousSnake() else this
}

override fun toString(): String {
Expand Down
Loading

0 comments on commit 63eb2b7

Please sign in to comment.