From bbe6d9bb77aa1d309a415090b140761c909d7204 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Sun, 4 Jan 2026 22:41:00 +0200 Subject: [PATCH] JSAutoCompleteProvier revamp: -Type resolution now happens directly in ScriptDocument -Extends JavaAutocompleteProvider -Removed redundant code/logic --- .../autocomplete/AutocompleteManager.java | 5 +- .../autocomplete/JSAutocompleteProvider.java | 298 +++++++----------- .../JavaAutocompleteProvider.java | 30 +- 3 files changed, 124 insertions(+), 209 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/AutocompleteManager.java b/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/AutocompleteManager.java index 47c05c286..4abfa173a 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/AutocompleteManager.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/AutocompleteManager.java @@ -390,10 +390,7 @@ public class AutocompleteManager { // Get suggestions from appropriate provider AutocompleteProvider provider = document.isJavaScript() ? jsProvider : javaProvider; - // For JavaScript, update variable types from analyzer - if (document.isJavaScript() && document.getJSAnalyzer() != null) { - jsProvider.updateVariableTypes(document.getJSAnalyzer().getVariableTypes()); - } + // No need to update variable types - JSAutocompleteProvider now gets them directly from ScriptDocument List suggestions = provider.getSuggestions(context); diff --git a/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JSAutocompleteProvider.java b/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JSAutocompleteProvider.java index 3fb105d7a..bf44aedab 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JSAutocompleteProvider.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JSAutocompleteProvider.java @@ -1,22 +1,21 @@ package noppes.npcs.client.gui.util.script.autocomplete; import noppes.npcs.client.gui.util.script.interpreter.ScriptDocument; +import noppes.npcs.client.gui.util.script.interpreter.field.FieldInfo; import noppes.npcs.client.gui.util.script.interpreter.js_parser.*; +import noppes.npcs.client.gui.util.script.interpreter.method.MethodInfo; +import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo; import java.util.*; /** * Autocomplete provider for JavaScript/ECMAScript scripts. - * Uses JSTypeRegistry for type resolution. + * Uses ScriptDocument's unified TypeInfo/TypeResolver system for type resolution. */ -public class JSAutocompleteProvider implements AutocompleteProvider { +public class JSAutocompleteProvider extends JavaAutocompleteProvider { - private ScriptDocument document; private final JSTypeRegistry registry; - // Variable types inferred from the document - private Map variableTypes = new HashMap<>(); - public JSAutocompleteProvider() { this.registry = JSTypeRegistry.getInstance(); if (!registry.isInitialized()) { @@ -24,17 +23,6 @@ public class JSAutocompleteProvider implements AutocompleteProvider { } } - public void setDocument(ScriptDocument document) { - this.document = document; - } - - /** - * Update the variable type map from document analysis. - */ - public void updateVariableTypes(Map types) { - this.variableTypes = types != null ? new HashMap<>(types) : new HashMap<>(); - } - @Override public boolean canProvide(Context context) { return document != null && document.isJavaScript(); @@ -43,13 +31,13 @@ public class JSAutocompleteProvider implements AutocompleteProvider { @Override public List getSuggestions(Context context) { List items = new ArrayList<>(); - - // Resolve owner type for usage tracking + + // Resolve owner type for usage tracking using ScriptDocument's unified type resolution String ownerFullName = null; if (context.isMemberAccess && context.receiverExpression != null) { - String receiverType = resolveReceiverType(context.receiverExpression); - if (receiverType != null) { - ownerFullName = receiverType; + TypeInfo receiverType = document.resolveExpressionType(context.receiverExpression, context.prefixStart); + if (receiverType != null && receiverType.isResolved()) { + ownerFullName = receiverType.getFullName(); } } @@ -62,7 +50,7 @@ public class JSAutocompleteProvider implements AutocompleteProvider { } // Filter and score by prefix, then apply usage boosts - filterAndScore(items, context.prefix, context.isMemberAccess, ownerFullName); + filterAndScore(items, context.prefix, context.isMemberAccess, false, ownerFullName); // Sort by score Collections.sort(items); @@ -77,35 +65,75 @@ public class JSAutocompleteProvider implements AutocompleteProvider { /** * Add suggestions for member access (after dot). + * Uses ScriptDocument's unified type resolution system. */ - private void addMemberSuggestions(Context context, List items) { + protected void addMemberSuggestions(Context context, List items) { String receiverExpr = context.receiverExpression; if (receiverExpr == null || receiverExpr.isEmpty()) { return; } - - // Infer type of receiver - String receiverType = inferExpressionType(receiverExpr); - if (receiverType == null) { + + // Use ScriptDocument's resolveExpressionType - handles both Java and JS + TypeInfo receiverType = document.resolveExpressionType(receiverExpr, context.prefixStart); + if (receiverType == null || !receiverType.isResolved()) { return; } - - JSTypeInfo typeInfo = registry.getType(receiverType); - if (typeInfo == null) { + + // For JS types, check JSTypeRegistry + JSTypeInfo jsTypeInfo = registry.getType(receiverType.getSimpleName()); + if (jsTypeInfo != null) { + // Add methods + addMethodsFromType(jsTypeInfo, items, new HashSet<>()); + // Add fields + addFieldsFromType(jsTypeInfo, items, new HashSet<>()); return; } - - // Add methods - addMethodsFromType(typeInfo, items, new HashSet<>()); - - // Add fields - addFieldsFromType(typeInfo, items, new HashSet<>()); + + // For Java types with Java class, use reflection to get members + Class javaClass = receiverType.getJavaClass(); + if (javaClass != null) { + try { + // Add methods from Java reflection + for (java.lang.reflect.Method method : javaClass.getMethods()) { + String methodName = method.getName(); + StringBuilder signature = new StringBuilder(); + signature.append(methodName).append("("); + Class[] params = method.getParameterTypes(); + for (int i = 0; i < params.length; i++) { + if (i > 0) + signature.append(", "); + signature.append(params[i].getSimpleName()); + } + signature.append(")"); + + items.add(new AutocompleteItem.Builder() + .name(methodName) + .insertText(methodName) + .kind(AutocompleteItem.Kind.METHOD) + .typeLabel(method.getReturnType().getSimpleName()) + .signature(signature.toString()) + .build()); + } + + // Add fields from Java reflection + for (java.lang.reflect.Field field : javaClass.getFields()) { + items.add(new AutocompleteItem.Builder() + .name(field.getName()) + .insertText(field.getName()) + .kind(AutocompleteItem.Kind.FIELD) + .typeLabel(field.getType().getSimpleName()) + .build()); + } + } catch (SecurityException e) { + // Can't access members, skip + } + } } /** * Recursively add methods from a type and its parents. */ - private void addMethodsFromType(JSTypeInfo type, List items, Set added) { + protected void addMethodsFromType(JSTypeInfo type, List items, Set added) { for (JSMethodInfo method : type.getMethods().values()) { String name = method.getName(); // Skip overload markers (name$1, name$2, etc.) @@ -127,7 +155,7 @@ public class JSAutocompleteProvider implements AutocompleteProvider { /** * Recursively add fields from a type and its parents. */ - private void addFieldsFromType(JSTypeInfo type, List items, Set added) { + protected void addFieldsFromType(JSTypeInfo type, List items, Set added) { for (JSFieldInfo field : type.getFields().values()) { if (!added.contains(field.getName())) { added.add(field.getName()); @@ -143,131 +171,55 @@ public class JSAutocompleteProvider implements AutocompleteProvider { /** * Add suggestions based on current scope (not after a dot). + * Uses ScriptDocument's unified data structures for variables and functions. */ - private void addScopeSuggestions(Context context, List items) { + protected void addScopeSuggestions(Context context, List items) { + + int pos = context.cursorPosition; + + // Find containing method + MethodInfo containingMethod = document.findContainingMethod(pos); + // Add local variables - for (Map.Entry entry : variableTypes.entrySet()) { - String varName = entry.getKey(); - String varType = entry.getValue(); + if (containingMethod != null) { + Map locals = document.getLocalsForMethod(containingMethod); + if (locals != null) { + for (FieldInfo local : locals.values()) { + if (local.isVisibleAt(pos)) { + items.add(AutocompleteItem.fromField(local)); + } + } + } + + // Add method parameters + for (FieldInfo param : containingMethod.getParameters()) { + items.add(AutocompleteItem.fromField(param)); + } + } + + // Add global fields + for (FieldInfo globalField : document.getGlobalFields().values()) { + if (globalField.isVisibleAt(pos)) { + items.add(AutocompleteItem.fromField(globalField)); + } + } + + // Add script-defined methods + for (MethodInfo method : document.getAllMethods()) { + items.add(AutocompleteItem.fromMethod(method)); - items.add(new AutocompleteItem.Builder() - .name(varName) - .insertText(varName) - .kind(AutocompleteItem.Kind.VARIABLE) - .typeLabel(varType != null ? varType : "any") - .build()); - } - - // Add hook functions - for (String hookName : registry.getHookNames()) { - List sigs = registry.getHookSignatures(hookName); - if (!sigs.isEmpty()) { - JSTypeRegistry.HookSignature sig = sigs.get(0); - items.add(new AutocompleteItem.Builder() - .name(hookName) - .insertText(hookName) - .kind(AutocompleteItem.Kind.METHOD) - .typeLabel("hook") - .signature("function " + hookName + "(e: " + sig.paramType + ")") - .documentation(sig.doc) - .build()); - } - } - - // Add global types - for (String typeName : registry.getTypeNames()) { - JSTypeInfo type = registry.getType(typeName); - if (type != null) { - items.add(new AutocompleteItem.Builder() - .name(typeName) - .insertText(typeName) - .kind(AutocompleteItem.Kind.CLASS) - .typeLabel("interface") - .build()); - } } // Add JavaScript keywords - addJSKeywords(items); + addKeywords(items); } - /** - * Infer the type of an expression. - */ - private String inferExpressionType(String expr) { - return resolveReceiverType(expr); - } - - /** - * Resolve the type of a receiver expression for member access. - */ - private String resolveReceiverType(String expr) { - if (expr == null || expr.isEmpty()) { - return null; - } - - expr = expr.trim(); - - // Check if it's a known variable - if (variableTypes.containsKey(expr)) { - return variableTypes.get(expr); - } - - // Handle method chain: x.y.z() -> resolve step by step - if (expr.contains(".")) { - String[] parts = expr.split("\\."); - String currentType = null; - - // Start with the first part - if (variableTypes.containsKey(parts[0])) { - currentType = variableTypes.get(parts[0]); - } - - if (currentType == null) { - return null; - } - - // Walk through the chain - for (int i = 1; i < parts.length; i++) { - String member = parts[i]; - // Remove () for method calls - if (member.endsWith("()")) { - member = member.substring(0, member.length() - 2); - } - - JSTypeInfo typeInfo = registry.getType(currentType); - if (typeInfo == null) { - return null; - } - - // Check method - JSMethodInfo method = typeInfo.getMethod(member); - if (method != null) { - currentType = method.getReturnType(); - continue; - } - - // Check field - JSFieldInfo field = typeInfo.getField(member); - if (field != null) { - currentType = field.getType(); - continue; - } - - // Unknown member - return null; - } - - return currentType; - } - - return null; - } + /** * Add JavaScript keywords. */ - private void addJSKeywords(List items) { + protected void addKeywords(List items) { String[] keywords = { "function", "var", "let", "const", "if", "else", "for", "while", "do", "switch", "case", "break", "continue", "return", "try", "catch", "finally", @@ -280,44 +232,8 @@ public class JSAutocompleteProvider implements AutocompleteProvider { items.add(AutocompleteItem.keyword(keyword)); } } - - /** - * Filter items by prefix, calculate match scores, and apply usage boosts. - */ - private void filterAndScore(List items, String prefix, - boolean isMemberAccess, String ownerFullName) { - UsageTracker tracker = UsageTracker.getJSInstance(); - - if (prefix == null || prefix.isEmpty()) { - for (AutocompleteItem item : items) { - item.calculateMatchScore(""); - applyUsageBoost(item, tracker, ownerFullName); - } - return; - } - - // For non-member access (first word), require strict prefix matching - // For member access (after dot), allow fuzzy/contains matching - boolean requirePrefix = !isMemberAccess; - - Iterator iter = items.iterator(); - while (iter.hasNext()) { - AutocompleteItem item = iter.next(); - int score = item.calculateMatchScore(prefix, requirePrefix); - if (score < 0) { - iter.remove(); - } else { - applyUsageBoost(item, tracker, ownerFullName); - } - } - } - - /** - * Apply usage-based score boost to an item. - */ - private void applyUsageBoost(AutocompleteItem item, UsageTracker tracker, String ownerFullName) { - int usageCount = tracker.getUsageCount(item, ownerFullName); - int boost = UsageTracker.calculateUsageBoost(usageCount); - item.addScoreBoost(boost); + + protected UsageTracker getUsageTracker() { + return UsageTracker.getJSInstance(); } } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JavaAutocompleteProvider.java b/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JavaAutocompleteProvider.java index d4f59926c..c0a897403 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JavaAutocompleteProvider.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JavaAutocompleteProvider.java @@ -19,7 +19,7 @@ import java.util.*; */ public class JavaAutocompleteProvider implements AutocompleteProvider { - private ScriptDocument document; + protected ScriptDocument document; public void setDocument(ScriptDocument document) { this.document = document; @@ -71,7 +71,7 @@ public class JavaAutocompleteProvider implements AutocompleteProvider { /** * Add suggestions for member access (after dot). */ - private void addMemberSuggestions(Context context, List items) { + protected void addMemberSuggestions(Context context, List items) { String receiverExpr = context.receiverExpression; if (receiverExpr == null || receiverExpr.isEmpty()) { return; @@ -132,7 +132,7 @@ public class JavaAutocompleteProvider implements AutocompleteProvider { /** * Add members from a script-defined type. */ - private void addScriptTypeMembers(ScriptTypeInfo scriptType, List items, boolean isStaticContext) { + protected void addScriptTypeMembers(ScriptTypeInfo scriptType, List items, boolean isStaticContext) { // Add methods (getMethods returns Map>) for (List overloads : scriptType.getMethods().values()) { for (MethodInfo method : overloads) { @@ -181,7 +181,7 @@ public class JavaAutocompleteProvider implements AutocompleteProvider { /** * Add suggestions based on current scope (not after a dot). */ - private void addScopeSuggestions(Context context, List items) { + protected void addScopeSuggestions(Context context, List items) { int pos = context.cursorPosition; // Find containing method @@ -256,7 +256,7 @@ public class JavaAutocompleteProvider implements AutocompleteProvider { * Add suggestions for unimported classes that match the prefix. * These will trigger auto-import when selected. */ - private void addUnimportedClassSuggestions(String prefix, List items) { + protected void addUnimportedClassSuggestions(String prefix, List items) { // Get the type resolver TypeResolver resolver = TypeResolver.getInstance(); // Find classes matching this prefix (not just exact matches) @@ -312,7 +312,7 @@ public class JavaAutocompleteProvider implements AutocompleteProvider { /** * Add Java keywords. */ - private void addKeywords(List items) { + protected void addKeywords(List items) { String[] keywords = { "if", "else", "for", "while", "do", "switch", "case", "break", "continue", "return", "try", "catch", "finally", "throw", "throws", "new", "this", "super", @@ -331,7 +331,7 @@ public class JavaAutocompleteProvider implements AutocompleteProvider { * Find the enclosing script type at a position. * This is a workaround since findEnclosingScriptType is package-private. */ - private ScriptTypeInfo findEnclosingType(int position) { + protected ScriptTypeInfo findEnclosingType(int position) { for (ScriptTypeInfo type : document.getScriptTypesMap().values()) { if (type.containsPosition(position)) { return type; @@ -344,18 +344,20 @@ public class JavaAutocompleteProvider implements AutocompleteProvider { * Check if the receiver expression represents static access (class type). * Similar logic to FieldChainMarker.isStaticContext(). */ - private boolean isStaticAccess(String receiverExpr, int position) { + protected boolean isStaticAccess(String receiverExpr, int position) { // Try to resolve the receiver expression as a type TypeInfo typeCheck = document.resolveType(receiverExpr); return typeCheck != null && typeCheck.isResolved(); } - + protected UsageTracker getUsageTracker() { + return UsageTracker.getJavaInstance(); + } /** * Filter items by prefix, calculate match scores, apply usage boosts, and penalize static members in instance contexts. */ - private void filterAndScore(List items, String prefix, + protected void filterAndScore(List items, String prefix, boolean isMemberAccess, boolean isStaticContext, String ownerFullName) { - UsageTracker tracker = UsageTracker.getJavaInstance(); + UsageTracker tracker = getUsageTracker(); if (prefix == null || prefix.isEmpty()) { // No filtering needed, all items get a base score + usage boost @@ -390,7 +392,7 @@ public class JavaAutocompleteProvider implements AutocompleteProvider { /** * Apply usage-based score boost to an item. */ - private void applyUsageBoost(AutocompleteItem item, UsageTracker tracker, String ownerFullName) { + protected void applyUsageBoost(AutocompleteItem item, UsageTracker tracker, String ownerFullName) { int usageCount = tracker.getUsageCount(item, ownerFullName); int boost = UsageTracker.calculateUsageBoost(usageCount); item.addScoreBoost(boost); @@ -402,7 +404,7 @@ public class JavaAutocompleteProvider implements AutocompleteProvider { * accessing through an instance (e.g., Minecraft.getMinecraft().getMinecraft()). * However, if the item is a very strong match (exact prefix), don't penalize as much. */ - private void applyStaticPenalty(AutocompleteItem item, boolean isMemberAccess, boolean isStaticContext) { + protected void applyStaticPenalty(AutocompleteItem item, boolean isMemberAccess, boolean isStaticContext) { // Only apply penalty in member access contexts (after dot) if (!isMemberAccess) { return; @@ -434,7 +436,7 @@ public class JavaAutocompleteProvider implements AutocompleteProvider { * Apply penalty to inherited Object methods to push them to bottom. * Strong matches get lighter penalty, weak matches get pushed all the way down. */ - private void applyObjectMethodPenalty(AutocompleteItem item) { + protected void applyObjectMethodPenalty(AutocompleteItem item) { if (!item.isInheritedObjectMethod()) { return; }