mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-14 02:34:10 +00:00
Bridged over JSDocs/param names for fields/methods of java reflected classes that have a matching JS type twin loaded from .d.ts files
This commit is contained in:
1 parent
64f8ca657c
commit
3f6972f9f6
4 files changed
+228
-5
No files matched your search
+167
@@ -0,0 +1,167 @@
|
||||
package noppes.npcs.client.gui.util.script.interpreter.bridge;
|
||||
|
||||
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.js_parser.JSTypeRegistry;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeResolver;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
public final class DtsJavaBridge {
|
||||
|
||||
private DtsJavaBridge() {}
|
||||
|
||||
public static JSMethodInfo findMatchingMethod(Method method, TypeInfo containingType) {
|
||||
JSTypeInfo jsType = findJSTypeInfo(method, containingType);
|
||||
if (jsType == null) return null;
|
||||
|
||||
List<JSMethodInfo> overloads = jsType.getMethodOverloads(method.getName());
|
||||
if (overloads.isEmpty()) return null;
|
||||
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
JSMethodInfo best = null;
|
||||
int bestScore = -1;
|
||||
|
||||
for (JSMethodInfo candidate : overloads) {
|
||||
if (candidate.getParameterCount() != paramTypes.length) continue;
|
||||
int score = scoreOverload(candidate, paramTypes, containingType);
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
best = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return bestScore >= 0 ? best : null;
|
||||
}
|
||||
|
||||
public static JSFieldInfo findMatchingField(Field field, TypeInfo containingType) {
|
||||
JSTypeInfo jsType = findJSTypeInfo(field, containingType);
|
||||
if (jsType == null) return null;
|
||||
return jsType.getField(field.getName());
|
||||
}
|
||||
|
||||
private static int scoreOverload(JSMethodInfo method, Class<?>[] paramTypes, TypeInfo containingType) {
|
||||
int score = 0;
|
||||
List<JSMethodInfo.JSParameterInfo> jsParams = method.getParameters();
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
int paramScore = scoreParam(paramTypes[i], jsParams.get(i), containingType);
|
||||
if (paramScore < 0) return -1;
|
||||
score += paramScore;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
private static int scoreParam(Class<?> javaParam, JSMethodInfo.JSParameterInfo jsParam, TypeInfo containingType) {
|
||||
if (javaParam == null || jsParam == null) return -1;
|
||||
|
||||
TypeInfo resolved = jsParam.getResolvedType(containingType);
|
||||
if (resolved != null && resolved.getJavaClass() != null) {
|
||||
Class<?> jsClass = resolved.getJavaClass();
|
||||
if (javaParam.equals(jsClass)) return 4;
|
||||
if (jsClass.isAssignableFrom(javaParam)) return 3;
|
||||
if (javaParam.isAssignableFrom(jsClass)) return 2;
|
||||
}
|
||||
|
||||
String jsTypeName = jsParam.getType();
|
||||
if (jsTypeName == null || jsTypeName.isEmpty()) return -1;
|
||||
|
||||
if (matchesPrimitive(jsTypeName, javaParam)) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (javaParam.isArray() && jsTypeName.endsWith("[]")) {
|
||||
String elementType = jsTypeName.substring(0, jsTypeName.length() - 2);
|
||||
return matchesArrayElement(javaParam.getComponentType(), elementType, containingType) ? 2 : -1;
|
||||
}
|
||||
|
||||
JSTypeInfo jsType = resolveJSTypeInfo(jsTypeName);
|
||||
if (jsType != null && jsType.getJavaFqn() != null) {
|
||||
String javaFqn = normalizeJavaFqn(jsType.getJavaFqn());
|
||||
String paramFqn = normalizeJavaFqn(javaParam.getName());
|
||||
if (javaFqn.equals(paramFqn)) return 3;
|
||||
}
|
||||
|
||||
if (jsTypeName.equals(javaParam.getSimpleName())) return 1;
|
||||
if (jsTypeName.equals(javaParam.getName())) return 1;
|
||||
if ("any".equals(jsTypeName)) return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static boolean matchesArrayElement(Class<?> elementClass, String jsElementType, TypeInfo containingType) {
|
||||
if (elementClass == null) return false;
|
||||
if (matchesPrimitive(jsElementType, elementClass)) return true;
|
||||
|
||||
JSTypeInfo jsType = resolveJSTypeInfo(jsElementType);
|
||||
if (jsType != null && jsType.getJavaFqn() != null) {
|
||||
String javaFqn = normalizeJavaFqn(jsType.getJavaFqn());
|
||||
String elementFqn = normalizeJavaFqn(elementClass.getName());
|
||||
return javaFqn.equals(elementFqn);
|
||||
}
|
||||
|
||||
return jsElementType.equals(elementClass.getSimpleName()) || jsElementType.equals(elementClass.getName());
|
||||
}
|
||||
|
||||
private static boolean matchesPrimitive(String jsTypeName, Class<?> javaParam) {
|
||||
switch (jsTypeName) {
|
||||
case "string":
|
||||
return javaParam == String.class || javaParam == char.class || javaParam == Character.class;
|
||||
case "boolean":
|
||||
return javaParam == boolean.class || javaParam == Boolean.class;
|
||||
case "number":
|
||||
return isNumberType(javaParam);
|
||||
case "void":
|
||||
return javaParam == void.class || javaParam == Void.class;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isNumberType(Class<?> type) {
|
||||
if (type == null) return false;
|
||||
return type == byte.class || type == short.class || type == int.class || type == long.class
|
||||
|| type == float.class || type == double.class
|
||||
|| Number.class.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
private static JSTypeInfo findJSTypeInfo(Method method, TypeInfo containingType) {
|
||||
if (containingType != null && containingType.isJSType()) {
|
||||
return containingType.getJSTypeInfo();
|
||||
}
|
||||
|
||||
Class<?> javaClass = containingType != null ? containingType.getJavaClass() : method.getDeclaringClass();
|
||||
return resolveJSTypeInfo(javaClass != null ? javaClass.getName() : null);
|
||||
}
|
||||
|
||||
private static JSTypeInfo findJSTypeInfo(Field field, TypeInfo containingType) {
|
||||
if (containingType != null && containingType.isJSType()) {
|
||||
return containingType.getJSTypeInfo();
|
||||
}
|
||||
|
||||
Class<?> javaClass = containingType != null ? containingType.getJavaClass() : field.getDeclaringClass();
|
||||
return resolveJSTypeInfo(javaClass != null ? javaClass.getName() : null);
|
||||
}
|
||||
|
||||
private static JSTypeInfo resolveJSTypeInfo(String javaFqnOrType) {
|
||||
if (javaFqnOrType == null || javaFqnOrType.isEmpty()) return null;
|
||||
JSTypeRegistry registry = TypeResolver.getInstance().getJSTypeRegistry();
|
||||
JSTypeInfo direct = registry.getTypeByJavaFqn(javaFqnOrType);
|
||||
if (direct != null) return direct;
|
||||
|
||||
String normalized = normalizeJavaFqn(javaFqnOrType);
|
||||
if (!normalized.equals(javaFqnOrType)) {
|
||||
JSTypeInfo normalizedType = registry.getTypeByJavaFqn(normalized);
|
||||
if (normalizedType != null) return normalizedType;
|
||||
}
|
||||
|
||||
return registry.getType(javaFqnOrType);
|
||||
}
|
||||
|
||||
private static String normalizeJavaFqn(String javaFqn) {
|
||||
if (javaFqn == null) return null;
|
||||
return javaFqn.replace('$', '.');
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ 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.bridge.DtsJavaBridge;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.jsdoc.JSDocInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.token.TokenType;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
|
||||
@@ -158,7 +159,21 @@ public final class FieldInfo {
|
||||
return constantInfo.getFieldInfo();
|
||||
}
|
||||
|
||||
return new FieldInfo(name, Scope.GLOBAL, type, -1, true, null, null, -1, -1, field.getModifiers(), field);
|
||||
// Try to find matching JSFieldInfo to bridge over documentation
|
||||
JSFieldInfo jsField = DtsJavaBridge.findMatchingField(field, containingType);
|
||||
String documentation = null;
|
||||
JSDocInfo jsDocInfo = null;
|
||||
if (jsField != null) {
|
||||
jsDocInfo = jsField.getJsDocInfo();
|
||||
String jsDocDesc = jsDocInfo != null ? jsDocInfo.getDescription() : null;
|
||||
documentation = jsDocDesc != null ? jsDocDesc : jsField.getDocumentation();
|
||||
}
|
||||
|
||||
FieldInfo fieldInfo = new FieldInfo(name, Scope.GLOBAL, type, -1, true, null, documentation, -1, -1, field.getModifiers(), field);
|
||||
if (jsDocInfo != null) {
|
||||
fieldInfo.setJSDocInfo(jsDocInfo);
|
||||
}
|
||||
return fieldInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+18
@@ -736,6 +736,10 @@ public class TokenHoverInfo {
|
||||
}
|
||||
|
||||
// Try to get actual Java method for more details
|
||||
if (methodInfo != null && shouldPreferMethodInfo(methodInfo)) {
|
||||
buildBasicMethodDeclaration(methodInfo, containingType);
|
||||
return;
|
||||
}
|
||||
if (methodInfo != null && methodInfo.getJavaMethod() != null) {
|
||||
buildMethodDeclaration(methodInfo.getJavaMethod(), containingType);
|
||||
extractJavadoc(methodInfo.getJavaMethod());
|
||||
@@ -1110,6 +1114,20 @@ public class TokenHoverInfo {
|
||||
|
||||
}
|
||||
|
||||
private boolean shouldPreferMethodInfo(MethodInfo methodInfo) {
|
||||
if (methodInfo.getJSDocInfo() != null) return true;
|
||||
String doc = methodInfo.getDocumentation();
|
||||
if (doc != null && !doc.isEmpty()) return true;
|
||||
|
||||
for (FieldInfo param : methodInfo.getParameters()) {
|
||||
String name = param.getName();
|
||||
if (name != null && !name.matches("arg\\d+")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format JSDoc information in IntelliJ-style with "Params:" and "Returns:" sections.
|
||||
* Creates colored documentation lines with parameter names highlighted.
|
||||
|
||||
+27
-4
@@ -2,6 +2,7 @@ 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.bridge.DtsJavaBridge;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.jsdoc.JSDocInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSMethodInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSTypeInfo;
|
||||
@@ -144,15 +145,37 @@ public final class MethodInfo {
|
||||
String name = method.getName();
|
||||
TypeInfo returnType = TypeInfo.fromClass(method.getReturnType());
|
||||
int modifiers = method.getModifiers();
|
||||
|
||||
|
||||
// Try to find matching JSFieldInfo to bridge parameter names and docs
|
||||
JSMethodInfo jsMethod = DtsJavaBridge.findMatchingMethod(method, containingType);
|
||||
List<FieldInfo> params = new ArrayList<>();
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
List<JSMethodInfo.JSParameterInfo> jsParams = jsMethod != null ? jsMethod.getParameters() : null;
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
TypeInfo paramType = TypeInfo.fromClass(paramTypes[i]);
|
||||
params.add(FieldInfo.reflectionParam("arg" + i, paramType));
|
||||
String paramName = "arg" + i;
|
||||
if (jsParams != null && i < jsParams.size()) {
|
||||
String jsName = jsParams.get(i).getName();
|
||||
if (jsName != null && !jsName.isEmpty()) {
|
||||
paramName = jsName;
|
||||
}
|
||||
}
|
||||
params.add(FieldInfo.reflectionParam(paramName, paramType));
|
||||
}
|
||||
|
||||
return new MethodInfo(name, returnType, containingType, params, -1, -1, -1, -1, -1, true, false, modifiers, null, method);
|
||||
|
||||
String documentation = null;
|
||||
JSDocInfo jsDocInfo = null;
|
||||
if (jsMethod != null) {
|
||||
jsDocInfo = jsMethod.getJsDocInfo();
|
||||
String jsDocDesc = jsDocInfo != null ? jsDocInfo.getDescription() : null;
|
||||
documentation = jsDocDesc != null ? jsDocDesc : jsMethod.getDocumentation();
|
||||
}
|
||||
|
||||
MethodInfo methodInfo = new MethodInfo(name, returnType, containingType, params, -1, -1, -1, -1, -1, true, false, modifiers, documentation, method);
|
||||
if (jsDocInfo != null) {
|
||||
methodInfo.setJSDocInfo(jsDocInfo);
|
||||
}
|
||||
return methodInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user