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();