diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/field/FieldInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/field/FieldInfo.java
index fd6890fb0..d10218d83 100644
--- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/field/FieldInfo.java
+++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/field/FieldInfo.java
@@ -1,5 +1,8 @@
package noppes.npcs.client.gui.util.script.interpreter.field;
+import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSFieldInfo;
+import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSTypeInfo;
+import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSTypeRegistry;
import noppes.npcs.client.gui.util.script.interpreter.token.TokenType;
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
import noppes.npcs.client.gui.util.script.interpreter.method.MethodCallInfo;
@@ -142,6 +145,75 @@ public final class FieldInfo {
return new FieldInfo(name, Scope.GLOBAL, type, -1, true, null, null, -1, -1, field.getModifiers(), field);
}
+
+ /**
+ * Create a FieldInfo from a JSFieldInfo (parsed from .d.ts files).
+ * Used when resolving field access on JavaScript types.
+ *
+ * @param jsField The JavaScript field info from the type registry
+ * @param containingType The TypeInfo that owns this field
+ * @return A FieldInfo representing the JavaScript field
+ */
+ public static FieldInfo fromJSField(JSFieldInfo jsField, TypeInfo containingType) {
+ String name = jsField.getName();
+
+ // Resolve the type from the JS type registry
+ TypeInfo type = resolveJSType(jsField.getType());
+
+ // JS fields are public by default, readonly maps to final
+ int modifiers = Modifier.PUBLIC;
+ if (jsField.isReadonly()) {
+ modifiers |= Modifier.FINAL;
+ }
+
+ // Use documentation if available
+ String documentation = jsField.getDocumentation();
+
+ return new FieldInfo(name, Scope.GLOBAL, type, -1, true, null, documentation, -1, -1, modifiers, null);
+ }
+
+ /**
+ * Resolves a JavaScript type name to a TypeInfo.
+ * Handles primitives, mapped types, and custom types from the registry.
+ */
+ private static TypeInfo resolveJSType(String jsTypeName) {
+ if (jsTypeName == null || jsTypeName.isEmpty() || "void".equals(jsTypeName)) {
+ return TypeInfo.fromPrimitive("void");
+ }
+
+ // Handle JS primitives
+ switch (jsTypeName) {
+ case "string":
+ return TypeInfo.fromClass(String.class);
+ case "number":
+ return TypeInfo.fromClass(double.class);
+ case "boolean":
+ return TypeInfo.fromClass(boolean.class);
+ case "any":
+ return TypeInfo.fromClass(Object.class);
+ case "void":
+ return TypeInfo.fromPrimitive("void");
+ }
+
+ // Handle array types
+ if (jsTypeName.endsWith("[]")) {
+ String elementType = jsTypeName.substring(0, jsTypeName.length() - 2);
+ TypeInfo elementTypeInfo = resolveJSType(elementType);
+ return TypeInfo.arrayOf(elementTypeInfo);
+ }
+
+ // Try to resolve from the JS type registry
+ JSTypeRegistry registry = JSTypeRegistry.getInstance();
+ if (registry != null) {
+ JSTypeInfo jsTypeInfo = registry.getType(jsTypeName);
+ if (jsTypeInfo != null) {
+ return TypeInfo.fromJSTypeInfo(jsTypeInfo);
+ }
+ }
+
+ // Fallback: unresolved type
+ return TypeInfo.unresolved(jsTypeName, jsTypeName);
+ }
/**
* Create a FieldInfo for an enum constant.
diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSHoverInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSHoverInfo.java
deleted file mode 100644
index edc2879ca..000000000
--- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSHoverInfo.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package noppes.npcs.client.gui.util.script.interpreter.js_parser;
-
-import java.util.List;
-
-/**
- * Container classes for hover information in JS scripts.
- */
-public class JSHoverInfo {
-
- /**
- * Hover info for a variable.
- */
- public static class VariableInfo {
- public final String name;
- public final String typeName;
- public final JSTypeInfo resolvedType;
-
- public VariableInfo(String name, String typeName, JSTypeInfo resolvedType) {
- this.name = name;
- this.typeName = typeName;
- this.resolvedType = resolvedType;
- }
-
- public String buildHoverHtml() {
- StringBuilder sb = new StringBuilder();
- sb.append("").append(name).append(": ").append(typeName).append("");
-
- if (resolvedType != null && resolvedType.getDocumentation() != null) {
- sb.append("
").append(resolvedType.getDocumentation());
- }
-
- return sb.toString();
- }
- }
-
- /**
- * Hover info for a method.
- */
- public static class MethodInfo {
- public final JSMethodInfo method;
- public final JSTypeInfo containingType;
-
- public MethodInfo(JSMethodInfo method, JSTypeInfo containingType) {
- this.method = method;
- this.containingType = containingType;
- }
-
- public String buildHoverHtml() {
- StringBuilder sb = new StringBuilder();
-
- // Show containing type
- if (containingType != null) {
- sb.append("").append(containingType.getFullName()).append("
");
- }
-
- // Show method signature
- sb.append(method.buildHoverInfo());
-
- return sb.toString();
- }
- }
-
- /**
- * Hover info for a field.
- */
- public static class FieldInfo {
- public final JSFieldInfo field;
- public final JSTypeInfo containingType;
-
- public FieldInfo(JSFieldInfo field, JSTypeInfo containingType) {
- this.field = field;
- this.containingType = containingType;
- }
-
- public String buildHoverHtml() {
- StringBuilder sb = new StringBuilder();
-
- // Show containing type
- if (containingType != null) {
- sb.append("").append(containingType.getFullName()).append("
");
- }
-
- // Show field info
- sb.append(field.buildHoverInfo());
-
- return sb.toString();
- }
- }
-
- /**
- * Hover info for a hook function.
- */
- public static class FunctionInfo {
- public final String name;
- public final List signatures;
-
- public FunctionInfo(String name, List signatures) {
- this.name = name;
- this.signatures = signatures;
- }
-
- public String buildHoverHtml() {
- StringBuilder sb = new StringBuilder();
- sb.append("Hook: ").append(name).append("");
-
- if (!signatures.isEmpty()) {
- sb.append("
Overloads:");
- for (JSTypeRegistry.HookSignature sig : signatures) {
- sb.append("
• function ").append(name).append("(")
- .append(sig.paramName).append(": ").append(sig.paramType).append(")");
- }
- }
-
- return sb.toString();
- }
- }
-}
diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSScriptAnalyzer.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSScriptAnalyzer.java
index 103ee677c..0f117c4d4 100644
--- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSScriptAnalyzer.java
+++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSScriptAnalyzer.java
@@ -2,7 +2,10 @@ package noppes.npcs.client.gui.util.script.interpreter.js_parser;
import noppes.npcs.client.gui.util.script.interpreter.ScriptDocument;
import noppes.npcs.client.gui.util.script.interpreter.ScriptLine;
+import noppes.npcs.client.gui.util.script.interpreter.field.FieldInfo;
+import noppes.npcs.client.gui.util.script.interpreter.method.MethodInfo;
import noppes.npcs.client.gui.util.script.interpreter.token.TokenType;
+import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
import scala.annotation.meta.field;
import java.util.*;
@@ -127,32 +130,48 @@ public class JSScriptAnalyzer {
// Check if this is a known hook
if (registry.isHook(funcName)) {
- marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_DECL,
- new JSHoverInfo.FunctionInfo(funcName, registry.getHookSignatures(funcName))));
-
- // Infer parameter types from hook
+ // Create a unified MethodInfo for the hook
List sigs = registry.getHookSignatures(funcName);
- if (!sigs.isEmpty() && !params.isEmpty()) {
- // Parse parameter names from function
- String[] paramNames = params.split(",");
- if (paramNames.length > 0) {
- String paramName = paramNames[0].trim();
- // Use first hook signature's type
- String paramType = sigs.get(0).paramType;
-
- // Store in function params and variable types
- Map funcParamMap = new HashMap<>();
- funcParamMap.put(paramName, paramType);
- functionParams.put(funcName, funcParamMap);
- variableTypes.put(paramName, paramType);
-
- // Mark the parameter
- int paramStart = m.start(2) + params.indexOf(paramName);
- int paramEnd = paramStart + paramName.length();
- JSTypeInfo typeInfo = registry.getType(paramType);
- marks.add(new ScriptLine.Mark(paramStart, paramEnd, TokenType.PARAMETER,
- new JSHoverInfo.VariableInfo(paramName, paramType, typeInfo)));
+ if (!sigs.isEmpty()) {
+ JSTypeRegistry.HookSignature sig = sigs.get(0);
+
+ // Create unified MethodInfo for hover info
+ TypeInfo paramTypeInfo = resolveJSType(sig.paramType);
+ List methodParams = new ArrayList<>();
+ methodParams.add(FieldInfo.reflectionParam(sig.paramName, paramTypeInfo));
+
+ MethodInfo hookMethod = MethodInfo.declaration(
+ funcName, null, TypeInfo.fromPrimitive("void"), methodParams,
+ nameStart, nameStart, nameStart, -1, -1, 0, sig.doc
+ );
+
+ marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_DECL, hookMethod));
+
+ // Infer parameter types from hook
+ if (!params.isEmpty()) {
+ // Parse parameter names from function
+ String[] paramNames = params.split(",");
+ if (paramNames.length > 0) {
+ String paramName = paramNames[0].trim();
+ String paramType = sig.paramType;
+
+ // Store in function params and variable types
+ Map funcParamMap = new HashMap<>();
+ funcParamMap.put(paramName, paramType);
+ functionParams.put(funcName, funcParamMap);
+ variableTypes.put(paramName, paramType);
+
+ // Mark the parameter with unified FieldInfo
+ int paramStart = m.start(2) + params.indexOf(paramName);
+ int paramEnd = paramStart + paramName.length();
+ FieldInfo paramFieldInfo = FieldInfo.parameter(
+ paramName, paramTypeInfo, paramStart, hookMethod
+ );
+ marks.add(new ScriptLine.Mark(paramStart, paramEnd, TokenType.PARAMETER, paramFieldInfo));
+ }
}
+ } else {
+ marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_DECL, funcName));
}
} else {
marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_DECL, funcName));
@@ -181,12 +200,12 @@ public class JSScriptAnalyzer {
variableTypes.put(varName, inferredType);
}
- // Mark variable declaration
+ // Mark variable declaration with unified FieldInfo
int varStart = m.start(1);
int varEnd = m.end(1);
- JSTypeInfo typeInfo = inferredType != null ? registry.getType(inferredType) : null;
- marks.add(new ScriptLine.Mark(varStart, varEnd, TokenType.LOCAL_FIELD,
- new JSHoverInfo.VariableInfo(varName, inferredType != null ? inferredType : "any", typeInfo)));
+ TypeInfo typeInfo = resolveJSType(inferredType != null ? inferredType : "any");
+ FieldInfo varFieldInfo = FieldInfo.localField(varName, typeInfo, varStart, null);
+ marks.add(new ScriptLine.Mark(varStart, varEnd, TokenType.LOCAL_FIELD, varFieldInfo));
}
}
@@ -270,9 +289,9 @@ public class JSScriptAnalyzer {
}
// Check if it's a field
- JSFieldInfo field = typeInfo.getField(member);
- if (field != null) {
- currentType = field.getType();
+ JSFieldInfo f = typeInfo.getField(member);
+ if (f != null) {
+ currentType = f.getType();
continue;
}
@@ -301,11 +320,11 @@ public class JSScriptAnalyzer {
int pos = m.start();
- // Mark receiver
+ // Mark receiver with unified FieldInfo
if (currentType != null) {
- JSTypeInfo typeInfo = registry.getType(currentType);
- marks.add(new ScriptLine.Mark(pos, pos + receiverName.length(), TokenType.LOCAL_FIELD,
- new JSHoverInfo.VariableInfo(receiverName, currentType, typeInfo)));
+ TypeInfo unifiedType = resolveJSType(currentType);
+ FieldInfo receiverField = FieldInfo.localField(receiverName, unifiedType, pos, null);
+ marks.add(new ScriptLine.Mark(pos, pos + receiverName.length(), TokenType.LOCAL_FIELD, receiverField));
}
pos += receiverName.length() + 1; // +1 for the dot
@@ -317,25 +336,29 @@ public class JSScriptAnalyzer {
int memberEnd = pos + member.length();
if (currentType != null) {
- JSTypeInfo typeInfo = registry.getType(currentType);
- if (typeInfo != null) {
+ JSTypeInfo jsTypeInfo = registry.getType(currentType);
+ if (jsTypeInfo != null) {
+ TypeInfo unifiedContainingType = TypeInfo.fromJSTypeInfo(jsTypeInfo);
+
// Check method first
- JSMethodInfo method = typeInfo.getMethod(member);
- if (method != null) {
- marks.add(new ScriptLine.Mark(memberStart, memberEnd, TokenType.METHOD_CALL,
- new JSHoverInfo.MethodInfo(method, typeInfo)));
- currentType = method.getReturnType();
+ JSMethodInfo jsMethod = jsTypeInfo.getMethod(member);
+ if (jsMethod != null) {
+ // Convert to unified MethodInfo
+ MethodInfo unifiedMethod = MethodInfo.fromJSMethod(jsMethod, unifiedContainingType);
+ marks.add(new ScriptLine.Mark(memberStart, memberEnd, TokenType.METHOD_CALL, unifiedMethod));
+ currentType = jsMethod.getReturnType();
} else {
// Check field
- JSFieldInfo field = typeInfo.getField(member);
- if (field != null) {
- marks.add(new ScriptLine.Mark(memberStart, memberEnd, TokenType.GLOBAL_FIELD,
- new JSHoverInfo.FieldInfo(field, typeInfo)));
- currentType = field.getType();
+ JSFieldInfo jsField = jsTypeInfo.getField(member);
+ if (jsField != null) {
+ // Convert to unified FieldInfo
+ FieldInfo unifiedField = FieldInfo.fromJSField(jsField, unifiedContainingType);
+ marks.add(new ScriptLine.Mark(memberStart, memberEnd, TokenType.GLOBAL_FIELD, unifiedField));
+ currentType = jsField.getType();
} else {
// Unknown member - mark as undefined
marks.add(new ScriptLine.Mark(memberStart, memberEnd, TokenType.UNDEFINED_VAR,
- "Unknown member '" + member + "' on type " + typeInfo.getFullName()));
+ "Unknown member '" + member + "' on type " + jsTypeInfo.getFullName()));
currentType = null;
}
}
@@ -364,9 +387,20 @@ public class JSScriptAnalyzer {
int nameEnd = m.end(1);
if (registry.isHook(callExpr)) {
- // It's a known hook being called
- marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_CALL,
- new JSHoverInfo.FunctionInfo(callExpr, registry.getHookSignatures(callExpr))));
+ // It's a known hook being called - create unified MethodInfo
+ List sigs = registry.getHookSignatures(callExpr);
+ if (!sigs.isEmpty()) {
+ JSTypeRegistry.HookSignature sig = sigs.get(0);
+ TypeInfo paramTypeInfo = resolveJSType(sig.paramType);
+ List methodParams = new ArrayList<>();
+ methodParams.add(FieldInfo.reflectionParam(sig.paramName, paramTypeInfo));
+
+ MethodInfo hookMethod = MethodInfo.declaration(
+ callExpr, null, TypeInfo.fromPrimitive("void"), methodParams,
+ nameStart, nameStart, nameStart, -1, -1, 0, sig.doc
+ );
+ marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_CALL, hookMethod));
+ }
}
}
// Chained calls are handled in markMemberAccesses
@@ -396,4 +430,46 @@ public class JSScriptAnalyzer {
public Map getVariableTypes() {
return new HashMap<>(variableTypes);
}
+
+ /**
+ * Resolves a JavaScript type name to a unified TypeInfo.
+ * Handles primitives, mapped types, and custom types from the registry.
+ */
+ private TypeInfo resolveJSType(String jsTypeName) {
+ if (jsTypeName == null || jsTypeName.isEmpty() || "void".equals(jsTypeName)) {
+ return TypeInfo.fromPrimitive("void");
+ }
+
+ // Handle JS primitives
+ switch (jsTypeName) {
+ case "string":
+ return TypeInfo.fromClass(String.class);
+ case "number":
+ return TypeInfo.fromClass(double.class);
+ case "boolean":
+ return TypeInfo.fromClass(boolean.class);
+ case "any":
+ return TypeInfo.fromClass(Object.class);
+ case "void":
+ return TypeInfo.fromPrimitive("void");
+ }
+
+ // Handle array types
+ if (jsTypeName.endsWith("[]")) {
+ String elementType = jsTypeName.substring(0, jsTypeName.length() - 2);
+ TypeInfo elementTypeInfo = resolveJSType(elementType);
+ return TypeInfo.arrayOf(elementTypeInfo);
+ }
+
+ // Try to resolve from the JS type registry
+ if (registry != null) {
+ JSTypeInfo jsTypeInfo = registry.getType(jsTypeName);
+ if (jsTypeInfo != null) {
+ return TypeInfo.fromJSTypeInfo(jsTypeInfo);
+ }
+ }
+
+ // Fallback: unresolved type
+ return TypeInfo.unresolved(jsTypeName, jsTypeName);
+ }
}
diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java
index e9bdf053f..741543fa2 100644
--- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java
+++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java
@@ -94,7 +94,7 @@ public class JSTypeRegistry {
sb.append(line).append("\n");
}
parser.parseDefinitionFile(sb.toString(), fileName);
- }
+ }
}
} catch (Exception e) {
// File might not exist, that's ok
diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/method/MethodInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/method/MethodInfo.java
index f39875b26..b5b1d9b7a 100644
--- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/method/MethodInfo.java
+++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/method/MethodInfo.java
@@ -2,6 +2,9 @@ package noppes.npcs.client.gui.util.script.interpreter.method;
import noppes.npcs.client.gui.util.script.interpreter.*;
import noppes.npcs.client.gui.util.script.interpreter.field.FieldInfo;
+import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSMethodInfo;
+import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSTypeInfo;
+import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSTypeRegistry;
import noppes.npcs.client.gui.util.script.interpreter.token.TokenType;
import noppes.npcs.client.gui.util.script.interpreter.type.TypeChecker;
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
@@ -160,6 +163,84 @@ public final class MethodInfo {
return new MethodInfo(name, returnType, containingType, params, -1, -1, -1, -1, -1, true, true, modifiers, null, null);
}
+ /**
+ * Create a MethodInfo from a JSMethodInfo (parsed from .d.ts files).
+ * Used when resolving method calls on JavaScript types.
+ *
+ * @param jsMethod The JavaScript method info from the type registry
+ * @param containingType The TypeInfo that owns this method
+ * @return A MethodInfo representing the JavaScript method
+ */
+ public static MethodInfo fromJSMethod(JSMethodInfo jsMethod, TypeInfo containingType) {
+ String name = jsMethod.getName();
+
+ // Resolve the return type from the JS type registry
+ TypeInfo returnType = resolveJSType(jsMethod.getReturnType());
+
+ // Convert JS parameters to FieldInfo
+ List params = new ArrayList<>();
+ List jsParams = jsMethod.getParameters();
+
+ for (JSMethodInfo.JSParameterInfo param : jsParams) {
+ String paramName = param.getName();
+ String paramTypeName = param.getType();
+ TypeInfo paramType = resolveJSType(paramTypeName);
+ params.add(FieldInfo.reflectionParam(paramName, paramType));
+ }
+
+ // JS methods are always public (no access modifiers in .d.ts)
+ int modifiers = Modifier.PUBLIC;
+
+ // Use the documentation from the method if available
+ String documentation = jsMethod.getDocumentation();
+
+ return new MethodInfo(name, returnType, containingType, params, -1, -1, -1, -1, -1, true, false, modifiers, documentation, null);
+ }
+
+ /**
+ * Resolves a JavaScript type name to a TypeInfo.
+ * Handles primitives, mapped types, and custom types from the registry.
+ */
+ private static TypeInfo resolveJSType(String jsTypeName) {
+ if (jsTypeName == null || jsTypeName.isEmpty() || "void".equals(jsTypeName)) {
+ return TypeInfo.fromPrimitive("void");
+ }
+
+ // Handle JS primitives
+ switch (jsTypeName) {
+ case "string":
+ return TypeInfo.fromClass(String.class);
+ case "number":
+ return TypeInfo.fromClass(double.class);
+ case "boolean":
+ return TypeInfo.fromClass(boolean.class);
+ case "any":
+ return TypeInfo.fromClass(Object.class);
+ case "void":
+ return TypeInfo.fromPrimitive("void");
+ }
+
+ // Handle array types
+ if (jsTypeName.endsWith("[]")) {
+ String elementType = jsTypeName.substring(0, jsTypeName.length() - 2);
+ TypeInfo elementTypeInfo = resolveJSType(elementType);
+ // For arrays, we create an array type representation
+ return TypeInfo.arrayOf(elementTypeInfo);
+ }
+
+ // Try to resolve from the JS type registry
+ JSTypeRegistry registry = JSTypeRegistry.getInstance();
+ if (registry != null) {
+ JSTypeInfo jsTypeInfo = registry.getType(jsTypeName);
+ if (jsTypeInfo != null) {
+ return TypeInfo.fromJSTypeInfo(jsTypeInfo);
+ }
+ }
+
+ // Fallback: unresolved type
+ return TypeInfo.unresolved(jsTypeName, jsTypeName);
+ }
+
// Getters
public String getName() { return name; }
public TypeInfo getReturnType() { return returnType; }
diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java
index c49a095bf..cf4dbfd11 100644
--- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java
+++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java
@@ -2,6 +2,9 @@ package noppes.npcs.client.gui.util.script.interpreter.type;
import noppes.npcs.client.gui.util.script.interpreter.field.EnumConstantInfo;
import noppes.npcs.client.gui.util.script.interpreter.field.FieldInfo;
+import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSFieldInfo;
+import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSMethodInfo;
+import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSTypeInfo;
import noppes.npcs.client.gui.util.script.interpreter.method.MethodInfo;
import noppes.npcs.client.gui.util.script.interpreter.token.TokenType;
@@ -11,7 +14,14 @@ import java.util.List;
/**
* Represents resolved type information for a class/interface/enum.
- * Base class for type metadata. Extended by ScriptTypeInfo for script-defined types.
+ *
+ * This is the unified type system that supports:
+ * - Java types (via Class> reflection)
+ * - Script-defined types (via ScriptTypeInfo subclass)
+ * - JavaScript/TypeScript types (via JSTypeInfo bridge)
+ *
+ * For JavaScript types, the jsTypeInfo field holds the parsed .d.ts data,
+ * and methods like hasMethod/getMethodInfo delegate to it.
*/
public class TypeInfo {
@@ -21,17 +31,31 @@ public class TypeInfo {
ENUM,
UNKNOWN
}
+
+ /**
+ * Singleton constant for the void type.
+ */
+ public static final TypeInfo VOID = fromPrimitive("void");
private final String simpleName; // e.g., "List", "ColorType"
private final String fullName; // e.g., "java.util.List", "kamkeel...IOverlay$ColorType"
private final String packageName; // e.g., "java.util", "kamkeel.npcdbc.api.client.overlay"
private final Kind kind; // CLASS, INTERFACE, ENUM
- private final Class> javaClass; // The actual resolved Java class (null if unresolved)
+ private final Class> javaClass; // The actual resolved Java class (null if unresolved or JS type)
private final boolean resolved; // Whether this type was successfully resolved
private final TypeInfo enclosingType; // For inner classes, the outer type (null if top-level)
+
+ // JavaScript/TypeScript type info (for types from .d.ts files)
+ private final JSTypeInfo jsTypeInfo; // The JS type info (null if Java type)
private TypeInfo(String simpleName, String fullName, String packageName,
Kind kind, Class> javaClass, boolean resolved, TypeInfo enclosingType) {
+ this(simpleName, fullName, packageName, kind, javaClass, resolved, enclosingType, null);
+ }
+
+ private TypeInfo(String simpleName, String fullName, String packageName,
+ Kind kind, Class> javaClass, boolean resolved, TypeInfo enclosingType,
+ JSTypeInfo jsTypeInfo) {
this.simpleName = simpleName;
this.fullName = fullName;
this.packageName = packageName;
@@ -39,6 +63,7 @@ public class TypeInfo {
this.javaClass = javaClass;
this.resolved = resolved;
this.enclosingType = enclosingType;
+ this.jsTypeInfo = jsTypeInfo;
}
// Protected constructor for subclasses (like ScriptTypeInfo)
@@ -52,6 +77,7 @@ public class TypeInfo {
this.javaClass = javaClass;
this.resolved = resolved;
this.enclosingType = enclosingType;
+ this.jsTypeInfo = null;
}
// Factory methods
@@ -122,6 +148,67 @@ public class TypeInfo {
}
return new TypeInfo(typeName, typeName, "", Kind.CLASS, primitiveClass, true, null);
}
+
+ /**
+ * Create a TypeInfo from a JavaScript/TypeScript type.
+ * This bridges JS types parsed from .d.ts files into the unified type system.
+ *
+ * @param jsType The parsed JS type info
+ * @return A TypeInfo wrapping the JS type
+ */
+ public static TypeInfo fromJSTypeInfo(JSTypeInfo jsType) {
+ if (jsType == null) return null;
+
+ String simpleName = jsType.getSimpleName();
+ String fullName = jsType.getFullName();
+ String namespace = jsType.getNamespace();
+
+ // JS interfaces are always Kind.INTERFACE
+ return new TypeInfo(simpleName, fullName, namespace != null ? namespace : "",
+ Kind.INTERFACE, null, true, null, jsType);
+ }
+
+ /**
+ * Create an array type wrapping the given element type.
+ *
+ * @param elementType The type of elements in the array
+ * @return A TypeInfo representing the array type
+ */
+ public static TypeInfo arrayOf(TypeInfo elementType) {
+ if (elementType == null) {
+ return fromClass(Object[].class);
+ }
+
+ String simpleName = elementType.getSimpleName() + "[]";
+ String fullName = elementType.getFullName() + "[]";
+ String pkg = elementType.getPackageName();
+
+ // Try to get the actual array class if we have a Java class
+ Class> arrayClass = null;
+ if (elementType.getJavaClass() != null) {
+ try {
+ arrayClass = java.lang.reflect.Array.newInstance(elementType.getJavaClass(), 0).getClass();
+ } catch (Exception e) {
+ // Fallback to Object array if we can't create the specific array type
+ }
+ }
+
+ return new TypeInfo(simpleName, fullName, pkg, Kind.CLASS, arrayClass, true, null);
+ }
+
+ /**
+ * Check if this is a JavaScript type (backed by JSTypeInfo).
+ */
+ public boolean isJSType() {
+ return jsTypeInfo != null;
+ }
+
+ /**
+ * Get the underlying JSTypeInfo if this is a JS type.
+ */
+ public JSTypeInfo getJSTypeInfo() {
+ return jsTypeInfo;
+ }
// Getters
public String getSimpleName() { return simpleName; }
@@ -160,6 +247,11 @@ public class TypeInfo {
* Check if this type has a method with the given name.
*/
public boolean hasMethod(String methodName) {
+ // Check JS type first
+ if (jsTypeInfo != null) {
+ return jsTypeInfo.hasMethod(methodName);
+ }
+
if (javaClass == null) return false;
try {
for (java.lang.reflect.Method m : javaClass.getMethods()) {
@@ -177,6 +269,17 @@ public class TypeInfo {
* Check if this type has a method with the given name and parameter count.
*/
public boolean hasMethod(String methodName, int paramCount) {
+ // Check JS type first
+ if (jsTypeInfo != null) {
+ List overloads = jsTypeInfo.getMethodOverloads(methodName);
+ for (JSMethodInfo m : overloads) {
+ if (m.getParameterCount() == paramCount) {
+ return true;
+ }
+ }
+ return false;
+ }
+
if (javaClass == null) return false;
try {
for (java.lang.reflect.Method m : javaClass.getMethods()) {
@@ -274,6 +377,11 @@ public class TypeInfo {
* Check if this type has a field with the given name.
*/
public boolean hasField(String fieldName) {
+ // Check JS type first
+ if (jsTypeInfo != null) {
+ return jsTypeInfo.hasField(fieldName);
+ }
+
if (javaClass == null) return false;
try {
for (java.lang.reflect.Field f : javaClass.getFields()) {
@@ -320,9 +428,18 @@ public class TypeInfo {
/**
* Get MethodInfo for a method by name. Returns null if not found.
- * Creates a synthetic MethodInfo based on reflection data.
+ * Creates a synthetic MethodInfo based on reflection data or JS type data.
*/
public MethodInfo getMethodInfo(String methodName) {
+ // Check JS type first
+ if (jsTypeInfo != null) {
+ JSMethodInfo jsMethod = jsTypeInfo.getMethod(methodName);
+ if (jsMethod != null) {
+ return MethodInfo.fromJSMethod(jsMethod, this);
+ }
+ return null;
+ }
+
if (javaClass == null) return null;
try {
for (java.lang.reflect.Method m : javaClass.getMethods()) {
@@ -343,6 +460,15 @@ public class TypeInfo {
*/
public java.util.List getAllMethodOverloads(String methodName) {
java.util.List overloads = new java.util.ArrayList<>();
+
+ // Check JS type first
+ if (jsTypeInfo != null) {
+ for (JSMethodInfo jsMethod : jsTypeInfo.getMethodOverloads(methodName)) {
+ overloads.add(MethodInfo.fromJSMethod(jsMethod, this));
+ }
+ return overloads;
+ }
+
if (javaClass == null) return overloads;
try {
for (java.lang.reflect.Method m : javaClass.getMethods()) {
@@ -507,9 +633,18 @@ public class TypeInfo {
/**
* Get FieldInfo for a field by name. Returns null if not found.
- * Creates a synthetic FieldInfo based on reflection data.
+ * Creates a synthetic FieldInfo based on reflection data or JS type data.
*/
public FieldInfo getFieldInfo(String fieldName) {
+ // Check JS type first
+ if (jsTypeInfo != null) {
+ JSFieldInfo jsField = jsTypeInfo.getField(fieldName);
+ if (jsField != null) {
+ return FieldInfo.fromJSField(jsField, this);
+ }
+ return null;
+ }
+
if (javaClass == null) return null;
try {
for (java.lang.reflect.Field f : javaClass.getFields()) {