Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
* @since 4.10.0
*/
@ApiStatus.NonExtendable
public interface Context {
public interface Context extends SerializationContext {

/**
* The target of the parse context, if provided.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
package net.kyori.adventure.text.minimessage;

import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
import net.kyori.adventure.pointer.Pointered;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.minimessage.internal.parser.ParsingExceptionImpl;
import net.kyori.adventure.text.minimessage.internal.parser.Token;
import net.kyori.adventure.text.minimessage.internal.parser.node.TagPart;
Expand All @@ -36,6 +38,7 @@
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;

import static java.util.Objects.requireNonNull;

Expand Down Expand Up @@ -170,6 +173,16 @@ public UnaryOperator<String> preProcessor() {
return new ParsingExceptionImpl(message, this.message, cause, false, tagsToTokens(((ArgumentQueueImpl<?>) tags).args));
}

@Override
public @Unmodifiable @NotNull Map<String, TextColor> namedColors() {
return this.miniMessage.namedColors();
}

@Override
public @Unmodifiable @NotNull Map<String, String> namedColorAliases() {
return this.miniMessage.namedColorAliases();
}

private @NotNull Component deserializeWithOptionalTarget(final @NotNull String message, final @NotNull TagResolver tagResolver) {
if (this.target != null) {
return this.miniMessage.deserialize(message, this.target, tagResolver);
Expand All @@ -185,5 +198,4 @@ private static Token[] tagsToTokens(final List<? extends Tag.Argument> tags) {
}
return tokens;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@
*/
package net.kyori.adventure.text.minimessage;

import java.util.Map;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
import net.kyori.adventure.builder.AbstractBuilder;
import net.kyori.adventure.pointer.Pointered;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.minimessage.tag.TagPattern;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.minimessage.tree.Node;
import net.kyori.adventure.text.serializer.ComponentSerializer;
Expand All @@ -44,7 +47,7 @@
*
* @since 4.10.0
*/
public interface MiniMessage extends ComponentSerializer<Component, Component, String> {
public interface MiniMessage extends ComponentSerializer<Component, Component, String>, SerializationContext {
/**
* Gets a simple instance with default settings.
*
Expand Down Expand Up @@ -331,6 +334,71 @@ interface Builder extends AbstractBuilder<MiniMessage> {
*/
@NotNull Builder editTags(final @NotNull Consumer<TagResolver.Builder> adder);

/**
* Set the known named colors of this MiniMessage instance.
*
* @param colors the colors to use
* @return this builder
* @since 4.26.0
*/
@NotNull Builder namedColors(@NotNull Map<String, TextColor> colors);

/**
* Add to the set of known named colors of this MiniMessage instance.
*
* @param name the name of the color
* @param color the color
* @return this builder
* @since 4.26.0
*/
@NotNull Builder namedColor(@TagPattern @NotNull String name, @NotNull TextColor color);

/**
* Remove from the set of known named colors of this MiniMessage instance.
*
* @param name the name of the color to remove
* @return this builder
* @since 4.26.0
*/
@NotNull Builder removeNamedColor(@TagPattern @NotNull String name);

/**
* Set the known named color aliases of this MiniMessage instance.
*
* <p>Named color aliases point to another color name. If the name does not exist, the aliases is silently ignored.
* Aliases not tied to a specific color value and are never serialized.</p>
*
* @param aliases the aliases to use
* @return this builder
* @since 4.26.0
*/
@NotNull Builder namedColorAliases(@NotNull Map<String, String> aliases);

/**
* Add to the set of known named color aliases of this MiniMessage instance.
*
* <p>Named color aliases point to another color name. If the name does not exist, the aliases is silently ignored.
* Aliases not tied to a specific color value and are never serialized.</p>
*
* @param name the name of the color alias
* @param color the name of the named color
* @return this builder
* @since 4.26.0
*/
@NotNull Builder namedColorAlias(@TagPattern @NotNull String name, @TagPattern @NotNull String color);

/**
* Remove from the set of known named color aliases of this MiniMessage instance.
*
* <p>Named color aliases point to another color name. If the name does not exist, the aliases is silently ignored.
* Aliases not tied to a specific color value and are never serialized.</p>
*
* @param name the name of the color alias
* @return this builder
* @since 4.26.0
*/
@NotNull Builder removeNamedColorAlias(@TagPattern @NotNull String name);

/**
* Enables strict mode (disabled by default).
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,27 @@
*/
package net.kyori.adventure.text.minimessage;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
import net.kyori.adventure.pointer.Pointered;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.minimessage.internal.TagInternals;
import net.kyori.adventure.text.minimessage.internal.serializer.SerializableResolver;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.minimessage.tree.Node;
import net.kyori.adventure.util.Services;
import org.intellij.lang.annotations.Subst;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;

import static java.util.Objects.requireNonNull;

Expand All @@ -55,7 +64,7 @@ final class MiniMessageImpl implements MiniMessage {
static final class Instances {
static final MiniMessage INSTANCE = SERVICE
.map(Provider::miniMessage)
.orElseGet(() -> new MiniMessageImpl(TagResolver.standard(), false, true, null, DEFAULT_NO_OP, DEFAULT_COMPACTING_METHOD));
.orElseGet(() -> new MiniMessageImpl(TagResolver.standard(), false, true, null, DEFAULT_NO_OP, DEFAULT_COMPACTING_METHOD, defaultNamedColors(), defaultNamedColorAliases()));
}

static final UnaryOperator<String> DEFAULT_NO_OP = UnaryOperator.identity();
Expand All @@ -66,15 +75,30 @@ static final class Instances {
private final @Nullable Consumer<String> debugOutput;
private final UnaryOperator<Component> postProcessor;
private final UnaryOperator<String> preProcessor;
private final Map<String, TextColor> namedColors;
private final Map<String, String> namedColorAliases;
final MiniMessageParser parser;

MiniMessageImpl(final @NotNull TagResolver resolver, final boolean strict, final boolean emitVirtuals, final @Nullable Consumer<String> debugOutput, final @NotNull UnaryOperator<String> preProcessor, final @NotNull UnaryOperator<Component> postProcessor) {
MiniMessageImpl(final @NotNull TagResolver resolver, final boolean strict, final boolean emitVirtuals, final @Nullable Consumer<String> debugOutput, final @NotNull UnaryOperator<String> preProcessor, final @NotNull UnaryOperator<Component> postProcessor, final Map<String, TextColor> namedColors, final Map<String, String> namedColorAliases) {
this.parser = new MiniMessageParser(resolver);
this.strict = strict;
this.emitVirtuals = emitVirtuals;
this.debugOutput = debugOutput;
this.preProcessor = preProcessor;
this.postProcessor = postProcessor;
this.namedColors = Collections.unmodifiableMap(namedColors);
this.namedColorAliases = Collections.unmodifiableMap(namedColorAliases);
}

private static @NotNull Map<String, TextColor> defaultNamedColors() {
return new TreeMap<>(NamedTextColor.NAMES.keyToValue());
}

private static @NotNull Map<String, String> defaultNamedColorAliases() {
final Map<String, String> out = new TreeMap<>();
out.put("grey", "gray");
out.put("dark_grey", "dark_gray");
return out;
}

@Override
Expand Down Expand Up @@ -119,7 +143,7 @@ static final class Instances {

@Override
public @NotNull String serialize(final @NotNull Component component) {
return MiniMessageSerializer.serialize(component, this.serialResolver(null), this.strict);
return MiniMessageSerializer.serialize(component, this.serialResolver(null), this.strict, this);
}

private SerializableResolver serialResolver(final @Nullable TagResolver extraResolver) {
Expand Down Expand Up @@ -157,6 +181,16 @@ private SerializableResolver serialResolver(final @Nullable TagResolver extraRes
return this.parser.stripTokens(this.newContext(input, null, tagResolver));
}

@Override
public @Unmodifiable @NotNull Map<String, TextColor> namedColors() {
return this.namedColors;
}

@Override
public @Unmodifiable @NotNull Map<String, String> namedColorAliases() {
return this.namedColorAliases;
}

@Override
public boolean strict() {
return this.strict;
Expand All @@ -179,6 +213,8 @@ static final class BuilderImpl implements Builder {
private Consumer<String> debug = null;
private UnaryOperator<Component> postProcessor = DEFAULT_COMPACTING_METHOD;
private UnaryOperator<String> preProcessor = DEFAULT_NO_OP;
private final Map<String, TextColor> namedColors = defaultNamedColors();
private final Map<String, String> namedColorAliases = defaultNamedColorAliases();

BuilderImpl() {
BUILDER.accept(this);
Expand Down Expand Up @@ -208,6 +244,54 @@ static final class BuilderImpl implements Builder {
return this;
}

@Override
public @NotNull Builder namedColors(final @NotNull Map<String, TextColor> colors) {
for (final @Subst("color") String name : colors.keySet()) {
TagInternals.assertValidTagName(name);
}
this.namedColors.clear();
this.namedColors.putAll(colors);
return this;
}

@Override
public @NotNull Builder namedColor(final @NotNull String name, final @NotNull TextColor color) {
TagInternals.assertValidTagName(name);
this.namedColors.put(name, color);
return this;
}

@Override
public @NotNull Builder removeNamedColor(final @NotNull String name) {
TagInternals.assertValidTagName(name);
this.namedColors.remove(name);
return this;
}

@Override
public @NotNull Builder namedColorAliases(final @NotNull Map<String, String> aliases) {
for (final @Subst("color") String name : aliases.keySet()) {
TagInternals.assertValidTagName(name);
}
this.namedColorAliases.clear();
this.namedColorAliases.putAll(aliases);
return this;
}

@Override
public @NotNull Builder namedColorAlias(final @NotNull String name, final @NotNull String color) {
TagInternals.assertValidTagName(name);
this.namedColorAliases.put(name, color);
return this;
}

@Override
public @NotNull Builder removeNamedColorAlias(final @NotNull String name) {
TagInternals.assertValidTagName(name);
this.namedColorAliases.remove(name);
return this;
}

@Override
public @NotNull Builder strict(final boolean strict) {
this.strict = strict;
Expand Down Expand Up @@ -240,7 +324,7 @@ static final class BuilderImpl implements Builder {

@Override
public @NotNull MiniMessage build() {
return new MiniMessageImpl(this.tagResolver, this.strict, this.emitVirtuals, this.debug, this.preProcessor, this.postProcessor);
return new MiniMessageImpl(this.tagResolver, this.strict, this.emitVirtuals, this.debug, this.preProcessor, this.postProcessor, new HashMap<>(this.namedColors), new HashMap<>(this.namedColorAliases));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private void processTokens(final @NotNull StringBuilder sb, final @NotNull Strin
continue;
}
final String sanitized = TokenParser.TagProvider.sanitizePlaceholderName(token.childTokens().get(0).get(richMessage).toString());
if (combinedResolver.has(sanitized)) {
if (combinedResolver.has(sanitized, context)) {
tagHandler.accept(token, sb);
} else {
sb.append(richMessage, token.startIndex(), token.endIndex());
Expand Down Expand Up @@ -189,7 +189,7 @@ private void processTokens(final @NotNull StringBuilder sb, final @NotNull Strin
}
final Predicate<String> tagNameChecker = name -> {
final String sanitized = TokenParser.TagProvider.sanitizePlaceholderName(name);
return combinedResolver.has(sanitized);
return combinedResolver.has(sanitized, context);
};

final String preProcessed = TokenParser.resolvePreProcessTags(processedMessage, transformationFactory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ private MiniMessageSerializer() {
// - abbreviated vs long tag names (tag-specific option)
//

static @NotNull String serialize(final @NotNull Component component, final @NotNull SerializableResolver resolver, final boolean strict) {
static @NotNull String serialize(final @NotNull Component component, final @NotNull SerializableResolver resolver, final boolean strict, final @NotNull SerializationContext ctx) {
final StringBuilder sb = new StringBuilder();
final Collector emitter = new Collector(resolver, strict, sb);
final Collector emitter = new Collector(resolver, strict, sb, ctx);

emitter.mark();
visit(component, emitter, resolver, true);
visit(component, emitter, resolver, true, ctx);
if (strict) {
// If we are in strict mode, we need to close all tags at the end of our serialization journey
emitter.popAll();
Expand All @@ -65,9 +65,9 @@ private MiniMessageSerializer() {
return sb.toString();
}

private static void visit(final @NotNull Component component, final Collector emitter, final SerializableResolver resolver, final boolean lastChild) {
private static void visit(final @NotNull Component component, final Collector emitter, final SerializableResolver resolver, final boolean lastChild, final @NotNull SerializationContext ctx) {
// visit self
resolver.handle(component, emitter);
resolver.handle(component, emitter, ctx);
Component childSource = emitter.flushClaims(component);
if (childSource == null) {
childSource = component;
Expand All @@ -76,7 +76,7 @@ private static void visit(final @NotNull Component component, final Collector em
// then children
for (final Iterator<Component> it = childSource.children().iterator(); it.hasNext();) {
emitter.mark();
visit(it.next(), emitter, resolver, lastChild && !it.hasNext());
visit(it.next(), emitter, resolver, lastChild && !it.hasNext(), ctx);
}

if (!lastChild) {
Expand Down Expand Up @@ -106,14 +106,16 @@ enum TagState {
private static final char[] SINGLE_QUOTED_ESCAPES = {TokenParser.ESCAPE, '\''};
private static final char[] DOUBLE_QUOTED_ESCAPES = {TokenParser.ESCAPE, '"'};

private final SerializationContext ctx;
private final SerializableResolver resolver;
private final boolean strict;
private final StringBuilder consumer;
private String[] activeTags = new String[4];
private int tagLevel = 0;
private TagState tagState = TagState.TEXT;

Collector(final SerializableResolver resolver, final boolean strict, final StringBuilder consumer) {
Collector(final SerializableResolver resolver, final boolean strict, final StringBuilder consumer, final SerializationContext ctx) {
this.ctx = ctx;
this.resolver = resolver;
this.strict = strict;
this.consumer = consumer;
Expand Down Expand Up @@ -211,7 +213,7 @@ void completeTag() {

@Override
public @NotNull TokenEmitter argument(final @NotNull Component arg) {
final String serialized = MiniMessageSerializer.serialize(arg, this.resolver, this.strict);
final String serialized = MiniMessageSerializer.serialize(arg, this.resolver, this.strict, this.ctx);
return this.argument(serialized, QuotingOverride.QUOTED); // always quote tokens
}

Expand Down
Loading
Loading