diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeInfo.java index 8bdab8493..0926ceafe 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeInfo.java @@ -18,6 +18,9 @@ public class JSTypeInfo { private final String simpleName; // e.g., "InteractEvent" private final String fullName; // e.g., "IPlayerEvent.InteractEvent" private final String namespace; // e.g., "IPlayerEvent" (parent namespace, null for top-level) + + // Java fully-qualified name (e.g., "noppes.npcs.api.entity.IPlayer") + private String javaFqn; // Type parameters (generics) private final List typeParams = new ArrayList<>(); @@ -53,6 +56,11 @@ public class JSTypeInfo { this.jsDocInfo = jsDocInfo; return this; } + + public JSTypeInfo setJavaFqn(String javaFqn) { + this.javaFqn = javaFqn; + return this; + } public void addMethod(JSMethodInfo method) { method.setContainingType(this); @@ -88,6 +96,7 @@ public class JSTypeInfo { public String getSimpleName() { return simpleName; } public String getFullName() { return fullName; } public String getNamespace() { return namespace; } + public String getJavaFqn() { return javaFqn; } public String getExtendsType() { return extendsType; } public JSTypeInfo getResolvedParent() { return resolvedParent; } public JSDocInfo getJsDocInfo() { return jsDocInfo; } 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 89624c1d4..7a4514a62 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 @@ -23,6 +23,9 @@ public class JSTypeRegistry { // All registered types by full name (e.g., "IPlayerEvent.InteractEvent") private final Map types = new LinkedHashMap<>(); + + // Registered types by Java fully-qualified name (e.g., "noppes.npcs.api.entity.IPlayer") + private final Map typesByJavaFqn = new LinkedHashMap<>(); // Type aliases (simple name -> full type name) private final Map typeAliases = new HashMap<>(); @@ -278,6 +281,17 @@ public class JSTypeRegistry { */ public void registerType(JSTypeInfo type) { types.put(type.getFullName(), type); + if (type.getJavaFqn() != null && !type.getJavaFqn().isEmpty()) { + typesByJavaFqn.put(type.getJavaFqn(), type); + } + } + + /** + * Get a type by Java fully-qualified name. + */ + public JSTypeInfo getTypeByJavaFqn(String javaFqn) { + if (javaFqn == null || javaFqn.isEmpty()) return null; + return typesByJavaFqn.get(javaFqn); } /** diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java index 32b832ba9..70f1e2d7b 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java @@ -1,6 +1,7 @@ package noppes.npcs.client.gui.util.script.interpreter.js_parser; import noppes.npcs.client.gui.util.script.interpreter.jsdoc.JSDocInfo; +import noppes.npcs.client.gui.util.script.interpreter.jsdoc.JSDocTag; import java.io.*; import java.util.*; @@ -130,9 +131,10 @@ public class TypeScriptDefinitionParser { parseIndexFile(content); return; } - + // Parse regular interface files - parseInterfaceFile(content, null); + String packageName = derivePackageName(fileName); + parseInterfaceFile(content, null, packageName); } /** @@ -208,7 +210,7 @@ public class TypeScriptDefinitionParser { /** * Parse interface and class definitions from content. */ - private void parseInterfaceFile(String content, String parentNamespace) { + private void parseInterfaceFile(String content, String parentNamespace, String packageName) { // Find exported interfaces Matcher interfaceMatcher = INTERFACE_PATTERN.matcher(content); while (interfaceMatcher.find()) { @@ -222,6 +224,11 @@ public class TypeScriptDefinitionParser { if (jsDoc != null) { typeInfo.setJsDocInfo(jsDoc); } + + String javaFqn = findJavaFqn(jsDoc, packageName, typeInfo.getFullName()); + if (javaFqn != null && !javaFqn.isEmpty()) { + typeInfo.setJavaFqn(javaFqn); + } // Parse type parameters if (typeParamsStr != null && !typeParamsStr.isEmpty()) { @@ -253,7 +260,7 @@ public class TypeScriptDefinitionParser { // Parse nested interfaces and type aliases within this interface body String fullNamespace = parentNamespace != null ? parentNamespace + "." + interfaceName : interfaceName; - parseNestedTypes(body, fullNamespace); + parseNestedTypes(body, fullNamespace, packageName); } registry.registerType(typeInfo); @@ -354,7 +361,7 @@ public class TypeScriptDefinitionParser { // Namespaces contain exported types, so use parseInterfaceFile String fullNamespace = parentNamespace != null ? parentNamespace + "." + namespaceName : namespaceName; - parseInterfaceFile(body, fullNamespace); + parseInterfaceFile(body, fullNamespace, packageName); // Don't call parseNestedTypes here - namespace members are all exported // and will be caught by parseInterfaceFile's INTERFACE_PATTERN } @@ -393,7 +400,7 @@ public class TypeScriptDefinitionParser { /** * Parse nested types (interfaces and type aliases) within a parent type or namespace. */ - private void parseNestedTypes(String content, String namespace) { + private void parseNestedTypes(String content, String namespace, String packageName) { // Parse nested interfaces Matcher nestedInterfaceMatcher = NESTED_INTERFACE_PATTERN.matcher(content); while (nestedInterfaceMatcher.find()) { @@ -402,6 +409,16 @@ public class TypeScriptDefinitionParser { String extendsClause = nestedInterfaceMatcher.group(3); JSTypeInfo typeInfo = new JSTypeInfo(interfaceName, namespace); + + JSDocInfo jsDoc = extractJSDocBefore(content, nestedInterfaceMatcher.start()); + if (jsDoc != null) { + typeInfo.setJsDocInfo(jsDoc); + } + + String javaFqn = findJavaFqn(jsDoc, packageName, typeInfo.getFullName()); + if (javaFqn != null && !javaFqn.isEmpty()) { + typeInfo.setJavaFqn(javaFqn); + } // Parse type parameters if (typeParamsStr != null && !typeParamsStr.isEmpty()) { @@ -432,6 +449,49 @@ public class TypeScriptDefinitionParser { // Parse type aliases within this context parseTypeAliases(content, namespace); } + + private String findJavaFqn(JSDocInfo jsDoc, String packageName, String typeFullName) { + String tagged = extractJavaFqnFromJSDoc(jsDoc); + if (tagged != null && !tagged.isEmpty()) { + return tagged; + } + return buildJavaFqnFromPackage(packageName, typeFullName); + } + + private String extractJavaFqnFromJSDoc(JSDocInfo jsDoc) { + if (jsDoc == null) return null; + for (JSDocTag tag : jsDoc.getAllTags()) { + if (tag == null) continue; + if ("javaFqn".equals(tag.getTagName())) { + if (tag.getDescription() != null && !tag.getDescription().trim().isEmpty()) { + return tag.getDescription().trim(); + } + if (tag.getTypeName() != null && !tag.getTypeName().trim().isEmpty()) { + return tag.getTypeName().trim(); + } + } + } + return null; + } + + private String buildJavaFqnFromPackage(String packageName, String typeFullName) { + if (typeFullName == null || typeFullName.isEmpty()) return null; + if (packageName == null || packageName.isEmpty()) return null; + return packageName + "." + typeFullName; + } + + private String derivePackageName(String fileName) { + if (fileName == null || fileName.isEmpty()) return null; + String normalized = fileName.replace('\\', '/'); + if (normalized.endsWith(".d.ts")) { + normalized = normalized.substring(0, normalized.length() - 5); + } + int lastSlash = normalized.lastIndexOf('/'); + if (lastSlash < 0) return null; + String pkgPath = normalized.substring(0, lastSlash); + if (pkgPath.isEmpty()) return null; + return pkgPath.replace('/', '.'); + } /** * Parse type aliases (export type X = Y).