Skip to content

WynnSource/TextMatcher

Repository files navigation

TextMatcher

A pattern matching library for Minecraft's Component text tree, shipped as a Fabric mod.

Unlike regex, TextMatcher operates at the node level — it flattens a Component into a sequence of styled FlatNodes and matches against that sequence. This means you can match on both text content and style properties (color, bold, font, etc.) at the same time.

中文文档

Why

Chat messages and GUI text in Wynncraft are composed of many Component nodes with different styles. Regex can only see the plain text — it can't distinguish styles and struggles with node boundaries. TextMatcher matches at the node level, which is a natural fit.

Usage

Basic matching

val pattern = textPattern {
    one(text("[GUILD]")) { color(0x2d8627) }
    one(text(" "))
    one(any(), "player") { color(0x55ff55) }
    one(text(": "))
    oneOrMore(any(), "message")
}

val result = pattern.match(chatComponent)
if (result.success) {
    val player = result["player"]
    val message = result["message"]
}

Content matchers

Function Description
text("exact") Exact match
regex("\\d+") Regex match
contains("sub") Contains substring
startsWith / endsWith Prefix / suffix
any() Matches anything
plainText() Plain text nodes only
translatable("key") Translation key match

Combinable with or, and, not:

one(text("A") or text("B"))
one(!plainText())

Quantifiers

one(...)              // exactly 1
optional(...)         // 0 or 1
oneOrMore(...)        // 1+
zeroOrMore(...)       // 0+
exactly(3, ...)       // exactly n
between(2..5, ...)    // range

Greedy by default. Pass greedy = false for lazy matching.

Style constraints

one(any()) {
    color(0xff5555)
    bold()
    italic(false)
    font("minecraft", "default")
}

Also supports not { ... } and anyOf { match { ... }; match { ... } }.

Branching & lookahead

either {
    branch {
        one(text("option A"))
    }
    branch {
        one(text("option B"))
    }
}

// lookahead (does not consume nodes)
lookahead { one(text("peek")) }
negativeLookahead { one(text("not this")) }

Capture groups

Add a capture parameter to any quantifier:

oneOrMore(any(), "content") { bold() }

Access via result["content"], result.groupText("content"), or result.groupNodes("content").

Search modes

pattern.match(component)     // full match — entire input must match
pattern.find(component)      // first occurrence
pattern.findAll(component)   // all non-overlapping matches (Sequence)

Extension functions are also available:

component.matches(pattern)
component.find(pattern)
component.findAll(pattern)

Benchmark

Measured with JMH on JDK 21 (kotlinx-benchmark, 3 warmups, 5 iterations × 1s).

Benchmark Throughput Description
simpleExactMatch 156.1 M op/s 10-node exact match
pathologicalWorstCase 44.9 M op/s Worst-case backtrack (hits limit)
findAllRepeating 36.3 M op/s findAll over 100 key-value pairs
greedyBacktrack 33.6 M op/s Greedy .* over 50 nodes + backtrack
patternConstruction 33.2 M op/s Pattern DSL compilation
endToEndFind 6.5 M op/s Full pipeline: flatten → compact → find
endToEndMatch 6.3 M op/s Full pipeline: flatten → compact → match
endToEndWithCapture 5.6 M op/s Full pipeline with capture group access
eitherBranchMatch 5.5 M op/s 4-branch either with capture
compactPreFlattened 5.2 M op/s Compact pre-flattened nodes
regexContentMatch 4.6 M op/s Content matching with regex
flattenShallow 4.5 M op/s Flatten 20-sibling component
eitherBranchEndToEnd 4.3 M op/s End-to-end either branch + capture
flattenAndCompact 2.2 M op/s Flatten + compact combined
flattenDeep 1.8 M op/s Flatten 50-deep nested component
flattenWide 111.5 K op/s Flatten tree (5^4 = 625 nodes)

Run benchmarks with:

./gradlew benchmark

Installation

Gradle (Kotlin DSL):

repositories {
    maven {
        name = "WynnSource"
        url = uri("https://git.fyw.fyi/api/packages/wynnsource/maven")
    }
}

dependencies {
    modImplementation("fyw.fyi.textmatcher:textmatcher:<version>")
    include("fyw.fyi.textmatcher:textmatcher:<version>")
}

Building

./gradlew build

Requirements

  • Minecraft 1.21.11
  • Fabric Loader >= 0.18.4
  • Fabric Language Kotlin
  • Java 21+

License

LGPL-3.0-or-later

About

A simple text matching utility for Minecraft mods.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages