Skip to content

Commit

Permalink
minimessage: Add a reset tag via a parser directive
Browse files Browse the repository at this point in the history
  • Loading branch information
kezz committed Feb 14, 2022
1 parent bc2f7b5 commit f7c5071
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 39 deletions.
Expand Up @@ -235,11 +235,14 @@ interface Builder extends Buildable.Builder<MiniMessage> {
@NotNull Builder editTags(final @NotNull Consumer<TagResolver.Builder> adder);

/**
* Allows to enable strict mode (disabled by default).
* Enables strict mode (disabled by default).
*
* <p>By default, MiniMessage will allow {@link net.kyori.adventure.text.minimessage.tag.Inserting#allowsChildren() child-allowing} tags to be implicitly closed. When strict mode
* is enabled, all child-allowing tags which are {@code <opened>} must be explicitly {@code </closed>} as well.</p>
*
* <p>Additionally, the {@link net.kyori.adventure.text.minimessage.tag.ParserDirective#RESET reset tag} is disabled in this mode.
* Any usage of this tag will throw a parser exception if strict mode is enabled.</p>
*
* @param strict if strict mode should be enabled
* @return this builder
* @since 4.10.0
Expand Down
Expand Up @@ -40,6 +40,7 @@
import net.kyori.adventure.text.minimessage.parser.node.TagPart;
import net.kyori.adventure.text.minimessage.parser.node.TextNode;
import net.kyori.adventure.text.minimessage.tag.Inserting;
import net.kyori.adventure.text.minimessage.tag.ParserDirective;
import net.kyori.adventure.text.minimessage.tag.Tag;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
Expand All @@ -53,8 +54,6 @@
@ApiStatus.Internal
public final class TokenParser {
private static final int MAX_DEPTH = 16;
public static final String RESET = "reset";
public static final String RESET_2 = "r";
// minimessage tags
public static final char TAG_START = '<';
public static final char TAG_END = '>';
Expand Down Expand Up @@ -364,33 +363,30 @@ private static ElementNode buildTree(

case OPEN_TAG:
final TagNode tagNode = new TagNode(node, token, message, tagProvider);
if (isReset(tagNode.name())) {
// <reset> tags get special treatment and don't appear in the tree
// instead, they close all currently open tags

if (strict) {
throw new ParsingExceptionImpl("<reset> tags are not allowed when strict mode is enabled", message, token);
}
node = root;
} else {
if (tagNameChecker.test(tagNode.name())) {
final Tag transformation = tagProvider.resolve(tagNode);
if (transformation == null) {
// something went wrong, ignore it
// if strict mode is enabled this will throw an exception for us
node.addChild(new TextNode(node, token, message));
} else {
// This is a recognized tag, goes in the tree
tagNode.tag(transformation);
node.addChild(tagNode);
if (!(transformation instanceof Inserting) || ((Inserting) transformation).allowsChildren()) {
node = tagNode; // TODO: self-terminating tags (i.e. <tag/>) don't set this, so they don't have children
}
if (tagNameChecker.test(tagNode.name())) {
final Tag tag = tagProvider.resolve(tagNode);
if (tag == null) {
// something went wrong, ignore it
// if strict mode is enabled this will throw an exception for us
node.addChild(new TextNode(node, token, message));
} else if (tag == ParserDirective.RESET) {
// <reset> tags get special treatment and don't appear in the tree
// instead, they close all currently open tags
if (strict) {
throw new ParsingExceptionImpl("<reset> tags are not allowed when strict mode is enabled", message, token);
}
node = root;
} else {
// not recognized, plain text
node.addChild(new TextNode(node, token, message));
// This is a recognized tag, goes in the tree
tagNode.tag(tag);
node.addChild(tagNode);
if (!(tag instanceof Inserting) || ((Inserting) tag).allowsChildren()) {
node = tagNode; // TODO: self-terminating tags (i.e. <tag/>) don't set this, so they don't have children
}
}
} else {
// not recognized, plain text
node.addChild(new TextNode(node, token, message));
}
break; // OPEN_TAG

Expand All @@ -407,12 +403,15 @@ private static ElementNode buildTree(
}

final String closeTagName = closeValues.get(0);
if (isReset(closeTagName)) {
// This is a synthetic node, closing it means nothing in the context of building a tree
continue;
}

if (!tagNameChecker.test(closeTagName)) {
if (tagNameChecker.test(closeTagName)) {
final Tag tag = tagProvider.resolve(closeTagName);

if (tag == ParserDirective.RESET) {
// This is a synthetic node, closing it means nothing in the context of building a tree
continue;
}
} else {
// tag does not exist, so treat it as text
node.addChild(new TextNode(node, token, message));
continue;
Expand Down Expand Up @@ -488,10 +487,6 @@ private static ElementNode buildTree(
return root;
}

private static boolean isReset(final String input) {
return input.equalsIgnoreCase(RESET) || input.equalsIgnoreCase(RESET_2);
}

/**
* Determine if a set of close string parts closes the given list of open tag parts. If the open parts starts with
* the set of close parts, then this method returns {@code true}.
Expand Down
@@ -0,0 +1,50 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2022 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.tag;

import org.jetbrains.annotations.ApiStatus;

/**
* Tags implementing this interface are used to provide directives, or instructions, to the parser directly.
*
* @see #RESET
* @since 4.10.0
*/
@ApiStatus.NonExtendable
public /* sealed */ interface ParserDirective extends Tag {
/**
* Instructs the parser to reset all style, events, insertions, etc.
*
* <p>If {@link net.kyori.adventure.text.minimessage.MiniMessage.Builder#strict(boolean) strict mode} is enabled, usage of
* this tag is disallowed and a parse exception will be thrown if this tag is present.</p>
*
* @since 4.10.0
*/
Tag RESET = new ParserDirective() {
@Override
public String toString() {
return "ParserDirective.RESET";
}
};
}
Expand Up @@ -40,11 +40,12 @@
/**
* A tag definition for the MiniMessage language.
*
* <p>All implementations of {@code Tag} must implement one of {@link Inserting}, {@link Modifying}, or {@link PreProcess}.</p>
* <p>All implementations of {@code Tag} must implement one of {@link Inserting}, {@link Modifying},
* {@link ParserDirective} or {@link PreProcess}.</p>
*
* @since 4.10.0
*/
public /* sealed */ interface Tag /* permits Inserting, Modifying, PreProcess, /internal/ AbstractTag */ {
public /* sealed */ interface Tag /* permits Inserting, Modifying, ParserDirective, PreProcess, /internal/ AbstractTag */ {

/**
* Create a tag that inserts the content literally into the parse string.
Expand Down
Expand Up @@ -29,6 +29,7 @@
import java.util.stream.Stream;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.minimessage.tag.ParserDirective;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;

/**
Expand All @@ -39,6 +40,8 @@
* @since 4.10.0
*/
public final class StandardTags {
private static final String RESET_TAG = "reset";

private StandardTags() {
}

Expand All @@ -62,6 +65,7 @@ private StandardTags() {
private static final TagResolver FONT = TagResolver.resolver(FontTag.FONT, FontTag::create);
private static final TagResolver GRADIENT = TagResolver.resolver(GradientTag.GRADIENT, GradientTag::create);
private static final TagResolver RAINBOW = TagResolver.resolver(RainbowTag.RAINBOW, RainbowTag::create);
private static final TagResolver RESET = TagResolver.resolver(RESET_TAG, ParserDirective.RESET);
private static final TagResolver ALL = TagResolver.builder()
.resolvers(
HOVER_EVENT,
Expand All @@ -73,7 +77,8 @@ private StandardTags() {
FONT,
DECORATION,
GRADIENT,
RAINBOW
RAINBOW,
RESET
)
.build();

Expand Down Expand Up @@ -171,6 +176,16 @@ public static TagResolver rainbow() {
return RAINBOW;
}

/**
* Get a resolver for the {@value RESET_TAG} tag.
*
* @return a resolver for the {@value RESET_TAG} tag.
* @since 4.10.0
*/
public static TagResolver reset() {
return RESET;
}

/**
* Get a resolver that handles all default standard tags.
*
Expand Down

0 comments on commit f7c5071

Please sign in to comment.