Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
9d6a113
Setup unstable publishing to PaperMC snapshots repo (DO NOT MERGE TO …
jpenilla Aug 21, 2025
80659e1
Init ObjectComponent
jpenilla Aug 21, 2025
ca21ba8
Fix atlas nullability
jpenilla Aug 21, 2025
68c5e9a
override examinableProperties
jpenilla Aug 21, 2025
3025b4d
Add tests
jpenilla Aug 21, 2025
ff3b13f
Refactor ObjectComponent to have a Contents
jpenilla Aug 22, 2025
4004b7c
make SpriteContents#atlas non-null
jpenilla Aug 22, 2025
4869176
jd fix
jpenilla Aug 22, 2025
0ed2897
Add net.kyori capabilities when group is something else
jpenilla Aug 22, 2025
511f853
Add PlayerHeadContents
jpenilla Aug 26, 2025
cfccd17
dont clear props on properties(map)
jpenilla Aug 26, 2025
614ad73
allow using SkinSource with builder
jpenilla Aug 26, 2025
fd8a901
Add more property helpers & a test
jpenilla Aug 26, 2025
1c8aabe
Add import layout to editorconfig
jpenilla Aug 26, 2025
55924a6
Move ObjectComponent inner classes to top level
jpenilla Aug 26, 2025
e8f91a5
clear existing profile when setting from skin source
jpenilla Aug 26, 2025
a9ee9a2
make it build (add javadocs and etc.)
jpenilla Aug 26, 2025
43231d3
Assert PlayerHeadObjectContents is non-empty
jpenilla Aug 26, 2025
380fe42
Rename objectContents field back to contents
jpenilla Aug 26, 2025
1ebf401
Allow empty steve contents
jpenilla Aug 26, 2025
42188eb
De-emphasize hat param, default to true
jpenilla Aug 27, 2025
17a2304
Merge pull request #1294 from KyoriPowered/player-head-contents-exp-1
jpenilla Aug 27, 2025
1962e73
fix profile properties collection type
jpenilla Aug 30, 2025
f9fd048
Add texture field to PlayerHeadObjectContents
jpenilla Sep 16, 2025
d342f10
Add missing copy for toBuilder
jpenilla Sep 16, 2025
4b0b339
Clean texture when setting from a SkinSource
jpenilla Sep 16, 2025
4b1827f
feat(minimessage): add sprite tag
Strokkur424 Aug 22, 2025
6c3b642
feat(minimessage): add atlas argument to sprite tag
Strokkur424 Aug 22, 2025
4fe2652
chore: apply suggestions
Strokkur424 Aug 22, 2025
c00715d
chore: spotless apply
Strokkur424 Sep 17, 2025
8dfb187
chore: fix rebase compile time errors
Strokkur424 Sep 17, 2025
6db5673
fix: gradlew build compile time errors
Strokkur424 Sep 19, 2025
ae092f4
Merge pull request #1292 from Strokkur424/feat/sprite-tag
jpenilla Sep 19, 2025
beb052d
feat: start work on named arguments in tags
Strokkur424 Sep 17, 2025
f1e2234
feat: modify token parser to parse named arguments
Strokkur424 Sep 18, 2025
6e0c648
chore: introduce new TokenType to uniquely distinguish value-less tog…
Strokkur424 Sep 18, 2025
3aea640
feat: (WIP) abstracting away TagProvider into QueuedTagProvider and N…
Strokkur424 Sep 18, 2025
1172751
fix: (WIP) parser should now theoretically be able to distinguish nam…
Strokkur424 Sep 19, 2025
17d553a
feat: flesh out parsing logic further and fix a bunch of issues
Strokkur424 Sep 19, 2025
7ee25ea
feat: add test for basic named argument parsing
Strokkur424 Sep 19, 2025
74484c7
feat: start adding more tests
Strokkur424 Sep 19, 2025
bdca761
fix: <red > tag with space being recognized as valid tag
Strokkur424 Sep 19, 2025
fe2fe03
feat: add a bunch more tests
Strokkur424 Sep 19, 2025
c20b3b7
chore: fix all compile time issues
Strokkur424 Sep 19, 2025
e76be23
feat: add test
Strokkur424 Sep 19, 2025
51913b9
chore: cleanup diff and rename to sequential
Strokkur424 Sep 19, 2025
783b5cd
chore: add a bunch more tests
Strokkur424 Sep 20, 2025
8c3b14a
feat: add inverted flag arguments
Strokkur424 Sep 20, 2025
c2d8f65
feat: split the claiming resolvers into named and sequenced resolvers
Strokkur424 Sep 20, 2025
d4894ba
feat: add missing context newException method and try to parse tag wi…
Strokkur424 Sep 20, 2025
7c55a07
feat: add isFlagPresent
Strokkur424 Sep 20, 2025
07d0557
fix: invalid tag in test
Strokkur424 Sep 20, 2025
6f4ca44
feat: add named argument support to token emitter
Strokkur424 Sep 23, 2025
92a93c3
feat: add flag support to token emitter
Strokkur424 Sep 23, 2025
f758731
chore: remove unused import
Strokkur424 Sep 23, 2025
b9c9547
feat: add head tag
Strokkur424 Sep 18, 2025
0a89d05
chore: migrate head tag to named arguments
Strokkur424 Sep 23, 2025
7383cb1
chore: update tests for head tag
Strokkur424 Sep 23, 2025
1817525
chore: rename HAT_DEFAULT to be consistent with other static default …
Strokkur424 Sep 23, 2025
8c944e4
feat: add simple sequential head tag for basic arguments
Strokkur424 Sep 23, 2025
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
Prev Previous commit
Next Next commit
feat: flesh out parsing logic further and fix a bunch of issues
  • Loading branch information
Strokkur424 committed Sep 23, 2025
commit 17d553a6ddc71a4f853b1d24e803e1d6d1f7aeb3
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,18 @@ private static void parseSecondPass(final String message, final List<Token> toke
currentStringChar = (char) codePoint;
} else if (codePoint == ' ') {
if (namedArguments == TriState.NOT_SET) {
// Having a whitespace here is nice and all, but there is a slight issue. In the event of a tag just looking like this <name >,
// it should not actually be interpreted as a named argument tag, since it has no arguments which would actually use that.
// We can simply check whether the remainer of this message is blank.
final String substring = message.substring(marker + 1, endIndex);
if (isBlank(substring)) {
i += substring.length();
break;
}

insert(token, new Token(marker, i, TokenType.TAG_VALUE));
namedArguments = TriState.TRUE;
marker = i;
marker = i + 1;
break;
} else if (namedArguments == TriState.FALSE) {
// If the arguments are unnamed, spaces are to be interpreted literally
Expand Down Expand Up @@ -413,6 +423,17 @@ private static void parseSecondPass(final String message, final List<Token> toke
// anything not matched is the final part
if (token.childTokens() == null || token.childTokens().isEmpty()) {
insert(token, new Token(startIndex, endIndex, TokenType.TAG_VALUE));
} else if (namedArguments == TriState.TRUE) {
if (marker < endIndex) {
if (nextNormalIsArgumentValue) {
insert(token, new Token(marker, endIndex, TokenType.TAG_VALUE_NAME));
} else {
// If there are only whitespace characters remaining, we do not want to create a new token here, as it would be empty
if (!isBlank(message.substring(marker, endIndex))) {
insert(token, new Token(marker, endIndex, TokenType.TAG_VALUE_TOGGLE));
}
}
}
} else {
final int end = token.childTokens().get(token.childTokens().size() - 1).endIndex();
if (end != endIndex) {
Expand All @@ -422,6 +443,19 @@ private static void parseSecondPass(final String message, final List<Token> toke
}
}

private static boolean isBlank(final CharSequence cs) {
int index = 0;
boolean isBlank = true;
while (index < cs.length()) {
if (!Character.isWhitespace(cs.charAt(index++))) {
isBlank = false;
break;
}
}

return isBlank;
}

/*
* Build a tree from the OPEN_TAG and CLOSE_TAG tokens
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,11 @@ public void accept(final int start, final int end, final @NotNull TokenType toke
}

// we might care if it's a pre-process!
if (this.tagProvider instanceof TokenParser.QueuedTagProvider) {
final @Nullable Tag replacement = this.tagProvider.resolveQueued(TagProvider.sanitizePlaceholderName(tag), parts, tokens.get(0));
final @Nullable Tag replacement = this.tagProvider.resolveQueued(TagProvider.sanitizePlaceholderName(tag), parts, tokens.get(0));

if (replacement instanceof PreProcess) {
this.builder.append(Objects.requireNonNull(((PreProcess) replacement).value(), "PreProcess replacements cannot return null"));
return;
}
if (replacement instanceof PreProcess) {
this.builder.append(Objects.requireNonNull(((PreProcess) replacement).value(), "PreProcess replacements cannot return null"));
return;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ public boolean has(final @NotNull String name) {
};
}

static @NotNull TagResolver namedResolver(final @NotNull String name, final @NotNull BiFunction<NamedArgumentMap, Context, Tag> handler) {
return namedResolver(Collections.singleton(name), handler);
}

static @NotNull TagResolver namedResolver(final @NotNull Set<String> names, final @NotNull BiFunction<NamedArgumentMap, Context, Tag> handler) {
final Set<String> ownNames = new HashSet<>(names);
for (final String name : ownNames) {
Expand Down Expand Up @@ -340,15 +344,15 @@ interface Queued extends TagResolver {
@Override
@Nullable
default Tag resolveNamed(final @NotNull String name, final @NotNull NamedArgumentMap arguments, final @NotNull Context ctx) throws ParsingException {
throw new UnsupportedOperationException();
return null;
}
}

interface Named extends TagResolver {
@Override
@Nullable
default Tag resolve(final @NotNull String name, final @NotNull ArgumentQueue arguments, final @NotNull Context ctx) throws ParsingException {
throw new UnsupportedOperationException();
return null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@
import net.kyori.adventure.text.minimessage.internal.parser.TokenType;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
import net.kyori.adventure.text.minimessage.tag.resolver.NamedArgumentMap;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.minimessage.tree.Node;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
Expand Down Expand Up @@ -571,19 +573,7 @@ void testValidTagNames() {
void invalidPreprocessTagNames() {
final String input = "Some<##>of<>these<tag>are<3 >tags";
final Component expected = Component.text("Some<##>of<>these(meow)are<3 >tags");
final TagResolver alwaysMatchingResolver = new TagResolver.Queued() {
@Override
public Tag resolve(final @NotNull String name, final @NotNull ArgumentQueue arguments, final @NotNull Context ctx) throws ParsingException {
return Tag.preProcessParsed("(meow)");
}

@Override
public boolean has(final @NotNull String name) {
return true;
}
};

this.assertParsedEquals(expected, input, alwaysMatchingResolver);
this.assertParsedEquals(expected, input, new AlwaysMatchingResolver());
}

// https://github.com/KyoriPowered/adventure/issues/1011
Expand All @@ -594,4 +584,21 @@ void testNonTerminatingQuoteArgument() {

this.assertParsedEquals(expected, input);
}

private static final class AlwaysMatchingResolver implements TagResolver.Queued, TagResolver.Named {
@Override
public @NotNull Tag resolve(final @NotNull String name, final @NotNull ArgumentQueue arguments, final @NotNull Context ctx) throws ParsingException {
return Tag.preProcessParsed("(meow)");
}

@Override
public @NotNull Tag resolveNamed(final @NotNull String name, final @NotNull NamedArgumentMap arguments, final @NotNull Context ctx) throws ParsingException {
return Tag.preProcessParsed("(meow)");
}

@Override
public boolean has(final @NotNull String name) {
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;

import com.google.common.collect.testing.google.TestStringBiMapGenerator;
import net.kyori.adventure.pointer.Pointered;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag;
Expand Down Expand Up @@ -439,6 +441,38 @@ void debugModeMoreComplexNoError() {
assertTrue(messages.contains("}"));
}

@Test
void debugNamedArguments() {
final String input = "<head name=Strokkur24 disable_outer_layer> I have a <red>red</red> text!";

final StringBuilder sb = new StringBuilder();
MiniMessage.builder()
.tags(TagResolver.resolver(
// At the time of writing, the <head> tag did not yet exist.
TagResolver.namedResolver("head", (args, ctx) -> Tag.selfClosingInserting(Component.text("dummy"))),
TagResolver.standard()
)).debug(sb::append).build().deserialize(input);
final List<String> messages = Arrays.asList(sb.toString().split("\n"));

assertTrue(messages.contains("Beginning parsing message <head name=Strokkur24 disable_outer_layer> I have a <red>red</red> text!"));
assertTrue(messages.contains("Attempting to match node as queued 'red' at column 0"));
assertTrue(anyMatch(messages, it -> it.startsWith("Successfully matched node 'red' to tag ")));
assertTrue(messages.contains("Attempting to match node as named 'head' at column 0"));
assertTrue(anyMatch(messages, it -> it.startsWith("Successfully matched node 'head' to tag ")));
assertTrue(messages.contains("Attempting to match node as queued 'red' at column 52"));
assertTrue(anyMatch(messages, it -> it.startsWith("Successfully matched node 'red' to tag ")));
assertTrue(messages.contains("Text parsed into element tree:"));
assertTrue(messages.contains("Node {"));
assertTrue(messages.contains(" TagNode('head', 'name', 'Strokkur24', 'disable_outer_layer') {"));
assertTrue(messages.contains(" }"));
assertTrue(messages.contains(" TextNode(' I have a ')"));
assertTrue(messages.contains(" TagNode('red') {"));
assertTrue(messages.contains(" TextNode('red')"));
assertTrue(messages.contains(" }"));
assertTrue(messages.contains(" TextNode(' text!')"));
assertTrue(messages.contains("}"));
}

static class TestTarget1 implements Pointered {
public String data;
}
Expand Down