From 02391e281611a8389cc983704ec521501219aad5 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Thu, 17 Oct 2024 20:41:44 -0400 Subject: [PATCH 01/16] proof of concept --- .../base/SimplePropertyExpression.java | 19 ++- .../skript/lang/errors/RuntimeError.java | 17 +++ .../lang/errors/RuntimeErrorProducer.java | 135 ++++++++++++++++++ 3 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/skriptlang/skript/lang/errors/RuntimeError.java create mode 100644 src/main/java/org/skriptlang/skript/lang/errors/RuntimeErrorProducer.java diff --git a/src/main/java/ch/njol/skript/expressions/base/SimplePropertyExpression.java b/src/main/java/ch/njol/skript/expressions/base/SimplePropertyExpression.java index dbbf968f9df..90071bdef6d 100644 --- a/src/main/java/ch/njol/skript/expressions/base/SimplePropertyExpression.java +++ b/src/main/java/ch/njol/skript/expressions/base/SimplePropertyExpression.java @@ -1,12 +1,14 @@ package ch.njol.skript.expressions.base; import ch.njol.skript.classes.Converter; +import ch.njol.skript.config.Node; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.util.LiteralUtils; import ch.njol.util.Kleenean; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.errors.RuntimeErrorProducer; /** * A base class for property expressions that requires only few overridden methods @@ -15,7 +17,10 @@ * @see PropertyExpression#register(Class, Class, String, String) */ @SuppressWarnings("deprecation") // for backwards compatibility -public abstract class SimplePropertyExpression extends PropertyExpression implements Converter { +public abstract class SimplePropertyExpression extends PropertyExpression implements Converter, RuntimeErrorProducer { + + private Node node; + private String rawExpr; @Override @SuppressWarnings("unchecked") @@ -25,6 +30,8 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is return LiteralUtils.canInitSafely(getExpr()); } setExpr((Expression) expressions[0]); + node = getParser().getNode(); + rawExpr = parseResult.expr; return true; } @@ -37,6 +44,16 @@ protected T[] get(Event event, F[] source) { return super.get(source, this); } + @Override + public Node getNode() { + return node; + } + + @Override + public String toUnderline() { + return rawExpr; + } + /** * Used to collect the property type used in the register method. * This forms the toString of this SimplePropertyExpression. diff --git a/src/main/java/org/skriptlang/skript/lang/errors/RuntimeError.java b/src/main/java/org/skriptlang/skript/lang/errors/RuntimeError.java new file mode 100644 index 00000000000..b83f8bf2a81 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/errors/RuntimeError.java @@ -0,0 +1,17 @@ +package org.skriptlang.skript.lang.errors; + +import ch.njol.skript.config.Node; +import ch.njol.skript.log.LogEntry; +import ch.njol.skript.log.LogHandler; +import ch.njol.skript.log.SkriptLogger; + +import java.util.logging.Level; + +public record RuntimeError(Level level, String message, Node node) { + + public void log(LogHandler log) { + SkriptLogger.log(new LogEntry(level, message, node)); + } + + +} diff --git a/src/main/java/org/skriptlang/skript/lang/errors/RuntimeErrorProducer.java b/src/main/java/org/skriptlang/skript/lang/errors/RuntimeErrorProducer.java new file mode 100644 index 00000000000..a4da0a74f6a --- /dev/null +++ b/src/main/java/org/skriptlang/skript/lang/errors/RuntimeErrorProducer.java @@ -0,0 +1,135 @@ +package org.skriptlang.skript.lang.errors; + +import ch.njol.skript.config.Config; +import ch.njol.skript.config.Node; +import ch.njol.skript.doc.Name; +import ch.njol.skript.lang.Condition; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.Section; +import ch.njol.skript.lang.SkriptParser; +import ch.njol.skript.lang.SyntaxElement; +import ch.njol.skript.lang.parser.ParserInstance; +import ch.njol.skript.log.SkriptLogger; +import ch.njol.skript.util.Utils; +import ch.njol.util.Kleenean; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.skriptlang.skript.lang.structure.Structure; + +import java.util.logging.Level; + +/** + * A RuntimeErrorProducer can throw runtime errors in a standardized and controlled manner. + * Only {@link SyntaxElement}s are intended to implement this interface. + */ +public interface RuntimeErrorProducer { + + // todo: try/catch + + /** + * Returns the {@link Node} that this is a part of. Used for accessing the line contents via {@link Node#getKey()} + * and the line number via {@link Node#getLine()}. + *
+ * A standard implementation is to store the Node during + * {@link SyntaxElement#init(Expression[], int, Kleenean, SkriptParser.ParseResult)} + * via {@link ParserInstance#getNode()}. + * @return The node that produced a runtime error. + */ + Node getNode(); + + /** + * Gets the text that should be underlined within the line contents. This should match the text the user wrote that + * was parsed as the syntax that threw the runtime issue. For example, if the skull expression in + * {@code give skull of player to all players} throws a runtime error, this method should return + * {@code "skull of player"} + *
+ * An example implementation for {@link Expression}s is to store {@link SkriptParser.ParseResult#expr} during + * {@link SyntaxElement#init(Expression[], int, Kleenean, SkriptParser.ParseResult)} and return that. + *
+ * For other syntax types, this may vary. Effects, for example, may underline the whole line. + * + * @return The text to underline in the line that produced a runtime error. + */ + String toUnderline(); + + /** + * Dispatches a runtime error with the given text. + * Metadata will be provided along with the message, including line number, the docs name of the producer, + * and the line content. + * + * @param message The text to display as the error message. + */ + default void error(String message) { + String formatted = toFormattedString(Level.SEVERE, message, getNode(), toUnderline()); + SkriptLogger.sendFormatted(Bukkit.getConsoleSender(), formatted); + // todo: check if error should be suppressed, set last error field for users + // todo: send notif to online players with permission. "Script x.sk produced a runtime error! Check console." + } + + /** + * Dispatches a runtime warning with the given text. + * Metadata will be provided along with the message, including line number, the docs name of the producer, + * and the line content. + * + * @param message The text to display as the error message. + */ + default void warning(String message) { + String formatted = toFormattedString(Level.WARNING, message, getNode(), toUnderline()); + SkriptLogger.sendFormatted(Bukkit.getConsoleSender(), formatted); + // todo: check if error should be suppressed, set last error field for users + // todo: send notif to online players with permission. "Script x.sk produced a runtime warning! Check console." + } + + /** + * Generates a formatted string based on the provided log level, message, node information, and text to underline. + * + * @param level The severity level of the log message. + * @param message The message to be included in the log. + * @param node The node associated with this log message. + * @param toUnderline The text in the node's line content that should be underlined in the log message. + * @return A formatted string that includes metadata such as the script filename, error message, + * and the specific line that caused the error. + */ + default String toFormattedString(Level level, String message, Node node, String toUnderline) { + // Replace configured messages chat styles without user variables + // todo: add lang entries, handle error vs warning + String skriptInfo = replaceNewline(Utils.replaceEnglishChatStyles("The script '%s' encountered an error while executing the '%s' %s:\n")); + String errorInfo = replaceNewline(Utils.replaceEnglishChatStyles("\t%s\n")); + String lineInfo = replaceNewline(Utils.replaceEnglishChatStyles("\tLine %s: %s\n")); + + if (node == null) + return "handle null nodes"; + + Config c = node.getConfig(); + String from = this.getClass().getAnnotation(Name.class).value().trim().replaceAll("\n", ""); + + return + String.format(skriptInfo, c.getFileName(), from, getSyntaxType()) + + String.format(errorInfo, message.replaceAll("§", "&")) + + String.format(lineInfo, node.getLine(), node.save().trim().replaceAll("§", "&").replaceAll(toUnderline, "§f§n" + toUnderline + "§7")); + } + + @Contract(pure = true) + private @NotNull String getSyntaxType() { + if (this instanceof Effect) { + return "effect"; + } else if (this instanceof Condition) { + return "condition"; + } else if (this instanceof Expression) { + return "expression"; + } else if (this instanceof Section) { + return "section"; + } else if (this instanceof Structure) { + return "structure"; + } + return "syntax"; + } + + @Contract(pure = true) + private @NotNull String replaceNewline(@NotNull String s) { + return s.replaceAll("\\\\n", "\n"); + } + +} From dbdf666ef28dc2b486f290e4ff2286143edbc314 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Thu, 17 Oct 2024 20:45:58 -0400 Subject: [PATCH 02/16] Delete RuntimeError.java --- .../skript/lang/errors/RuntimeError.java | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 src/main/java/org/skriptlang/skript/lang/errors/RuntimeError.java diff --git a/src/main/java/org/skriptlang/skript/lang/errors/RuntimeError.java b/src/main/java/org/skriptlang/skript/lang/errors/RuntimeError.java deleted file mode 100644 index b83f8bf2a81..00000000000 --- a/src/main/java/org/skriptlang/skript/lang/errors/RuntimeError.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.skriptlang.skript.lang.errors; - -import ch.njol.skript.config.Node; -import ch.njol.skript.log.LogEntry; -import ch.njol.skript.log.LogHandler; -import ch.njol.skript.log.SkriptLogger; - -import java.util.logging.Level; - -public record RuntimeError(Level level, String message, Node node) { - - public void log(LogHandler log) { - SkriptLogger.log(new LogEntry(level, message, node)); - } - - -} From 729ab9866335d664753356ab3919206382f000c2 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Thu, 14 Nov 2024 19:31:52 -0500 Subject: [PATCH 03/16] prototype limits --- .../java/ch/njol/skript/ScriptLoader.java | 4 + .../skript/expressions/ExprSkullOwner.java | 5 +- .../lang/errors/RuntimeErrorManager.java | 107 ++++++++++++++++++ .../lang/errors/RuntimeErrorProducer.java | 9 +- 4 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/skriptlang/skript/lang/errors/RuntimeErrorManager.java diff --git a/src/main/java/ch/njol/skript/ScriptLoader.java b/src/main/java/ch/njol/skript/ScriptLoader.java index 9d246d9565e..b69d01deee4 100644 --- a/src/main/java/ch/njol/skript/ScriptLoader.java +++ b/src/main/java/ch/njol/skript/ScriptLoader.java @@ -47,6 +47,7 @@ import org.bukkit.Bukkit; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.lang.errors.RuntimeErrorProducer; import org.skriptlang.skript.util.event.EventRegistry; import org.skriptlang.skript.lang.script.Script; import org.skriptlang.skript.lang.script.ScriptWarning; @@ -887,6 +888,9 @@ public static ScriptInfo unloadScripts(Set