diff --git a/src/main/java/ch/njol/skript/effects/EffReplace.java b/src/main/java/ch/njol/skript/effects/EffReplace.java
index ef2faf44282..4a16ba6e88b 100644
--- a/src/main/java/ch/njol/skript/effects/EffReplace.java
+++ b/src/main/java/ch/njol/skript/effects/EffReplace.java
@@ -1,21 +1,3 @@
-/**
- * This file is part of Skript.
- *
- * Skript is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Skript is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Skript. If not, see .
- *
- * Copyright Peter Güttinger, SkriptLang team and contributors
- */
package ch.njol.skript.effects;
import ch.njol.skript.Skript;
@@ -39,61 +21,74 @@
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
+import java.util.regex.Pattern;
@Name("Replace")
-@Description("Replaces all occurrences of a given text with another text. Please note that you can only change variables and a few expressions, e.g. a message or a line of a sign.")
-@Examples({"replace \"- \" in {textvar} with \"%item%\"",
- "replace every \"&\" with \"§\" in line 1",
- "# The following acts as a simple chat censor, but it will e.g. censor mass, hassle, assassin, etc. as well:",
- "on chat:",
- " replace all \"kys\", \"idiot\" and \"noob\" with \"****\" in the message",
- " ",
- "replace all stone and dirt in player's inventory and player's top inventory with diamond"})
-@Since("2.0, 2.2-dev24 (replace in multiple strings and replace items in inventory), 2.5 (replace first, case sensitivity)")
+@Description(
+ "Replaces all occurrences of a given text or regex with another text. Please note that you can only change " +
+ "variables and a few expressions, e.g. a message or a line of a sign."
+)
+@Examples({
+ "replace \"
- \" in {_msg} with \"[%name of player's tool%]\"",
+ "replace every \"&\" with \"§\" in line 1 of targeted block",
+ "",
+ "# Very simple chat censor",
+ "on chat:",
+ "\treplace all \"idiot\" and \"noob\" with \"****\" in the message",
+ "\tregex replace \"\\b(idiot|noob)\\b\" with \"****\" in the message # Regex version using word boundaries for better results",
+ "",
+ "replace all stone and dirt in player's inventory and player's top inventory with diamond"
+})
+@Since("2.0, 2.2-dev24 (multiple strings, items in inventory), 2.5 (replace first, case sensitivity), INSERT VERSION (regex)")
public class EffReplace extends Effect {
static {
Skript.registerEffect(EffReplace.class,
- "replace (all|every|) %strings% in %strings% with %string% [(1¦with case sensitivity)]",
- "replace (all|every|) %strings% with %string% in %strings% [(1¦with case sensitivity)]",
- "replace first %strings% in %strings% with %string% [(1¦with case sensitivity)]",
- "replace first %strings% with %string% in %string% [(1¦with case sensitivity)]",
- "replace (all|every|) %itemtypes% in %inventories% with %itemtype%",
- "replace (all|every|) %itemtypes% with %itemtype% in %inventories%");
+ "replace [(all|every)|first:[the] first] %strings% in %strings% with %string% [case:with case sensitivity]",
+ "replace [(all|every)|first:[the] first] %strings% with %string% in %strings% [case:with case sensitivity]",
+ "(replace [with|using] regex|regex replace) %strings% in %strings% with %string%",
+ "(replace [with|using] regex|regex replace) %strings% with %string% in %strings%",
+ "replace [all|every] %itemtypes% in %inventories% with %itemtype%",
+ "replace [all|every] %itemtypes% with %itemtype% in %inventories%");
}
-
- @SuppressWarnings("null")
+
private Expression> haystack, needles, replacement;
- private boolean replaceString = true;
- private boolean replaceFirst = false;
+ private boolean replaceString;
+ private boolean replaceRegex;
+ private boolean replaceFirst;
private boolean caseSensitive = false;
- @SuppressWarnings({"null"})
@Override
- public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
- haystack = exprs[1 + matchedPattern % 2];
+ public boolean init(Expression>[] expressions, int matchedPattern,
+ Kleenean isDelayed, ParseResult parseResult) {
+ haystack = expressions[1 + matchedPattern % 2];
replaceString = matchedPattern < 4;
- replaceFirst = matchedPattern > 1 && matchedPattern < 4;
+ replaceFirst = parseResult.hasTag("first");
+ replaceRegex = matchedPattern == 2 || matchedPattern == 3;
+
if (replaceString && !ChangerUtils.acceptsChange(haystack, ChangeMode.SET, String.class)) {
- Skript.error(haystack + " cannot be changed and can thus not have parts replaced.");
+ Skript.error(haystack + " cannot be changed and can thus not have parts replaced");
return false;
}
- if (SkriptConfig.caseSensitive.value() || parseResult.mark == 1) {
+
+ if (SkriptConfig.caseSensitive.value() || parseResult.hasTag("case")) {
caseSensitive = true;
}
- needles = exprs[0];
- replacement = exprs[2 - matchedPattern % 2];
+
+ needles = expressions[0];
+ replacement = expressions[2 - matchedPattern % 2];
return true;
}
-
- @SuppressWarnings("null")
+
@Override
protected void execute(Event event) {
Object[] needles = this.needles.getAll(event);
- if (haystack instanceof ExpressionList) {
- for (Expression> haystackExpr : ((ExpressionList>) haystack).getExpressions()) {
+ if (haystack instanceof ExpressionList> list) {
+ for (Expression> haystackExpr : list.getExpressions()) {
replace(event, needles, haystackExpr);
}
} else {
@@ -104,27 +99,46 @@ protected void execute(Event event) {
private void replace(Event event, Object[] needles, Expression> haystackExpr) {
Object[] haystack = haystackExpr.getAll(event);
Object replacement = this.replacement.getSingle(event);
+
if (replacement == null || haystack == null || haystack.length == 0 || needles == null || needles.length == 0)
return;
+
if (replaceString) {
- if (replaceFirst) {
- for (int x = 0; x < haystack.length; x++)
- for (Object n : needles) {
- assert n != null;
- haystack[x] = StringUtils.replaceFirst((String)haystack[x], (String)n, Matcher.quoteReplacement((String)replacement), caseSensitive);
+ String stringReplacement = (String) replacement;
+ if (replaceRegex) { // replace all/first - regex
+ List patterns = new ArrayList<>(needles.length);
+ for (Object needle : needles) {
+ try {
+ patterns.add(Pattern.compile((String) needle));
+ } catch (Exception ignored) { }
+ }
+ for (int i = 0; i < haystack.length; i++) {
+ for (Pattern pattern : patterns) {
+ Matcher matcher = pattern.matcher((String) haystack[i]);
+ if (replaceFirst) // first
+ haystack[i] = matcher.replaceFirst(stringReplacement);
+ else // all
+ haystack[i] = matcher.replaceAll(stringReplacement);
+ }
+ }
+ } else if (replaceFirst) { // replace first - string
+ for (int i = 0; i < haystack.length; i++) {
+ for (Object needle : needles) {
+ haystack[i] = StringUtils.replaceFirst((String) haystack[i], (String) needle, Matcher.quoteReplacement(stringReplacement), caseSensitive);
}
- } else {
- for (int x = 0; x < haystack.length; x++)
- for (Object n : needles) {
- assert n != null;
- haystack[x] = StringUtils.replace((String) haystack[x], (String) n, (String) replacement, caseSensitive);
+ }
+ } else { // replace all - string
+ for (int i = 0; i < haystack.length; i++) {
+ for (Object needle : needles) {
+ haystack[i] = StringUtils.replace((String) haystack[i], (String) needle, stringReplacement, caseSensitive);
}
+ }
}
haystackExpr.change(event, haystack, ChangeMode.SET);
} else {
- for (Inventory inv : (Inventory[]) haystack)
- for (ItemType needle : (ItemType[]) needles)
- for (Map.Entry entry : inv.all(needle.getMaterial()).entrySet()) {
+ for (Inventory inventory : (Inventory[]) haystack) {
+ for (ItemType needle : (ItemType[]) needles) {
+ for (Map.Entry entry : inventory.all(needle.getMaterial()).entrySet()) {
int slot = entry.getKey();
ItemStack itemStack = entry.getValue();
@@ -132,13 +146,15 @@ private void replace(Event event, Object[] needles, Expression> haystackExpr)
ItemStack newItemStack = ((ItemType) replacement).getRandom();
if (newItemStack != null) {
newItemStack.setAmount(itemStack.getAmount());
- inv.setItem(slot, newItemStack);
+ inventory.setItem(slot, newItemStack);
}
}
}
+ }
+ }
}
}
-
+
@Override
public String toString(@Nullable Event event, boolean debug) {
SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug);
@@ -146,6 +162,8 @@ public String toString(@Nullable Event event, boolean debug) {
builder.append("replace");
if (replaceFirst)
builder.append("the first");
+ if (replaceRegex)
+ builder.append("regex");
builder.append(needles, "in", haystack, "with", replacement);
if (caseSensitive)
builder.append("with case sensitivity");
diff --git a/src/test/skript/tests/syntaxes/effects/EffReplace.sk b/src/test/skript/tests/syntaxes/effects/EffReplace.sk
index 26c38b89690..63828c0218c 100644
--- a/src/test/skript/tests/syntaxes/effects/EffReplace.sk
+++ b/src/test/skript/tests/syntaxes/effects/EffReplace.sk
@@ -48,3 +48,26 @@ test "replace strings":
assert {_list::1} is "(replaced)" with "Incorrect ExpressionList replacement value (1)"
assert {_list::2} is "2" with "Incorrect ExpressionList replacement value (2)"
assert {_list::3} is "(replaced)2" with "Incorrect ExpressionList replacement value (3)"
+
+ # Regex replacing
+ set {_x} to "abc123aACAB"
+ regex replace "123a" in {_x} with "123"
+ assert {_x} is "abc123ACAB" with "regex replace failed"
+ regex replace "\d" with "x" in {_x}
+ assert {_x} is "abcxxxACAB" with "regex replace failed"
+ regex replace "^[acb]+" in {_x} with "y"
+ assert {_x} is "yxxxACAB" with "regex replace failed"
+ regex replace "(?i)[bAc]" in {_x} with "z"
+ assert {_x} is "yxxxzzzz" with "regex replace failed"
+
+ set {_x} to "a" parsed as number
+ regex replace "a" in {_x} with "b"
+ assert {_x} is not set with "regex replace with invalid value succeeded"
+
+ set {_y} to "a"
+ regex replace {_x} in {_y} with "b"
+ assert {_y} is "a" with "regex replace with invalid pattern succeeded"
+ regex replace {_x} in {_y} with {_x}
+ assert {_y} is "a" with "regex replace with invalid pattern and replacement succeeded"
+ regex replace "a" in {_y} with {_x}
+ assert {_y} is "a" with "regex replace with invalid pattern succeeded"