diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/FieldInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/FieldInfo.java index dc8f0da89..6fd9ff0e1 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/FieldInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/FieldInfo.java @@ -125,4 +125,18 @@ public final class FieldInfo { public int hashCode() { return name.hashCode() * 31 + scope.ordinal(); } + + /** + * Wrapper class that holds both FieldInfo and MethodCallInfo for a variable usage. + * This is used when a variable is used as an argument to a method call. + */ + public static class ArgInfo { + public final FieldInfo fieldInfo; + public final MethodCallInfo methodCallInfo; + + public ArgInfo(FieldInfo fieldInfo, MethodCallInfo methodCallInfo) { + this.fieldInfo = fieldInfo; + this.methodCallInfo = methodCallInfo; + } + } } 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 79b7198a5..a4b16eba1 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,6 +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)) { @@ -211,6 +212,10 @@ public class MethodCallInfo { return false; } + public boolean hasArgCountError(Token t) { + return hasArgCountError() && t.isMethodCall(); + } + /** * Check if this is a static access error (underline method name). */ diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java index e5ecc117a..83a9bff89 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java @@ -1,6 +1,5 @@ package noppes.npcs.client.gui.util.script.interpreter; -import net.minecraft.client.Minecraft; import noppes.npcs.client.ClientProxy; import java.util.*; @@ -74,6 +73,9 @@ public class ScriptDocument { // Local variables per method (methodStartOffset -> {varName -> FieldInfo}) private final Map> methodLocals = new HashMap<>(); + // Method calls - stores all parsed method call information + private final List methodCalls = new ArrayList<>(); + // Excluded regions (strings/comments) - positions where other patterns shouldn't match private final List excludedRanges = new ArrayList<>(); @@ -180,6 +182,7 @@ public class ScriptDocument { excludedRanges.clear(); methodLocals.clear(); scriptTypes.clear(); + methodCalls.clear(); // Phase 1: Find excluded regions (strings/comments) findExcludedRanges(); @@ -858,13 +861,14 @@ public class ScriptDocument { // Numbers addPatternMarks(marks, NUMBER_PATTERN, TokenType.NUMBER); + // Method calls (parse before variables so we can attach context) + markMethodCalls(marks); + // Variables and fields markVariables(marks); // Chained field accesses (e.g., mc.player.world, this.field) markChainedFieldAccesses(marks); - - markMethodCalls(marks); // Imported class usages markImportedClassUsages(marks); @@ -1321,6 +1325,9 @@ public class ScriptDocument { arguments, receiverType, resolvedMethod, isStaticAccess ); callInfo.validate(); + + // Store in collection for later lookup + methodCalls.add(callInfo); // Always mark method call as green (METHOD_CALL), with error info attached for underline // The MethodCallInfo is attached so rendering can draw curly underline if there's an error @@ -1349,6 +1356,9 @@ public class ScriptDocument { arguments, null, resolvedMethod ); callInfo.validate(); + + // Store in collection for later lookup + methodCalls.add(callInfo); // Always mark method call as green (METHOD_CALL), with error info attached for underline marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_CALL, callInfo)); @@ -1422,6 +1432,25 @@ public class ScriptDocument { return !ident.isEmpty() && Character.isUpperCase(ident.charAt(0)); } + /** + * Find the MethodCallInfo that contains the given position as an argument. + * Returns null if the position is not inside any method call's argument list. + */ + private MethodCallInfo findMethodCallContainingPosition(int position) { + for (MethodCallInfo call : methodCalls) { + // Check if position is within the argument list (between open and close parens) + if (position >= call.getOpenParenOffset() && position <= call.getCloseParenOffset()) { + // Check if it's within any of the arguments + for (MethodCallInfo.Argument arg : call.getArguments()) { + if (position >= arg.getStartOffset() && position <= arg.getEndOffset()) { + return call; + } + } + } + } + return null; + } + /** * Find the matching closing parenthesis for an opening parenthesis. * Handles nested parentheses, strings, and comments. @@ -1951,6 +1980,8 @@ public class ScriptDocument { // Find containing method MethodInfo containingMethod = findMethodAtPosition(position); + // Check if this variable is an argument to a method call + MethodCallInfo callInfo = findMethodCallContainingPosition(position); // For uppercase identifiers, only process if it's a known field // Otherwise, let type handling (markImportedClassUsages) handle it @@ -1970,15 +2001,17 @@ public class ScriptDocument { FieldInfo localInfo = locals.get(name); // Only highlight if the position is after the declaration if (localInfo.isVisibleAt(position)) { - marks.add(new ScriptLine.Mark(m.start(1), m.end(1), TokenType.LOCAL_FIELD, localInfo)); + Object metadata = callInfo != null ? new FieldInfo.ArgInfo(localInfo, callInfo) : localInfo; + marks.add(new ScriptLine.Mark(m.start(1), m.end(1), TokenType.LOCAL_FIELD, metadata)); continue; } } - + } else { // Check global fields if (globalFields.containsKey(name)) { FieldInfo fieldInfo = globalFields.get(name); - marks.add(new ScriptLine.Mark(m.start(1), m.end(1), TokenType.GLOBAL_FIELD, fieldInfo)); + Object metadata = callInfo != null ? new FieldInfo.ArgInfo(fieldInfo, callInfo) : fieldInfo; + marks.add(new ScriptLine.Mark(m.start(1), m.end(1), TokenType.GLOBAL_FIELD, metadata)); continue; } @@ -1987,22 +2020,7 @@ public class ScriptDocument { continue; // Unknown variable inside method - mark as undefined - marks.add(new ScriptLine.Mark(m.start(1), m.end(1), TokenType.UNDEFINED_VAR)); - } else { - // Outside any method - // Check global fields - if (globalFields.containsKey(name)) { - FieldInfo fieldInfo = globalFields.get(name); - marks.add(new ScriptLine.Mark(m.start(1), m.end(1), TokenType.GLOBAL_FIELD, fieldInfo)); - continue; - } - - // Skip uppercase if not a known field - type handling will deal with it - if (isUppercase) - continue; - - // Unknown variable outside method - mark as undefined - marks.add(new ScriptLine.Mark(m.start(1), m.end(1), TokenType.UNDEFINED_VAR)); + marks.add(new ScriptLine.Mark(m.start(1), m.end(1), TokenType.UNDEFINED_VAR, callInfo)); } } } 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 f2268e242..a95d0a6b1 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 @@ -1,6 +1,5 @@ package noppes.npcs.client.gui.util.script.interpreter; -import net.minecraft.client.gui.Gui; import noppes.npcs.client.ClientProxy; import org.lwjgl.opengl.GL11; @@ -144,6 +143,10 @@ public class ScriptLine { if (mark.metadata != null) { if (mark.metadata instanceof TypeInfo) { token.setTypeInfo((TypeInfo) mark.metadata); + } else if (mark.metadata instanceof FieldInfo.ArgInfo) { + FieldInfo.ArgInfo ctx = (FieldInfo.ArgInfo) mark.metadata; + token.setFieldInfo(ctx.fieldInfo); + token.setMethodCallInfo(ctx.methodCallInfo); } else if (mark.metadata instanceof FieldInfo) { token.setFieldInfo((FieldInfo) mark.metadata); } else if (mark.metadata instanceof MethodInfo) { @@ -206,7 +209,6 @@ public class ScriptLine { int lastIndex = 0; // Track positions for underlines - List underlines = new ArrayList<>(); // [startX, endX, color] int currentX = x; for (Token t : tokens) { @@ -222,18 +224,10 @@ public class ScriptLine { // Track underline position if token has one if (t.hasUnderline()) { int tokenWidth = ClientProxy.Font.width(t.getText()); - underlines.add(new int[]{currentX, currentX + tokenWidth, t.getUnderlineColor()}); + int currentY = y + ClientProxy.Font.height() - 1; + drawCurlyUnderline(currentX + 1, currentY, tokenWidth + 1, t.getUnderlineColor()); } - if (t.getMethodCallInfo() != null) { - MethodCallInfo callInfo = t.getMethodCallInfo(); - if (callInfo.hasArgTypeError(t)) { - underlines.add( - new int[]{currentX, currentX + ClientProxy.Font.width(t.getText()), t.getUnderlineColor()}); - } - } - - // Append the colored token builder.append(COLOR_CHAR) .append(t.getColorCode()) @@ -252,11 +246,6 @@ public class ScriptLine { // Draw the text ClientProxy.Font.drawString(builder.toString(), x, y, defaultColor); - - // Draw curly underlines for error tokens - for (int[] ul : underlines) { - drawCurlyUnderline(ul[0], y + ClientProxy.Font.height() - 1, ul[1] - ul[0], ul[2]); - } } /** @@ -288,12 +277,12 @@ public class ScriptLine { GL11.glBegin(GL11.GL_LINE_STRIP); // Wave parameters: 2 pixels amplitude, 4 pixels wavelength - int waveHeight = 2; + int waveHeight = 1; int waveLength = 4; for (int i = 0; i <= width; i++) { // Create a sine-like wave pattern double phase = (double) i / waveLength * Math.PI * 2; - int yOffset = (int) (Math.sin(phase) * waveHeight); + float yOffset = (float) (Math.sin(phase) * waveHeight) - 0.5f; GL11.glVertex2f(x + i, y + yOffset); } GL11.glEnd(); 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 de8ed2a66..7c6851d69 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 @@ -26,7 +26,7 @@ public class Token { // Rendering flags private boolean hasUnderline; // True if this token should be underlined (for errors) - private int underlineColor; // Color of the underline (if any) + private int underlineColor = 0xFF5555; // Color of the underline (if any) // Navigation - set by ScriptLine private Token prev; @@ -119,8 +119,21 @@ public class Token { public MethodCallInfo getMethodCallInfo() { return methodCallInfo; } public ImportData getImportData() { return importData; } public ScriptLine getParentLine() { return parentLine; } - public boolean hasUnderline() { return hasUnderline; } - public int getUnderlineColor() { return underlineColor; } + + public boolean hasUnderline() { + if (methodCallInfo != null) { + if (methodCallInfo.hasArgCountError(this)) + return true; + else if (methodCallInfo.hasArgTypeError(this)) { + return true; + } + } + return false; + } + + public int getUnderlineColor() { + return underlineColor; + } // ==================== SETTERS ====================