Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add regex replacement #7167

Merged
merged 41 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
b767f4a
🚀 Add regex replace
AyhamAl-Ali Sep 7, 2021
7ae2743
Merge branch 'master' into ench/regex-replace
AyhamAl-Ali Jun 24, 2022
fbf3112
RC
AyhamAl-Ali Jun 24, 2022
b932fce
Merge branch 'master' into ench/regex-replace
TheLimeGlass Jul 13, 2022
58ef654
Update src/main/java/ch/njol/skript/effects/EffReplace.java
AyhamAl-Ali Aug 6, 2022
94be128
Update src/main/java/ch/njol/skript/effects/EffReplace.java
AyhamAl-Ali Aug 6, 2022
9f6b2df
Update src/main/java/ch/njol/skript/effects/EffReplace.java
AyhamAl-Ali Aug 6, 2022
785ef94
Auto stash before merge of "ench/regex-replace" and "AyhamAl-Ali/ench…
AyhamAl-Ali Aug 6, 2022
5820bd9
Add [the]
AyhamAl-Ali Aug 6, 2022
fbeb4ba
Fix build
AyhamAl-Ali Aug 6, 2022
e4cf911
Merge branch 'master' into ench/regex-replace
AyhamAl-Ali Aug 26, 2022
e603f6c
Update src/main/java/ch/njol/skript/effects/EffReplace.java
AyhamAl-Ali Jan 1, 2023
6f6241c
Better variable naming
AyhamAl-Ali Jan 2, 2023
26f5014
p -> pattern
AyhamAl-Ali Jan 2, 2023
05f6068
Merge branch 'master' into ench/regex-replace
AyhamAl-Ali Aug 21, 2023
f55124d
Fix merge to master conflicts
AyhamAl-Ali Aug 22, 2023
bf321ab
Fix tests
AyhamAl-Ali Aug 22, 2023
c545523
Merge branch 'master' into ench/regex-replace
AyhamAl-Ali Sep 13, 2023
3a501f5
Merge branch 'dev/feature' into ench/regex-replace
AyhamAl-Ali Oct 5, 2023
d78fe22
Address reviews
AyhamAl-Ali Apr 5, 2024
2750f1f
Merge remote-tracking branch 'AyhamAl-Ali/ench/regex-replace' into en…
AyhamAl-Ali Apr 5, 2024
255696e
Fix build
AyhamAl-Ali Apr 5, 2024
73da21b
Merge branch 'dev/feature' into ench/regex-replace
Moderocky Apr 10, 2024
026655e
Update src/main/java/ch/njol/skript/effects/EffReplace.java
sovdeeth Jun 28, 2024
48ec446
Merge branch 'fork/AyhamAl-Ali/ench/regex-replace' into regex-replace
Efnilite Oct 29, 2024
2fc31d0
init commit
Efnilite Oct 29, 2024
d73c7a2
add tests
Efnilite Oct 29, 2024
f33a5e1
oops
Efnilite Oct 29, 2024
c0998fe
minor change
Efnilite Oct 29, 2024
01201ef
fix errors
Efnilite Oct 29, 2024
b5dfb1d
Merge branch 'dev/feature' into regex-replace
Efnilite Nov 10, 2024
d8f08f0
Merge branch 'dev/feature' into regex-replace
Efnilite Nov 16, 2024
9b9f7e5
le fix
Efnilite Nov 16, 2024
74e1298
Merge branch 'dev/feature' into regex-replace
Moderocky Nov 21, 2024
f623029
Merge branch 'dev/feature' into regex-replace
Efnilite Nov 23, 2024
94b5ea1
Merge branch 'dev/feature' into regex-replace
Efnilite Dec 16, 2024
02c99e7
Merge branch 'dev/feature' into regex-replace
Efnilite Dec 16, 2024
b6e6645
oops
Efnilite Dec 16, 2024
364ceb0
Merge remote-tracking branch 'origin/regex-replace' into regex-replace
Efnilite Dec 16, 2024
3b0a34a
Update src/main/java/ch/njol/skript/effects/EffReplace.java
Efnilite Dec 17, 2024
04e9d26
Merge branch 'dev/feature' into regex-replace
Moderocky Dec 18, 2024
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
146 changes: 82 additions & 64 deletions src/main/java/ch/njol/skript/effects/EffReplace.java
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*
* Copyright Peter Güttinger, SkriptLang team and contributors
*/
package ch.njol.skript.effects;

import ch.njol.skript.Skript;
Expand All @@ -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 <a href='./expressions.html#ExprMessage'>message</a> or a line of a sign.")
@Examples({"replace \"<item>\" 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 <a href='/expressions.html#ExprMessage'>message</a> or a line of a sign."
)
@Examples({
"replace \"<item>\" 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 {
Expand All @@ -104,48 +99,71 @@ 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<Pattern> 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<Integer, ? extends ItemStack> entry : inv.all(needle.getMaterial()).entrySet()) {
for (Inventory inventory : (Inventory[]) haystack) {
for (ItemType needle : (ItemType[]) needles) {
for (Map.Entry<Integer, ? extends ItemStack> entry : inventory.all(needle.getMaterial()).entrySet()) {
int slot = entry.getKey();
ItemStack itemStack = entry.getValue();

if (new ItemType(itemStack).isSimilar(needle)) {
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);

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");
Expand Down
23 changes: 23 additions & 0 deletions src/test/skript/tests/syntaxes/effects/EffReplace.sk
Original file line number Diff line number Diff line change
Expand Up @@ -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"