diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index 9256ca3d..a1d4803b 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -3812,7 +3812,7 @@ Moves the entity by a vector from its current location. Sets the motion vector (where and how much entity is moving). -### `modify(e, 'motion_z', x), modify(e, 'motion_y', y), modify(e, 'motion_z', z)` +### `modify(e, 'motion_x', x), modify(e, 'motion_y', y), modify(e, 'motion_z', z)` Sets the corresponding component of the motion vector. diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index a25c3c20..ef67e102 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -43,6 +43,7 @@ import net.minecraft.nbt.NbtElement; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.BaseText; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; @@ -58,6 +59,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.TreeMap; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Predicate; @@ -1022,7 +1024,7 @@ public class CarpetScriptHost extends ScriptHost public void setChatErrorSnooper(ServerCommandSource source) { responsibleSource = source; - errorSnooper = (expr, /*Nullable*/ token, message) -> + errorSnooper = (expr, /*Nullable*/ token, ctx, message) -> { try { @@ -1049,13 +1051,13 @@ public class CarpetScriptHost extends ScriptHost Messenger.m(source, "r " + shebang); if (lines.length > 1 && token.lineno > 0) { - Messenger.m(source, "l " + lines[token.lineno - 1]); + Messenger.m(source, withLocals("l", lines[token.lineno - 1], ctx)); } - Messenger.m(source, "l " + lines[token.lineno].substring(0, token.linepos), "r HERE>> ", "l " + - lines[token.lineno].substring(token.linepos)); + Messenger.m(source, withLocals("l", lines[token.lineno].substring(0, token.linepos), ctx), "r HERE>> ", + withLocals("l", lines[token.lineno].substring(token.linepos), ctx)); if (lines.length > 1 && token.lineno < lines.length - 1) { - Messenger.m(source, "l " + lines[token.lineno + 1]); + Messenger.m(source, withLocals("l", lines[token.lineno + 1], ctx)); } } else @@ -1066,6 +1068,59 @@ public class CarpetScriptHost extends ScriptHost }; } + /** + *

Creates a {@link BaseText} using {@link Messenger} that has the locals in the {@code line} snippet with a hover over + * tooltip with the value of the local at that location

+ * @param line The line to find references to locals on + * @param context The {@link Context} to extract the locals from + * @param format The format to apply to each part of the line, without the trailing space + * @return A BaseText of the given line with the given format, that is visibly the same as passing those to Messenger, but with references to the + * locals in the {@link Context} with a hover over tooltip text + * @implNote The implementation of this method is far from perfect, and won't detect actual references to variables, but try to find the strings + * and add the hover effect to anything that equals to any variable name, so short variable names may appear on random positions + */ + private static BaseText withLocals(String format, String line, Context context) + { + format += " "; + List stringsToFormat = new ArrayList<>(); + TreeMap posToLocal = new TreeMap<>(); //Holds whether a local variable name is found at a specific index + for (String local: context.variables.keySet()) + { + int pos = line.indexOf(local); + while (pos != -1) + { + posToLocal.merge(pos, local, (existingLocal, newLocal) -> + { + if (newLocal.length() > existingLocal.length()) // Prefer longer variable names at the same position, since else single chars everywhere + return local; + else + return existingLocal; + }); + pos = line.indexOf(local, pos + 1); + } + } + int lastPos = 0; + for (Entry foundLocal : posToLocal.entrySet()) + { + if (foundLocal.getKey() < lastPos) // system isn't perfect: part of another local + continue; + stringsToFormat.add(format + line.substring(lastPos, foundLocal.getKey())); + stringsToFormat.add(format + foundLocal.getValue()); + String value; + try { + value = context.variables.get(foundLocal.getValue()).evalValue(context).getPrettyString(); + } catch (StackOverflowError e) { + value = "Exception while rendering variable, there seems to be a recursive reference in there"; + } + stringsToFormat.add("^ Value of '" + foundLocal.getValue() + "' at position: \n" + + value); + lastPos = foundLocal.getKey() + foundLocal.getValue().length(); + } + if (line.length() != lastPos) + stringsToFormat.add(format + line.substring(lastPos, line.length())); + return Messenger.c(stringsToFormat.toArray()); + } + @Override public void resetErrorSnooper() { diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java index 05b3e0f2..9da1e036 100644 --- a/src/main/java/carpet/script/ScriptHost.java +++ b/src/main/java/carpet/script/ScriptHost.java @@ -108,7 +108,7 @@ public abstract class ScriptHost @FunctionalInterface public interface ErrorSnooper { - List apply(Expression expression, Tokenizer.Token token, String message); + List apply(Expression expression, Tokenizer.Token token, Context context, String message); } public ErrorSnooper errorSnooper = null; diff --git a/src/main/java/carpet/script/exception/ExpressionException.java b/src/main/java/carpet/script/exception/ExpressionException.java index 7e494b1d..00eb0e9d 100644 --- a/src/main/java/carpet/script/exception/ExpressionException.java +++ b/src/main/java/carpet/script/exception/ExpressionException.java @@ -74,8 +74,8 @@ public class ExpressionException extends RuntimeException implements ResolvedExc { if (c.getErrorSnooper() != null) { - List alternative = c.getErrorSnooper().apply(e, t, message); - if (alternative!= null) + List alternative = c.getErrorSnooper().apply(e, t, c, message); + if (alternative != null) { return String.join("\n", alternative); } diff --git a/src/main/java/carpet/settings/SettingsManager.java b/src/main/java/carpet/settings/SettingsManager.java index 745c5505..ce8711ac 100644 --- a/src/main/java/carpet/settings/SettingsManager.java +++ b/src/main/java/carpet/settings/SettingsManager.java @@ -17,6 +17,7 @@ import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import net.fabricmc.api.EnvType; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.command.CommandSource; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerTask; @@ -550,14 +551,24 @@ public class SettingsManager } static CompletableFuture suggestMatchingContains(Stream stream, SuggestionsBuilder suggestionsBuilder) { + List regularSuggestionList = new ArrayList<>(); + List smartSuggestionList = new ArrayList<>(); String query = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT); - var filteredSuggestionList = stream.filter((listItem) -> { //Regex camelCase Search + stream.forEach((listItem) -> { + // Regex camelCase Search var words = Arrays.stream(listItem.split("(? s.toLowerCase(Locale.ROOT)).collect(Collectors.toList()); var prefixes = new ArrayList(words.size()); for (int i = 0; i < words.size(); i++) prefixes.add(String.join("",words.subList(i, words.size()))); - return prefixes.stream().anyMatch(s -> s.startsWith(query)); + if (prefixes.stream().anyMatch(s -> s.startsWith(query))) { + smartSuggestionList.add(listItem); + } + // Regular prefix matching, reference: CommandSource.suggestMatching + if (CommandSource.method_27136(query, listItem.toLowerCase())) { + regularSuggestionList.add(listItem); + } }); + var filteredSuggestionList = regularSuggestionList.isEmpty() ? smartSuggestionList : regularSuggestionList; Objects.requireNonNull(suggestionsBuilder); filteredSuggestionList.forEach(suggestionsBuilder::suggest); return suggestionsBuilder.buildFuture();