Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do another general cleanup #114

Merged
merged 6 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

Check warning on line 46 in src/commonMain/kotlin/io/github/petertrr/diffutils/StringUtils.kt

View check run for this annotation

Codecov / codecov/patch

src/commonMain/kotlin/io/github/petertrr/diffutils/StringUtils.kt#L46

Added line #L46 was not covered by tests
}

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
Loading