Skip to content

Commit

Permalink
Add FragmentFormatter
Browse files Browse the repository at this point in the history
  • Loading branch information
valencik committed Nov 18, 2024
1 parent 82ae0c3 commit 9c93fa6
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package pink.cozydev.protosearch.highlight

case class FragmentFormatter(
maxSize: Int,
startTag: String,
endTag: String,
) {
private val tagSize = startTag.size + endTag.size
private val tags: Array[String] = Array(startTag, endTag)

def format(fragment: String, offsets: Iterable[Int]): String =
if (offsets.size == 0) fragment
else {
val offsetArr = offsets.toArray
assert(offsetArr.size % 2 == 0, "even number of offsets required")

val sb = new StringBuilder()
sb.sizeHint(fragment.size + tagSize * (offsets.size / 2))
val chars = fragment.toCharArray()

try {
// Add initial characters
sb.appendAll(chars, 0, offsetArr(0))
var charsOffset = offsetArr(0)

// Loop through offsets, two at a time
var i = 0
while (i < offsetArr.size) {
val offsetStart = offsetArr(i)
// add chars between offsets
val inbetweenChars = offsetStart - charsOffset
sb.appendAll(chars, charsOffset, inbetweenChars)
charsOffset += inbetweenChars

// start new offset
sb.append(startTag)
val offsetLength = offsetArr(i + 1)
sb.appendAll(chars, charsOffset, offsetLength)
sb.append(endTag)
charsOffset += offsetLength
i += 2
}
// Add remaining characters
sb.appendAll(chars, charsOffset, chars.size - charsOffset)
sb.result()
} catch {
case _: IndexOutOfBoundsException =>
throw new IllegalArgumentException("Offset exceeded string length")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package pink.cozydev.protosearch.highlight

import munit.FunSuite

class FragmentFormatterSuite extends FunSuite {
val formatter = FragmentFormatter(100, "<b>", "</b>")

test("formats string with empty offsets") {
val s = "hello world"
val actual = formatter.format(s, List.empty)
val expected = s
assertEquals(actual, expected)
}

test("formats string with one pair of offsets") {
val s = "hello world"
val actual = formatter.format(s, List(6, 5))
val expected = "hello <b>world</b>"
assertEquals(actual, expected)
}

test("formats string with offset in the middle") {
val s = "hello world, how are you?"
val actual = formatter.format(s, List(6, 5))
val expected = "hello <b>world</b>, how are you?"
assertEquals(actual, expected)
}

test("formats string with two pair of offsets") {
val s = "hello world"
val actual = formatter.format(s, List(0, 5, 6, 5))
val expected = "<b>hello</b> <b>world</b>"
assertEquals(actual, expected)
}

test("formats whole string if one offset") {
val s = "hello world"
val actual = formatter.format(s, List(0, 11))
val expected = "<b>hello world</b>"
assertEquals(actual, expected)
}

test("throws if offset length exceeds string boundary") {
val s = "hello world"
intercept[java.lang.IllegalArgumentException] {
formatter.format(s, List(6, 5 + 1))
}
}

test("throws if odd number of offset integers") {
intercept[java.lang.AssertionError] {
formatter.format("", List(1))
}
}
}

0 comments on commit 9c93fa6

Please sign in to comment.