aboutsummaryrefslogtreecommitdiff
path: root/src/texturePacks/java/moe/nea/firmament/features/texturepack/TreeishTextReplacer.kt
blob: ed486f53e08947f3c635b9f3a6a50724e1192285 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package moe.nea.firmament.features.texturepack

import java.util.regex.Matcher
import util.json.CodecSerializer
import kotlinx.serialization.Serializable
import net.minecraft.network.chat.Style
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.ComponentSerialization
import moe.nea.firmament.util.directLiteralStringContent
import moe.nea.firmament.util.transformEachRecursively

@Serializable
data class TreeishTextReplacer(
	val match: StringMatcher,
	val replacements: List<SubPartReplacement>
) {
	@Serializable
	data class SubPartReplacement(
        val match: StringMatcher,
        val style: @Serializable(StyleSerializer::class) Style? = null,
        val replace: @Serializable(TextSerializer::class) Component,
	)

	object TextSerializer : CodecSerializer<Component>(ComponentSerialization.CODEC)
	object StyleSerializer : CodecSerializer<Style>(Style.Serializer.CODEC)
	companion object {
		val pattern = "[$]\\{(?<name>[^}]+)}".toPattern()
		fun injectMatchResults(text: Component, matches: Matcher): Component {
			return text.transformEachRecursively { it ->
				val content = it.directLiteralStringContent ?: return@transformEachRecursively it
				val matcher = pattern.matcher(content)
				val builder = StringBuilder()
				while (matcher.find()) {
					matcher.appendReplacement(builder, matches.group(matcher.group("name")).toString())
				}
				matcher.appendTail(builder)
				Component.literal(builder.toString()).setStyle(it.style)
			}
		}
	}

	fun match(text: Component): Boolean {
		return match.matches(text)
	}

	fun replaceText(text: Component): Component {
		return text.transformEachRecursively { part ->
			var part: Component = part
			for (replacement in replacements) {
				val rawPartText = part.string
				replacement.style?.let { expectedStyle ->
					val parentStyle = part.style
					val parented = expectedStyle.applyTo(parentStyle)
					if (parented.isStrikethrough != parentStyle.isStrikethrough
						|| parented.isObfuscated != parentStyle.isObfuscated
						|| parented.isBold != parentStyle.isBold
						|| parented.isUnderlined != parentStyle.isUnderlined
						|| parented.isItalic != parentStyle.isItalic
						|| parented.color?.value != parentStyle.color?.value)
						continue
				}
				val matcher = replacement.match.asRegex.matcher(rawPartText)
				if (!matcher.find()) continue
				val p = Component.literal("")
				p.setStyle(part.style)
				var lastAppendPosition = 0
				do {
					p.append(rawPartText.substring(lastAppendPosition, matcher.start()))
					lastAppendPosition = matcher.end()
					p.append(injectMatchResults(replacement.replace, matcher))
				} while (matcher.find())
				p.append(rawPartText.substring(lastAppendPosition))
				part = p
			}
			part
		}
	}

}