Stored MethodCallInfo and FieldCallInfo properly for method call args, and underlined them when errored for 2 error types; wrong method call args count and wrong arg type

This commit is contained in:
bigguy345 committed 2025-12-24 20:10:10 +02:00
1 parent 9cb4c96eb3
commit d9f16fef8a
5 files changed
+83 -44

No files matched your search

@@ -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;
}
}
}
@@ -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).
*/
@@ -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<Integer, Map<String, FieldInfo>> methodLocals = new HashMap<>();
// Method calls - stores all parsed method call information
private final List<MethodCallInfo> methodCalls = new ArrayList<>();
// Excluded regions (strings/comments) - positions where other patterns shouldn't match
private final List<int[]> 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));
}
}
}
@@ -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<int[]> 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();
@@ -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 ====================