From dfc0232e995e32c674e53eaa75ca0d7bb678b001 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Sun, 28 Dec 2025 00:19:00 +0200 Subject: [PATCH] Got classes defined in script fully functional --- .../script/interpreter/ScriptDocument.java | 70 ++++++++++++++----- .../script/interpreter/ScriptTypeInfo.java | 13 ++-- .../interpreter/hover/TokenHoverInfo.java | 38 ++++++++++ 3 files changed, 100 insertions(+), 21 deletions(-) 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 f44f8c9d6..b3b6bd21e 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 @@ -336,9 +336,11 @@ public class ScriptDocument { * Creates ScriptTypeInfo instances and stores them for later resolution. */ private void parseScriptTypes() { - // Pattern: (class|interface|enum) ClassName { ... } + // Pattern: [modifiers] (class|interface|enum) ClassName [optional ()] { ... } + // Matches optional modifiers (public, private, static, final, abstract) before class/interface/enum + // Also allows optional () after the class name (common mistake) Pattern typeDecl = Pattern.compile( - "\\b(class|interface|enum)\\s+([A-Za-z_][a-zA-Z0-9_]*)\\s*(?:extends\\s+[A-Za-z_][a-zA-Z0-9_.]*)?\\s*(?:implements\\s+[A-Za-z_][a-zA-Z0-9_.,\\s]*)?\\s*\\{"); + "(?:(?:public|private|protected|static|final|abstract)\\s+)*(class|interface|enum)\\s+([A-Za-z_][a-zA-Z0-9_]*)\\s*(?:\\(\\))?\\s*(?:extends\\s+[A-Za-z_][a-zA-Z0-9_.]*)?\\s*(?:implements\\s+[A-Za-z_][a-zA-Z0-9_.,\\s]*)?\\s*\\{"); Matcher m = typeDecl.matcher(text); while (m.find()) { @@ -361,8 +363,12 @@ public class ScriptDocument { default: kind = TypeInfo.Kind.CLASS; break; } + // Extract modifiers from the matched text + String fullMatch = text.substring(m.start(), m.end()); + int modifiers = parseModifiers(fullMatch); + ScriptTypeInfo scriptType = ScriptTypeInfo.create( - typeName, kind, m.start(), bodyStart, bodyEnd); + typeName, kind, m.start(), bodyStart, bodyEnd, modifiers); // Parse fields and methods inside this type parseScriptTypeMembers(scriptType); @@ -1697,22 +1703,30 @@ public class ScriptDocument { } else { if (isScriptMethod(methodName)) { resolvedMethod = getScriptMethodInfo(methodName); - MethodCallInfo callInfo = new MethodCallInfo( - methodName, nameStart, nameEnd, openParen, closeParen, - arguments, null, resolvedMethod - ); - // Only set expected type if this is the final expression (not followed by .field or .method) - if (!isFollowedByDot(closeParen)) { - TypeInfo expectedType = findExpectedTypeAtPosition(nameStart); - if (expectedType != null) { - callInfo.setExpectedType(expectedType); + // Check if this is a method from a script type + // Instance methods from script types cannot be called without a receiver + if (resolvedMethod != null && isMethodFromScriptType(resolvedMethod) && !resolvedMethod.isStatic()) { + // Instance method called without receiver - mark as undefined + marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.UNDEFINED_VAR)); + } else { + MethodCallInfo callInfo = new MethodCallInfo( + methodName, nameStart, nameEnd, openParen, closeParen, + arguments, null, resolvedMethod + ); + + // Only set expected type if this is the final expression (not followed by .field or .method) + if (!isFollowedByDot(closeParen)) { + TypeInfo expectedType = findExpectedTypeAtPosition(nameStart); + if (expectedType != null) { + callInfo.setExpectedType(expectedType); + } } + + callInfo.validate(); + methodCalls.add(callInfo); + marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_CALL, callInfo)); } - - callInfo.validate(); - methodCalls.add(callInfo); - marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_CALL, callInfo)); } else { marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.UNDEFINED_VAR)); } @@ -2476,6 +2490,30 @@ public class ScriptDocument { } return null; } + + /** + * Check if a method belongs to a script-defined type (class/interface/enum). + * Returns true if the method is defined inside a script type. + */ + private boolean isMethodFromScriptType(MethodInfo method) { + if (method == null || !method.isDeclaration()) { + return false; + } + + int declPos = method.getDeclarationOffset(); + if (declPos < 0) { + return false; + } + + // Check if the method's declaration position is inside any script type's body + for (ScriptTypeInfo scriptType : scriptTypes.values()) { + if (scriptType.containsPosition(declPos)) { + return true; + } + } + + return false; + } private void markVariables(List marks) { Set knownKeywords = new HashSet<>(Arrays.asList( diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptTypeInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptTypeInfo.java index b0ab251a8..7c7f47619 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptTypeInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptTypeInfo.java @@ -18,6 +18,7 @@ public class ScriptTypeInfo extends TypeInfo { private final int declarationOffset; private final int bodyStart; private final int bodyEnd; + private final int modifiers; // Java reflection modifiers (public, static, final, etc.) // Script-defined members private final Map fields = new HashMap<>(); @@ -28,24 +29,25 @@ public class ScriptTypeInfo extends TypeInfo { private ScriptTypeInfo outerClass; private ScriptTypeInfo(String simpleName, String fullName, Kind kind, - int declarationOffset, int bodyStart, int bodyEnd) { + int declarationOffset, int bodyStart, int bodyEnd, int modifiers) { super(simpleName, fullName, "", kind, null, true, null, true); this.scriptClassName = simpleName; this.declarationOffset = declarationOffset; this.bodyStart = bodyStart; this.bodyEnd = bodyEnd; + this.modifiers = modifiers; } // Factory method public static ScriptTypeInfo create(String simpleName, Kind kind, - int declarationOffset, int bodyStart, int bodyEnd) { - return new ScriptTypeInfo(simpleName, simpleName, kind, declarationOffset, bodyStart, bodyEnd); + int declarationOffset, int bodyStart, int bodyEnd, int modifiers) { + return new ScriptTypeInfo(simpleName, simpleName, kind, declarationOffset, bodyStart, bodyEnd, modifiers); } public static ScriptTypeInfo createInner(String simpleName, Kind kind, ScriptTypeInfo outer, - int declarationOffset, int bodyStart, int bodyEnd) { + int declarationOffset, int bodyStart, int bodyEnd, int modifiers) { String fullName = outer.getFullName() + "$" + simpleName; - ScriptTypeInfo inner = new ScriptTypeInfo(simpleName, fullName, kind, declarationOffset, bodyStart, bodyEnd); + ScriptTypeInfo inner = new ScriptTypeInfo(simpleName, fullName, kind, declarationOffset, bodyStart, bodyEnd, modifiers); inner.outerClass = outer; outer.innerClasses.add(inner); return inner; @@ -56,6 +58,7 @@ public class ScriptTypeInfo extends TypeInfo { public int getDeclarationOffset() { return declarationOffset; } public int getBodyStart() { return bodyStart; } public int getBodyEnd() { return bodyEnd; } + public int getModifiers() { return modifiers; } public ScriptTypeInfo getOuterClass() { return outerClass; } public List getInnerClasses() { return innerClasses; } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/hover/TokenHoverInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/hover/TokenHoverInfo.java index fba3f9b43..6fa3efc93 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/hover/TokenHoverInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/hover/TokenHoverInfo.java @@ -351,6 +351,44 @@ public class TokenHoverInfo { addSegment(", ...", TokenType.DEFAULT.getHexColor()); } } + } else if (typeInfo instanceof ScriptTypeInfo) { + // Script-defined type + ScriptTypeInfo scriptType = (ScriptTypeInfo) typeInfo; + + // Icon + if (scriptType.getKind() == TypeInfo.Kind.INTERFACE) { + iconIndicator = "I"; + } else if (scriptType.getKind() == TypeInfo.Kind.ENUM) { + iconIndicator = "E"; + } else { + iconIndicator = "C"; + } + + // Build declaration with modifiers + int mods = scriptType.getModifiers(); + + // Modifiers + if (Modifier.isPublic(mods)) addSegment("public ", TokenType.MODIFIER.getHexColor()); + if (Modifier.isAbstract(mods) && scriptType.getKind() != TypeInfo.Kind.INTERFACE) + addSegment("abstract ", TokenType.MODIFIER.getHexColor()); + if (Modifier.isFinal(mods)) addSegment("final ", TokenType.MODIFIER.getHexColor()); + if (Modifier.isStatic(mods)) addSegment("static ", TokenType.MODIFIER.getHexColor()); + + // Class type keyword + if (scriptType.getKind() == TypeInfo.Kind.INTERFACE) { + addSegment("interface ", TokenType.MODIFIER.getHexColor()); + } else if (scriptType.getKind() == TypeInfo.Kind.ENUM) { + addSegment("enum ", TokenType.MODIFIER.getHexColor()); + } else { + addSegment("class ", TokenType.MODIFIER.getHexColor()); + } + + // Class name + int classColor = scriptType.getKind() == TypeInfo.Kind.INTERFACE ? TokenType.INTERFACE_DECL.getHexColor() + : scriptType.getKind() == TypeInfo.Kind.ENUM ? TokenType.ENUM_DECL.getHexColor() + : TokenType.IMPORTED_CLASS.getHexColor(); + addSegment(typeInfo.getSimpleName(), classColor); + } else { // Unresolved type iconIndicator = "?";