Skip to content

Commit 4fd47d2

Browse files
committed
add layout
1 parent fbd4078 commit 4fd47d2

1 file changed

Lines changed: 99 additions & 0 deletions

File tree

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package io.wdsj.asw.bukkit.listener
2+
3+
internal data class SignReplacementSpan(
4+
val start: Int,
5+
val end: Int,
6+
val replacement: String,
7+
)
8+
9+
internal object SignTextLayout {
10+
fun replaceSegments(
11+
segmentLengths: List<Int>,
12+
content: String,
13+
spans: List<SignReplacementSpan>,
14+
): List<String> {
15+
if (segmentLengths.isEmpty()) return emptyList()
16+
17+
val lengths = segmentLengths.toIntArray()
18+
require(lengths.all { it > 0 }) { "Segment lengths must be positive" }
19+
require(lengths.sum() == content.length) { "Segment lengths must cover the content exactly" }
20+
21+
val starts = segmentStarts(lengths)
22+
val replacements = Array(lengths.size) { StringBuilder() }
23+
var cursor = 0
24+
for (span in spans.sortedWith(compareBy<SignReplacementSpan> { it.start }.thenByDescending { it.end })) {
25+
val start = span.start.coerceIn(0, content.length)
26+
val end = span.end.coerceIn(start, content.length)
27+
if (start < cursor || start == end) continue
28+
29+
appendUnchanged(replacements, lengths, starts, content, cursor, start)
30+
appendReplacement(replacements, lengths, starts, start, end, span.replacement)
31+
cursor = end
32+
}
33+
appendUnchanged(replacements, lengths, starts, content, cursor, content.length)
34+
return replacements.map(StringBuilder::toString)
35+
}
36+
37+
private fun segmentStarts(lengths: IntArray): IntArray {
38+
val starts = IntArray(lengths.size)
39+
for (index in 1 until lengths.size) {
40+
starts[index] = starts[index - 1] + lengths[index - 1]
41+
}
42+
return starts
43+
}
44+
45+
private fun appendUnchanged(
46+
replacements: Array<StringBuilder>,
47+
lengths: IntArray,
48+
starts: IntArray,
49+
content: String,
50+
start: Int,
51+
end: Int,
52+
) {
53+
var cursor = start
54+
while (cursor < end) {
55+
val segmentIndex = segmentIndexAt(starts, cursor)
56+
val segmentEnd = starts[segmentIndex] + lengths[segmentIndex]
57+
val copyEnd = minOf(end, segmentEnd)
58+
replacements[segmentIndex].append(content, cursor, copyEnd)
59+
cursor = copyEnd
60+
}
61+
}
62+
63+
private fun appendReplacement(
64+
replacements: Array<StringBuilder>,
65+
lengths: IntArray,
66+
starts: IntArray,
67+
start: Int,
68+
end: Int,
69+
replacement: String,
70+
) {
71+
if (replacement.length != end - start) {
72+
replacements[segmentIndexAt(starts, start)].append(replacement)
73+
return
74+
}
75+
76+
var contentOffset = start
77+
var replacementOffset = 0
78+
while (contentOffset < end) {
79+
val segmentIndex = segmentIndexAt(starts, contentOffset)
80+
val segmentEnd = starts[segmentIndex] + lengths[segmentIndex]
81+
val copyEnd = minOf(end, segmentEnd)
82+
val copyLength = copyEnd - contentOffset
83+
replacements[segmentIndex].append(
84+
replacement,
85+
replacementOffset,
86+
replacementOffset + copyLength,
87+
)
88+
contentOffset = copyEnd
89+
replacementOffset += copyLength
90+
}
91+
}
92+
93+
private fun segmentIndexAt(starts: IntArray, index: Int): Int {
94+
for (segmentIndex in starts.indices.reversed()) {
95+
if (index >= starts[segmentIndex]) return segmentIndex
96+
}
97+
return 0
98+
}
99+
}

0 commit comments

Comments
 (0)