From 2ff1b3a6cfd3dc4caa9762de68381a7e76036d0f Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Wed, 24 Dec 2025 23:50:58 +0200 Subject: [PATCH] Improved arg count/type error underline rendering logic --- .../script/interpreter/MethodCallInfo.java | 20 +---- .../util/script/interpreter/ScriptLine.java | 6 +- .../gui/util/script/interpreter/Token.java | 89 +++++++++++++++++-- 3 files changed, 87 insertions(+), 28 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/MethodCallInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/MethodCallInfo.java index a4b16eba1..f9440a492 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/MethodCallInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/MethodCallInfo.java @@ -202,19 +202,7 @@ public class MethodCallInfo { public boolean hasArgTypeError() { return !this.argumentTypeErrors.isEmpty(); } - - public boolean hasArgTypeError(Token t) { - for (ArgumentTypeError ate : this.argumentTypeErrors) { - if (ate.getArg().equals(t)) { - return true; - } - } - return false; - } - - public boolean hasArgCountError(Token t) { - return hasArgCountError() && t.isMethodCall(); - } + /** * Check if this is a static access error (underline method name). @@ -233,6 +221,9 @@ public class MethodCallInfo { this.argumentTypeErrors.add(new ArgumentTypeError(arguments.get(argIndex), argIndex, message)); } + public List getArgumentTypeErrors() { + return argumentTypeErrors; + } public class ArgumentTypeError { private ErrorType type = ErrorType.WRONG_ARG_TYPE; @@ -298,14 +289,11 @@ public class MethodCallInfo { if (!isTypeCompatible(argType, paramType)) { setArgTypeError(i, "Expected " + paramType.getSimpleName() + " but got " + argType.getSimpleName()); - return; } } else if (paramType == null) { setArgTypeError(i, "Parameter type of '" + para.getName() + "' is unresolved"); - return; } else if (argType == null) { setArgTypeError(i, "Cannot resolve type of argument '" + arg.getText() + "'"); - return; } } } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptLine.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptLine.java index 2e33bb9ae..081bb1c7f 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptLine.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptLine.java @@ -223,10 +223,8 @@ public class ScriptLine { } // Track underline position if token has one - if (t.hasUnderline()) { - int currentY = y + ClientProxy.Font.height() - 1; - drawCurlyUnderline(currentX + 1, currentY, tokenWidth + 1, t.getUnderlineColor()); - } + t.drawUnderline(currentX, y + ClientProxy.Font.height() - 1); + // Append the colored token builder.append(COLOR_CHAR) diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/Token.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/Token.java index 7c6851d69..d0d6f7ee6 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/Token.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/Token.java @@ -1,5 +1,7 @@ package noppes.npcs.client.gui.util.script.interpreter; +import noppes.npcs.client.ClientProxy; + /** * Represents a single token in the source code with its type and metadata. * Tokens are the atomic units of the syntax highlighting system. @@ -121,20 +123,91 @@ public class Token { public ScriptLine getParentLine() { return parentLine; } public boolean hasUnderline() { - if (methodCallInfo != null) { - if (methodCallInfo.hasArgCountError(this)) - return true; - else if (methodCallInfo.hasArgTypeError(this)) { - return true; - } - } - return false; + return isMethodCall() && (methodCallInfo.hasArgTypeError() || methodCallInfo.hasArgCountError()); } public int getUnderlineColor() { return underlineColor; } + /** + * Draw underlines for this token's errors. + * @param x start pixel of this token + * @param y baseline pixel for underline + */ + public void drawUnderline(int x, int y) { + if (methodCallInfo == null) + return; + + if (methodCallInfo.hasArgCountError()) { + int tokenWidth = ClientProxy.Font.width(text); + ScriptLine.drawCurlyUnderline(x + 1, y, tokenWidth + 1, getUnderlineColor()); + return; + } + + if (methodCallInfo.hasArgTypeError()) { + // For arg type errors, underline the specific argument span(s). + // Compute pixel X for each argument by walking tokens on the same line + ScriptLine parent = getParentLine(); + if (parent == null) + return; + + for (MethodCallInfo.ArgumentTypeError error : methodCallInfo.getArgumentTypeErrors()) { + MethodCallInfo.Argument arg = error.getArg(); + int argStart = arg.getStartOffset(); + int argEnd = arg.getEndOffset(); + + // Skip args not on this line + if (!parent.containsPosition(argStart)) + continue; + + // Compute X position by summing token widths from this token up to argStart + int argX = x; + Token cur = this; + + // If this token starts after the arg (shouldn't normally happen), skip + if (cur.getGlobalStart() > argStart) + continue; + + while (cur != null && cur.getParentLine() == parent && cur.getGlobalEnd() <= argStart) { + argX += ClientProxy.Font.width(cur.getText()); + cur = cur.nextOnLine(); + } + + if (cur == null || cur.getParentLine() != parent) + continue; + + // Add partial width of the token containing the arg start + int overlapStart = Math.max(0, argStart - cur.getGlobalStart()); + if (overlapStart > 0) { + String part = cur.getText().substring(0, Math.min(overlapStart, cur.getText().length())); + argX += ClientProxy.Font.width(part); + } + + // Compute total width of the argument (may span multiple tokens) + int argWidth = 0; + Token cur2 = cur; + int remainingStart = argStart; + while (cur2 != null && cur2.getParentLine() == parent && remainingStart < argEnd) { + int segStart = Math.max(remainingStart, cur2.getGlobalStart()); + int segEnd = Math.min(argEnd, cur2.getGlobalEnd()); + if (segEnd > segStart) { + int localStart = segStart - cur2.getGlobalStart(); + int localEnd = segEnd - cur2.getGlobalStart(); + String segText = cur2.getText() + .substring(localStart, Math.min(localEnd, cur2.getText().length())); + argWidth += ClientProxy.Font.width(segText); + } + remainingStart = cur2.getGlobalEnd(); + cur2 = cur2.nextOnLine(); + } + + if (argWidth > 0) { + ScriptLine.drawCurlyUnderline(argX, y, argWidth, getUnderlineColor()); + } + } + } + } // ==================== SETTERS ==================== public void setType(TokenType type) { this.type = type; }