Add optional short format for PlaceholderAPI (e.g. %player% instead of {papi: player})

This commit is contained in:
filoghost 2023-01-06 23:41:27 +01:00
parent 788cdc2c45
commit 5d2c97f194
7 changed files with 116 additions and 7 deletions

View File

@ -123,6 +123,22 @@ class Parser {
}
}
public static String addEscapes(String string) {
StringBuilder output = new StringBuilder(string.length() + 16); // String gets longer with escapes
for (int i = 0; i < string.length(); i++) {
char currentChar = string.charAt(i);
if (isSpecialCharacter(currentChar)) {
output.append(ESCAPE_CHAR);
}
output.append(currentChar);
}
return output.toString();
}
private static boolean isSpecialCharacter(char currentChar) {
return currentChar == ESCAPE_CHAR
|| currentChar == PLACEHOLDER_START_CHAR

View File

@ -27,6 +27,10 @@ public final class StringWithPlaceholders {
return Parser.parse(string, true);
}
public static String addEscapes(@NotNull String string) {
return Parser.addEscapes(string);
}
StringWithPlaceholders(@NotNull String string, @Nullable List<Part> parts) {
this.string = string;
this.parts = parts;
@ -71,7 +75,7 @@ public final class StringWithPlaceholders {
return replace(player, replaceFunction, StringReplaceFunction.NO_REPLACEMENTS);
}
public @NotNull String replaceStrings(StringReplaceFunction replaceFunction) {
public @NotNull String replaceOutsidePlaceholders(StringReplaceFunction replaceFunction) {
return replace(null, PlaceholderReplaceFunction.NO_REPLACEMENTS, replaceFunction);
}

View File

@ -37,6 +37,8 @@ class StringWithPlaceholdersTest {
Arguments.of(" {p} ", " # "),
Arguments.of("{p} {p} {p}", "# # #"),
Arguments.of("{{p}}", "{#}"), // Only the innermost placeholder should be replaced
Arguments.of("{{p}", "{#"),
Arguments.of("{p}}", "#}"),
Arguments.of("{p abc", "{p abc"), // Placeholder without closing tag
Arguments.of("abc p}", "abc p}") // Placeholder without opening tag
);
@ -46,7 +48,7 @@ class StringWithPlaceholdersTest {
@MethodSource("replaceLiteralPartsTestArguments")
void replaceLiteralParts(String input, String expectedOutput) {
StringWithPlaceholders s = StringWithPlaceholders.of(input);
assertThat(s.replaceStrings(literalPart -> "_")).isEqualTo(expectedOutput);
assertThat(s.replaceOutsidePlaceholders(literalPart -> "_")).isEqualTo(expectedOutput);
}
static Stream<Arguments> replaceLiteralPartsTestArguments() {
@ -112,7 +114,7 @@ class StringWithPlaceholdersTest {
@ParameterizedTest(name = "[{index}] {0} -> {0}")
@MethodSource("escapesTestArguments")
void preservingEscapes(String input) {
String output = StringWithPlaceholders.withEscapes(input).replaceStrings(StringReplaceFunction.NO_REPLACEMENTS);
String output = StringWithPlaceholders.withEscapes(input).replaceOutsidePlaceholders(StringReplaceFunction.NO_REPLACEMENTS);
assertThat(output).isEqualTo(input);
}
@ -133,4 +135,10 @@ class StringWithPlaceholdersTest {
);
}
@Test
void addEscapes() {
String escaped = StringWithPlaceholders.addEscapes("} { \\");
assertThat(escaped).isEqualTo("\\} \\{ \\\\");
}
}

View File

@ -8,22 +8,25 @@ package me.filoghost.holographicdisplays.plugin.config;
import me.filoghost.fcommons.Colors;
import me.filoghost.fcommons.MaterialsHelper;
import me.filoghost.fcommons.Strings;
import me.filoghost.holographicdisplays.core.placeholder.parsing.StringWithPlaceholders;
import me.filoghost.holographicdisplays.plugin.format.DisplayFormat;
import me.filoghost.holographicdisplays.plugin.internal.hologram.InternalHologramLine;
import me.filoghost.holographicdisplays.plugin.internal.hologram.ItemInternalHologramLine;
import me.filoghost.holographicdisplays.plugin.internal.hologram.TextInternalHologramLine;
import me.filoghost.holographicdisplays.plugin.lib.nbt.parser.MojangsonParseException;
import me.filoghost.holographicdisplays.plugin.lib.nbt.parser.MojangsonParser;
import me.filoghost.holographicdisplays.core.placeholder.parsing.StringWithPlaceholders;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class InternalHologramLineParser {
private static final String ICON_PREFIX = "icon:";
private static final Pattern PLACEHOLDER_API_SHORT_FORMAT = Pattern.compile("%(.+?)%");
public static InternalHologramLine parseLine(String serializedLine) throws InternalHologramLoadException {
if (serializedLine.toLowerCase(Locale.ROOT).startsWith(ICON_PREFIX)) {
@ -32,13 +35,42 @@ public class InternalHologramLineParser {
return new ItemInternalHologramLine(serializedLine, icon);
} else {
String displayText = DisplayFormat.apply(serializedLine, false);
// Apply colors only outside placeholders
displayText = StringWithPlaceholders.withEscapes(displayText).replaceStrings(Colors::colorize);
String displayText = parseText(serializedLine);
return new TextInternalHologramLine(serializedLine, displayText);
}
}
protected static String parseText(String serializedLine) {
String displayText = DisplayFormat.apply(serializedLine, false);
if (Settings.placeholderAPIExpandShortFormat) {
displayText = expandPlaceholderAPIShortFormat(displayText);
}
// Apply colors only outside placeholders
displayText = StringWithPlaceholders.withEscapes(displayText).replaceOutsidePlaceholders(Colors::colorize);
return displayText;
}
private static String expandPlaceholderAPIShortFormat(String text) {
Matcher matcher = PLACEHOLDER_API_SHORT_FORMAT.matcher(text);
boolean foundMatch = matcher.find();
if (!foundMatch) {
return text;
}
StringBuffer result = new StringBuffer();
while (foundMatch) {
String placeholderContent = matcher.group(1);
matcher.appendReplacement(result, "");
result.append("{papi: ").append(StringWithPlaceholders.addEscapes(placeholderContent)).append("}");
foundMatch = matcher.find();
}
matcher.appendTail(result);
return result.toString();
}
@SuppressWarnings("deprecation")
private static ItemStack parseItemStack(String serializedItem) throws InternalHologramLoadException {
serializedItem = serializedItem.trim();

View File

@ -26,6 +26,7 @@ public class Settings {
public static boolean updateNotification;
public static boolean placeholderAPIEnabled;
public static boolean placeholderAPIExpandShortFormat;
public static int placeholderAPIDefaultRefreshInternalTicks;
public static String imageSymbol;
@ -49,6 +50,7 @@ public class Settings {
updateNotification = config.updateNotification;
placeholderAPIEnabled = config.placeholderAPIEnabled;
placeholderAPIExpandShortFormat = config.placeholderAPIShortFormat;
placeholderAPIDefaultRefreshInternalTicks = config.placeholderAPIDefaultRefreshIntervalTicks;
imageSymbol = DisplayFormat.apply(config.imageRenderingSolidPixel);

View File

@ -27,6 +27,9 @@ public class SettingsModel implements MappedConfig {
@Path("placeholders.PlaceholderAPI.enabled")
boolean placeholderAPIEnabled = true;
@Path("placeholders.PlaceholderAPI.expand-short-format")
boolean placeholderAPIShortFormat = true;
@Path("placeholders.PlaceholderAPI.default-refresh-interval-ticks")
int placeholderAPIDefaultRefreshIntervalTicks = 200;

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.plugin.config;
import org.bukkit.ChatColor;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.*;
class InternalHologramLineParserTest {
@ParameterizedTest(name = "[{index}] {0} -> {1}")
@MethodSource({"parseTextArguments"})
void parseText(String input, String expectedOutput) {
Settings.placeholderAPIExpandShortFormat = true;
String output = InternalHologramLineParser.parseText(input);
assertThat(output).isEqualTo(expectedOutput);
}
static Stream<Arguments> parseTextArguments() {
return Stream.of(
// PlaceholderAPI replacing and escaping
Arguments.of("test1 %test2% test3", "test1 {papi: test2} test3"),
Arguments.of("%&0colored%", "{papi: &0colored}"),
Arguments.of("%test1% {test2} %test3%", "{papi: test1} {test2} {papi: test3}"),
Arguments.of("%test_{escape\\this}%", "{papi: test_\\{escape\\\\this\\}}"),
Arguments.of("%t{e%s}t", "{papi: t\\{e}s}t"),
Arguments.of("{inside %placeholder%}", "{inside {papi: placeholder}}"),
Arguments.of("\\{keep escapes\\}", "\\{keep escapes\\}"),
Arguments.of("single % symbol", "single % symbol"),
// Placeholders and colors
Arguments.of("&0 {p: &0}", ChatColor.COLOR_CHAR + "0 {p: &0}")
);
}
}