Skip to content

Inline Style Parsers

This document explains how to register custom inline style parsers, allowing Ageratum's Markdown renderer to recognize and apply your mod's custom inline tags.


Overview

Inline Style Parsers let you extend Ageratum's inline Markdown syntax. By registering an MDInlineStyleParser, you can map specific tag pairs (e.g. <mytag>...</mytag>) to Minecraft Style objects, enabling custom colors, effects, or interactive behaviors.


Core Interface

MDInlineStyleParser

java
public interface MDInlineStyleParser {
    /** Priority: lower value = higher precedence at the same position */
    int priority();

    /**
     * Attempt to parse an inline style tag starting at pos in text.
     * Returns null if no match is found.
     */
    @Nullable
    MDComponent.InlineStyleMatch parse(String text, int pos);

    /** Create a simple open/close tag parser */
    static MDInlineStyleParser create(
        int priority,
        Pattern openTagPattern,
        String closeTag,
        BiFunction<Style, Matcher, Style> styleFactory
    ) { ... }
}

MDComponent.InlineStyleMatch

An InlineStyleMatch describes a successful tag match, containing:

  • Start and end positions of the tag
  • The Style to apply to the inner text
  • The inner text content

Registration Steps

1. Define the Parsers

java
package com.example.mymod.client.markdown;

import dev.anvilcraft.resource.ageratum.client.feat.markdown.component.MDInlineStyleParser;
import dev.anvilcraft.resource.ageratum.client.registries.AgeratumRegistries;
import net.minecraft.network.chat.Style;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;

import java.util.regex.Pattern;

public final class MyModInlineStyleParsers {

    public static final DeferredRegister<MDInlineStyleParser> INLINE_STYLE_PARSERS =
        DeferredRegister.create(
            AgeratumRegistries.INLINE_STYLE_PARSER_REGISTRY_KEY,
            "mymod"
        );

    // ── Example 1: <rainbow>text</rainbow> - Purple text ──────────────────────
    public static final DeferredHolder<MDInlineStyleParser, MDInlineStyleParser> RAINBOW =
        INLINE_STYLE_PARSERS.register(
            "rainbow",
            () -> MDInlineStyleParser.create(
                100,                             // priority (lower = higher precedence)
                Pattern.compile("<rainbow>"),   // open tag pattern
                "</rainbow>",                   // close tag string
                (parentStyle, matcher) ->        // returns modified Style
                    parentStyle.withColor(0xFF55FF).withBold(false)
            )
        );

    // ── Example 2: <highlight=#RRGGBB>text</highlight> - Custom color text ────
    private static final Pattern HIGHLIGHT_PATTERN =
        Pattern.compile("<highlight=#([0-9a-fA-F]{6})>", Pattern.CASE_INSENSITIVE);

    public static final DeferredHolder<MDInlineStyleParser, MDInlineStyleParser> HIGHLIGHT =
        INLINE_STYLE_PARSERS.register(
            "highlight",
            () -> MDInlineStyleParser.create(
                50,
                HIGHLIGHT_PATTERN,
                "</highlight>",
                (parentStyle, matcher) -> {
                    int color = Integer.parseInt(matcher.group(1), 16);
                    // Note: Minecraft Style has no background color;
                    // this demonstrates colored + underlined text
                    return parentStyle.withColor(color).withUnderlined(true);
                }
            )
        );

    // ── Example 3: <spoiler>text</spoiler> - Obfuscated mask ──────────────────
    public static final DeferredHolder<MDInlineStyleParser, MDInlineStyleParser> SPOILER =
        INLINE_STYLE_PARSERS.register(
            "spoiler",
            () -> MDInlineStyleParser.create(
                200,
                Pattern.compile("<spoiler>"),
                "</spoiler>",
                (parentStyle, matcher) -> parentStyle.withObfuscated(true)
            )
        );

    private MyModInlineStyleParsers() {}
}

2. Bind to the Event Bus in Client Init

java
@Mod(value = "mymod", dist = Dist.CLIENT)
public class MyModClient {
    public MyModClient(IEventBus modEventBus, ModContainer modContainer) {
        MyModInlineStyleParsers.INLINE_STYLE_PARSERS.register(modEventBus);
    }
}

Using in Documents

markdown
Normal text <rainbow>This is colorful text</rainbow> normal text

<highlight=#FFAA00>This is highlighted text</highlight>

Spoiler: <spoiler>The answer is 42</spoiler>

Priority Rules

When multiple parsers match at the same position, the one with the lower priority value is tried first.
If priorities are equal, registration order determines precedence.

Built-in parsers all use priority 0. Custom parsers are recommended to use the 50200 range to avoid conflicting with built-ins.


Advanced: Fully Custom Parser

If the MDInlineStyleParser.create() factory method isn't sufficient (e.g. you need to parse nested content or multiple text segments), implement the full interface:

java
public class MyAdvancedParser implements MDInlineStyleParser {
    private static final String OPEN  = "<mymod>";
    private static final String CLOSE = "</mymod>";

    @Override
    public int priority() { return 150; }

    @Override
    @Nullable
    public MDComponent.InlineStyleMatch parse(String text, int pos) {
        int start = text.indexOf(OPEN, pos);
        if (start < 0) return null;

        int contentStart = start + OPEN.length();
        int end = text.indexOf(CLOSE, contentStart);
        if (end < 0) return null;

        // Build match using the static factory helper
        Pattern openTag = Pattern.compile(Pattern.quote(OPEN));
        Matcher m = openTag.matcher(text);
        if (!m.find(start)) return null;
        return MDComponent.InlineStyleMatch.of(
            openTag, m, CLOSE,
            (parentStyle, matcher) -> parentStyle.withColor(0x00FFFF)
        );
    }
}

Note: See MDComponent.java source for the full InlineStyleMatch.of() signature.


Interaction with Built-in Parsers

Ageratum's built-in inline parsers (color, obfuscated, hover, click) are all registered with priority 0.
To avoid tag name conflicts:

  1. Prefix your tag names with your mod ID (e.g. <mymod_rainbow>)
  2. Or choose names that don't conflict with built-ins: color, o, hover, click

See Also

Released under the CC-BY-NC-SA 4.0 License.