Skip to content

Commit

Permalink
feature(minimessage): Add context argument and unit tests
Browse files Browse the repository at this point in the history
Also, a few small bug fixes alongside
  • Loading branch information
kezz committed Mar 7, 2025
1 parent 5415714 commit 530cde0
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
package net.kyori.adventure.text.minimessage.translation;

import net.kyori.adventure.pointer.Pointered;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.TranslatableComponent;
Expand Down Expand Up @@ -64,7 +65,7 @@ private Argument() {
* @return the named argument
* @since 4.20.0
*/
static @NotNull ComponentLike bool(final @TagPattern @NotNull String name, final boolean value) {
public static @NotNull ComponentLike bool(final @TagPattern @NotNull String name, final boolean value) {
return argument(name, TranslationArgument.bool(value));
}

Expand All @@ -76,7 +77,7 @@ private Argument() {
* @return the named argument
* @since 4.20.0
*/
static @NotNull ComponentLike numeric(final @TagPattern @NotNull String name, final @NotNull Number value) {
public static @NotNull ComponentLike numeric(final @TagPattern @NotNull String name, final @NotNull Number value) {
return argument(name, TranslationArgument.numeric(value));
}

Expand All @@ -88,7 +89,7 @@ private Argument() {
* @return the named argument
* @since 4.20.0
*/
static @NotNull ComponentLike numeric(final @TagPattern @NotNull String name, final @NotNull String value) {
public static @NotNull ComponentLike numeric(final @TagPattern @NotNull String name, final @NotNull String value) {
return argument(name, TranslationArgument.component(Component.text(value)));
}

Expand All @@ -100,7 +101,7 @@ private Argument() {
* @return the named argument
* @since 4.20.0
*/
static @NotNull ComponentLike component(final @TagPattern @NotNull String name, final @NotNull ComponentLike value) {
public static @NotNull ComponentLike component(final @TagPattern @NotNull String name, final @NotNull ComponentLike value) {
return argument(name, TranslationArgument.component(value));
}

Expand All @@ -112,7 +113,7 @@ private Argument() {
* @return the named argument
* @since 4.20.0
*/
static @NotNull ComponentLike argument(final @TagPattern @NotNull String name, final @NotNull TranslationArgumentLike argument) {
public static @NotNull ComponentLike argument(final @TagPattern @NotNull String name, final @NotNull TranslationArgumentLike argument) {
return argument(name, requireNonNull(argument, "argument").asTranslationArgument());
}

Expand All @@ -124,7 +125,7 @@ private Argument() {
* @return the named argument
* @since 4.20.0
*/
static @NotNull ComponentLike argument(final @TagPattern @NotNull String name, final @NotNull TranslationArgument argument) {
public static @NotNull ComponentLike argument(final @TagPattern @NotNull String name, final @NotNull TranslationArgument argument) {
return Component.virtual(Void.class, new MiniMessageTranslatorArgument<>(name, requireNonNull(argument, "argument")));
}

Expand All @@ -136,7 +137,7 @@ private Argument() {
* @return the named argument
* @since 4.20.0
*/
static @NotNull ComponentLike tag(final @TagPattern @NotNull String name, final @NotNull Tag tag) {
public static @NotNull ComponentLike tag(final @TagPattern @NotNull String name, final @NotNull Tag tag) {
return Component.virtual(Void.class, new MiniMessageTranslatorArgument<>(name, requireNonNull(tag, "tag")));
}

Expand All @@ -147,7 +148,7 @@ private Argument() {
* @return the argument
* @since 4.20.0
*/
static @NotNull ComponentLike tagResolver(final @NotNull TagResolver @NotNull... resolvers) {
public static @NotNull ComponentLike tagResolver(final @NotNull TagResolver @NotNull... resolvers) {
return tagResolver(TagResolver.resolver(resolvers));
}

Expand All @@ -158,7 +159,7 @@ private Argument() {
* @return the argument
* @since 4.20.0
*/
static @NotNull ComponentLike tagResolver(final @NotNull Iterable<TagResolver> resolvers) {
public static @NotNull ComponentLike tagResolver(final @NotNull Iterable<TagResolver> resolvers) {
return tagResolver(TagResolver.resolver(resolvers));
}

Expand All @@ -169,8 +170,19 @@ private Argument() {
* @return the argument
* @since 4.20.0
*/
static @NotNull ComponentLike tagResolver(final @NotNull TagResolver tagResolver) {
public static @NotNull ComponentLike tagResolver(final @NotNull TagResolver tagResolver) {
// The name field is unused here.
return Component.virtual(Void.class, new MiniMessageTranslatorArgument<>("unused", requireNonNull(tagResolver, "tagResolver")));
}

/**
* Creates an argument used to set the target of the deserialization process.
*
* @param target the target
* @return the argument
* @since 4.20.0
*/
public static @NotNull ComponentLike target(final @NotNull Pointered target) {
return Component.virtual(Void.class, new MiniMessageTranslatorTarget(requireNonNull(target, "target")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,7 @@
*/
package net.kyori.adventure.text.minimessage.translation;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.TranslationArgumentLike;
import net.kyori.adventure.text.VirtualComponent;
import net.kyori.adventure.text.VirtualComponentRenderer;
import net.kyori.adventure.text.minimessage.Context;
import net.kyori.adventure.text.minimessage.ParsingException;
import net.kyori.adventure.text.minimessage.tag.Tag;
Expand All @@ -45,42 +37,11 @@ final class ArgumentTag implements TagResolver {
private static final String NAME_1 = "arg";

private final List<Tag> arguments;
private final Map<String, Tag> namedArguments;
private final TagResolver fallbackTagResolver;
private final TagResolver tagResolver;

ArgumentTag(final @NotNull List<? extends ComponentLike> argumentComponents) {
final List<Tag> argumentTags = new ArrayList<>(argumentComponents.size());
final Map<String, Tag> namedArgumentMap = new HashMap<>(argumentComponents.size());
final TagResolver.Builder tagResolverBuilder = TagResolver.builder();

for (final ComponentLike argument : argumentComponents) {
if (argument instanceof VirtualComponent) {
final VirtualComponentRenderer<?> renderer = ((VirtualComponent) argument).renderer();

if (renderer instanceof MiniMessageTranslatorArgument) {
final MiniMessageTranslatorArgument<?> translatorArgument = (MiniMessageTranslatorArgument<?>) renderer;
final Object data = translatorArgument.data();

if (data instanceof TranslationArgumentLike) {
final Tag tag = Tag.selfClosingInserting((TranslationArgumentLike) data);
namedArgumentMap.put(translatorArgument.name(), tag);
argumentTags.add(tag);
} else if (data instanceof Tag) {
final Tag tag = (Tag) data;
namedArgumentMap.put(translatorArgument.name(), tag);
argumentTags.add(tag);
} else if (data instanceof TagResolver) {
tagResolverBuilder.resolvers((TagResolver) data);
}
}
} else {
argumentTags.add(Tag.selfClosingInserting(argument));
}
}

this.arguments = Collections.unmodifiableList(argumentTags);
this.namedArguments = Collections.unmodifiableMap(namedArgumentMap);
this.fallbackTagResolver = tagResolverBuilder.build();
ArgumentTag(final @NotNull List<Tag> arguments, final @NotNull TagResolver tagResolver) {
this.arguments = arguments;
this.tagResolver = tagResolver;
}

@Override
Expand All @@ -94,19 +55,12 @@ final class ArgumentTag implements TagResolver {

return this.arguments.get(index);
} else {
final Tag tag = this.namedArguments.get(name);

if (tag != null) {
return tag;
}
return this.tagResolver.resolve(name, arguments, ctx);
}

// Fallback to user-provided tags.
return this.fallbackTagResolver.resolve(name, arguments, ctx);
}

@Override
public boolean has(final @NotNull String name) {
return name.equals(NAME) || name.equals(NAME_1) || this.namedArguments.containsKey(name) || this.fallbackTagResolver.has(name);
return name.equals(NAME) || name.equals(NAME_1) || this.tagResolver.has(name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,17 @@
package net.kyori.adventure.text.minimessage.translation;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import net.kyori.adventure.pointer.Pointered;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.TranslationArgument;
import net.kyori.adventure.text.TranslationArgumentLike;
import net.kyori.adventure.text.VirtualComponent;
import net.kyori.adventure.text.VirtualComponentRenderer;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
Expand Down Expand Up @@ -136,10 +143,60 @@ public MiniMessageTranslator(final @NotNull MiniMessage miniMessage) {

final Component resultingComponent;

if (component.arguments().isEmpty()) {
final List<TranslationArgument> translationArguments = component.arguments();

if (translationArguments.isEmpty()) {
resultingComponent = this.miniMessage.deserialize(miniMessageString);
} else {
resultingComponent = this.miniMessage.deserialize(miniMessageString, new ArgumentTag(component.arguments()));
final TagResolver.Builder tagResolverBuilder = TagResolver.builder();
final List<Tag> indexedArguments = new ArrayList<>(translationArguments.size());
Pointered target = null;

for (final TranslationArgument argument : translationArguments) {
final Object value = argument.value();

if (value instanceof VirtualComponent) {
final VirtualComponentRenderer<?> renderer = ((VirtualComponent) value).renderer();

if (renderer instanceof MiniMessageTranslatorTarget) {
if (target != null) {
throw new IllegalArgumentException("Multiple Argument.target() translation arguments have been set!");
}

target = ((MiniMessageTranslatorTarget) renderer).pointered();
continue;
} else if (renderer instanceof MiniMessageTranslatorArgument<?>) {
final MiniMessageTranslatorArgument<?> translatorArgument = (MiniMessageTranslatorArgument<?>) renderer;
final Object data = translatorArgument.data();

if (data instanceof TranslationArgumentLike) {
final Tag tag = Tag.selfClosingInserting((TranslationArgumentLike) data);
tagResolverBuilder.tag(translatorArgument.name(), tag);
indexedArguments.add(tag);
continue;
} else if (data instanceof Tag) {
final Tag tag = (Tag) data;
tagResolverBuilder.tag(translatorArgument.name(), tag);
indexedArguments.add(tag);
continue;
} else if (data instanceof TagResolver) {
tagResolverBuilder.resolvers((TagResolver) data);
} else {
throw new IllegalArgumentException("Unknown translator argument type: " + data.getClass());
}
}
}

indexedArguments.add(Tag.selfClosingInserting(argument));
}

final ArgumentTag argumentTag = new ArgumentTag(indexedArguments, tagResolverBuilder.build());

if (target == null) {
resultingComponent = this.miniMessage.deserialize(miniMessageString, argumentTag);
} else {
resultingComponent = this.miniMessage.deserialize(miniMessageString, target, argumentTag);
}
}

return resultingComponent.append(component.children());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
import org.jetbrains.annotations.UnknownNullability;

final class MiniMessageTranslatorArgument<T> implements VirtualComponentRenderer<Void> {
final @NotNull String name;
final @NotNull T data;
private final @NotNull String name;
private final @NotNull T data;

MiniMessageTranslatorArgument(final @TagPattern @NotNull String name, final @NotNull T data) {
Objects.requireNonNull(name, "name");
Expand All @@ -60,9 +60,4 @@ final class MiniMessageTranslatorArgument<T> implements VirtualComponentRenderer
return null;
}
}

@Override
public @NotNull String fallbackString() {
return ""; // Not for display purposes.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2025 KyoriPowered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.kyori.adventure.text.minimessage.translation;

import net.kyori.adventure.pointer.Pointered;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.VirtualComponentRenderer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnknownNullability;

final class MiniMessageTranslatorTarget implements VirtualComponentRenderer<Void> {
private final Pointered pointered;

MiniMessageTranslatorTarget(final @NotNull Pointered pointered) {
this.pointered = pointered;
}

@NotNull Pointered pointered() {
return this.pointered;
}

@Override
public @UnknownNullability ComponentLike apply(final @NotNull Void context) {
return null;
}
}
Loading

0 comments on commit 530cde0

Please sign in to comment.